diff --git a/.gitignore b/.gitignore index 38292b880..a5c9c7e66 100644 --- a/.gitignore +++ b/.gitignore @@ -59,8 +59,8 @@ 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/z3consts.py -src/api/python/z3core.py +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 diff --git a/CMakeLists.txt b/CMakeLists.txt index ee0810295..1ca94689e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,9 +32,10 @@ project(Z3 C CXX) # Project version ################################################################################ set(Z3_VERSION_MAJOR 4) -set(Z3_VERSION_MINOR 4) -set(Z3_VERSION_PATCH 2) +set(Z3_VERSION_MINOR 5) +set(Z3_VERSION_PATCH 1) set(Z3_VERSION_TWEAK 0) +set(Z3_FULL_VERSION 0) set(Z3_VERSION "${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}.${Z3_VERSION_PATCH}.${Z3_VERSION_TWEAK}") message(STATUS "Z3 version ${Z3_VERSION}") @@ -246,7 +247,7 @@ endif() # FP math ################################################################################ # FIXME: Support ARM "-mfpu=vfp -mfloat-abi=hard" -if ("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") +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")) set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2") # FIXME: Remove "x.." when CMP0054 is set to NEW @@ -304,7 +305,10 @@ endif() # library. If not building a shared library ``-fPIC`` isn't needed and would add # unnecessary overhead. if (BUILD_LIBZ3_SHARED) - if (NOT MSVC) + # 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() @@ -387,4 +391,3 @@ option(ENABLE_EXAMPLE_TARGETS "Build Z3 api examples" ON) if (ENABLE_EXAMPLE_TARGETS) add_subdirectory(examples) endif() - diff --git a/README.md b/README.md index 3985fad27..e0821ac79 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ Z3 can be built using [Visual Studio][1], a [Makefile][2] or using [CMake][3]. I See the [release notes](RELEASE_NOTES) for notes on various stable releases of Z3. +## Build status + +| Windows x86 | Windows x64 | Ubuntu x64 | Ubuntu x86 | Debian x64 | OSX | +| ----------- | ----------- | ---------- | ---------- | ---------- | --- | +![win32-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/4/badge) | ![win64-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/7/badge) | ![ubuntu-x64-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/3/badge) | ![ubuntu-x86-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/6/badge) | ![debian-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/5/badge) | ![osx-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/2/badge) + [1]: #building-z3-on-windows-using-visual-studio-command-prompt [2]: #building-z3-using-make-and-gccclang [3]: #building-z3-using-cmake @@ -56,6 +62,16 @@ 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 diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 6746c1a54..357c17e78 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,5 +1,48 @@ RELEASE NOTES +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 avaialble 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 ============= diff --git a/contrib/cmake/examples/CMakeLists.txt b/contrib/cmake/examples/CMakeLists.txt index b8cecbe63..e596ed3dd 100644 --- a/contrib/cmake/examples/CMakeLists.txt +++ b/contrib/cmake/examples/CMakeLists.txt @@ -1,2 +1,4 @@ add_subdirectory(c) add_subdirectory(c++) +add_subdirectory(tptp) +add_subdirectory(python) diff --git a/contrib/cmake/examples/c++/CMakeLists.txt b/contrib/cmake/examples/c++/CMakeLists.txt index 85bbd77c7..fdc5cf387 100644 --- a/contrib/cmake/examples/c++/CMakeLists.txt +++ b/contrib/cmake/examples/c++/CMakeLists.txt @@ -1,4 +1,9 @@ +# FIXME: We should build this as an external project and consume +# Z3 as `find_package(z3 CONFIG)`. add_executable(cpp_example EXCLUDE_FROM_ALL example.cpp) target_link_libraries(cpp_example PRIVATE libz3) target_include_directories(cpp_example PRIVATE "${CMAKE_SOURCE_DIR}/src/api") target_include_directories(cpp_example PRIVATE "${CMAKE_SOURCE_DIR}/src/api/c++") +if (NOT BUILD_LIBZ3_SHARED) + z3_append_linker_flag_list_to_target(cpp_example ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) +endif() diff --git a/contrib/cmake/examples/c/CMakeLists.txt b/contrib/cmake/examples/c/CMakeLists.txt index 1a14573ac..fc6eaee18 100644 --- a/contrib/cmake/examples/c/CMakeLists.txt +++ b/contrib/cmake/examples/c/CMakeLists.txt @@ -1,3 +1,9 @@ +# FIXME: We should build this as an external project and consume +# Z3 as `find_package(z3 CONFIG)`. add_executable(c_example EXCLUDE_FROM_ALL test_capi.c) target_link_libraries(c_example PRIVATE libz3) target_include_directories(c_example PRIVATE "${CMAKE_SOURCE_DIR}/src/api") +# This is needed for when libz3 is built as a static library +if (NOT BUILD_LIBZ3_SHARED) + z3_append_linker_flag_list_to_target(c_example ${Z3_DEPENDENT_EXTRA_C_LINK_FLAGS}) +endif() diff --git a/contrib/cmake/examples/python/CMakeLists.txt b/contrib/cmake/examples/python/CMakeLists.txt new file mode 100644 index 000000000..fdbb7891f --- /dev/null +++ b/contrib/cmake/examples/python/CMakeLists.txt @@ -0,0 +1,24 @@ +set(python_example_files + example.py + visitor.py +) + +set(z3py_bindings_build_dest "${CMAKE_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}" + "${z3py_bindings_build_dest}/${example_file}" + 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} +) diff --git a/contrib/cmake/examples/tptp/CMakeLists.txt b/contrib/cmake/examples/tptp/CMakeLists.txt new file mode 100644 index 000000000..41fa9cc65 --- /dev/null +++ b/contrib/cmake/examples/tptp/CMakeLists.txt @@ -0,0 +1,4 @@ +add_executable(z3_tptp5 EXCLUDE_FROM_ALL tptp5.cpp tptp5.lex.cpp) +target_link_libraries(z3_tptp5 PRIVATE libz3) +target_include_directories(z3_tptp5 PRIVATE "${CMAKE_SOURCE_DIR}/src/api") +target_include_directories(z3_tptp5 PRIVATE "${CMAKE_SOURCE_DIR}/src/api/c++") diff --git a/contrib/cmake/src/CMakeLists.txt b/contrib/cmake/src/CMakeLists.txt index cba30c46f..65eef8094 100644 --- a/contrib/cmake/src/CMakeLists.txt +++ b/contrib/cmake/src/CMakeLists.txt @@ -117,14 +117,16 @@ else() set(lib_type "STATIC") endif() add_library(libz3 ${lib_type} ${object_files}) -# FIXME: Set "VERSION" and "SOVERSION" properly set_target_properties(libz3 PROPERTIES - # FIXME: Should we be using ${Z3_VERSION} here? - # VERSION: Sets up symlinks, does it do anything else? + # 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: On platforms that use ELF this sets the API version - # and should be incremented everytime the API changes - SOVERSION ${Z3_VERSION}) + SOVERSION ${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}) if (NOT MSVC) # On UNIX like platforms if we don't change the OUTPUT_NAME @@ -136,9 +138,10 @@ if (NOT MSVC) set_target_properties(libz3 PROPERTIES OUTPUT_NAME z3) endif() -# Using INTERFACE means that targets that try link against libz3 will -# automatically link against the libs in Z3_DEPENDENT_LIBS -target_link_libraries(libz3 INTERFACE ${Z3_DEPENDENT_LIBS}) +# 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}) z3_append_linker_flag_list_to_target(libz3 ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) diff --git a/contrib/cmake/src/api/dotnet/CMakeLists.txt b/contrib/cmake/src/api/dotnet/CMakeLists.txt index 7440f021b..93f5929d9 100644 --- a/contrib/cmake/src/api/dotnet/CMakeLists.txt +++ b/contrib/cmake/src/api/dotnet/CMakeLists.txt @@ -156,7 +156,7 @@ elseif (DOTNET_TOOLCHAIN_IS_MONO) # We need to give the assembly a strong name so that it can be installed # into the GAC. list(APPEND CSC_FLAGS - "/keyfile:${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Z3.mono.snk" + "/keyfile:${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Z3.snk" ) else() message(FATAL_ERROR "Unknown .NET toolchain") diff --git a/contrib/cmake/src/api/java/CMakeLists.txt b/contrib/cmake/src/api/java/CMakeLists.txt index 6e16ddb74..b34277266 100644 --- a/contrib/cmake/src/api/java/CMakeLists.txt +++ b/contrib/cmake/src/api/java/CMakeLists.txt @@ -110,7 +110,9 @@ set(Z3_JAVA_JAR_SOURCE_FILES BitVecSort.java BoolExpr.java BoolSort.java + ConstructorDecRefQueue.java Constructor.java + ConstructorListDecRefQueue.java ConstructorList.java Context.java DatatypeExpr.java @@ -136,7 +138,6 @@ set(Z3_JAVA_JAR_SOURCE_FILES GoalDecRefQueue.java Goal.java IDecRefQueue.java - IDisposable.java InterpolationContext.java IntExpr.java IntNum.java diff --git a/contrib/cmake/src/api/python/CMakeLists.txt b/contrib/cmake/src/api/python/CMakeLists.txt index 555333c9f..6efdc15ef 100644 --- a/contrib/cmake/src/api/python/CMakeLists.txt +++ b/contrib/cmake/src/api/python/CMakeLists.txt @@ -4,32 +4,35 @@ message(STATUS "Emitting rules to build Z3 python bindings") ############################################################################### # This allows the python bindings to be used directly from the build directory set(z3py_files - z3.py - z3num.py - z3poly.py - z3printer.py - z3rcf.py + z3/__init__.py + z3/z3.py + z3/z3num.py + z3/z3poly.py + z3/z3printer.py + z3/z3rcf.py z3test.py - z3types.py - z3util.py + z3/z3types.py + z3/z3util.py ) -set(z3py_bindings_build_dest "${CMAKE_BINARY_DIR}") +set(z3py_bindings_build_dest "${CMAKE_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_bindings_build_dest}/${z3py_file}" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${z3py_file}" - COMMENT "Copying \"${z3py_file}\" to ${z3py_bindings_build_dest}" + 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}/z3core.py" +add_custom_command(OUTPUT "${z3py_bindings_build_dest}/z3/z3core.py" COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/scripts/update_api.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} @@ -44,10 +47,10 @@ add_custom_command(OUTPUT "${z3py_bindings_build_dest}/z3core.py" COMMENT "Generating z3core.py" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} ) -list(APPEND build_z3_python_bindings_target_depends "${z3py_bindings_build_dest}/z3core.py") +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}/z3consts.py" +add_custom_command(OUTPUT "${z3py_bindings_build_dest}/z3/z3consts.py" COMMAND "${PYTHON_EXECUTABLE}" "${CMAKE_SOURCE_DIR}/scripts/mk_consts_files.py" ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} @@ -60,13 +63,29 @@ add_custom_command(OUTPUT "${z3py_bindings_build_dest}/z3consts.py" COMMENT "Generating z3consts.py" ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} ) -list(APPEND build_z3_python_bindings_target_depends "${z3py_bindings_build_dest}/z3consts.py") +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}" + "${CMAKE_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}" ) ############################################################################### @@ -117,7 +136,7 @@ if (INSTALL_PYTHON_BINDINGS) # 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}" + DESTINATION "${CMAKE_INSTALL_PYTHON_PKG_DIR}/z3" ) else() message(STATUS "Not emitting rules to install Z3 python bindings") diff --git a/contrib/cmake/src/ast/fpa/CMakeLists.txt b/contrib/cmake/src/ast/fpa/CMakeLists.txt index 7369a9b3d..4a9506d16 100644 --- a/contrib/cmake/src/ast/fpa/CMakeLists.txt +++ b/contrib/cmake/src/ast/fpa/CMakeLists.txt @@ -1,10 +1,12 @@ z3_add_component(fpa SOURCES + bv2fpa_converter.cpp fpa2bv_converter.cpp fpa2bv_rewriter.cpp COMPONENT_DEPENDENCIES ast simplifier + model util PYG_FILES fpa2bv_rewriter_params.pyg diff --git a/contrib/cmake/src/ast/rewriter/CMakeLists.txt b/contrib/cmake/src/ast/rewriter/CMakeLists.txt index 14bcebf46..74fddd2bb 100644 --- a/contrib/cmake/src/ast/rewriter/CMakeLists.txt +++ b/contrib/cmake/src/ast/rewriter/CMakeLists.txt @@ -4,10 +4,12 @@ z3_add_component(rewriter array_rewriter.cpp ast_counter.cpp bool_rewriter.cpp + bv_bounds.cpp bv_rewriter.cpp datatype_rewriter.cpp der.cpp dl_rewriter.cpp + enum2bv_rewriter.cpp expr_replacer.cpp expr_safe_replace.cpp factor_rewriter.cpp @@ -15,6 +17,7 @@ z3_add_component(rewriter label_rewriter.cpp mk_simplified_app.cpp pb_rewriter.cpp + pb2bv_rewriter.cpp quant_hoist.cpp rewriter.cpp seq_rewriter.cpp diff --git a/contrib/cmake/src/math/polynomial/CMakeLists.txt b/contrib/cmake/src/math/polynomial/CMakeLists.txt index 1792f50aa..1d320d751 100644 --- a/contrib/cmake/src/math/polynomial/CMakeLists.txt +++ b/contrib/cmake/src/math/polynomial/CMakeLists.txt @@ -3,7 +3,6 @@ z3_add_component(polynomial algebraic_numbers.cpp polynomial_cache.cpp polynomial.cpp - polynomial_factorization.cpp rpolynomial.cpp sexpr2upolynomial.cpp upolynomial.cpp diff --git a/contrib/cmake/src/muz/rel/CMakeLists.txt b/contrib/cmake/src/muz/rel/CMakeLists.txt index 720714ed8..f03a90406 100644 --- a/contrib/cmake/src/muz/rel/CMakeLists.txt +++ b/contrib/cmake/src/muz/rel/CMakeLists.txt @@ -12,7 +12,6 @@ z3_add_component(rel dl_interval_relation.cpp dl_lazy_table.cpp dl_mk_explanations.cpp - dl_mk_partial_equiv.cpp dl_mk_similarity_compressor.cpp dl_mk_simple_joins.cpp dl_product_relation.cpp diff --git a/contrib/cmake/src/opt/CMakeLists.txt b/contrib/cmake/src/opt/CMakeLists.txt index c50f8be52..05a62b6c2 100644 --- a/contrib/cmake/src/opt/CMakeLists.txt +++ b/contrib/cmake/src/opt/CMakeLists.txt @@ -1,20 +1,15 @@ z3_add_component(opt SOURCES - bcd2.cpp - fu_malik.cpp - hitting_sets.cpp - maxhs.cpp maxres.cpp - maxsls.cpp maxsmt.cpp mss.cpp - mus.cpp opt_cmds.cpp opt_context.cpp opt_pareto.cpp optsmt.cpp opt_solver.cpp pb_sls.cpp + sortmax.cpp wmax.cpp COMPONENT_DEPENDENCIES sat_solver diff --git a/contrib/cmake/src/sat/CMakeLists.txt b/contrib/cmake/src/sat/CMakeLists.txt index cfc3835c1..d88a73708 100644 --- a/contrib/cmake/src/sat/CMakeLists.txt +++ b/contrib/cmake/src/sat/CMakeLists.txt @@ -2,7 +2,6 @@ z3_add_component(sat SOURCES dimacs.cpp sat_asymm_branch.cpp - sat_bceq.cpp sat_clause.cpp sat_clause_set.cpp sat_clause_use_list.cpp @@ -13,10 +12,10 @@ z3_add_component(sat sat_integrity_checker.cpp sat_model_converter.cpp sat_mus.cpp + sat_par.cpp sat_probing.cpp sat_scc.cpp sat_simplifier.cpp - sat_sls.cpp sat_solver.cpp sat_watched.cpp COMPONENT_DEPENDENCIES diff --git a/contrib/cmake/src/smt/CMakeLists.txt b/contrib/cmake/src/smt/CMakeLists.txt index e9306b2d6..bd8ad3f31 100644 --- a/contrib/cmake/src/smt/CMakeLists.txt +++ b/contrib/cmake/src/smt/CMakeLists.txt @@ -18,6 +18,7 @@ z3_add_component(smt smt_checker.cpp smt_clause.cpp smt_conflict_resolution.cpp + smt_consequences.cpp smt_context.cpp smt_context_inv.cpp smt_context_pp.cpp @@ -42,6 +43,7 @@ z3_add_component(smt smt_statistics.cpp smt_theory.cpp smt_value_sort.cpp + smt2_extra_cmds.cpp theory_arith.cpp theory_array_base.cpp theory_array.cpp diff --git a/contrib/cmake/src/solver/CMakeLists.txt b/contrib/cmake/src/solver/CMakeLists.txt index 5e76c91ce..56864a691 100644 --- a/contrib/cmake/src/solver/CMakeLists.txt +++ b/contrib/cmake/src/solver/CMakeLists.txt @@ -2,8 +2,11 @@ z3_add_component(solver SOURCES check_sat_result.cpp combined_solver.cpp + mus.cpp + smt_logics.cpp solver.cpp solver_na2as.cpp + solver2tactic.cpp tactic2solver.cpp COMPONENT_DEPENDENCIES model diff --git a/contrib/cmake/src/tactic/CMakeLists.txt b/contrib/cmake/src/tactic/CMakeLists.txt index 318803cd2..324d8089b 100644 --- a/contrib/cmake/src/tactic/CMakeLists.txt +++ b/contrib/cmake/src/tactic/CMakeLists.txt @@ -12,6 +12,7 @@ z3_add_component(tactic probe.cpp proof_converter.cpp replace_proof_converter.cpp + sine_filter.cpp tactical.cpp tactic.cpp COMPONENT_DEPENDENCIES diff --git a/contrib/cmake/src/tactic/bv/CMakeLists.txt b/contrib/cmake/src/tactic/bv/CMakeLists.txt index 42cff2bfc..90ed65e3f 100644 --- a/contrib/cmake/src/tactic/bv/CMakeLists.txt +++ b/contrib/cmake/src/tactic/bv/CMakeLists.txt @@ -5,8 +5,10 @@ z3_add_component(bv_tactics 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 diff --git a/contrib/cmake/src/tactic/core/CMakeLists.txt b/contrib/cmake/src/tactic/core/CMakeLists.txt index ed3a84edf..fcd26bd84 100644 --- a/contrib/cmake/src/tactic/core/CMakeLists.txt +++ b/contrib/cmake/src/tactic/core/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(core_tactics 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 diff --git a/contrib/cmake/src/tactic/portfolio/CMakeLists.txt b/contrib/cmake/src/tactic/portfolio/CMakeLists.txt index d20af772b..c6f621f25 100644 --- a/contrib/cmake/src/tactic/portfolio/CMakeLists.txt +++ b/contrib/cmake/src/tactic/portfolio/CMakeLists.txt @@ -1,6 +1,10 @@ z3_add_component(portfolio SOURCES default_tactic.cpp + enum2bv_solver.cpp + pb2bv_solver.cpp + bounded_int2bv_solver.cpp + fd_solver.cpp smt_strategic_solver.cpp COMPONENT_DEPENDENCIES aig_tactic diff --git a/contrib/cmake/src/test/CMakeLists.txt b/contrib/cmake/src/test/CMakeLists.txt index 427cedcdb..6ea07e84c 100644 --- a/contrib/cmake/src/test/CMakeLists.txt +++ b/contrib/cmake/src/test/CMakeLists.txt @@ -42,6 +42,7 @@ add_executable(test-z3 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 @@ -77,10 +78,10 @@ add_executable(test-z3 old_interval.cpp optional.cpp parray.cpp + pb2bv.cpp pdr.cpp permutation.cpp polynomial.cpp - polynomial_factorization.cpp polynorm.cpp prime_generator.cpp proof_checker.cpp diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index beb6596d1..45bc9a99c 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -82,7 +82,7 @@ try: mk_dir('tmp') shutil.copyfile('website-adj.dox', 'tmp/website.dox') os.remove('website-adj.dox') - shutil.copyfile('../src/api/python/z3.py', 'tmp/z3py.py') + shutil.copyfile('../src/api/python/z3/z3.py', 'tmp/z3py.py') cleanup_API('../src/api/z3_api.h', 'tmp/z3_api.h') cleanup_API('../src/api/z3_ast_containers.h', 'tmp/z3_ast_containers.h') cleanup_API('../src/api/z3_algebraic.h', 'tmp/z3_algebraic.h') @@ -119,7 +119,7 @@ try: print("Removed temporary file z3py.py") os.removedirs('tmp') print("Removed temporary directory tmp.") - sys.path.append('../src/api/python') + sys.path.append('../src/api/python/z3') pydoc.writedoc('z3') shutil.move('z3.html', 'api/html/z3.html') print("Generated Python documentation.") diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index 5635e6d12..2d7d051c5 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -23,7 +23,7 @@ void demorgan() { expr x = c.bool_const("x"); expr y = c.bool_const("y"); - expr conjecture = !(x && y) == (!x || !y); + expr conjecture = (!(x && y)) == (!x || !y); solver s(c); // adding the negation of the conjecture as a constraint. @@ -1111,6 +1111,35 @@ void param_descrs_example() { } } +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 a = c.parse_string("(assert a)", sorts, decls); + std::cout << a << "\n"; +} + int main() { try { @@ -1154,6 +1183,8 @@ int main() { 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"; std::cout << "done\n"; } catch (exception & ex) { diff --git a/examples/dotnet/Program.cs b/examples/dotnet/Program.cs index 26c081ee2..20bb012b1 100644 --- a/examples/dotnet/Program.cs +++ b/examples/dotnet/Program.cs @@ -818,6 +818,7 @@ namespace test_mapi BigIntCheck(ctx, ctx.MkReal("234234333/2")); +#if !FRAMEWORK_LT_4 string bn = "1234567890987654321"; if (ctx.MkInt(bn).BigInteger.ToString() != bn) @@ -828,6 +829,7 @@ namespace test_mapi if (ctx.MkBV(bn, 32).BigInteger.ToString() == bn) throw new TestFailedException(); +#endif // Error handling test. try @@ -1094,8 +1096,10 @@ namespace test_mapi static void BigIntCheck(Context ctx, RatNum r) { +#if !FRAMEWORK_LT_4 Console.WriteLine("Num: " + r.BigIntNumerator); Console.WriteLine("Den: " + r.BigIntDenominator); +#endif } /// @@ -2159,6 +2163,8 @@ namespace test_mapi 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(); diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 74e94617d..5810dab37 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -188,7 +188,7 @@ class JavaExample /* do something with the context */ /* be kind to dispose manually and not wait for the GC. */ - ctx.dispose(); + ctx.close(); } } @@ -2291,6 +2291,8 @@ class JavaExample 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(); diff --git a/examples/ml/ml_example.ml b/examples/ml/ml_example.ml index bab0ba2fc..eb64f8ee8 100644 --- a/examples/ml/ml_example.ml +++ b/examples/ml/ml_example.ml @@ -323,6 +323,7 @@ let _ = 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 diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Properties/AssemblyInfo.cs b/examples/msf/SolverFoundation.Plugin.Z3/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..6d495a895 --- /dev/null +++ b/examples/msf/SolverFoundation.Plugin.Z3/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("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")] diff --git a/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs index af8bc92a2..1c82406be 100644 --- a/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs +++ b/examples/msf/SolverFoundation.Plugin.Z3/Z3BaseSolver.cs @@ -47,7 +47,7 @@ namespace Microsoft.SolverFoundation.Plugin.Z3 private Dictionary _variables = new Dictionary(); /// A map from MSF variable ids to Z3 goal ids - private Dictionary _goals = new Dictionary(); + private Dictionary _goals = new Dictionary(); internal Z3BaseSolver(IRowVariableModel model) { @@ -64,7 +64,7 @@ namespace Microsoft.SolverFoundation.Plugin.Z3 get { return _variables; } } - internal Dictionary Goals + internal Dictionary Goals { get { return _goals; } } @@ -332,7 +332,7 @@ namespace Microsoft.SolverFoundation.Plugin.Z3 // Remember all objective values foreach (var pair in _goals) { - var optimalValue = Utils.ToRational(_optSolver.GetUpper(pair.Value)); + var optimalValue = Utils.ToRational(pair.Value.Upper); _model.SetValue(pair.Key.Index, optimalValue); } model.Dispose(); @@ -356,7 +356,7 @@ namespace Microsoft.SolverFoundation.Plugin.Z3 // Remember all objective values foreach (var pair in _goals) { - var optimalValue = Utils.ToRational(_optSolver.GetUpper(pair.Value)); + var optimalValue = Utils.ToRational(pair.Value.Upper); _model.SetValue(pair.Key.Index, optimalValue); } subOptimalModel.Dispose(); diff --git a/examples/python/all_interval_series.py b/examples/python/all_interval_series.py new file mode 100644 index 000000000..d55017a56 --- /dev/null +++ b/examples/python/all_interval_series.py @@ -0,0 +1,76 @@ +# 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 z3 import * +import time + +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) diff --git a/examples/python/example.py b/examples/python/example.py index e0c9374e7..a17668506 100644 --- a/examples/python/example.py +++ b/examples/python/example.py @@ -1,4 +1,30 @@ -# Copyright (c) Microsoft Corporation 2015 +# 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 PYTHON_PATH 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 OSX: +# export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:MYZ3/bin +# export PYTHONPATH=MYZ3/bin/python +# python example.py + from z3 import * diff --git a/examples/python/mus/marco.py b/examples/python/mus/marco.py new file mode 100644 index 000000000..b6689636d --- /dev/null +++ b/examples/python/mus/marco.py @@ -0,0 +1,185 @@ +############################################ +# 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() + diff --git a/examples/python/mus/mss.py b/examples/python/mus/mss.py new file mode 100644 index 000000000..fd2d209da --- /dev/null +++ b/examples/python/mus/mss.py @@ -0,0 +1,168 @@ +############################################ +# 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 CompareSetSize(): + def __call__(self, s1, s2): + return len(s1) < len(s2) + + +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 + # encoutered 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 encoutered. + + 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(CompareSetSize()) + for core in cores: + if len(core & core_literals) == 0: + self.relax_core(core) + core_literals |= core + return mss + + +main() diff --git a/examples/python/socrates.py b/examples/python/socrates.py new file mode 100644 index 000000000..8d44b41ea --- /dev/null +++ b/examples/python/socrates.py @@ -0,0 +1,34 @@ +############################################ +# 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 coherents + +# classical refutation +s.add(Not(Mortal(socrates))) + +print(s.check()) # prints unsat so socrates is Mortal diff --git a/examples/tptp/tptp5.cpp b/examples/tptp/tptp5.cpp index 2773b6cc9..b2736df4c 100644 --- a/examples/tptp/tptp5.cpp +++ b/examples/tptp/tptp5.cpp @@ -828,7 +828,10 @@ class env { } else if (!strcmp(ch,"$to_real")) { check_arity(terms.size(), 1); - r = to_real(terms[0]); + 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); @@ -1224,21 +1227,14 @@ public: void display_axiom(std::ostream& out, z3::expr e) { out << "tff(formula" << (++m_formula_id) << ", axiom,\n "; - display(out, e); + display(out, e, true); out << ").\n"; } - void display(std::ostream& out, z3::expr e) { - if (e.is_numeral()) { - __int64 num, den; - if (Z3_get_numeral_small(ctx, e, &num, &den)) { - if (num < 0 && den == 1 && num != std::numeric_limits<__int64>::min()) { - out << "-" << (-num); - return; - } - } - // potential incompatibility: prints negative numbers with a space. - out << e; + 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); @@ -1253,32 +1249,33 @@ public: out << "$false"; break; case Z3_OP_AND: - display_infix(out, "&", e); + display_infix(out, "&", e, in_paren); break; case Z3_OP_OR: - display_infix(out, "|", e); + display_infix(out, "|", e, in_paren); break; case Z3_OP_IMPLIES: - display_infix(out, "=>", e); + display_infix(out, "=>", e, in_paren); break; case Z3_OP_NOT: - out << "(~"; - display(out, e.arg(0)); - out << ")"; + 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); + display_infix(out, "<=>", e, in_paren); } else { - display_infix(out, "=", e); + display_infix(out, "=", e, in_paren); } break; case Z3_OP_IFF: - display_infix(out, "<=>", e); + display_infix(out, "<=>", e, in_paren); break; case Z3_OP_XOR: - display_infix(out, "<~>", e); + display_infix(out, "<~>", e, in_paren); break; case Z3_OP_MUL: display_binary(out, "$product", e); @@ -1355,7 +1352,7 @@ public: } } out << "] : "; - display(out, e.body()); + display(out, e.body(), false); for (unsigned i = 0; i < nb; ++i) { names.pop_back(); } @@ -1370,7 +1367,7 @@ public: out << lower_case_fun(e.decl().name()) << "("; unsigned n = e.num_args(); for(unsigned i = 0; i < n; ++i) { - display(out, e.arg(i)); + display(out, e.arg(i), n == 1); if (i + 1 < n) { out << ", "; } @@ -1393,23 +1390,23 @@ public: } } - void display_infix(std::ostream& out, char const* conn, z3::expr& e) { - out << "("; + 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)); + display(out, e.arg(i), false); if (i + 1 < sz) { out << " " << conn << " "; } } - out << ")"; + 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)); + display(out, e.arg(i), sz == 1); if (i + 1 < sz) { out << ", "; } @@ -1422,7 +1419,7 @@ public: unsigned sz = e.num_args(); unsigned np = 1; for (unsigned i = 0; i < sz; ++i) { - display(out, e.arg(i)); + display(out, e.arg(i), false); if (i + 1 < sz) { out << ", "; } @@ -1566,7 +1563,7 @@ public: formula_file = "unknown"; } out << "tff(" << m_node_number << ",axiom,("; - display(out, get_proof_formula(p)); + display(out, get_proof_formula(p), true); out << "), file('" << formula_file << "','"; out << formula_name << "')).\n"; break; @@ -1629,12 +1626,12 @@ public: break; case Z3_OP_PR_HYPOTHESIS: out << "tff(" << m_node_number << ",assumption,("; - display(out, get_proof_formula(p)); + 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)); + 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; @@ -1751,7 +1748,7 @@ public: 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)); + display(out, get_proof_formula(p), true); out << "),\n inference(" << name << ",[status(" << status << ")"; if (!hyps.empty()) { out << ", assumptions("; @@ -1780,7 +1777,7 @@ public: 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); + display(out, conclusion, true); out << "),\n inference(" << name << ",[status(" << status << ")],"; out << "[" << hyp1; if (hyp2) { @@ -2369,7 +2366,7 @@ static void prove_tptp() { } catch (failure_ex& ex) { std::cerr << ex.msg << "\n"; - std::cout << "SZS status GaveUp\n"; + std::cout << "% SZS status GaveUp\n"; return; } @@ -2401,14 +2398,16 @@ static void prove_tptp() { std::cout << result << "\n"; } else if (fmls.has_conjecture()) { - std::cout << "SZS status Theorem\n"; + std::cout << "% SZS status Theorem\n"; } else { - std::cout << "SZS status Unsatisfiable\n"; + 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"; @@ -2416,7 +2415,7 @@ static void prove_tptp() { } if (g_generate_core) { z3::expr_vector core = solver.unsat_core(); - std::cout << "SZS core "; + std::cout << "% SZS core "; for (unsigned i = 0; i < core.size(); ++i) { std::cout << core[i] << " "; } @@ -2428,13 +2427,15 @@ static void prove_tptp() { std::cout << result << "\n"; } else if (fmls.has_conjecture()) { - std::cout << "SZS status CounterSatisfiable\n"; + std::cout << "% SZS status CounterSatisfiable\n"; } else { - std::cout << "SZS status Satisfiable\n"; + 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: @@ -2442,12 +2443,12 @@ static void prove_tptp() { std::cout << result << "\n"; } else if (!g_first_interrupt) { - std::cout << "SZS status Interrupted\n"; + std::cout << "% SZS status Interrupted\n"; } else { - std::cout << "SZS status GaveUp\n"; + std::cout << "% SZS status GaveUp\n"; std::string reason = solver.reason_unknown(); - std::cout << "SZS reason " << reason << "\n"; + std::cout << "% SZS reason " << reason << "\n"; } break; } @@ -2467,7 +2468,6 @@ static void prove_tptp() { int main(int argc, char** argv) { - //std::ostream* out = &std::cout; g_start_time = static_cast(clock()); signal(SIGINT, on_ctrl_c); @@ -2480,7 +2480,12 @@ int main(int argc, char** argv) { display_smt2(*g_out); } else { - prove_tptp(); + try { + prove_tptp(); + } + catch (z3::exception& ex) { + std::cerr << "Exception during proof: " << ex.msg() << "\n"; + } } return 0; } diff --git a/scripts/mk_consts_files.py b/scripts/mk_consts_files.py index e582d8468..d0502c19d 100755 --- a/scripts/mk_consts_files.py +++ b/scripts/mk_consts_files.py @@ -22,6 +22,7 @@ def main(args): 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): @@ -60,6 +61,15 @@ def main(args): 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 langauge bindings') diff --git a/scripts/mk_genfile_common.py b/scripts/mk_genfile_common.py index b8d6ac5e1..98346f99f 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -98,7 +98,7 @@ def mk_z3consts_py_internal(api_files, output_dir): openbrace_pat = re.compile("{ *") closebrace_pat = re.compile("}.*;") - z3consts = open(os.path.join(output_dir, 'z3consts.py'), 'w') + 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: @@ -323,6 +323,9 @@ def mk_z3consts_java_internal(api_files, package_name, output_dir): 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) @@ -342,10 +345,19 @@ def mk_z3consts_java_internal(api_files, package_name, output_dir): 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(' for (%s k: values()) \n' % name) - efile.write(' if (k.intValue == v) return k;\n') - efile.write(' return values()[0];\n') + 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) @@ -364,6 +376,180 @@ def mk_z3consts_java_internal(api_files, package_name, output_dir): 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 diff --git a/scripts/mk_project.py b/scripts/mk_project.py index e7177000f..7ba88c1b3 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -9,7 +9,7 @@ from mk_util import * # Z3 Project definition def init_project_def(): - set_version(4, 4, 2, 0) + set_version(4, 5, 1, 0) add_lib('util', []) add_lib('polynomial', ['util'], 'math/polynomial') add_lib('sat', ['util']) @@ -45,7 +45,7 @@ def init_project_def(): # Simplifier module will be deleted in the future. # It has been replaced with rewriter module. add_lib('simplifier', ['rewriter'], 'ast/simplifier') - add_lib('fpa', ['ast', 'util', 'simplifier'], 'ast/fpa') + add_lib('fpa', ['ast', 'util', 'simplifier', 'model'], 'ast/fpa') add_lib('macros', ['simplifier'], 'ast/macros') add_lib('pattern', ['normal_forms', 'smt2parser', 'simplifier'], 'ast/pattern') add_lib('bit_blaster', ['rewriter', 'simplifier'], 'ast/rewriter/bit_blaster') @@ -87,12 +87,14 @@ def init_project_def(): reexports=['api'], dll_name='libz3', static=build_static_lib(), - export_files=API_files) - add_dot_net_dll('dotnet', ['api_dll'], 'api/dotnet', dll_name='Microsoft.Z3', assembly_info_dir='Properties') + export_files=API_files, + staging_link='python') + add_dot_net_dll('dotnet', ['api_dll'], 'api/dotnet', dll_name='Microsoft.Z3', assembly_info_dir='Properties', default_key_file='src/api/dotnet/Microsoft.Z3.snk') add_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) # Examples add_cpp_example('cpp_example', 'c++') diff --git a/scripts/mk_unix_dist.py b/scripts/mk_unix_dist.py index 0c28058ed..488bc4364 100644 --- a/scripts/mk_unix_dist.py +++ b/scripts/mk_unix_dist.py @@ -23,8 +23,11 @@ VERBOSE=True DIST_DIR='dist' FORCE_MK=False DOTNET_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 @@ -52,13 +55,15 @@ def display_help(): 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_ENABLED + global FORCE_MK, JAVA_ENABLED, GIT_HASH, DOTNET_ENABLED, DOTNET_KEY_FILE path = BUILD_DIR options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:hsf', ['build=', 'help', @@ -66,7 +71,9 @@ def parse_options(): 'force', 'nojava', 'nodotnet', - 'githash' + 'dotnet-key=', + 'githash', + 'nopython' ]) for opt, arg in options: if opt in ('-b', '--build'): @@ -80,7 +87,11 @@ def parse_options(): elif opt in ('-f', '--force'): FORCE_MK = True elif opt == '--nodotnet': - DOTNET_ENABLED = False + DOTNET_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': @@ -98,11 +109,16 @@ def mk_build_dir(path): if not check_build_dir(path) or FORCE_MK: opts = ["python", os.path.join('scripts', 'mk_make.py'), "-b", path, "--staticlib"] if DOTNET_ENABLED: - opts.append('--dotnet') + 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) @@ -124,7 +140,7 @@ class cd: def mk_z3(): with cd(BUILD_DIR): try: - return subprocess.call(['make', '-j', '8']) + return subprocess.call(['make', '-j', MAKEJOBS]) except: return 1 @@ -171,7 +187,9 @@ def mk_dist_dir(): dist_path = os.path.join(DIST_DIR, get_z3_name()) mk_dir(dist_path) mk_util.DOTNET_ENABLED = DOTNET_ENABLED + 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) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index a73a0e59c..9451e67c0 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -63,6 +63,7 @@ DOTNET_COMPONENT='dotnet' JAVA_COMPONENT='java' ML_COMPONENT='ml' CPP_COMPONENT='cpp' +PYTHON_COMPONENT='python' ##################### IS_WINDOWS=False IS_LINUX=False @@ -70,6 +71,7 @@ IS_OSX=False IS_FREEBSD=False IS_OPENBSD=False IS_CYGWIN=False +IS_CYGWIN_MINGW=False VERBOSE=True DEBUG_MODE=False SHOW_CPPS = True @@ -80,7 +82,9 @@ ONLY_MAKEFILES = False Z3PY_SRC_DIR=None VS_PROJ = False TRACE = False +PYTHON_ENABLED=False DOTNET_ENABLED=False +DOTNET_KEY_FILE=getenv("Z3_DOTNET_KEY_FILE", None) JAVA_ENABLED=False ML_ENABLED=False PYTHON_INSTALL_ENABLED=False @@ -98,8 +102,10 @@ VS_PAR=False VS_PAR_NUM=8 GPROF=False GIT_HASH=False +GIT_DESCRIBE=False SLOW_OPTIMIZE=False USE_OMP=True +LOG_SYNC=False FPMATH="Default" FPMATH_FLAGS="-mfpmath=sse -msse -msse2" @@ -143,6 +149,9 @@ def is_osx(): def is_cygwin(): return IS_CYGWIN +def is_cygwin_mingw(): + return IS_CYGWIN_MINGW + def norm_path(p): # We use '/' on mk_project for convenience return os.path.join(*(p.split('/'))) @@ -219,7 +228,10 @@ def rmf(fname): def exec_compiler_cmd(cmd): r = exec_cmd(cmd) - rmf('a.out') + if is_windows() or is_cygwin_mingw(): + rmf('a.exe') + else: + rmf('a.out') return r def test_cxx_compiler(cc): @@ -527,11 +539,14 @@ def find_c_compiler(): 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_REVISION + global VER_MAJOR, VER_MINOR, VER_BUILD, VER_REVISION, GIT_DESCRIBE VER_MAJOR = major VER_MINOR = minor VER_BUILD = build VER_REVISION = revision + if GIT_DESCRIBE: + branch = check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']) + VER_REVISION = int(check_output(['git', 'rev-list', '--count', 'HEAD'])) def get_version(): return (VER_MAJOR, VER_MINOR, VER_BUILD, VER_REVISION) @@ -597,6 +612,8 @@ elif os.name == 'posix': IS_OPENBSD=True elif os.uname()[0][:6] == 'CYGWIN': IS_CYGWIN=True + if (CC != None and "mingw" in CC): + IS_CYGWIN_MINGW=True def display_help(exit_code): print("mk_make.py: Z3 Makefile generator\n") @@ -612,6 +629,7 @@ def display_help(exit_code): 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: @@ -624,6 +642,7 @@ def display_help(exit_code): if IS_WINDOWS: print(" --optimize generate optimized code during linking.") print(" --dotnet generate .NET bindings.") + print(" --dotnet-key= sign the .NET assembly using the private key in .") print(" --java generate Java bindings.") print(" --ml generate OCaml bindings.") print(" --python generate Python bindings.") @@ -634,6 +653,7 @@ def display_help(exit_code): print(" --gprof enable gprof") print(" -f --foci2= use foci2 library at path") print(" --noomp disable OpenMP and all features that require it.") + print(" --log-sync synchronize access to API log files to enable multi-thread API logging.") print("") print("Some influential environment variables:") if not IS_WINDOWS: @@ -659,14 +679,14 @@ def display_help(exit_code): # Parse configuration option for mk_make script def parse_options(): global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE, VS_PAR, VS_PAR_NUM - global DOTNET_ENABLED, JAVA_ENABLED, ML_ENABLED, STATIC_LIB, STATIC_BIN, PREFIX, GMP, FOCI2, FOCI2LIB, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH, PYTHON_INSTALL_ENABLED - global LINUX_X64, SLOW_OPTIMIZE, USE_OMP + global DOTNET_ENABLED, DOTNET_KEY_FILE, JAVA_ENABLED, ML_ENABLED, STATIC_LIB, STATIC_BIN, PREFIX, GMP, FOCI2, FOCI2LIB, PYTHON_PACKAGE_DIR, GPROF, GIT_HASH, GIT_DESCRIBE, PYTHON_INSTALL_ENABLED, PYTHON_ENABLED + global LINUX_X64, SLOW_OPTIMIZE, USE_OMP, LOG_SYNC try: options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:df:sxhmcvtnp:gj', ['build=', 'debug', 'silent', 'x64', 'help', 'makefiles', 'showcpp', 'vsproj', - 'trace', 'dotnet', 'staticlib', 'prefix=', 'gmp', 'foci2=', 'java', 'parallel=', 'gprof', - 'githash=', 'x86', 'ml', 'optimize', 'noomp', 'pypkgdir=', 'python', 'staticbin']) + 'trace', 'dotnet', 'dotnet-key=', 'staticlib', 'prefix=', 'gmp', 'foci2=', 'java', 'parallel=', 'gprof', + 'githash=', 'git-describe', 'x86', 'ml', 'optimize', 'noomp', 'pypkgdir=', 'python', 'staticbin', 'log-sync']) except: print("ERROR: Invalid command line option") display_help(1) @@ -699,6 +719,8 @@ def parse_options(): TRACE = True elif opt in ('-.net', '--dotnet'): DOTNET_ENABLED = True + elif opt in ('--dotnet-key'): + DOTNET_KEY_FILE = arg elif opt in ('--staticlib'): STATIC_LIB = True elif opt in ('--staticbin'): @@ -723,11 +745,16 @@ def parse_options(): GPROF = True elif opt == '--githash': GIT_HASH=arg + elif opt == '--git-describe': + GIT_DESCRIBE = True elif opt in ('', '--ml'): ML_ENABLED = True elif opt in ('', '--noomp'): USE_OMP = False + elif opt in ('', '--log-sync'): + LOG_SYNC = True elif opt in ('--python'): + PYTHON_ENABLED = True PYTHON_INSTALL_ENABLED = True else: print("ERROR: Invalid command line option '%s'" % opt) @@ -824,6 +851,9 @@ def is_ml_enabled(): def is_dotnet_enabled(): return DOTNET_ENABLED +def is_python_enabled(): + return PYTHON_ENABLED + def is_python_install_enabled(): return PYTHON_INSTALL_ENABLED @@ -851,8 +881,8 @@ def is_CXX_gpp(): return is_compiler(CXX, 'g++') def is_clang_in_gpp_form(cc): - version_string = check_output([cc, '--version']) - return str(version_string).find('clang') != -1 + version_string = check_output([cc, '--version']).encode('utf-8').decode('utf-8') + return version_string.find('clang') != -1 def is_CXX_clangpp(): if is_compiler(CXX, 'g++'): @@ -1138,7 +1168,8 @@ class ExeComponent(Component): c_dep = get_component(dep) out.write(' ' + c_dep.get_link_name()) out.write('\n') - out.write('\t$(LINK) $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % exefile) + 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) @@ -1210,7 +1241,7 @@ def get_so_ext(): return 'dll' class DLLComponent(Component): - def __init__(self, name, dll_name, path, deps, export_files, reexports, install, static): + 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 @@ -1219,6 +1250,7 @@ class DLLComponent(Component): 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: @@ -1274,6 +1306,13 @@ class DLLComponent(Component): 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: @@ -1354,6 +1393,32 @@ class DLLComponent(Component): 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 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) @@ -1412,13 +1477,18 @@ class PythonInstallComponent(Component): def mk_install(self, out): if not is_python_install_enabled(): return - MakeRuleCmd.make_install_directory(out, self.pythonPkgDir, in_prefix=self.in_prefix_install) + 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, + os.path.join(self.pythonPkgDir, 'z3', 'lib', self.libz3Component.dll_file()), in_prefix=self.in_prefix_install ) @@ -1429,34 +1499,30 @@ class PythonInstallComponent(Component): # staged installs that use DESTDIR). MakeRuleCmd.create_relative_symbolic_link(out, self.libz3Component.install_path(), - os.path.join(self.pythonPkgDir, + os.path.join(self.pythonPkgDir, 'z3', 'lib', self.libz3Component.dll_file() ), ) - MakeRuleCmd.install_files(out, 'z3*.py', self.pythonPkgDir, + 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, '__pycache__') + pythonPycacheDir = os.path.join(self.pythonPkgDir, 'z3', '__pycache__') MakeRuleCmd.make_install_directory(out, pythonPycacheDir, in_prefix=self.in_prefix_install) MakeRuleCmd.install_files(out, - '{}*.pyc'.format(os.path.join('__pycache__', 'z3')), + os.path.join('python', 'z3', '__pycache__', '*.pyc'), pythonPycacheDir, in_prefix=self.in_prefix_install) else: MakeRuleCmd.install_files(out, - 'z3*.pyc', - self.pythonPkgDir, + 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(): - if os.uname()[0] == 'Darwin': - LD_LIBRARY_PATH = "DYLD_LIBRARY_PATH" - else: - LD_LIBRARY_PATH = "LD_LIBRARY_PATH" - out.write('\t@echo Z3 shared libraries were installed at \'%s\', make sure this directory is in your %s environment variable.\n' % - (os.path.join(PREFIX, INSTALL_LIB_DIR), LD_LIBRARY_PATH)) 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): @@ -1468,21 +1534,24 @@ class PythonInstallComponent(Component): in_prefix=self.in_prefix_install ) MakeRuleCmd.remove_installed_files(out, - '{}*.py'.format(os.path.join(self.pythonPkgDir, 'z3')), + os.path.join(self.pythonPkgDir, 'z3', '*.py'), in_prefix=self.in_prefix_install) MakeRuleCmd.remove_installed_files(out, - '{}*.pyc'.format(os.path.join(self.pythonPkgDir, 'z3')), + os.path.join(self.pythonPkgDir, 'z3', '*.pyc'), in_prefix=self.in_prefix_install) MakeRuleCmd.remove_installed_files(out, - '{}*.pyc'.format(os.path.join(self.pythonPkgDir, '__pycache__', 'z3')), + 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 class DotNetDLLComponent(Component): - def __init__(self, name, dll_name, path, deps, assembly_info_dir): + 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 @@ -1490,6 +1559,7 @@ class DotNetDLLComponent(Component): assembly_info_dir = "." self.dll_name = dll_name self.assembly_info_dir = assembly_info_dir + self.key_file = default_key_file def mk_pkg_config_file(self): """ @@ -1515,6 +1585,8 @@ class DotNetDLLComponent(Component): configure_file(pkg_config_template, pkg_config_output, substitutions) def mk_makefile(self, out): + global DOTNET_KEY_FILE + if not is_dotnet_enabled(): return cs_fp_files = [] @@ -1545,11 +1617,24 @@ class DotNetDLLComponent(Component): '/linkresource:{}.dll'.format(get_component(Z3_DLL_COMPONENT).dll_name), ] ) - else: - # We need to give the assembly a strong name so that it - # can be installed into the GAC with ``make install`` - pathToSnk = os.path.join(self.to_src_dir, 'Microsoft.Z3.mono.snk') - cscCmdLine.append('/keyfile:{}'.format(pathToSnk)) + + # 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 + + if not self.key_file is None: + print("%s.dll will be signed using key '%s'." % (self.dll_name, self.key_file)) + cscCmdLine.append('/keyfile:{}'.format(self.key_file)) cscCmdLine.extend( ['/unsafe+', '/nowarn:1701,1702', @@ -1573,6 +1658,7 @@ class DotNetDLLComponent(Component): ) else: cscCmdLine.extend(['/optimize+']) + if IS_WINDOWS: if VS_X64: cscCmdLine.extend(['/platform:x64']) @@ -1821,7 +1907,7 @@ class MLComponent(Component): CP_CMD='copy' OCAML_FLAGS = '' - if DEBUG_MODE: + if DEBUG_MODE: OCAML_FLAGS += '-g' OCAMLCF = OCAMLC + ' ' + OCAML_FLAGS OCAMLOPTF = OCAMLOPT + ' ' + OCAML_FLAGS @@ -1875,16 +1961,19 @@ class MLComponent(Component): OCAMLMKLIB = 'ocamlmklib' - LIBZ3 = '-L. -lz3' - if is_cygwin(): - # Some ocamlmklib's don't like -g; observed on cygwin, but may be others as well. + + LIBZ3 = '-L. -lz3' + if is_cygwin() and not(is_cygwin_mingw()): LIBZ3 = 'libz3.dll' - elif DEBUG_MODE: + + 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, z3dllso)) 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\n' % (z3mls, cmxs, stubso, z3dllso)) + out.write('%s.cmxa: %s %s %s %s.cma\n' % (z3mls, cmxs, stubso, z3dllso, 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 -shared -o %s.cmxs -I %s %s.cmxa\n' % (OCAMLOPTF, z3mls, self.sub_dir, z3mls)) @@ -1943,7 +2032,7 @@ class MLComponent(Component): 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: + if is_windows() or is_cygwin_mingw(): out.write('.dll') else: out.write('.so') # .so also on OSX! @@ -2117,11 +2206,20 @@ class PythonExampleComponent(ExampleComponent): 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, py)) + 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, BUILD_DIR)) + 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 @@ -2149,19 +2247,23 @@ 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): - c = DLLComponent(name, dll_name, path, deps, export_files, reexports, install, static) +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_dll(name, deps=[], path=None, dll_name=None, assembly_info_dir=None): - c = DotNetDLLComponent(name, dll_name, path, deps, assembly_info_dir) +def add_dot_net_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_python_install(libz3Component): name = 'python_install' reg_component(name, PythonInstallComponent(name, libz3Component)) @@ -2198,6 +2300,7 @@ def mk_config(): if ONLY_MAKEFILES: return config = open(os.path.join(BUILD_DIR, 'config.mk'), 'w') + global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG, FPMATH_FLAGS, HAS_OMP, LOG_SYNC if IS_WINDOWS: config.write( 'CC=cl\n' @@ -2219,7 +2322,9 @@ def mk_config(): if HAS_OMP: extra_opt = ' /openmp' else: - extra_opt = ' -D_NO_OMP_' + extra_opt = ' /D_NO_OMP_' + if HAS_OMP and LOG_SYNC: + extra_opt = '%s /DZ3_LOG_SYNC' % extra_opt if GIT_HASH: extra_opt = ' %s /D Z3GITHASH=%s' % (extra_opt, GIT_HASH) if STATIC_BIN: @@ -2288,7 +2393,6 @@ def mk_config(): print('OCaml Native: %s' % OCAMLOPT) print('OCaml Library: %s' % OCAML_LIB) else: - global CXX, CC, GMP, FOCI2, CPPFLAGS, CXXFLAGS, LDFLAGS, EXAMP_DEBUG_FLAG, FPMATH_FLAGS OS_DEFINES = "" ARITH = "internal" check_ar() @@ -2326,6 +2430,8 @@ def mk_config(): SLIBEXTRAFLAGS = '%s -fopenmp' % SLIBEXTRAFLAGS else: CXXFLAGS = '%s -D_NO_OMP_' % CXXFLAGS + if HAS_OMP and LOG_SYNC: + CXXFLAGS = '%s -DZ3_LOG_SYNC' % CXXFLAGS if DEBUG_MODE: CXXFLAGS = '%s -g -Wall' % CXXFLAGS EXAMP_DEBUG_FLAG = '-g' @@ -2336,7 +2442,7 @@ def mk_config(): CXXFLAGS = '%s -O3 -D _EXTERNAL_RELEASE -fomit-frame-pointer' % CXXFLAGS if is_CXX_clangpp(): CXXFLAGS = '%s -Wno-unknown-pragmas -Wno-overloaded-virtual -Wno-unused-value' % CXXFLAGS - sysname = os.uname()[0] + sysname, _, _, _, machine = os.uname() if sysname == 'Darwin': SO_EXT = '.dylib' SLIBFLAGS = '-dynamiclib' @@ -2382,6 +2488,14 @@ def mk_config(): CPPFLAGS = '%s -DNDEBUG -D_EXTERNAL_RELEASE' % CPPFLAGS 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) @@ -2395,10 +2509,7 @@ def mk_config(): config.write('AR_OUTFLAG=\n') config.write('EXE_EXT=\n') config.write('LINK=%s\n' % CXX) - if STATIC_BIN: - config.write('LINK_FLAGS=-static\n') - else: - config.write('LINK_FLAGS=\n') + config.write('LINK_FLAGS=\n') config.write('LINK_OUT_FLAG=-o \n') config.write('LINK_EXTRA_FLAGS=-lpthread %s\n' % LDFLAGS) config.write('SO_EXT=%s\n' % SO_EXT) @@ -2411,6 +2522,8 @@ def mk_config(): 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('OpenMP: %s' % HAS_OMP) @@ -2471,8 +2584,9 @@ def mk_makefile(): 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" % BUILD_DIR) - out.write("\t@echo \"Z3Py scripts stored in arbitrary directories can be executed if the \'%s\' directory is added to the PYTHONPATH environment variable.\"\n" % BUILD_DIR) + 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') @@ -2566,6 +2680,16 @@ def update_version(): 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', '--long']) + res += " master " + 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) @@ -2579,6 +2703,7 @@ def mk_version_dot_h(major, minor, build, revision): '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: @@ -2677,33 +2802,42 @@ def mk_def_files(): 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_DIR, force=1) != 1: + 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 - for py in filter(lambda f: f.endswith('.py'), os.listdir(Z3PY_SRC_DIR)): - shutil.copyfile(os.path.join(Z3PY_SRC_DIR, py), os.path.join(BUILD_DIR, py)) + 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_DIR)): - shutil.copyfile(os.path.join(Z3PY_SRC_DIR, pyc), os.path.join(BUILD_DIR, pyc)) + 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("Generated '%s'" % pyc) + print("Copied '%s'" % pyc) # Python 3.x support - src_pycache = os.path.join(Z3PY_SRC_DIR, '__pycache__') + 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)): - target_pycache = os.path.join(BUILD_DIR, '__pycache__') mk_dir(target_pycache) shutil.copyfile(os.path.join(src_pycache, pyc), os.path.join(target_pycache, pyc)) if is_verbose(): - print("Generated '%s'" % pyc) + 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: @@ -2738,7 +2872,8 @@ def mk_bindings(api_files): dotnet_output_dir=dotnet_output_dir, java_output_dir=java_output_dir, java_package_name=java_package_name, - ml_output_dir=ml_output_dir + ml_output_dir=ml_output_dir, + ml_src_dir=ml_output_dir ) cp_z3py_to_build() if is_ml_enabled(): @@ -2792,172 +2927,17 @@ def mk_z3consts_java(api_files): # Extract enumeration types from z3_api.h, and add ML definitions def mk_z3consts_ml(api_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("}.*;") - ml = get_component(ML_COMPONENT) - - DeprecatedEnums = [ 'Z3_search_failure' ] - gendir = ml.src_dir - if not os.path.exists(gendir): - os.mkdir(gendir) - - efile = open('%s.ml' % os.path.join(gendir, "z3enums"), 'w') - efile.write('(* Automatically generated file *)\n\n') - efile.write('(** The enumeration types of Z3. *)\n\n') + 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) - - 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 decls.items(): - 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 decls.items(): - 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 decls.items(): - 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() + 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/z3enums.ml"' % ('%s' % gendir)) - # 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 decls.items(): - # 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)) + print ('Generated "%s"' % generated_file) def mk_gui_str(id): return '4D2F40D8-E5F9-473B-B548-%012d' % id @@ -2996,6 +2976,11 @@ def mk_vs_proj_property_groups(f, name, target_ext, 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') @@ -3145,11 +3130,6 @@ def mk_vs_proj_dll(name, components): def mk_win_dist(build_path, dist_path): for c in get_components(): c.mk_win_dist(build_path, dist_path) - # Add Z3Py to bin directory - print("Adding to %s\n" % dist_path) - 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)) def mk_unix_dist(build_path, dist_path): for c in get_components(): @@ -3198,7 +3178,7 @@ class MakeRuleCmd(object): #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) + # assert not os.path.isabs(path) install_root = cls.install_root() return install_root diff --git a/scripts/mk_win_dist.py b/scripts/mk_win_dist.py index cee8c0460..66f44426f 100644 --- a/scripts/mk_win_dist.py +++ b/scripts/mk_win_dist.py @@ -25,8 +25,13 @@ VERBOSE=True DIST_DIR='dist' FORCE_MK=False DOTNET_ENABLED=True +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 @@ -57,13 +62,17 @@ def display_help(): 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.") + 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_ENABLED + global FORCE_MK, JAVA_ENABLED, GIT_HASH, DOTNET_ENABLED, DOTNET_KEY_FILE, PYTHON_ENABLED, X86ONLY, X64ONLY path = BUILD_DIR options, remainder = getopt.gnu_getopt(sys.argv[1:], 'b:hsf', ['build=', 'help', @@ -71,7 +80,11 @@ def parse_options(): 'force', 'nojava', 'nodotnet', - 'githash' + 'dotnet-key=', + 'githash', + 'nopython', + 'x86-only', + 'x64-only' ]) for opt, arg in options: if opt in ('-b', '--build'): @@ -86,10 +99,18 @@ def parse_options(): FORCE_MK = True elif opt == '--nodotnet': DOTNET_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 + 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) @@ -101,15 +122,21 @@ def check_build_dir(path): # Create a build directory using mk_make.py def mk_build_dir(path, x64): if not check_build_dir(path) or FORCE_MK: - opts = ["python", os.path.join('scripts', 'mk_make.py'), "--parallel=24", "-b", path] + parallel = '--parallel=' + MAKEJOBS + opts = ["python", os.path.join('scripts', 'mk_make.py'), parallel, "-b", path] if DOTNET_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 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) @@ -145,7 +172,7 @@ def exec_cmds(cmds): return res # Compile Z3 (if x64 == True, then it builds it in x64 mode). -def mk_z3_core(x64): +def mk_z3(x64): cmds = [] if x64: cmds.append('call "%VCINSTALLDIR%vcvarsall.bat" amd64') @@ -157,9 +184,9 @@ def mk_z3_core(x64): if exec_cmds(cmds) != 0: raise MKException("Failed to make z3, x64: %s" % x64) -def mk_z3(): - mk_z3_core(False) - mk_z3_core(True) +def mk_z3s(): + mk_z3(False) + mk_z3(True) def get_z3_name(x64): major, minor, build, revision = get_version() @@ -172,7 +199,7 @@ def get_z3_name(x64): else: return 'z3-%s.%s.%s-%s-win' % (major, minor, build, platform) -def mk_dist_dir_core(x64): +def mk_dist_dir(x64): if x64: platform = "x64" build_path = BUILD_X64_DIR @@ -182,19 +209,21 @@ def mk_dist_dir_core(x64): dist_path = os.path.join(DIST_DIR, get_z3_name(x64)) mk_dir(dist_path) mk_util.DOTNET_ENABLED = DOTNET_ENABLED + 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_dir(): - mk_dist_dir_core(False) - mk_dist_dir_core(True) +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_core(x64): +def mk_zip(x64): dist_path = get_dist_path(x64) old = os.getcwd() try: @@ -211,31 +240,17 @@ def mk_zip_core(x64): os.chdir(old) # Create a zip file for each platform -def mk_zip(): - mk_zip_core(False) - mk_zip_core(True) +def mk_zips(): + mk_zip(False) + mk_zip(True) VS_RUNTIME_PATS = [re.compile('vcomp.*\.dll'), re.compile('msvcp.*\.dll'), re.compile('msvcr.*\.dll')] - -VS_RUNTIME_FILES = [] -def cp_vs_runtime_visitor(pattern, dir, files): - global VS_RUNTIME_FILES - for filename in files: - for pat in VS_RUNTIME_PATS: - if pat.match(filename): - if fnmatch(filename, pattern): - fname = os.path.join(dir, filename) - if not os.path.isdir(fname): - VS_RUNTIME_FILES.append(fname) - break - # Copy Visual Studio Runtime libraries -def cp_vs_runtime_core(x64): - global VS_RUNTIME_FILES +def cp_vs_runtime(x64): if x64: platform = "x64" @@ -244,34 +259,64 @@ def cp_vs_runtime_core(x64): vcdir = os.environ['VCINSTALLDIR'] path = '%sredist\\%s' % (vcdir, platform) VS_RUNTIME_FILES = [] - os.walk(path, cp_vs_runtime_visitor, '*.dll') + for root, dirs, files in os.walk(path): + for filename in files: + if fnmatch(filename, '*.dll'): + 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) + 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_runtime(): - cp_vs_runtime_core(True) - cp_vs_runtime_core(False) +def cp_vs_runtimes(): + cp_vs_runtime(True) + cp_vs_runtime(False) -def cp_license(): - shutil.copy("LICENSE.txt", os.path.join(DIST_DIR, get_dist_path(True))) - shutil.copy("LICENSE.txt", os.path.join(DIST_DIR, get_dist_path(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() - mk_build_dirs() - mk_z3() - init_project_def() - mk_dist_dir() - cp_license() - cp_vs_runtime() - mk_zip() + + 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() diff --git a/scripts/update_api.py b/scripts/update_api.py index a06f1fbb1..26f19cc1c 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -363,9 +363,9 @@ def mk_dotnet(dotnet): 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 unsafe class LIB\n') + dotnet.write(' public class LIB\n') dotnet.write(' {\n') - dotnet.write(' const string Z3_DLL_NAME = \"libz3.dll\";\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') @@ -942,7 +942,7 @@ def def_API(name, result, params): log_c.write(" Au(a%s);\n" % sz) exe_c.write("in.get_int_array(%s)" % i) else: - error ("unsupported parameter for %s, %s" % (ty, name, p)) + error ("unsupported parameter for %s, %s, %s" % (ty, name, p)) elif kind == OUT_ARRAY: sz = param_array_capacity_pos(p) sz_p = params[sz] @@ -1195,13 +1195,13 @@ def ml_alloc_and_store(t, lhs, rhs): 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_dir): +def mk_ml(ml_src_dir, ml_output_dir): global Type2Str - ml_nativef = os.path.join(ml_dir, 'z3native.ml') + 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_dir, 'z3native.ml.pre'), 'r') + 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() @@ -1250,14 +1250,14 @@ def mk_ml(ml_dir): if mk_util.is_verbose(): print ('Generated "%s"' % ml_nativef) - mk_z3native_stubs_c(ml_dir) + mk_z3native_stubs_c(ml_src_dir, ml_output_dir) -def mk_z3native_stubs_c(ml_dir): # C interface - ml_wrapperf = os.path.join(ml_dir, 'z3native_stubs.c') +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_dir, 'z3native_stubs.c.pre'), 'r') + 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() @@ -1324,36 +1324,80 @@ def mk_z3native_stubs_c(ml_dir): # C interface if len(ap) > 0: ml_wrapper.write(' unsigned _i;\n') - # declare locals, preprocess arrays, strings, in/out arguments - have_context = False + # 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 = *(Z3_context_plus*) Data_custom_val(a' + str(i) + ');\n') - ml_wrapper.write(' Z3_context _a0 = ctx_p->ctx;\n') - have_context = True + 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 = (%s*) malloc(sizeof(%s) * (_a%s));\n' % ( - type2str(param_type(param)), + 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(' %s * _a%s = 0;\n' % (type2str(param_type(param)), i)) + 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(' %s * _a%s = (%s*) malloc(sizeof(%s) * _a%s);\n' % (ts, i, ts, ts, param_array_capacity_pos(param))) + 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(' %s _a%s = %s;\n' % (type2str(t), i, ml_unwrap(t, type2str(t), 'a' + str(i)))) - elif k == OUT: - ml_wrapper.write(' %s _a%s;\n' % (type2str(param_type(param)), i)) - elif k == INOUT: - ml_wrapper.write(' %s _a%s = a%s;\n' % (type2str(param_type(param)), i, i)) + ml_wrapper.write(' _a%s = %s;\n' % (i, ml_unwrap(t, type2str(t), 'a' + str(i)))) i = i + 1 i = 0 @@ -1375,9 +1419,9 @@ def mk_z3native_stubs_c(ml_dir): # C interface if result != VOID: ts = type2str(result) if ml_has_plus_type(ts): - ml_wrapper.write('%s z3rv_m = ' % ts) + ml_wrapper.write('z3rv_m = ') else: - ml_wrapper.write('%s z3rv = ' % ts) + ml_wrapper.write('z3rv = ') # invoke procedure ml_wrapper.write('%s(' % name) @@ -1397,8 +1441,8 @@ def mk_z3native_stubs_c(ml_dir): # C interface ml_wrapper.write(');\n') if have_context and name not in Unwrapped: - ml_wrapper.write(' int ec = Z3_get_error_code(ctx_p->ctx);\n') - ml_wrapper.write(' if (ec != 0) {\n') + 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') @@ -1408,9 +1452,9 @@ def mk_z3native_stubs_c(ml_dir): # C interface if ml_has_plus_type(ts): pts = ml_plus_type(ts) if name in NULLWrapped: - ml_wrapper.write(' %s z3rv = %s_mk(z3rv_m);\n' % (pts, pts)) + ml_wrapper.write(' z3rv = %s_mk(z3rv_m);\n' % pts) else: - ml_wrapper.write(' %s z3rv = %s_mk(ctx_p, (%s) z3rv_m);\n' % (pts, pts, ml_minus_type(ts))) + ml_wrapper.write(' z3rv = %s_mk(ctx_p, (%s) z3rv_m);\n' % (pts, ml_minus_type(ts))) # convert output params if len(op) > 0: @@ -1433,10 +1477,10 @@ def mk_z3native_stubs_c(ml_dir): # C interface 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]);\n' % (pts, i, pts, ml_minus_type(ts), i)) + 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]' % i)) + 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) @@ -1450,7 +1494,7 @@ def mk_z3native_stubs_c(ml_dir): # C interface elif is_out_param(p): if ml_has_plus_type(ts): pts = ml_plus_type(ts) - ml_wrapper.write(' %s _a%dp = %s_mk(ctx_p, (%s) _a%d);\n' % (pts, i, pts, ml_minus_type(ts), i)) + 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)) @@ -1530,6 +1574,7 @@ def write_log_h_preamble(log_h): log_h.write('#define _Z3_UNUSED\n') log_h.write('#endif\n') # + log_h.write('#include\n') log_h.write('extern std::ostream * g_z3_log;\n') log_h.write('extern bool 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) { g_z3_log_enabled = false; } ~z3_log_ctx() { g_z3_log_enabled = m_prev; } bool enabled() const { return m_prev; } };\n') @@ -1556,27 +1601,25 @@ def write_core_py_preamble(core_py): core_py.write('# Automatically generated file\n') core_py.write('import sys, os\n') core_py.write('import ctypes\n') - core_py.write('from z3types import *\n') - core_py.write('from z3consts import *\n') + core_py.write('import pkg_resources\n') + core_py.write('from .z3types import *\n') + core_py.write('from .z3consts import *\n') core_py.write( """ +_ext = 'dll' if sys.platform in ('win32', 'cygwin') else 'dylib' if sys.platform == 'darwin' else 'so' + _lib = None def lib(): global _lib - if _lib == None: - _dir = os.path.dirname(os.path.abspath(__file__)) - for ext in ['dll', 'so', 'dylib']: + if _lib is None: + _dirs = ['.', os.path.dirname(os.path.abspath(__file__)), pkg_resources.resource_filename('z3', 'lib'), os.path.join(sys.prefix, 'lib'), None] + for _dir in _dirs: try: - init('libz3.%s' % ext) + init(_dir) break except: pass - try: - init(os.path.join(_dir, 'libz3.%s' % ext)) - break - except: - pass - if _lib == None: + if _lib is None: raise Z3Exception("init(Z3_LIBRARY_PATH) must be invoked before using Z3-python") return _lib @@ -1599,6 +1642,13 @@ else: return "" def init(PATH): + if PATH: + PATH = os.path.realpath(PATH) + if os.path.isdir(PATH): + PATH = os.path.join(PATH, 'libz3.%s' % _ext) + else: + PATH = 'libz3.%s' % _ext + global _lib _lib = ctypes.CDLL(PATH) """ @@ -1617,7 +1667,8 @@ def generate_files(api_files, dotnet_output_dir=None, java_output_dir=None, java_package_name=None, - ml_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`` @@ -1662,7 +1713,7 @@ def generate_files(api_files, 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, 'z3core.py') as core_py: + 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) @@ -1692,7 +1743,8 @@ def generate_files(api_files, mk_java(java_output_dir, java_package_name) if ml_output_dir: - mk_ml(ml_output_dir) + assert not ml_src_dir is None + mk_ml(ml_src_dir, ml_output_dir) def main(args): logging.basicConfig(level=logging.INFO) @@ -1719,6 +1771,10 @@ def main(args): 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, @@ -1730,6 +1786,11 @@ def main(args): 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)) @@ -1741,7 +1802,8 @@ def main(args): dotnet_output_dir=pargs.dotnet_output_dir, java_output_dir=pargs.java_output_dir, java_package_name=pargs.java_package_name, - ml_output_dir=pargs.ml_output_dir) + ml_output_dir=pargs.ml_output_dir, + ml_src_dir=pargs.ml_src_dir) return 0 if __name__ == '__main__': diff --git a/src/ackermannization/ackr_info.h b/src/ackermannization/ackr_info.h index 703f1f3d5..76be45e2b 100644 --- a/src/ackermannization/ackr_info.h +++ b/src/ackermannization/ackr_info.h @@ -20,6 +20,7 @@ Revision History: #include"ast.h" #include"ref.h" #include"expr_replacer.h" +#include"ast_translation.h" /** \brief Information about how a formula is being converted into @@ -35,7 +36,6 @@ class ackr_info { public: ackr_info(ast_manager& m) : m_m(m) - , m_consts(m) , m_er(mk_default_expr_replacer(m)) , m_subst(m_m) , m_ref_count(0) @@ -43,16 +43,20 @@ class ackr_info { {} virtual ~ackr_info() { - m_consts.reset(); + for (t2ct::iterator i = m_t2c.begin(); i != m_t2c.end(); ++i) { + m_m.dec_ref(i->m_key); + m_m.dec_ref(i->m_value); + } } inline void set_abstr(app* term, app* c) { SASSERT(!m_sealed); - SASSERT(c); + SASSERT(c && term); m_t2c.insert(term,c); m_c2t.insert(c->get_decl(),term); m_subst.insert(term, c); - m_consts.push_back(c); + m_m.inc_ref(term); + m_m.inc_ref(c); } inline void abstract(expr * e, expr_ref& res) { @@ -77,6 +81,17 @@ class ackr_info { m_er->set_substitution(&m_subst); } + virtual ackr_info * translate(ast_translation & translator) { + ackr_info * const retv = alloc(ackr_info, translator.to()); + for (t2ct::iterator i = m_t2c.begin(); i != m_t2c.end(); ++i) { + app * const k = translator(i->m_key); + app * const v = translator(i->m_value); + retv->set_abstr(k, v); + } + if (m_sealed) retv->seal(); + return retv; + } + // // Reference counting // @@ -94,7 +109,6 @@ class ackr_info { t2ct m_t2c; // terms to constants c2tt m_c2t; // constants to terms (inversion of m_t2c) - expr_ref_vector m_consts; // the constants introduced during abstraction // replacer and substitution used to compute abstractions scoped_ptr m_er; diff --git a/src/ackermannization/ackr_model_converter.cpp b/src/ackermannization/ackr_model_converter.cpp index eb24ee927..ea4f858ad 100644 --- a/src/ackermannization/ackr_model_converter.cpp +++ b/src/ackermannization/ackr_model_converter.cpp @@ -53,7 +53,16 @@ public: //void display(std::ostream & out); - virtual model_converter * translate(ast_translation & translator) {NOT_IMPLEMENTED_YET();} + virtual model_converter * translate(ast_translation & translator) { + 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); + } + } protected: ast_manager& m; const ackr_info_ref info; diff --git a/src/api/api_algebraic.cpp b/src/api/api_algebraic.cpp index bee39aa2a..c4e4dac5d 100644 --- a/src/api/api_algebraic.cpp +++ b/src/api/api_algebraic.cpp @@ -7,7 +7,7 @@ Module Name: Abstract: - Additional APIs for handling Z3 algebraic numbers encoded as + Additional APIs for handling Z3 algebraic numbers encoded as Z3_ASTs Author: @@ -15,9 +15,8 @@ Author: Leonardo de Moura (leonardo) 2012-12-07 Notes: - + --*/ -#include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" @@ -74,9 +73,9 @@ 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)) || + return + is_expr(a) && + (_c->autil().is_numeral(to_expr(a)) || _c->autil().is_irrational_algebraic_numeral(to_expr(a))); } @@ -162,9 +161,9 @@ extern "C" { 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, 0); - CHECK_IS_ALGEBRAIC_X(b, 0); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC_X(a, 0); + CHECK_IS_ALGEBRAIC_X(b, 0); BIN_OP(+,add); Z3_CATCH_RETURN(0); } @@ -172,9 +171,9 @@ extern "C" { 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, 0); - CHECK_IS_ALGEBRAIC_X(b, 0); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC_X(a, 0); + CHECK_IS_ALGEBRAIC_X(b, 0); BIN_OP(-,sub); Z3_CATCH_RETURN(0); } @@ -182,9 +181,9 @@ extern "C" { 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, 0); - CHECK_IS_ALGEBRAIC_X(b, 0); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC_X(a, 0); + CHECK_IS_ALGEBRAIC_X(b, 0); BIN_OP(*,mul); Z3_CATCH_RETURN(0); } @@ -219,8 +218,8 @@ extern "C" { 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()); + scoped_anum av(_am); + _am.set(av, get_rational(c, a).to_mpq()); _am.root(av, k, _r); } else { @@ -241,8 +240,8 @@ extern "C" { 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()); + scoped_anum av(_am); + _am.set(av, get_rational(c, a).to_mpq()); _am.power(av, k, _r); } else { @@ -328,7 +327,7 @@ extern "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()); + _am.set(tmp, get_rational(c, a[i]).to_mpq()); as.push_back(tmp); } else if (is_irrational(c, a[i])) { @@ -378,7 +377,7 @@ extern "C" { vector_var2anum v2a(as); _am.isolate_roots(_p, v2a, roots); } - Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, mk_c(c)->m()); + 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)); diff --git a/src/api/api_arith.cpp b/src/api/api_arith.cpp index dcd250c98..51aea9676 100644 --- a/src/api/api_arith.cpp +++ b/src/api/api_arith.cpp @@ -15,7 +15,6 @@ Author: Revision History: --*/ -#include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" @@ -37,7 +36,7 @@ extern "C" { RETURN_Z3(r); Z3_CATCH_RETURN(0); } - + Z3_sort Z3_API Z3_mk_real_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_real_sort(c); @@ -50,7 +49,7 @@ extern "C" { 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(); + RESET_ERROR_CODE(); if (den == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); @@ -60,7 +59,7 @@ extern "C" { RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } - + 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); @@ -70,17 +69,17 @@ extern "C" { 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(); + 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, 0, 2, args); - mk_c(c)->save_ast_trail(a); - check_sorts(c, a); + 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, 0, 2, args); + mk_c(c)->save_ast_trail(a); + check_sorts(c, a); RETURN_Z3(of_ast(a)); Z3_CATCH_RETURN(0); } @@ -142,7 +141,7 @@ extern "C" { 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); + mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } @@ -160,7 +159,7 @@ extern "C" { 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); + mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } @@ -176,7 +175,7 @@ extern "C" { RETURN_Z3(0); } expr * r = mk_c(c)->autil().mk_numeral(numerator(val), true); - mk_c(c)->save_ast_trail(r); + mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } @@ -192,7 +191,7 @@ extern "C" { RETURN_Z3(0); } expr * r = mk_c(c)->autil().mk_numeral(denominator(val), true); - mk_c(c)->save_ast_trail(r); + mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(0); } diff --git a/src/api/api_array.cpp b/src/api/api_array.cpp index d3dda5d9d..ed431882e 100644 --- a/src/api/api_array.cpp +++ b/src/api/api_array.cpp @@ -15,7 +15,6 @@ Author: Revision History: --*/ -#include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" @@ -27,7 +26,7 @@ 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(); + 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); @@ -57,7 +56,7 @@ extern "C" { RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } - + Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v) { Z3_TRY; LOG_Z3_mk_store(c, a, i, v); @@ -82,7 +81,7 @@ extern "C" { RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } - + Z3_ast Z3_API Z3_mk_map(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast const* args) { Z3_TRY; LOG_Z3_mk_map(c, f, n, args); @@ -94,7 +93,7 @@ extern "C" { ast_manager & m = mk_c(c)->m(); func_decl* _f = to_func_decl(f); expr* const* _args = to_exprs(args); - + ptr_vector domain; for (unsigned i = 0; i < n; ++i) { domain.push_back(m.get_sort(_args[i])); @@ -111,7 +110,7 @@ extern "C" { 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(); + RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _v = to_expr(v); sort * _range = m.get_sort(_v); @@ -123,14 +122,14 @@ extern "C" { app * r = m.mk_app(cd, 1, &_v); mk_c(c)->save_ast_trail(r); check_sorts(c, r); - RETURN_Z3(of_ast(r)); + RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } 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(); + RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _a = to_expr(array); @@ -138,12 +137,12 @@ extern "C" { app * r = m.mk_app(f, 1, &_a); mk_c(c)->save_ast_trail(r); check_sorts(c, r); - RETURN_Z3(of_ast(r)); + RETURN_Z3(of_ast(r)); Z3_CATCH_RETURN(0); } Z3_ast mk_app_array_core(Z3_context c, Z3_sort domain, Z3_ast v) { - RESET_ERROR_CODE(); + RESET_ERROR_CODE(); ast_manager & m = mk_c(c)->m(); expr * _v = to_expr(v); sort * _range = m.get_sort(_v); @@ -178,7 +177,7 @@ extern "C" { 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); + RETURN_Z3(r); Z3_CATCH_RETURN(0); } @@ -205,8 +204,8 @@ extern "C" { Z3_TRY; LOG_Z3_get_array_sort_domain(c, t); RESET_ERROR_CODE(); - CHECK_VALID_AST(t, 0); - if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && + CHECK_VALID_AST(t, 0); + if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && to_sort(t)->get_decl_kind() == ARRAY_SORT) { Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(0).get_ast()); RETURN_Z3(r); @@ -215,13 +214,13 @@ extern "C" { RETURN_Z3(0); Z3_CATCH_RETURN(0); } - + 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(); + RESET_ERROR_CODE(); CHECK_VALID_AST(t, 0); - if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && + if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && to_sort(t)->get_decl_kind() == ARRAY_SORT) { Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(1).get_ast()); RETURN_Z3(r); diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 7774efcd9..f3b3b7edf 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -335,7 +335,7 @@ extern "C" { Z3_bool Z3_API Z3_is_app(Z3_context c, Z3_ast a) { LOG_Z3_is_app(c, a); RESET_ERROR_CODE(); - return is_app(reinterpret_cast(a)); + return a != 0 && is_app(reinterpret_cast(a)); } Z3_app Z3_API Z3_to_app(Z3_context c, Z3_ast a) { @@ -727,7 +727,7 @@ extern "C" { Z3_TRY; LOG_Z3_simplify_get_param_descrs(c); RESET_ERROR_CODE(); - Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); + 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); @@ -970,8 +970,7 @@ extern "C" { case PR_TH_LEMMA: return Z3_OP_PR_TH_LEMMA; case PR_HYPER_RESOLVE: return Z3_OP_PR_HYPER_RESOLVE; default: - UNREACHABLE(); - return Z3_OP_UNINTERPRETED; + return Z3_OP_INTERNAL; } } if (mk_c(c)->get_arith_fid() == _d->get_family_id()) { @@ -995,8 +994,7 @@ extern "C" { case OP_TO_INT: return Z3_OP_TO_INT; case OP_IS_INT: return Z3_OP_IS_INT; default: - UNREACHABLE(); - return Z3_OP_UNINTERPRETED; + return Z3_OP_INTERNAL; } } if (mk_c(c)->get_array_fid() == _d->get_family_id()) { @@ -1014,8 +1012,7 @@ extern "C" { case OP_AS_ARRAY: return Z3_OP_AS_ARRAY; case OP_ARRAY_EXT: return Z3_OP_ARRAY_EXT; default: - UNREACHABLE(); - return Z3_OP_UNINTERPRETED; + return Z3_OP_INTERNAL; } } @@ -1072,17 +1069,17 @@ extern "C" { 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_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: - UNREACHABLE(); - return Z3_OP_UNINTERPRETED; + return Z3_OP_INTERNAL; } } if (mk_c(c)->get_dt_fid() == _d->get_family_id()) { @@ -1092,8 +1089,7 @@ extern "C" { case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR; case OP_DT_UPDATE_FIELD: return Z3_OP_DT_UPDATE_FIELD; default: - UNREACHABLE(); - return Z3_OP_UNINTERPRETED; + return Z3_OP_INTERNAL; } } if (mk_c(c)->get_datalog_fid() == _d->get_family_id()) { @@ -1114,34 +1110,40 @@ extern "C" { case datalog::OP_DL_CONSTANT: return Z3_OP_FD_CONSTANT; case datalog::OP_DL_LT: return Z3_OP_FD_LT; default: - UNREACHABLE(); - return Z3_OP_UNINTERPRETED; + return Z3_OP_INTERNAL; } } if (mk_c(c)->get_seq_fid() == _d->get_family_id()) { switch (_d->get_decl_kind()) { - case Z3_OP_SEQ_UNIT: return Z3_OP_SEQ_UNIT; - case Z3_OP_SEQ_EMPTY: return Z3_OP_SEQ_EMPTY; - case Z3_OP_SEQ_CONCAT: return Z3_OP_SEQ_CONCAT; - case Z3_OP_SEQ_PREFIX: return Z3_OP_SEQ_PREFIX; - case Z3_OP_SEQ_SUFFIX: return Z3_OP_SEQ_SUFFIX; - case Z3_OP_SEQ_CONTAINS: return Z3_OP_SEQ_CONTAINS; - case Z3_OP_SEQ_EXTRACT: return Z3_OP_SEQ_EXTRACT; - case Z3_OP_SEQ_REPLACE: return Z3_OP_SEQ_REPLACE; - case Z3_OP_SEQ_AT: return Z3_OP_SEQ_AT; - case Z3_OP_SEQ_LENGTH: return Z3_OP_SEQ_LENGTH; - case Z3_OP_SEQ_INDEX: return Z3_OP_SEQ_INDEX; - case Z3_OP_SEQ_TO_RE: return Z3_OP_SEQ_TO_RE; - case Z3_OP_SEQ_IN_RE: return Z3_OP_SEQ_IN_RE; + 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_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 Z3_OP_RE_PLUS: return Z3_OP_RE_PLUS; - case Z3_OP_RE_STAR: return Z3_OP_RE_STAR; - case Z3_OP_RE_OPTION: return Z3_OP_RE_OPTION; - case Z3_OP_RE_CONCAT: return Z3_OP_RE_CONCAT; - case Z3_OP_RE_UNION: return Z3_OP_RE_UNION; + 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_SET: return Z3_OP_RE_FULL_SET; + case OP_RE_EMPTY_SET: return Z3_OP_RE_EMPTY_SET; default: - return Z3_OP_UNINTERPRETED; + return Z3_OP_INTERNAL; } } @@ -1211,8 +1213,7 @@ extern "C" { case OP_FPA_INTERNAL_TO_IEEE_BV_UNSPECIFIED: return Z3_OP_UNINTERPRETED; default: - UNREACHABLE(); - return Z3_OP_UNINTERPRETED; + return Z3_OP_INTERNAL; } } @@ -1221,8 +1222,7 @@ extern "C" { case OP_LABEL: return Z3_OP_LABEL; case OP_LABEL_LIT: return Z3_OP_LABEL_LIT; default: - UNREACHABLE(); - return Z3_OP_UNINTERPRETED; + return Z3_OP_INTERNAL; } } @@ -1230,8 +1230,10 @@ extern "C" { 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; - default: UNREACHABLE(); + case OP_AT_LEAST_K: return Z3_OP_PB_AT_LEAST; + default: return Z3_OP_INTERNAL; } } diff --git a/src/api/api_ast_map.cpp b/src/api/api_ast_map.cpp index d9e08ec3e..aa52faa98 100644 --- a/src/api/api_ast_map.cpp +++ b/src/api/api_ast_map.cpp @@ -34,7 +34,7 @@ extern "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)->m()); + 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); @@ -137,7 +137,7 @@ extern "C" { Z3_TRY; LOG_Z3_ast_map_keys(c, m); RESET_ERROR_CODE(); - Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, to_ast_map(m)->m); + 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(); diff --git a/src/api/api_ast_map.h b/src/api/api_ast_map.h index 8e4f24623..dc1cf90ad 100644 --- a/src/api/api_ast_map.h +++ b/src/api/api_ast_map.h @@ -24,7 +24,7 @@ Revision History: struct Z3_ast_map_ref : public api::object { ast_manager & m; obj_map m_map; - Z3_ast_map_ref(ast_manager & _m):m(_m) {} + Z3_ast_map_ref(api::context& c, ast_manager & _m): api::object(c), m(_m) {} virtual ~Z3_ast_map_ref(); }; diff --git a/src/api/api_ast_vector.cpp b/src/api/api_ast_vector.cpp index e1d4d78ff..40df13708 100644 --- a/src/api/api_ast_vector.cpp +++ b/src/api/api_ast_vector.cpp @@ -29,7 +29,7 @@ extern "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)->m()); + 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); @@ -111,7 +111,7 @@ extern "C" { RETURN_Z3(0); } 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)->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++) { diff --git a/src/api/api_ast_vector.h b/src/api/api_ast_vector.h index 20efa21c5..4fcf8ffb8 100644 --- a/src/api/api_ast_vector.h +++ b/src/api/api_ast_vector.h @@ -20,9 +20,13 @@ Revision History: #include"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(ast_manager & m):m_ast_vector(m) {} + Z3_ast_vector_ref(api::context& c, ast_manager & m): api::object(c), m_ast_vector(m) {} virtual ~Z3_ast_vector_ref() {} }; diff --git a/src/api/api_bv.cpp b/src/api/api_bv.cpp index 353cf913c..ff090ef54 100644 --- a/src/api/api_bv.cpp +++ b/src/api/api_bv.cpp @@ -15,7 +15,6 @@ Author: Revision History: --*/ -#include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" @@ -27,7 +26,7 @@ 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(); + RESET_ERROR_CODE(); if (sz == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); } @@ -39,7 +38,7 @@ extern "C" { #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); @@ -75,11 +74,11 @@ extern "C" { 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); + 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); @@ -88,7 +87,7 @@ extern "C" { RETURN_Z3(r); Z3_CATCH_RETURN(0); } - + #define MK_BV_PUNARY(NAME, OP) \ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_TRY; \ @@ -113,7 +112,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_ast Z3_API Z3_mk_bv2int(Z3_context c, Z3_ast n, Z3_bool is_signed) { Z3_TRY; LOG_Z3_mk_bv2int(c, n, is_signed); - RESET_ERROR_CODE(); + RESET_ERROR_CODE(); Z3_sort int_s = Z3_mk_int_sort(c); if (is_signed) { Z3_ast r = Z3_mk_bv2int(c, n, false); @@ -125,7 +124,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ 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_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 }; @@ -140,19 +139,19 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ 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)); + 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(0); } /** \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) @@ -343,7 +342,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ return Z3_mk_not(c, eq); Z3_CATCH_RETURN(0); } - + // 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; @@ -369,7 +368,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ return result; Z3_CATCH_RETURN(0); } - + 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); @@ -389,7 +388,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ 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(); + 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(); @@ -398,5 +397,5 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ return 0; Z3_CATCH_RETURN(0); } - + }; diff --git a/src/api/api_config_params.cpp b/src/api/api_config_params.cpp index 5b385a872..a04a9f12c 100644 --- a/src/api/api_config_params.cpp +++ b/src/api/api_config_params.cpp @@ -37,7 +37,7 @@ extern "C" { catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. - warning_msg(ex.msg()); + warning_msg("%s", ex.msg()); } } @@ -62,7 +62,7 @@ extern "C" { catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. - warning_msg(ex.msg()); + warning_msg("%s", ex.msg()); return Z3_FALSE; } } @@ -88,7 +88,7 @@ extern "C" { catch (z3_exception & ex) { // The error handler is only available for contexts // Just throw a warning. - warning_msg(ex.msg()); + warning_msg("%s", ex.msg()); } } diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 8fbb02598..fd3d16bd0 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -17,6 +17,7 @@ Author: Revision History: --*/ +#include #include"api_context.h" #include"smtparser.h" #include"version.h" @@ -32,6 +33,28 @@ 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); @@ -47,22 +70,6 @@ namespace api { // // ------------------------ - context::set_interruptable::set_interruptable(context & ctx, event_handler & i): - m_ctx(ctx) { - #pragma omp critical (set_interruptable) - { - SASSERT(m_ctx.m_interruptable == 0); - m_ctx.m_interruptable = &i; - } - } - - context::set_interruptable::~set_interruptable() { - #pragma omp critical (set_interruptable) - { - m_ctx.m_interruptable = 0; - } - } - context::context(context_params * p, bool user_ref_count): m_params(p != 0 ? *p : context_params()), m_user_ref_count(user_ref_count), @@ -83,11 +90,10 @@ namespace api { m_print_mode = Z3_PRINT_SMTLIB_FULL; m_searching = false; - m_interruptable = 0; - m_smtlib_parser = 0; m_smtlib_parser_has_decls = false; - + + m_interruptable = 0; m_error_handler = &default_error_handler; m_basic_fid = m().get_basic_family_id(); @@ -108,6 +114,30 @@ namespace api { context::~context() { reset_parser(); + m_last_obj = 0; + u_map::iterator it = m_allocated_objects.begin(); + while (it != m_allocated_objects.end()) { + DEBUG_CODE(warning_msg("Uncollected memory: %d: %s", it->m_key, typeid(*it->m_value).name());); + m_allocated_objects.remove(it->m_key); + dealloc(it->m_value); + it = m_allocated_objects.begin(); + } + } + + context::set_interruptable::set_interruptable(context & ctx, event_handler & i): + m_ctx(ctx) { + #pragma omp critical (set_interruptable) + { + SASSERT(m_ctx.m_interruptable == 0); + m_ctx.m_interruptable = &i; + } + } + + context::set_interruptable::~set_interruptable() { + #pragma omp critical (set_interruptable) + { + m_ctx.m_interruptable = 0; + } } void context::interrupt() { @@ -386,6 +416,7 @@ extern "C" { return; } mk_c(c)->m().dec_ref(to_ast(a)); + Z3_CATCH; } @@ -401,6 +432,11 @@ extern "C" { *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); @@ -465,6 +501,10 @@ extern "C" { return _get_error_msg(c, err); } + Z3_API char const * Z3_get_error_msg_ex(Z3_context c, Z3_error_code err) { + return Z3_get_error_msg(c, err); + } + void Z3_API Z3_set_ast_print_mode(Z3_context c, Z3_ast_print_mode mode) { Z3_TRY; diff --git a/src/api/api_context.h b/src/api/api_context.h index 0f2104a2b..7459bd102 100644 --- a/src/api/api_context.h +++ b/src/api/api_context.h @@ -36,6 +36,7 @@ Revision History: #include"tactic_manager.h" #include"context_params.h" #include"api_polynomial.h" +#include"hashtable.h" namespace smtlib { class parser; @@ -72,6 +73,8 @@ namespace api { 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; @@ -95,7 +98,7 @@ namespace api { event_handler * m_interruptable; // Reference to an object that can be interrupted by Z3_interrupt - public: + public: // Scoped obj for setting m_interruptable class set_interruptable { context & m_ctx; @@ -147,6 +150,9 @@ namespace api { // 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; } @@ -245,7 +251,7 @@ inline api::context * mk_c(Z3_context c) { return reinterpret_castcheck_searching(); inline bool is_expr(Z3_ast a) { return is_expr(to_ast(a)); } -#define CHECK_IS_EXPR(_p_, _ret_) { if (!is_expr(_p_)) { SET_ERROR_CODE(Z3_INVALID_ARG); return _ret_; } } +#define CHECK_IS_EXPR(_p_, _ret_) { if (_p_ == 0 || !is_expr(_p_)) { SET_ERROR_CODE(Z3_INVALID_ARG); 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); return _ret_; } } inline void check_sorts(Z3_context c, ast * n) { mk_c(c)->check_sorts(n); } diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index be9f5894f..8843256c6 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -189,7 +189,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, unsigned __int64 size) { + Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, __uint64 size) { Z3_TRY; LOG_Z3_mk_finite_domain_sort(c, name, size); RESET_ERROR_CODE(); @@ -199,7 +199,7 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_bool Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, unsigned __int64 * out) { + Z3_bool Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, __uint64 * out) { Z3_TRY; if (out) { *out = 0; @@ -223,7 +223,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_fixedpoint(c); RESET_ERROR_CODE(); - Z3_fixedpoint_ref * d = alloc(Z3_fixedpoint_ref); + 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); @@ -290,8 +290,8 @@ extern "C" { r = to_fixedpoint_ref(d)->ctx().query(to_expr(q)); } catch (z3_exception& ex) { - mk_c(c)->handle_exception(ex); r = l_undef; + mk_c(c)->handle_exception(ex); } to_fixedpoint_ref(d)->ctx().cleanup(); } @@ -369,7 +369,7 @@ extern "C" { return 0; } - Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, m); + Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, *mk_c(c), m); mk_c(c)->save_object(v); for (unsigned i = 0; i < coll.m_queries.size(); ++i) { v->m_ast_vector.push_back(coll.m_queries[i].get()); @@ -421,7 +421,7 @@ extern "C" { Z3_TRY; LOG_Z3_fixedpoint_get_statistics(c, d); RESET_ERROR_CODE(); - Z3_stats_ref * st = alloc(Z3_stats_ref); + 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); @@ -460,7 +460,7 @@ extern "C" { 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, 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; @@ -483,7 +483,7 @@ extern "C" { 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, 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) { @@ -568,7 +568,7 @@ extern "C" { Z3_TRY; LOG_Z3_fixedpoint_get_param_descrs(c, f); RESET_ERROR_CODE(); - Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); + 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); diff --git a/src/api/api_datalog.h b/src/api/api_datalog.h index 8317426c1..8c095d546 100644 --- a/src/api/api_datalog.h +++ b/src/api/api_datalog.h @@ -30,13 +30,14 @@ typedef void (*reduce_assign_callback_fptr)(void*, func_decl*, unsigned, expr*co 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():m_datalog(0) {} + Z3_fixedpoint_ref(api::context& c): api::object(c), m_datalog(0) {} virtual ~Z3_fixedpoint_ref() { dealloc(m_datalog); } }; diff --git a/src/api/api_datatype.cpp b/src/api/api_datatype.cpp index 706ba9d89..5096c8e80 100644 --- a/src/api/api_datatype.cpp +++ b/src/api/api_datatype.cpp @@ -15,7 +15,6 @@ Author: Revision History: --*/ -#include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" @@ -24,16 +23,16 @@ Revision History: extern "C" { - Z3_sort Z3_API Z3_mk_tuple_sort(Z3_context c, + Z3_sort Z3_API Z3_mk_tuple_sort(Z3_context c, Z3_symbol name, - unsigned num_fields, + 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(); + RESET_ERROR_CODE(); mk_c(c)->reset_last_result(); ast_manager& m = mk_c(c)->m(); datatype_util& dt_util = mk_c(c)->dtutil(); @@ -43,14 +42,14 @@ extern "C" { 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(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(to_symbol(name), 1, constrs); bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, tuples); @@ -63,7 +62,7 @@ extern "C" { } // create tuple type - SASSERT(tuples.size() == 1); + SASSERT(tuples.size() == 1); tuple = tuples[0].get(); mk_c(c)->save_multiple_ast_trail(tuple); @@ -72,9 +71,9 @@ extern "C" { 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_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); if (!accs) { @@ -90,8 +89,8 @@ extern "C" { RETURN_Z3_mk_tuple_sort(of_sort(tuple)); Z3_CATCH_RETURN(0); } - - Z3_sort Z3_API Z3_mk_enumeration_sort(Z3_context c, + + Z3_sort Z3_API Z3_mk_enumeration_sort(Z3_context c, Z3_symbol name, unsigned n, Z3_symbol const enum_names[], @@ -106,7 +105,7 @@ extern "C" { 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])); @@ -128,9 +127,9 @@ extern "C" { RETURN_Z3(0); } } - + // create enum type. - SASSERT(sorts.size() == 1); + SASSERT(sorts.size() == 1); e = sorts[0].get(); mk_c(c)->save_multiple_ast_trail(e); @@ -141,10 +140,10 @@ extern "C" { SASSERT(decls && decls->size() == n); for (unsigned i = 0; i < n; ++i) { func_decl* decl = (*decls)[i]; - mk_c(c)->save_multiple_ast_trail(decl); + mk_c(c)->save_multiple_ast_trail(decl); enum_consts[i] = of_func_decl(decl); decl = dt_util.get_constructor_recognizer(decl); - mk_c(c)->save_multiple_ast_trail(decl); + mk_c(c)->save_multiple_ast_trail(decl); enum_testers[i] = of_func_decl(decl); } @@ -168,11 +167,11 @@ extern "C" { ast_manager& m = mk_c(c)->m(); mk_c(c)->reset_last_result(); datatype_util data_util(m); - accessor_decl* head_tail[2] = { + accessor_decl* head_tail[2] = { mk_accessor_decl(symbol("head"), type_ref(to_sort(elem_sort))), mk_accessor_decl(symbol("tail"), type_ref(0)) }; - constructor_decl* constrs[2] = { + constructor_decl* constrs[2] = { mk_constructor_decl(symbol("nil"), symbol("is_nil"), 0, 0), // Leo: SMT 2.0 document uses 'insert' instead of cons mk_constructor_decl(symbol("cons"), symbol("is_cons"), 2, head_tail) @@ -197,22 +196,22 @@ extern "C" { func_decl* f; if (nil_decl) { f = cnstrs[0]; - mk_c(c)->save_multiple_ast_trail(f); + mk_c(c)->save_multiple_ast_trail(f); *nil_decl = of_func_decl(f); } if (is_nil_decl) { f = data_util.get_constructor_recognizer(cnstrs[0]); - mk_c(c)->save_multiple_ast_trail(f); + mk_c(c)->save_multiple_ast_trail(f); *is_nil_decl = of_func_decl(f); } if (cons_decl) { f = cnstrs[1]; - mk_c(c)->save_multiple_ast_trail(f); + mk_c(c)->save_multiple_ast_trail(f); *cons_decl = of_func_decl(f); } if (is_cons_decl) { f = data_util.get_constructor_recognizer(cnstrs[1]); - mk_c(c)->save_multiple_ast_trail(f); + mk_c(c)->save_multiple_ast_trail(f); *is_cons_decl = of_func_decl(f); } if (head_decl) { @@ -220,7 +219,7 @@ extern "C" { SASSERT(acc); SASSERT(acc->size() == 2); f = (*acc)[0]; - mk_c(c)->save_multiple_ast_trail(f); + mk_c(c)->save_multiple_ast_trail(f); *head_decl = of_func_decl(f); } if (tail_decl) { @@ -228,7 +227,7 @@ extern "C" { SASSERT(acc); SASSERT(acc->size() == 2); f = (*acc)[1]; - mk_c(c)->save_multiple_ast_trail(f); + mk_c(c)->save_multiple_ast_trail(f); *tail_decl = of_func_decl(f); } RETURN_Z3_mk_list_sort(of_sort(s)); @@ -255,7 +254,7 @@ extern "C" { ) { Z3_TRY; LOG_Z3_mk_constructor(c, name, tester, num_fields, field_names, sorts, sort_refs); - RESET_ERROR_CODE(); + RESET_ERROR_CODE(); ast_manager& m = mk_c(c)->m(); constructor* cnstr = alloc(constructor, m); cnstr->m_name = to_symbol(name); @@ -291,7 +290,7 @@ extern "C" { if (!f) { SET_ERROR_CODE(Z3_INVALID_ARG); return; - } + } if (constructor_decl) { mk_c(c)->save_multiple_ast_trail(f); *constructor_decl = of_func_decl(f); @@ -301,15 +300,15 @@ extern "C" { mk_c(c)->save_multiple_ast_trail(f2); *tester = of_func_decl(f2); } - + ptr_vector const* accs = data_util.get_constructor_accessors(f); if (!accs && num_fields > 0) { SET_ERROR_CODE(Z3_INVALID_ARG); - return; + return; } for (unsigned i = 0; i < num_fields; ++i) { func_decl* f2 = (*accs)[i]; - mk_c(c)->save_multiple_ast_trail(f2); + mk_c(c)->save_multiple_ast_trail(f2); accessors[i] = of_func_decl(f2); } RETURN_Z3_query_constructor; @@ -324,7 +323,7 @@ extern "C" { Z3_CATCH; } - static datatype_decl* mk_datatype_decl(Z3_context c, + static datatype_decl* mk_datatype_decl(Z3_context c, Z3_symbol name, unsigned num_constructors, Z3_constructor constructors[]) { @@ -342,7 +341,7 @@ extern "C" { } constrs.push_back(mk_constructor_decl(cn->m_name, cn->m_tester, acc.size(), acc.c_ptr())); } - return mk_datatype_decl(to_symbol(name), num_constructors, constrs.c_ptr()); + return mk_datatype_decl(to_symbol(name), num_constructors, constrs.c_ptr()); } Z3_sort Z3_API Z3_mk_datatype(Z3_context c, @@ -352,9 +351,9 @@ extern "C" { Z3_TRY; LOG_Z3_mk_datatype(c, name, num_constructors, constructors); RESET_ERROR_CODE(); - ast_manager& m = mk_c(c)->m(); + 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); @@ -370,7 +369,7 @@ extern "C" { 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]; @@ -411,7 +410,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_datatypes(c, num_sorts, sort_names, sorts, constructor_lists); RESET_ERROR_CODE(); - ast_manager& m = mk_c(c)->m(); + ast_manager& m = mk_c(c)->m(); mk_c(c)->reset_last_result(); datatype_util data_util(m); @@ -423,7 +422,7 @@ extern "C" { sort_ref_vector _sorts(m); bool ok = mk_c(c)->get_dt_plugin()->mk_datatypes(datas.size(), datas.c_ptr(), _sorts); del_datatype_decls(datas.size(), datas.c_ptr()); - + if (!ok) { SET_ERROR_CODE(Z3_INVALID_ARG); return; @@ -437,8 +436,8 @@ extern "C" { 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]; + constructor* cn = (*cl)[j]; + cn->m_constructor = (*cnstrs)[j]; } } RETURN_Z3_mk_datatypes; @@ -452,15 +451,15 @@ extern "C" { 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); - return 0; + return 0; } ptr_vector const * decls = dt_util.get_datatype_constructors(_t); if (!decls) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return 0; } return decls->size(); Z3_CATCH_RETURN(0); @@ -468,7 +467,7 @@ extern "C" { Z3_func_decl get_datatype_sort_constructor_core(Z3_context c, Z3_sort t, unsigned idx) { RESET_ERROR_CODE(); - CHECK_VALID_AST(t, 0); + CHECK_VALID_AST(t, 0); sort * _t = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { @@ -497,10 +496,10 @@ extern "C" { 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(); + 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); RETURN_Z3(0); @@ -520,13 +519,13 @@ extern "C" { 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(); + 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); - RETURN_Z3(0); + RETURN_Z3(0); } ptr_vector const * decls = dt_util.get_datatype_constructors(_t); if (!decls || idx_c >= decls->size()) { @@ -536,24 +535,24 @@ extern "C" { func_decl* decl = (*decls)[idx_c]; if (decl->get_arity() <= idx_a) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(0); } ptr_vector const * accs = dt_util.get_constructor_accessors(decl); SASSERT(accs && accs->size() == decl->get_arity()); if (!accs || accs->size() <= idx_a) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(0); } decl = (*accs)[idx_a]; mk_c(c)->save_ast_trail(decl); - RETURN_Z3(of_func_decl(decl)); + RETURN_Z3(of_func_decl(decl)); Z3_CATCH_RETURN(0); } 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(); + 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) { @@ -564,34 +563,34 @@ extern "C" { RETURN_Z3(r); Z3_CATCH_RETURN(0); } - + 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(); + 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); - return 0; + return 0; } ptr_vector const * decls = dt_util.get_datatype_constructors(tuple); if (!decls || decls->size() != 1) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return 0; } ptr_vector const * accs = dt_util.get_constructor_accessors((*decls)[0]); if (!accs) { - return 0; + return 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(); + 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) { @@ -619,14 +618,14 @@ extern "C" { } Z3_ast Z3_datatype_update_field( - Z3_context c, Z3_func_decl f, Z3_ast t, Z3_ast v) { + 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* _v = to_expr(v); expr* args[2] = { _t, _v }; sort* domain[2] = { m.get_sort(_t), m.get_sort(_v) }; parameter param(_f); diff --git a/src/api/api_fpa.cpp b/src/api/api_fpa.cpp index 37197e730..6dad5048f 100644 --- a/src/api/api_fpa.cpp +++ b/src/api/api_fpa.cpp @@ -909,12 +909,18 @@ extern "C" { 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 == 0) { + SET_ERROR_CODE(Z3_INVALID_ARG); + return 0; + } ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); - fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(mk_c(c)->get_fpa_fid()); 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)) { + if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } @@ -929,10 +935,44 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_string Z3_API Z3_fpa_get_numeral_significand_string(Z3_context c, Z3_ast t) { + Z3_ast Z3_API Z3_fpa_get_numeral_sign_bv(Z3_context c, Z3_ast t) { Z3_TRY; - LOG_Z3_fpa_get_numeral_significand_string(c, t); + LOG_Z3_fpa_get_numeral_sign_bv(c, t); RESET_ERROR_CODE(); + CHECK_NON_NULL(t, 0); + CHECK_VALID_AST(t, 0); + 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); + RETURN_Z3(0); + } + 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); + return 0; + } + 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(0); + } + + 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, 0); + CHECK_VALID_AST(t, 0); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); unsynch_mpq_manager & mpqm = mpfm.mpq_manager(); @@ -940,17 +980,47 @@ extern "C" { 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_app_of(e, fid, OP_FPA_PLUS_INF) || - is_app_of(e, fid, OP_FPA_MINUS_INF)) { + if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { + SET_ERROR_CODE(Z3_INVALID_ARG); + RETURN_Z3(0); + } + 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); + RETURN_Z3(0); + } + 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(0); + } + + 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, 0); + CHECK_VALID_AST(t, 0); + 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); 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))) { - SET_ERROR_CODE(Z3_INVALID_ARG) + if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { + SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } unsigned sbits = val.get().get_sbits(); @@ -958,6 +1028,7 @@ extern "C" { 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()); @@ -968,6 +1039,12 @@ extern "C" { 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 == 0) { + SET_ERROR_CODE(Z3_INVALID_ARG); + return 0; + } ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); unsynch_mpz_manager & mpzm = mpfm.mpz_manager(); @@ -975,10 +1052,7 @@ extern "C" { 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_app_of(e, fid, OP_FPA_PLUS_INF) || - is_app_of(e, fid, OP_FPA_MINUS_INF)) { + if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); *n = 0; return 0; @@ -987,7 +1061,7 @@ extern "C" { 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_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val)) || !mpzm.is_uint64(z)) { SET_ERROR_CODE(Z3_INVALID_ARG); *n = 0; @@ -998,74 +1072,137 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_string Z3_API Z3_fpa_get_numeral_exponent_string(Z3_context c, Z3_ast t) { + Z3_string Z3_API Z3_fpa_get_numeral_exponent_string(Z3_context c, Z3_ast t, Z3_bool biased) { Z3_TRY; - LOG_Z3_fpa_get_numeral_exponent_string(c, t); + LOG_Z3_fpa_get_numeral_exponent_string(c, t, biased); RESET_ERROR_CODE(); + CHECK_NON_NULL(t, 0); + CHECK_VALID_AST(t, 0); 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_app_of(e, fid, OP_FPA_PLUS_INF) || - is_app_of(e, fid, OP_FPA_MINUS_INF)) { + if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); 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))) { + if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } - mpf_exp_t exp = mpfm.is_zero(val) ? 0 : - mpfm.is_denormal(val) ? mpfm.mk_min_exp(val.get().get_ebits()) : - mpfm.exp(val); + 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(""); } - Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, __int64 * n) { + Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, __int64 * n, Z3_bool biased) { Z3_TRY; - LOG_Z3_fpa_get_numeral_exponent_int64(c, t, n); + 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 == 0) { + SET_ERROR_CODE(Z3_INVALID_ARG); + return 0; + } 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_app_of(e, fid, OP_FPA_PLUS_INF) || - is_app_of(e, fid, OP_FPA_MINUS_INF)) { + if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); *n = 0; return 0; } 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))) { + if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { SET_ERROR_CODE(Z3_INVALID_ARG); *n = 0; return 0; } - *n = mpfm.is_zero(val) ? 0 : - mpfm.is_denormal(val) ? mpfm.mk_min_exp(val.get().get_ebits()) : - mpfm.exp(val); + 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 1; Z3_CATCH_RETURN(0); } + Z3_ast Z3_API Z3_fpa_get_numeral_exponent_bv(Z3_context c, Z3_ast t, Z3_bool biased) { + Z3_TRY; + LOG_Z3_fpa_get_numeral_exponent_bv(c, t, biased); + RESET_ERROR_CODE(); + CHECK_NON_NULL(t, 0); + CHECK_VALID_AST(t, 0); + 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); + RETURN_Z3(0); + } + 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); + RETURN_Z3(0); + } + 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(0); + } + 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, 0); + CHECK_VALID_AST(t, 0); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); @@ -1095,4 +1232,102 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_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); + return 0; + } + return fu.is_nan(to_expr(t)); + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_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); + return 0; + } + return fu.is_inf(to_expr(t)); + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_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); + return 0; + } + return fu.is_zero(to_expr(t)); + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_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); + return 0; + } + return fu.is_normal(to_expr(t)); + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_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); + return 0; + } + return fu.is_subnormal(to_expr(t)); + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_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); + return 0; + } + return fu.is_positive(to_expr(t)); + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_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); + return 0; + } + return fu.is_negative(to_expr(t)); + Z3_CATCH_RETURN(Z3_FALSE); + } + }; diff --git a/src/api/api_goal.cpp b/src/api/api_goal.cpp index 10164f5b9..295e1d939 100644 --- a/src/api/api_goal.cpp +++ b/src/api/api_goal.cpp @@ -32,7 +32,7 @@ extern "C" { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } - Z3_goal_ref * g = alloc(Z3_goal_ref); + 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); @@ -156,7 +156,7 @@ extern "C" { 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); + 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); diff --git a/src/api/api_goal.h b/src/api/api_goal.h index c3d4d44f2..1e47b832a 100644 --- a/src/api/api_goal.h +++ b/src/api/api_goal.h @@ -23,6 +23,7 @@ Revision History: struct Z3_goal_ref : public api::object { goal_ref m_goal; + Z3_goal_ref(api::context& c) : api::object(c) {} virtual ~Z3_goal_ref() {} }; diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp index 1e201c5b3..10aa06568 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -15,7 +15,6 @@ Revision History: --*/ -#include #include #include #include"z3.h" @@ -212,7 +211,7 @@ extern "C" { LOG_Z3_get_interpolant(c, pf, pat, p); RESET_ERROR_CODE(); - Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); ast *_pf = to_ast(pf); @@ -303,7 +302,7 @@ extern "C" { if (_status == l_false){ // copy result back - v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); + v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(v); for (unsigned i = 0; i < interp.size(); i++){ v->m_ast_vector.push_back(interp[i]); @@ -314,7 +313,7 @@ extern "C" { model_ref mr; m_solver.get()->get_model(mr); if(mr.get()){ - Z3_model_ref *tmp_val = alloc(Z3_model_ref); + Z3_model_ref *tmp_val = alloc(Z3_model_ref, *mk_c(c)); tmp_val->m_model = mr.get(); mk_c(c)->save_object(tmp_val); *model = of_model(tmp_val); @@ -375,7 +374,7 @@ extern "C" { for(int i = 0; i < num_theory; i++) fmlas[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),fmlas[i]); std::copy(cnsts,cnsts+num,fmlas.begin()+num_theory); - Z3_string smt = Z3_benchmark_to_smtlib_string(ctx,"none","AUFLIA","unknown","",num_fmlas-1,&fmlas[0],fmlas[num_fmlas-1]); + Z3_string smt = Z3_benchmark_to_smtlib_string(ctx,"none","AUFLIA","unknown","",num_fmlas-1,&fmlas[0],fmlas[num_fmlas-1]); std::ofstream f(filename); if(num_theory) f << ";! THEORY=" << num_theory << "\n"; @@ -469,7 +468,7 @@ extern "C" { } f.close(); -#if 0 +#if 0 if(!parents){ diff --git a/src/api/api_log.cpp b/src/api/api_log.cpp index 4a1ae27e5..43ed98986 100644 --- a/src/api/api_log.cpp +++ b/src/api/api_log.cpp @@ -15,40 +15,75 @@ Author: Revision History: --*/ -#include #include #include"z3.h" #include"api_log_macros.h" #include"util.h" +#include"version.h" std::ostream * g_z3_log = 0; bool g_z3_log_enabled = false; extern "C" { - Z3_bool Z3_API Z3_open_log(Z3_string filename) { - if (g_z3_log != 0) - Z3_close_log(); - g_z3_log = alloc(std::ofstream, filename); - g_z3_log_enabled = true; - if (g_z3_log->bad() || g_z3_log->fail()) { - dealloc(g_z3_log); - g_z3_log = 0; - return Z3_FALSE; - } - return Z3_TRUE; - } - - void Z3_API Z3_append_log(Z3_string str) { - if (g_z3_log == 0) - return; - _Z3_append_log(static_cast(str)); - } - - void Z3_API Z3_close_log(void) { + void Z3_close_log_unsafe(void) { if (g_z3_log != 0) { dealloc(g_z3_log); g_z3_log_enabled = false; g_z3_log = 0; } } + + Z3_bool Z3_API Z3_open_log(Z3_string filename) { + Z3_bool res = Z3_TRUE; + +#ifdef Z3_LOG_SYNC + #pragma omp critical (z3_log) + { +#endif + if (g_z3_log != 0) + 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 = 0; + res = Z3_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; + } +#ifdef Z3_LOG_SYNC + } +#endif + + return res; + } + + void Z3_API Z3_append_log(Z3_string str) { + if (g_z3_log == 0) + return; +#ifdef Z3_LOG_SYNC + #pragma omp critical (z3_log) + { +#endif + if (g_z3_log != 0) + _Z3_append_log(static_cast(str)); +#ifdef Z3_LOG_SYNC + } +#endif + } + + void Z3_API Z3_close_log(void) { + if (g_z3_log != 0) { +#ifdef Z3_LOG_SYNC + #pragma omp critical (z3_log) + { +#endif + Z3_close_log_unsafe(); +#ifdef Z3_LOG_SYNC + } +#endif + } + } } diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index 10ede0922..67c0f2f08 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -86,7 +86,7 @@ extern "C" { SET_ERROR_CODE(Z3_INVALID_ARG); RETURN_Z3(0); } - Z3_func_interp_ref * fi = alloc(Z3_func_interp_ref, to_model_ref(m)); + 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)); @@ -192,7 +192,7 @@ extern "C" { RETURN_Z3(0); } 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)->m()); + 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 = universe.size(); for (unsigned i = 0; i < sz; i++) { @@ -262,7 +262,7 @@ extern "C" { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } - Z3_func_entry_ref * e = alloc(Z3_func_entry_ref, to_func_interp(f)->m_model.get()); + 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); diff --git a/src/api/api_model.h b/src/api/api_model.h index 719326aaf..9a53b59bc 100644 --- a/src/api/api_model.h +++ b/src/api/api_model.h @@ -23,7 +23,7 @@ Revision History: struct Z3_model_ref : public api::object { model_ref m_model; - Z3_model_ref() {} + Z3_model_ref(api::context& c): api::object(c) {} virtual ~Z3_model_ref() {} }; @@ -34,7 +34,7 @@ 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(model * m):m_model(m), m_func_interp(0) {} + Z3_func_interp_ref(api::context& c, model * m): api::object(c), m_model(m), m_func_interp(0) {} virtual ~Z3_func_interp_ref() {} }; @@ -46,7 +46,7 @@ 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(model * m):m_model(m), m_func_interp(0), m_func_entry(0) {} + Z3_func_entry_ref(api::context& c, model * m):api::object(c), m_model(m), m_func_interp(0), m_func_entry(0) {} virtual ~Z3_func_entry_ref() {} }; diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index 87510293f..3d8580178 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -23,15 +23,17 @@ Revision History: #include"api_util.h" #include"api_model.h" #include"opt_context.h" +#include"opt_cmds.h" #include"cancel_eh.h" #include"scoped_timer.h" - +#include"smt2parser.h" +#include"api_ast_vector.h" extern "C" { struct Z3_optimize_ref : public api::object { opt::context* m_opt; - Z3_optimize_ref():m_opt(0) {} + Z3_optimize_ref(api::context& c): api::object(c), m_opt(0) {} virtual ~Z3_optimize_ref() { dealloc(m_opt); } }; inline Z3_optimize_ref * to_optimize(Z3_optimize o) { return reinterpret_cast(o); } @@ -42,7 +44,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_optimize(c); RESET_ERROR_CODE(); - Z3_optimize_ref * o = alloc(Z3_optimize_ref); + 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)); @@ -158,7 +160,7 @@ extern "C" { RESET_ERROR_CODE(); model_ref _m; to_optimize_ptr(o)->get_model(_m); - Z3_model_ref * m_ref = alloc(Z3_model_ref); + Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); if (_m) { m_ref->m_model = _m; } @@ -186,7 +188,7 @@ extern "C" { Z3_TRY; LOG_Z3_optimize_get_param_descrs(c, o); RESET_ERROR_CODE(); - Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); + 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); @@ -240,7 +242,7 @@ extern "C" { Z3_TRY; LOG_Z3_optimize_get_statistics(c, d); RESET_ERROR_CODE(); - Z3_stats_ref * st = alloc(Z3_stats_ref); + 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); @@ -248,6 +250,86 @@ extern "C" { Z3_CATCH_RETURN(0); } + static void Z3_optimize_from_stream( + Z3_context c, + Z3_optimize opt, + std::istream& s) { + ast_manager& m = mk_c(c)->m(); + cmd_context ctx(false, &m); + install_opt_cmds(ctx, to_optimize_ptr(opt)); + ctx.set_ignore_check(true); + if (!parse_smt2_commands(ctx, s)) { + SET_ERROR_CODE(Z3_PARSER_ERROR); + return; + } + ptr_vector::const_iterator it = ctx.begin_assertions(); + ptr_vector::const_iterator end = ctx.end_assertions(); + for (; it != end; ++it) { + to_optimize_ptr(opt)->add_hard_constraint(*it); + } + } + + 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); + 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()); + + SET_ERROR_CODE(Z3_PARSER_ERROR); + return; + } + Z3_optimize_from_stream(c, d, is); + 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 (unsigned i = 0; i < hard.size(); i++) { + v->m_ast_vector.push_back(hard[i].get()); + } + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(0); + } + + 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(0); + } + }; diff --git a/src/api/api_params.cpp b/src/api/api_params.cpp index f2dab4d77..1efaffca8 100644 --- a/src/api/api_params.cpp +++ b/src/api/api_params.cpp @@ -30,7 +30,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_params(c); RESET_ERROR_CODE(); - Z3_params_ref * p = alloc(Z3_params_ref); + 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); diff --git a/src/api/api_pb.cpp b/src/api/api_pb.cpp index 6d5a56d2c..f19fd8661 100644 --- a/src/api/api_pb.cpp +++ b/src/api/api_pb.cpp @@ -15,7 +15,6 @@ Author: Revision History: --*/ -#include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" @@ -23,8 +22,8 @@ Revision History: #include"pb_decl_plugin.h" extern "C" { - - Z3_ast Z3_API Z3_mk_atmost(Z3_context c, unsigned num_args, + + 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); @@ -38,8 +37,21 @@ extern "C" { Z3_CATCH_RETURN(0); } + 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(args), k); + mk_c(c)->save_ast_trail(a); + check_sorts(c, a); + RETURN_Z3(of_ast(a)); + Z3_CATCH_RETURN(0); + } - Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args, + Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args, Z3_ast const args[], int _coeffs[], int k) { Z3_TRY; @@ -57,5 +69,41 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_ast Z3_API Z3_mk_pbge(Z3_context c, unsigned num_args, + Z3_ast const args[], int _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(args), rational(k)); + mk_c(c)->save_ast_trail(a); + check_sorts(c, a); + RETURN_Z3(of_ast(a)); + Z3_CATCH_RETURN(0); + } + + Z3_ast Z3_API Z3_mk_pbeq(Z3_context c, unsigned num_args, + Z3_ast const args[], int _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(args), rational(k)); + mk_c(c)->save_ast_trail(a); + check_sorts(c, a); + RETURN_Z3(of_ast(a)); + Z3_CATCH_RETURN(0); + } + }; diff --git a/src/api/api_polynomial.cpp b/src/api/api_polynomial.cpp index c68427960..eebe36717 100644 --- a/src/api/api_polynomial.cpp +++ b/src/api/api_polynomial.cpp @@ -14,9 +14,8 @@ Author: Leonardo de Moura (leonardo) 2012-12-08 Notes: - + --*/ -#include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" @@ -35,7 +34,7 @@ namespace api { pmanager::~pmanager() { } - + }; extern "C" { @@ -53,7 +52,7 @@ extern "C" { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } - Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, mk_c(c)->m()); + 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(); diff --git a/src/api/api_quant.cpp b/src/api/api_quant.cpp index ddcd90cca..bf64aa571 100644 --- a/src/api/api_quant.cpp +++ b/src/api/api_quant.cpp @@ -15,7 +15,6 @@ Author: Revision History: --*/ -#include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" @@ -26,17 +25,17 @@ Revision History: extern "C" { Z3_ast Z3_API Z3_mk_quantifier( - Z3_context c, - Z3_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) + Z3_context c, + Z3_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, + c, + is_forall, weight, 0, 0, @@ -50,15 +49,15 @@ extern "C" { } Z3_ast mk_quantifier_ex_core( - Z3_context c, - Z3_bool is_forall, - unsigned weight, + Z3_context c, + Z3_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[], + 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(); @@ -86,9 +85,9 @@ extern "C" { expr_ref result(mk_c(c)->m()); if (num_decls > 0) { result = mk_c(c)->m().mk_quantifier( - (0 != is_forall), - names.size(), ts, names.c_ptr(), to_expr(body), - weight, + (0 != is_forall), + names.size(), ts, names.c_ptr(), to_expr(body), + weight, to_symbol(quantifier_id), to_symbol(skolem_id), num_patterns, ps, @@ -104,44 +103,44 @@ extern "C" { } Z3_ast Z3_API Z3_mk_quantifier_ex( - Z3_context c, - Z3_bool is_forall, - unsigned weight, + Z3_context c, + Z3_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[], + 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, + 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, + 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 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, 1, 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 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, 0, weight, num_patterns, patterns, num_decls, types, decl_names, body); } - Z3_ast Z3_API Z3_mk_quantifier_const_ex(Z3_context c, + Z3_ast Z3_API Z3_mk_quantifier_const_ex(Z3_context c, Z3_bool is_forall, unsigned weight, Z3_symbol quantifier_id, @@ -166,7 +165,7 @@ extern "C" { } if (num_bound == 0) { SET_ERROR_CODE(Z3_INVALID_USAGE); - RETURN_Z3(0); + RETURN_Z3(0); } for (unsigned i = 0; i < num_bound; ++i) { app* a = to_app(bound[i]); @@ -191,7 +190,7 @@ extern "C" { 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); + 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())); @@ -205,25 +204,25 @@ extern "C" { } 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); + 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, + Z3_ast result = mk_quantifier_ex_core(c, is_forall, weight, quantifier_id, skolem_id, - num_patterns, _patterns.c_ptr(), + num_patterns, _patterns.c_ptr(), num_no_patterns, _no_patterns.c_ptr(), - names.size(), types.c_ptr(), names.c_ptr(), + names.size(), types.c_ptr(), names.c_ptr(), of_ast(abs_body.get())); RETURN_Z3(result); Z3_CATCH_RETURN(0); } - Z3_ast Z3_API Z3_mk_quantifier_const(Z3_context c, + Z3_ast Z3_API Z3_mk_quantifier_const(Z3_context c, Z3_bool is_forall, unsigned weight, unsigned num_bound, @@ -231,14 +230,14 @@ extern "C" { unsigned num_patterns, Z3_pattern const patterns[], Z3_ast body) { - return Z3_mk_quantifier_const_ex(c, is_forall, weight, 0, 0, - num_bound, bound, + return Z3_mk_quantifier_const_ex(c, is_forall, weight, 0, 0, + num_bound, bound, num_patterns, patterns, 0, 0, body); } - Z3_ast Z3_API Z3_mk_forall_const(Z3_context c, + Z3_ast Z3_API Z3_mk_forall_const(Z3_context c, unsigned weight, unsigned num_bound, Z3_app const bound[], @@ -248,7 +247,7 @@ extern "C" { 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, + Z3_ast Z3_API Z3_mk_exists_const(Z3_context c, unsigned weight, unsigned num_bound, Z3_app const bound[], @@ -257,7 +256,7 @@ extern "C" { 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); @@ -273,7 +272,7 @@ extern "C" { RETURN_Z3(of_pattern(a)); Z3_CATCH_RETURN(0); } - + Z3_ast Z3_API Z3_mk_bound(Z3_context c, unsigned index, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_bound(c, index, ty); @@ -436,7 +435,7 @@ extern "C" { else { SET_ERROR_CODE(Z3_SORT_ERROR); return 0; - } + } Z3_CATCH_RETURN(0); } @@ -450,7 +449,7 @@ extern "C" { } else { SET_ERROR_CODE(Z3_SORT_ERROR); - return 0; + return 0; } Z3_CATCH_RETURN(0); } @@ -471,13 +470,13 @@ extern "C" { Z3_CATCH_RETURN(0); } - Z3_ast Z3_API Z3_pattern_to_ast(Z3_context c, Z3_pattern p) { + Z3_ast Z3_API Z3_pattern_to_ast(Z3_context c, Z3_pattern p) { RESET_ERROR_CODE(); - return (Z3_ast)(p); - } + 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)); } - + }; diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index 5704ffdb0..986e6f497 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -16,7 +16,6 @@ Author: Revision History: --*/ -#include #include"z3.h" #include"api_log_macros.h" #include"api_context.h" @@ -28,7 +27,7 @@ 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(); + 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)); @@ -38,7 +37,7 @@ extern "C" { 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(); + 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)); @@ -48,14 +47,14 @@ extern "C" { Z3_ast Z3_API Z3_mk_string(Z3_context c, Z3_string str) { Z3_TRY; LOG_Z3_mk_string(c, str); - RESET_ERROR_CODE(); + 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(0); } - + Z3_sort Z3_API Z3_mk_string_sort(Z3_context c) { Z3_TRY; LOG_Z3_mk_string_sort(c); @@ -71,8 +70,8 @@ extern "C" { LOG_Z3_is_seq_sort(c, s); RESET_ERROR_CODE(); bool result = mk_c(c)->sutil().is_seq(to_sort(s)); - return result?Z3_TRUE:Z3_FALSE; - Z3_CATCH_RETURN(Z3_FALSE); + return result?Z3_TRUE:Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_is_re_sort(Z3_context c, Z3_sort s) { @@ -80,8 +79,8 @@ extern "C" { LOG_Z3_is_re_sort(c, s); RESET_ERROR_CODE(); bool result = mk_c(c)->sutil().is_re(to_sort(s)); - return result?Z3_TRUE:Z3_FALSE; - Z3_CATCH_RETURN(Z3_FALSE); + return result?Z3_TRUE:Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_is_string_sort(Z3_context c, Z3_sort s) { @@ -89,8 +88,8 @@ extern "C" { LOG_Z3_is_string_sort(c, s); RESET_ERROR_CODE(); bool result = mk_c(c)->sutil().is_string(to_sort(s)); - return result?Z3_TRUE:Z3_FALSE; - Z3_CATCH_RETURN(Z3_FALSE); + return result?Z3_TRUE:Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); } Z3_bool Z3_API Z3_is_string(Z3_context c, Z3_ast s) { @@ -98,7 +97,7 @@ extern "C" { LOG_Z3_is_string(c, s); RESET_ERROR_CODE(); bool result = mk_c(c)->sutil().str.is_string(to_expr(s)); - return result?Z3_TRUE:Z3_FALSE; + return result?Z3_TRUE:Z3_FALSE; Z3_CATCH_RETURN(Z3_FALSE); } @@ -116,16 +115,19 @@ extern "C" { Z3_CATCH_RETURN(""); } - Z3_ast Z3_API Z3_mk_seq_empty(Z3_context c, Z3_sort seq) { - Z3_TRY; - LOG_Z3_mk_seq_empty(c, seq); - RESET_ERROR_CODE(); - app* a = mk_c(c)->sutil().str.mk_empty(to_sort(seq)); - mk_c(c)->save_ast_trail(a); - RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); +#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); @@ -139,12 +141,31 @@ extern "C" { 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(0); + } 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); diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index be03e5daf..6dc41efb2 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -32,6 +32,7 @@ Revision History: #include"smt_strategic_solver.h" #include"smt_solver.h" #include"smt_implied_equalities.h" +#include"smt_logics.h" extern "C" { @@ -58,7 +59,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_simple_solver(c); RESET_ERROR_CODE(); - Z3_solver_ref * s = alloc(Z3_solver_ref, mk_smt_solver_factory()); + 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); @@ -69,7 +70,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_solver(c); RESET_ERROR_CODE(); - Z3_solver_ref * s = alloc(Z3_solver_ref, mk_smt_strategic_solver_factory()); + 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); @@ -80,10 +81,18 @@ extern "C" { Z3_TRY; LOG_Z3_mk_solver_for_logic(c, logic); RESET_ERROR_CODE(); - Z3_solver_ref * s = alloc(Z3_solver_ref, mk_smt_strategic_solver_factory(to_symbol(logic))); - mk_c(c)->save_object(s); - Z3_solver r = of_solver(s); - RETURN_Z3(r); + 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(0); + } + 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(0); } @@ -91,7 +100,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_solver_from_tactic(c, t); RESET_ERROR_CODE(); - Z3_solver_ref * s = alloc(Z3_solver_ref, mk_tactic2solver_factory(to_tactic_ref(t))); + 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); @@ -103,7 +112,7 @@ extern "C" { 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, 0); + Z3_solver_ref * sr = alloc(Z3_solver_ref, *mk_c(target), 0); init_solver(c, s); sr->m_solver = to_solver(s)->m_solver->translate(mk_c(target)->m(), p); mk_c(target)->save_object(sr); @@ -134,7 +143,7 @@ extern "C" { Z3_TRY; LOG_Z3_solver_get_param_descrs(c, s); RESET_ERROR_CODE(); - Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); + 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() != 0; if (!initialized) @@ -255,7 +264,7 @@ extern "C" { 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)->m()); + 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++) { @@ -323,7 +332,7 @@ extern "C" { SET_ERROR_CODE(Z3_INVALID_USAGE); RETURN_Z3(0); } - Z3_model_ref * m_ref = alloc(Z3_model_ref); + 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)); @@ -352,7 +361,7 @@ extern "C" { init_solver(c, s); ptr_vector core; to_solver_ref(s)->get_unsat_core(core); - Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, mk_c(c)->m()); + 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 < core.size(); i++) { v->m_ast_vector.push_back(core[i]); @@ -375,7 +384,7 @@ extern "C" { LOG_Z3_solver_get_statistics(c, s); RESET_ERROR_CODE(); init_solver(c, s); - Z3_stats_ref * st = alloc(Z3_stats_ref); + 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); @@ -413,4 +422,59 @@ extern "C" { 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); + unsigned sz = __assumptions.size(); + for (unsigned i = 0; i < sz; ++i) { + if (!is_expr(__assumptions[i])) { + SET_ERROR_CODE(Z3_INVALID_USAGE); + return Z3_L_UNDEF; + } + _assumptions.push_back(to_expr(__assumptions[i])); + } + ast_ref_vector const& __variables = to_ast_vector_ref(variables); + sz = __variables.size(); + for (unsigned i = 0; i < sz; ++i) { + if (!is_expr(__variables[i])) { + SET_ERROR_CODE(Z3_INVALID_USAGE); + return Z3_L_UNDEF; + } + _variables.push_back(to_expr(__variables[i])); + } + 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", false); + cancel_eh eh(mk_c(c)->m().limit()); + api::context::set_interruptable si(*(mk_c(c)), eh); + { + scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); + scoped_timer timer(timeout, &eh); + scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); + try { + result = to_solver_ref(s)->get_consequences(_assumptions, _variables, _consequences); + } + catch (z3_exception & ex) { + mk_c(c)->handle_exception(ex); + return Z3_L_UNDEF; + } + } + for (unsigned i = 0; i < _consequences.size(); ++i) { + to_ast_vector_ref(consequences).push_back(_consequences[i].get()); + } + return static_cast(result); + Z3_CATCH_RETURN(Z3_L_UNDEF); + } + }; diff --git a/src/api/api_solver.h b/src/api/api_solver.h index 5ed7a15f7..ea59ccf33 100644 --- a/src/api/api_solver.h +++ b/src/api/api_solver.h @@ -26,7 +26,7 @@ struct Z3_solver_ref : public api::object { ref m_solver; params_ref m_params; symbol m_logic; - Z3_solver_ref(solver_factory * f):m_solver_factory(f), m_solver(0), m_logic(symbol::null) {} + Z3_solver_ref(api::context& c, solver_factory * f): api::object(c), m_solver_factory(f), m_solver(0), m_logic(symbol::null) {} virtual ~Z3_solver_ref() {} }; diff --git a/src/api/api_stats.h b/src/api/api_stats.h index e0e370336..1c0c0c82e 100644 --- a/src/api/api_stats.h +++ b/src/api/api_stats.h @@ -23,6 +23,7 @@ Revision History: struct Z3_stats_ref : public api::object { statistics m_stats; + Z3_stats_ref(api::context& c): api::object(c) {} virtual ~Z3_stats_ref() {} }; diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index 78e042528..7068619c1 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -25,13 +25,13 @@ Revision History: #include"cancel_eh.h" #include"scoped_timer.h" -Z3_apply_result_ref::Z3_apply_result_ref(ast_manager & m):m_core(m) { +Z3_apply_result_ref::Z3_apply_result_ref(api::context& c, ast_manager & m): api::object(c), m_core(m) { } extern "C" { #define RETURN_TACTIC(_t_) { \ - Z3_tactic_ref * _ref_ = alloc(Z3_tactic_ref); \ + 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_); \ @@ -39,7 +39,7 @@ extern "C" { } #define RETURN_PROBE(_t_) { \ - Z3_probe_ref * _ref_ = alloc(Z3_probe_ref); \ + 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_); \ @@ -367,7 +367,7 @@ extern "C" { Z3_TRY; LOG_Z3_tactic_get_param_descrs(c, t); RESET_ERROR_CODE(); - Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); + 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); @@ -404,7 +404,7 @@ extern "C" { 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)->m()); + 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); @@ -505,7 +505,7 @@ extern "C" { SET_ERROR_CODE(Z3_IOB); RETURN_Z3(0); } - Z3_goal_ref * g = alloc(Z3_goal_ref); + 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); @@ -524,7 +524,7 @@ extern "C" { model_ref new_m = to_model_ref(m)->copy(); if (to_apply_result(r)->m_mc) to_apply_result(r)->m_mc->operator()(new_m, i); - Z3_model_ref * m_ref = alloc(Z3_model_ref); + Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); m_ref->m_model = new_m; mk_c(c)->save_object(m_ref); RETURN_Z3(of_model(m_ref)); diff --git a/src/api/api_tactic.h b/src/api/api_tactic.h index 6f8d83fd9..373b85b39 100644 --- a/src/api/api_tactic.h +++ b/src/api/api_tactic.h @@ -21,13 +21,20 @@ Revision History: #include"api_goal.h" #include"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) {} virtual ~Z3_tactic_ref() {} }; struct Z3_probe_ref : public api::object { probe_ref m_probe; + Z3_probe_ref(api::context& c):api::object(c) {} virtual ~Z3_probe_ref() {} }; @@ -44,7 +51,7 @@ struct Z3_apply_result_ref : public api::object { model_converter_ref m_mc; proof_converter_ref m_pc; expr_dependency_ref m_core; - Z3_apply_result_ref(ast_manager & m); + Z3_apply_result_ref(api::context& c, ast_manager & m); virtual ~Z3_apply_result_ref() {} }; diff --git a/src/api/api_util.h b/src/api/api_util.h index 3b7baeac0..7857e7c38 100644 --- a/src/api/api_util.h +++ b/src/api/api_util.h @@ -31,15 +31,20 @@ Revision History: #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():m_ref_count(0) {} + object(context& c); virtual ~object() {} 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--; if (m_ref_count == 0) dealloc(this); } + unsigned id() const { return m_id; } + void inc_ref(); + void dec_ref(); }; }; @@ -82,6 +87,7 @@ 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) {} virtual ~Z3_params_ref() {} }; @@ -91,6 +97,7 @@ inline params_ref to_param_ref(Z3_params p) { return p == 0 ? params_ref() : to_ struct Z3_param_descrs_ref : public api::object { param_descrs m_descrs; + Z3_param_descrs_ref(api::context& c): api::object(c) {} virtual ~Z3_param_descrs_ref() {} }; diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 9fb664819..21a3fedf4 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -63,7 +63,6 @@ namespace z3 { class func_entry; class statistics; class apply_result; - class fixedpoint; template class ast_vector_tpl; typedef ast_vector_tpl ast_vector; typedef ast_vector_tpl expr_vector; @@ -122,20 +121,30 @@ namespace z3 { unsat, sat, unknown }; + 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 { + bool m_enable_exceptions; Z3_context m_ctx; - static void error_handler(Z3_context c, Z3_error_code e) { /* do nothing */ } + static void error_handler(Z3_context /*c*/, Z3_error_code /*e*/) { /* do nothing */ } void init(config & c) { m_ctx = Z3_mk_context_rc(c); + m_enable_exceptions = true; Z3_set_error_handler(m_ctx, error_handler); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } void init_interp(config & c) { m_ctx = Z3_mk_interpolation_context(c); + m_enable_exceptions = true; Z3_set_error_handler(m_ctx, error_handler); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } @@ -146,19 +155,31 @@ namespace z3 { struct interpolation {}; context() { config c; init(c); } context(config & c) { init(c); } - context(config & c, interpolation) { init_interp(c); } + context(config & c, interpolation) { init_interp(c); } ~context() { Z3_del_context(m_ctx); } operator Z3_context() const { return m_ctx; } /** \brief Auxiliary method used to check for API usage errors. */ - void check_error() const { + Z3_error_code check_error() const { Z3_error_code e = Z3_get_error_code(m_ctx); - if (e != Z3_OK) + if (e != Z3_OK && enable_exceptions()) throw exception(Z3_get_error_msg(m_ctx, e)); + return e; } + /** + \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 very careful about using check_error() after calls that may result in an errornous + 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. */ @@ -279,6 +300,15 @@ namespace z3 { expr num_val(int n, sort const & s); + /** + \brief parsing + */ + expr parse_string(char const* s); + expr parse_file(char const* file); + + expr parse_string(char const* s, sort_vector const& sorts, func_decl_vector const& decls); + expr parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls); + /** \brief Interpolation support */ @@ -315,7 +345,7 @@ namespace z3 { object(context & c):m_ctx(&c) {} object(object const & s):m_ctx(s.m_ctx) {} context & ctx() const { return *m_ctx; } - void check_error() const { m_ctx->check_error(); } + 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) { assert(a.m_ctx == b.m_ctx); } @@ -405,6 +435,8 @@ namespace z3 { 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. @@ -435,7 +467,10 @@ namespace z3 { \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. */ @@ -654,12 +689,18 @@ namespace z3 { /** \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 the is_numeral_i function. \pre is_numeral() */ int get_numeral_int() const { - int result; + int result = 0; if (!is_numeral_i(result)) { + assert(ctx().enable_exceptions()); + if (!ctx().enable_exceptions()) return 0; throw exception("numeral does not fit in machine int"); } return result; @@ -668,13 +709,18 @@ namespace z3 { /** \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 the is_numeral_u function. \pre is_numeral() */ unsigned get_numeral_uint() const { assert(is_numeral()); - unsigned result; + unsigned result = 0; if (!is_numeral_u(result)) { + assert(ctx().enable_exceptions()); + if (!ctx().enable_exceptions()) return 0; throw exception("numeral does not fit in machine uint"); } return result; @@ -688,8 +734,10 @@ namespace z3 { */ __int64 get_numeral_int64() const { assert(is_numeral()); - __int64 result; + __int64 result = 0; if (!is_numeral_i64(result)) { + assert(ctx().enable_exceptions()); + if (!ctx().enable_exceptions()) return 0; throw exception("numeral does not fit in machine __int64"); } return result; @@ -703,13 +751,33 @@ namespace z3 { */ __uint64 get_numeral_uint64() const { assert(is_numeral()); - __uint64 result; + __uint64 result = 0; if (!is_numeral_u64(result)) { + assert(ctx().enable_exceptions()); + if (!ctx().enable_exceptions()) return 0; throw exception("numeral does not fit in machine __uint64"); } 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); + } operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } @@ -802,6 +870,9 @@ namespace z3 { 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); friend expr distinct(expr_vector const& args); @@ -824,13 +895,23 @@ namespace z3 { friend expr operator*(expr const & a, int b); friend expr operator*(int a, expr const & b); - /** - \brief Power operator - */ + /* \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); @@ -876,7 +957,7 @@ namespace z3 { 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 sequence and regular expression operations. + is overloaeded as sequence concatenation and regular expression union. concat is overloaded to handle sequences and regular expressions @@ -913,7 +994,31 @@ namespace z3 { 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); + } /** @@ -937,33 +1042,46 @@ namespace z3 { }; +#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) { - check_context(a, b); - assert(a.is_bool() && b.is_bool()); - Z3_ast r = Z3_mk_implies(a.ctx(), a, b); - a.check_error(); - return expr(a.ctx(), r); + 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) { - assert(a.is_arith() && b.is_arith()); - check_context(a, b); - Z3_ast r = Z3_mk_power(a.ctx(), a, b); - a.check_error(); - return expr(a.ctx(), r); - } + + 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) { _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) { - assert(a.is_bool()); - Z3_ast r = Z3_mk_not(a.ctx(), a); - a.check_error(); - return expr(a.ctx(), r); - } + inline expr rem(expr const& a, expr const& b) { _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); @@ -1205,7 +1323,6 @@ namespace z3 { - /** \brief Create the if-then-else expression ite(c, t, e) @@ -1275,51 +1392,51 @@ namespace z3 { 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 reminder 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 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 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)); } - + /** + \brief signed reminder 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 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 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)); } + template class cast_ast; template<> class cast_ast { @@ -1497,6 +1614,20 @@ namespace z3 { 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) { @@ -1567,7 +1698,7 @@ namespace z3 { Z3_ast r = 0; Z3_bool status = Z3_model_eval(ctx(), m_model, n, model_completion, &r); check_error(); - if (status == Z3_FALSE) + if (status == Z3_FALSE && ctx().enable_exceptions()) throw exception("failed to evaluate expression"); return expr(ctx(), r); } @@ -1578,9 +1709,9 @@ namespace z3 { 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()); - } + 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 @@ -1597,6 +1728,13 @@ namespace z3 { 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 0 != Z3_model_has_interp(ctx(), m_model, f); + } friend std::ostream & operator<<(std::ostream & out, model const & m); }; @@ -1639,11 +1777,6 @@ namespace z3 { return out; } - 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; - } class solver : public object { Z3_solver m_solver; @@ -1705,6 +1838,11 @@ namespace z3 { 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); } @@ -1731,6 +1869,7 @@ namespace z3 { fmls, fml)); } + param_descrs get_param_descrs() { return param_descrs(ctx(), Z3_solver_get_param_descrs(ctx(), m_solver)); } }; @@ -1847,6 +1986,8 @@ namespace z3 { 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)); } }; @@ -1880,7 +2021,21 @@ namespace z3 { t.check_error(); return tactic(t.ctx(), r); } + inline tactic par_or(unsigned n, tactic const* tactics) { + if (n == 0) { + 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; @@ -2010,8 +2165,12 @@ namespace z3 { 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; } @@ -2297,11 +2456,68 @@ namespace z3 { 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())); } + +#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) { - check_context(d, v); - Z3_ast r = Z3_mk_const_array(d.ctx(), d, v); - d.check_error(); - return expr(d.ctx(), r); + 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. @@ -2332,37 +2548,101 @@ namespace z3 { return expr(s.ctx(), r); } inline expr to_re(expr const& s) { - Z3_ast r = Z3_mk_seq_to_re(s.ctx(), s); - s.check_error(); - return expr(s.ctx(), r); + MK_EXPR1(Z3_mk_seq_to_re, s); } inline expr in_re(expr const& s, expr const& re) { - check_context(s, re); - Z3_ast r = Z3_mk_seq_in_re(s.ctx(), s, 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 plus(expr const& re) { - Z3_ast r = Z3_mk_re_plus(re.ctx(), re); - re.check_error(); - return expr(re.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 option(expr const& re) { - Z3_ast r = Z3_mk_re_option(re.ctx(), re); - re.check_error(); - return expr(re.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 star(expr const& re) { - Z3_ast r = Z3_mk_re_star(re.ctx(), re); - re.check_error(); - return expr(re.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 interpolant(expr const& a) { return expr(a.ctx(), Z3_mk_interpolant(a.ctx(), a)); } + inline expr context::parse_string(char const* s) { + Z3_ast r = Z3_parse_smtlib2_string(*this, s, 0, 0, 0, 0, 0, 0); + check_error(); + return expr(*this, r); + + } + inline expr context::parse_file(char const* s) { + Z3_ast r = Z3_parse_smtlib2_file(*this, s, 0, 0, 0, 0, 0, 0); + check_error(); + return expr(*this, r); + } + + inline expr 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 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(*this, r); + } + + inline expr 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 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(*this, r); + } + + inline check_result context::compute_interpolant(expr const& pat, params const& p, expr_vector& i, model& m) { Z3_ast_vector interp = 0; Z3_model mdl = 0; diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 5bff8960a..a656be3eb 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -286,8 +286,8 @@ namespace Microsoft.Z3 Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); - CheckContextMatch(fieldNames); - CheckContextMatch(fieldSorts); + CheckContextMatch(fieldNames); + CheckContextMatch(fieldSorts); return new TupleSort(this, name, (uint)fieldNames.Length, fieldNames, fieldSorts); } @@ -303,7 +303,7 @@ namespace Microsoft.Z3 Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); - CheckContextMatch(enumNames); + CheckContextMatch(enumNames); return new EnumSort(this, name, enumNames); } @@ -423,7 +423,7 @@ namespace Microsoft.Z3 Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); - CheckContextMatch(constructors); + CheckContextMatch(constructors); return new DatatypeSort(this, name, constructors); } @@ -436,7 +436,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(constructors, c => c != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(constructors); + CheckContextMatch(constructors); return new DatatypeSort(this, MkSymbol(name), constructors); } @@ -454,7 +454,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(names, name => name != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(names); + CheckContextMatch(names); uint n = (uint)names.Length; ConstructorList[] cla = new ConstructorList[n]; IntPtr[] n_constr = new IntPtr[n]; @@ -462,7 +462,7 @@ namespace Microsoft.Z3 { Constructor[] constructor = c[i]; Contract.Assume(Contract.ForAll(constructor, arr => arr != null), "Clousot does not support yet quantified formula on multidimensional arrays"); - CheckContextMatch(constructor); + CheckContextMatch(constructor); cla[i] = new ConstructorList(this, constructor); n_constr[i] = cla[i].NativeObject; } @@ -520,7 +520,7 @@ namespace Microsoft.Z3 Contract.Ensures(Contract.Result() != null); CheckContextMatch(name); - CheckContextMatch(domain); + CheckContextMatch(domain); CheckContextMatch(range); return new FuncDecl(this, name, domain, range); } @@ -551,7 +551,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(domain, d => d != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(domain); + CheckContextMatch(domain); CheckContextMatch(range); return new FuncDecl(this, MkSymbol(name), domain, range); } @@ -582,7 +582,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(domain, d => d != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(domain); + CheckContextMatch(domain); CheckContextMatch(range); return new FuncDecl(this, prefix, domain, range); } @@ -811,7 +811,7 @@ namespace Microsoft.Z3 Contract.Ensures(Contract.Result() != null); CheckContextMatch(f); - CheckContextMatch(args); + CheckContextMatch(args); return Expr.Create(this, f, args); } @@ -884,7 +884,7 @@ namespace Microsoft.Z3 Contract.Ensures(Contract.Result() != null); - CheckContextMatch(args); + CheckContextMatch(args); return new BoolExpr(this, Native.Z3_mk_distinct(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } @@ -970,7 +970,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(t); + CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } @@ -982,7 +982,7 @@ namespace Microsoft.Z3 Contract.Requires(t != null); Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(t); + CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Count(), AST.EnumToNative(t))); } @@ -995,7 +995,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(t); + CheckContextMatch(t); return new BoolExpr(this, Native.Z3_mk_or(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } @@ -1025,7 +1025,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(t); + CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_add(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } @@ -1051,10 +1051,23 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(t); + 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) + { + Contract.Requires(t != null); + Contract.Requires(Contract.ForAll(t, a => a != null)); + Contract.Ensures(Contract.Result() != 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] - .... /// @@ -1064,7 +1077,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(t); + CheckContextMatch(t); return (ArithExpr)Expr.Create(this, Native.Z3_mk_sub(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } @@ -2192,7 +2205,7 @@ namespace Microsoft.Z3 Contract.Ensures(Contract.Result() != null); CheckContextMatch(f); - CheckContextMatch(args); + CheckContextMatch(args); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_map(nCtx, f.NativeObject, AST.ArrayLength(args), AST.ArrayToNative(args))); } @@ -2302,7 +2315,7 @@ namespace Microsoft.Z3 Contract.Requires(args != null); Contract.Requires(Contract.ForAll(args, a => a != null)); - CheckContextMatch(args); + CheckContextMatch(args); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_union(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } @@ -2315,7 +2328,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(args, a => a != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(args); + CheckContextMatch(args); return (ArrayExpr)Expr.Create(this, Native.Z3_mk_set_intersect(nCtx, (uint)args.Length, AST.ArrayToNative(args))); } @@ -2407,6 +2420,29 @@ namespace Microsoft.Z3 return new SeqExpr(this, Native.Z3_mk_string(nCtx, s)); } + /// + /// Convert an integer expression to a string. + /// + public SeqExpr IntToString(Expr e) + { + Contract.Requires(e != null); + Contract.Requires(e is ArithExpr); + Contract.Ensures(Contract.Result() != null); + 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) + { + Contract.Requires(e != null); + Contract.Requires(e is SeqExpr); + Contract.Ensures(Contract.Result() != null); + return new IntExpr(this, Native.Z3_mk_str_to_int(nCtx, e.NativeObject)); + } + + /// /// Concatentate sequences. /// @@ -2416,7 +2452,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(t); + CheckContextMatch(t); return new SeqExpr(this, Native.Z3_mk_seq_concat(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } @@ -2551,10 +2587,20 @@ namespace Microsoft.Z3 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) + { + Contract.Requires(re != null); + Contract.Ensures(Contract.Result() != 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 MPlus(ReExpr re) + public ReExpr MkPlus(ReExpr re) { Contract.Requires(re != null); Contract.Ensures(Contract.Result() != null); @@ -2564,13 +2610,23 @@ namespace Microsoft.Z3 /// /// Create the optional regular expression. /// - public ReExpr MOption(ReExpr re) + public ReExpr MkOption(ReExpr re) { Contract.Requires(re != null); Contract.Ensures(Contract.Result() != null); return new ReExpr(this, Native.Z3_mk_re_option(nCtx, re.NativeObject)); } + /// + /// Create the complement regular expression. + /// + public ReExpr MkComplement(ReExpr re) + { + Contract.Requires(re != null); + Contract.Ensures(Contract.Result() != null); + return new ReExpr(this, Native.Z3_mk_re_complement(nCtx, re.NativeObject)); + } + /// /// Create the concatenation of regular languages. /// @@ -2580,7 +2636,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(t); + CheckContextMatch(t); return new ReExpr(this, Native.Z3_mk_re_concat(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } @@ -2593,9 +2649,55 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(t, a => a != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(t); + 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) + { + Contract.Requires(t != null); + Contract.Requires(Contract.ForAll(t, a => a != null)); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(t); + return new ReExpr(this, Native.Z3_mk_re_intersect(nCtx, (uint)t.Length, AST.ArrayToNative(t))); + } + + /// + /// Create the empty regular expression. + /// + public ReExpr MkEmptyRe(Sort s) + { + Contract.Requires(s != null); + Contract.Ensures(Contract.Result() != null); + return new ReExpr(this, Native.Z3_mk_re_empty(nCtx, s.NativeObject)); + } + + /// + /// Create the full regular expression. + /// + public ReExpr MkFullRe(Sort s) + { + Contract.Requires(s != null); + Contract.Ensures(Contract.Result() != null); + return new ReExpr(this, Native.Z3_mk_re_full(nCtx, s.NativeObject)); + } + + + /// + /// Create a range expression. + /// + public ReExpr MkRange(SeqExpr lo, SeqExpr hi) + { + Contract.Requires(lo != null); + Contract.Requires(hi != null); + Contract.Ensures(Contract.Result() != null); + CheckContextMatch(lo, hi); + return new ReExpr(this, Native.Z3_mk_re_range(nCtx, lo.NativeObject, hi.NativeObject)); + } #endregion @@ -2608,11 +2710,23 @@ namespace Microsoft.Z3 { Contract.Requires(args != null); Contract.Requires(Contract.Result() != null); - CheckContextMatch(args); + CheckContextMatch(args); return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint) args.Length, AST.ArrayToNative(args), k)); } + /// + /// Create an at-least-k constraint. + /// + public BoolExpr MkAtLeast(BoolExpr[] args, uint k) + { + Contract.Requires(args != null); + Contract.Requires(Contract.Result() != null); + CheckContextMatch(args); + return new BoolExpr(this, Native.Z3_mk_atleast(nCtx, (uint) args.Length, + AST.ArrayToNative(args), k)); + } + /// /// Create a pseudo-Boolean less-or-equal constraint. /// @@ -2622,11 +2736,40 @@ namespace Microsoft.Z3 Contract.Requires(coeffs != null); Contract.Requires(args.Length == coeffs.Length); Contract.Requires(Contract.Result() != null); - CheckContextMatch(args); + 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) + { + Contract.Requires(args != null); + Contract.Requires(coeffs != null); + Contract.Requires(args.Length == coeffs.Length); + Contract.Requires(Contract.Result() != null); + 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) + { + Contract.Requires(args != null); + Contract.Requires(coeffs != null); + Contract.Requires(args.Length == coeffs.Length); + Contract.Requires(Contract.Result() != null); + CheckContextMatch(args); + return new BoolExpr(this, Native.Z3_mk_pbeq(nCtx, (uint) args.Length, + AST.ArrayToNative(args), + coeffs, k)); + } #endregion #region Numerals @@ -3373,7 +3516,7 @@ namespace Microsoft.Z3 CheckContextMatch(t1); CheckContextMatch(t2); - CheckContextMatch(ts); + CheckContextMatch(ts); IntPtr last = IntPtr.Zero; if (ts != null && ts.Length > 0) @@ -3564,7 +3707,7 @@ namespace Microsoft.Z3 Contract.Requires(t == null || Contract.ForAll(t, tactic => tactic != null)); Contract.Ensures(Contract.Result() != null); - CheckContextMatch(t); + CheckContextMatch(t); return new Tactic(this, Native.Z3_tactic_par_or(nCtx, Tactic.ArrayLength(t), Tactic.ArrayToNative(t))); } @@ -4797,7 +4940,7 @@ namespace Microsoft.Z3 } [Pure] - internal void CheckContextMatch(IEnumerable arr) + internal void CheckContextMatch(IEnumerable arr) where T : Z3Object { Contract.Requires(arr == null || Contract.ForAll(arr, a => a != null)); @@ -4940,11 +5083,12 @@ namespace Microsoft.Z3 // Console.WriteLine("Context Finalizer from " + System.Threading.Thread.CurrentThread.ManagedThreadId); Dispose(); - if (refCount == 0) + if (refCount == 0 && m_ctx != IntPtr.Zero) { m_n_err_handler = null; - Native.Z3_del_context(m_ctx); + IntPtr ctx = m_ctx; m_ctx = IntPtr.Zero; + Native.Z3_del_context(ctx); } else GC.ReRegisterForFinalize(this); diff --git a/src/api/dotnet/Expr.cs b/src/api/dotnet/Expr.cs index c995a12bd..cac62d2f0 100644 --- a/src/api/dotnet/Expr.cs +++ b/src/api/dotnet/Expr.cs @@ -98,7 +98,7 @@ namespace Microsoft.Z3 Contract.Requires(args != null); Contract.Requires(Contract.ForAll(args, a => a != null)); - Context.CheckContextMatch(args); + 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)); @@ -120,8 +120,8 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(to, t => t != null)); Contract.Ensures(Contract.Result() != null); - Context.CheckContextMatch(from); - Context.CheckContextMatch(to); + 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))); @@ -152,7 +152,7 @@ namespace Microsoft.Z3 Contract.Requires(Contract.ForAll(to, t => t != null)); Contract.Ensures(Contract.Result() != null); - Context.CheckContextMatch(to); + Context.CheckContextMatch(to); return Expr.Create(Context, Native.Z3_substitute_vars(Context.nCtx, NativeObject, (uint)to.Length, Expr.ArrayToNative(to))); } diff --git a/src/api/dotnet/FPNum.cs b/src/api/dotnet/FPNum.cs index ac1fae5f5..808752eaa 100644 --- a/src/api/dotnet/FPNum.cs +++ b/src/api/dotnet/FPNum.cs @@ -14,7 +14,7 @@ Author: Christoph Wintersteiger (cwinter) 2013-06-10 Notes: - + --*/ using System; using System.Diagnostics.Contracts; @@ -27,6 +27,20 @@ namespace Microsoft.Z3 [ContractVerification(true)] 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 /// @@ -38,7 +52,7 @@ namespace Microsoft.Z3 get { int res = 0; - if (Native.Z3_fpa_get_numeral_sign(Context.nCtx, NativeObject, ref 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; } @@ -63,7 +77,7 @@ namespace Microsoft.Z3 /// The significand value of a floating-point numeral as a UInt64 /// /// - /// This function extracts the significand bits, without the + /// This function extracts the significand bits, without the /// hidden bit or normalization. Throws an exception if the /// significand does not fit into a UInt64. /// @@ -73,36 +87,90 @@ namespace Microsoft.Z3 { 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"); + throw new Z3Exception("Significand is not a 64 bit unsigned integer"); return result; } } /// - /// Return the exponent value of a floating-point numeral as a string + /// The significand of a floating-point numeral as a bit-vector expression /// - public string Exponent + /// + /// +oo, -oo and NaN's do not have a bit-vector significand, so they are invalid arguments. + /// + public BitVecExpr SignificandBV { get { - return Native.Z3_fpa_get_numeral_exponent_string(Context.nCtx, NativeObject); + 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, biased ? 1 : 0); + } + /// /// Return the exponent value of a floating-point numeral as a signed 64-bit integer /// - public Int64 ExponentInt64 + public Int64 ExponentInt64(bool biased = true) { - get - { - Int64 result = 0; - if (Native.Z3_fpa_get_numeral_exponent_int64(Context.nCtx, NativeObject, ref result) == 0) - throw new Z3Exception("Exponent is not a 64 bit integer"); - return result; - } + Int64 result = 0; + if (Native.Z3_fpa_get_numeral_exponent_int64(Context.nCtx, NativeObject, ref result, 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, 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) @@ -113,7 +181,7 @@ namespace Microsoft.Z3 /// /// Returns a string representation of the numeral. - /// + /// public override string ToString() { return Native.Z3_get_numeral_string(Context.nCtx, NativeObject); diff --git a/src/api/dotnet/Fixedpoint.cs b/src/api/dotnet/Fixedpoint.cs index 66449ddbb..e2fb7fe5a 100644 --- a/src/api/dotnet/Fixedpoint.cs +++ b/src/api/dotnet/Fixedpoint.cs @@ -71,7 +71,7 @@ namespace Microsoft.Z3 Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); - Context.CheckContextMatch(constraints); + Context.CheckContextMatch(constraints); foreach (BoolExpr a in constraints) { Native.Z3_fixedpoint_assert(Context.nCtx, NativeObject, a.NativeObject); @@ -151,7 +151,7 @@ namespace Microsoft.Z3 Contract.Requires(relations != null); Contract.Requires(Contract.ForAll(0, relations.Length, i => relations[i] != null)); - Context.CheckContextMatch(relations); + Context.CheckContextMatch(relations); Z3_lbool r = (Z3_lbool)Native.Z3_fixedpoint_query_relations(Context.nCtx, NativeObject, AST.ArrayLength(relations), AST.ArrayToNative(relations)); switch (r) diff --git a/src/api/dotnet/FuncDecl.cs b/src/api/dotnet/FuncDecl.cs index 15b6a59db..2f5cd0ce8 100644 --- a/src/api/dotnet/FuncDecl.cs +++ b/src/api/dotnet/FuncDecl.cs @@ -339,7 +339,7 @@ namespace Microsoft.Z3 { Contract.Requires(args == null || Contract.ForAll(args, a => a != null)); - Context.CheckContextMatch(args); + Context.CheckContextMatch(args); return Expr.Create(Context, this, args); } diff --git a/src/api/dotnet/Goal.cs b/src/api/dotnet/Goal.cs index 5ee44f34b..521b453f8 100644 --- a/src/api/dotnet/Goal.cs +++ b/src/api/dotnet/Goal.cs @@ -82,7 +82,7 @@ namespace Microsoft.Z3 Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); - Context.CheckContextMatch(constraints); + Context.CheckContextMatch(constraints); foreach (BoolExpr c in constraints) { Contract.Assert(c != null); // It was an assume, now made an assert just to be sure we do not regress diff --git a/src/api/dotnet/InterpolationContext.cs b/src/api/dotnet/InterpolationContext.cs index 17e92a40e..3f2feb5a6 100644 --- a/src/api/dotnet/InterpolationContext.cs +++ b/src/api/dotnet/InterpolationContext.cs @@ -30,7 +30,7 @@ namespace Microsoft.Z3 /// /// Constructor. /// - /// + /// public InterpolationContext(Dictionary settings) : base(settings) { } #region Terms diff --git a/src/api/dotnet/Microsoft.Z3.mono.snk b/src/api/dotnet/Microsoft.Z3.snk similarity index 100% rename from src/api/dotnet/Microsoft.Z3.mono.snk rename to src/api/dotnet/Microsoft.Z3.snk diff --git a/src/api/dotnet/Optimize.cs b/src/api/dotnet/Optimize.cs index 304e3e86f..c8954a473 100644 --- a/src/api/dotnet/Optimize.cs +++ b/src/api/dotnet/Optimize.cs @@ -258,10 +258,13 @@ namespace Microsoft.Z3 /// /// Return a string the describes why the last to check returned unknown /// - public String getReasonUnknown() + public String ReasonUnknown { - Contract.Ensures(Contract.Result() != null); - return Native.Z3_optimize_get_reason_unknown(Context.nCtx, NativeObject); + get + { + Contract.Ensures(Contract.Result() != null); + return Native.Z3_optimize_get_reason_unknown(Context.nCtx, NativeObject); + } } @@ -273,6 +276,52 @@ namespace Microsoft.Z3 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 + { + Contract.Ensures(Contract.Result() != null); + + 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 + { + Contract.Ensures(Contract.Result() != null); + + ASTVector objectives = new ASTVector(Context, Native.Z3_optimize_get_objectives(Context.nCtx, NativeObject)); + return objectives.ToExprArray(); + } + } + + /// /// Optimize statistics. /// diff --git a/src/api/dotnet/Quantifier.cs b/src/api/dotnet/Quantifier.cs index 38e435309..eb21ed2b9 100644 --- a/src/api/dotnet/Quantifier.cs +++ b/src/api/dotnet/Quantifier.cs @@ -172,10 +172,10 @@ namespace Microsoft.Z3 Contract.Requires(patterns == null || Contract.ForAll(patterns, p => p != null)); Contract.Requires(noPatterns == null || Contract.ForAll(noPatterns, np => np != null)); - Context.CheckContextMatch(patterns); - Context.CheckContextMatch(noPatterns); - Context.CheckContextMatch(sorts); - Context.CheckContextMatch(names); + Context.CheckContextMatch(patterns); + Context.CheckContextMatch(noPatterns); + Context.CheckContextMatch(sorts); + Context.CheckContextMatch(names); Context.CheckContextMatch(body); if (sorts.Length != names.Length) @@ -212,8 +212,8 @@ namespace Microsoft.Z3 Contract.Requires(noPatterns == null || Contract.ForAll(noPatterns, np => np != null)); Contract.Requires(bound == null || Contract.ForAll(bound, n => n != null)); - Context.CheckContextMatch(noPatterns); - Context.CheckContextMatch(patterns); + Context.CheckContextMatch(noPatterns); + Context.CheckContextMatch(patterns); //Context.CheckContextMatch(bound); Context.CheckContextMatch(body); diff --git a/src/api/dotnet/Solver.cs b/src/api/dotnet/Solver.cs index c9e76e68e..dff2677df 100644 --- a/src/api/dotnet/Solver.cs +++ b/src/api/dotnet/Solver.cs @@ -18,6 +18,8 @@ Notes: --*/ using System; +using System.Linq; +using System.Collections.Generic; using System.Diagnostics.Contracts; namespace Microsoft.Z3 @@ -110,7 +112,7 @@ namespace Microsoft.Z3 Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); - Context.CheckContextMatch(constraints); + Context.CheckContextMatch(constraints); foreach (BoolExpr a in constraints) { Native.Z3_solver_assert(Context.nCtx, NativeObject, a.NativeObject); @@ -125,6 +127,14 @@ namespace Microsoft.Z3 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. @@ -141,8 +151,8 @@ namespace Microsoft.Z3 Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); Contract.Requires(Contract.ForAll(ps, c => c != null)); - Context.CheckContextMatch(constraints); - Context.CheckContextMatch(ps); + Context.CheckContextMatch(constraints); + Context.CheckContextMatch(ps); if (constraints.Length != ps.Length) throw new Z3Exception("Argument size mismatch"); @@ -212,12 +222,34 @@ namespace Microsoft.Z3 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)); - switch (r) - { - case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE; - case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE; - default: return Status.UNKNOWN; - } + 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); } /// @@ -295,7 +327,7 @@ namespace Microsoft.Z3 /// public Solver Translate(Context ctx) { - Contract.Requires(ctx != null); + Contract.Requires(ctx != null); Contract.Ensures(Contract.Result() != null); return new Solver(ctx, Native.Z3_solver_translate(Context.nCtx, NativeObject, ctx.nCtx)); } @@ -355,6 +387,17 @@ namespace Microsoft.Z3 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 } } diff --git a/src/api/dotnet/Version.cs b/src/api/dotnet/Version.cs index 6c22ce7fe..364ada781 100644 --- a/src/api/dotnet/Version.cs +++ b/src/api/dotnet/Version.cs @@ -83,6 +83,17 @@ namespace Microsoft.Z3 } } + /// + /// A full version string + /// + public static string FullVersion + { + get + { + return Native.Z3_get_full_version(); + } + } + /// /// A string representation of the version information. /// diff --git a/src/api/dotnet/Z3Exception.cs b/src/api/dotnet/Z3Exception.cs index adda43995..b0e05900c 100644 --- a/src/api/dotnet/Z3Exception.cs +++ b/src/api/dotnet/Z3Exception.cs @@ -14,17 +14,20 @@ Author: Christoph Wintersteiger (cwinter) 2012-03-15 Notes: - + --*/ using System; namespace Microsoft.Z3 { - /// - /// The exception base class for error reporting from Z3 - /// - public class Z3Exception : Exception + /// + /// The exception base class for error reporting from Z3 + /// +#if !DOTNET_CORE + [Serializable] +#endif + public class Z3Exception : Exception { /// /// Constructor. diff --git a/src/api/dotnet/Z3Object.cs b/src/api/dotnet/Z3Object.cs index cd6803341..f32ba30af 100644 --- a/src/api/dotnet/Z3Object.cs +++ b/src/api/dotnet/Z3Object.cs @@ -138,7 +138,7 @@ namespace Microsoft.Z3 } [Pure] - internal static IntPtr[] EnumToNative(IEnumerable a) + internal static IntPtr[] EnumToNative(IEnumerable a) where T : Z3Object { Contract.Ensures(a == null || Contract.Result() != null); Contract.Ensures(a == null || Contract.Result().Length == a.Count()); diff --git a/src/api/dotnet/core/DummyContracts.cs b/src/api/dotnet/core/DummyContracts.cs new file mode 100644 index 000000000..49b498b1a --- /dev/null +++ b/src/api/dotnet/core/DummyContracts.cs @@ -0,0 +1,65 @@ +/*++ +Copyright () 2016 Microsoft Corporation + +Module Name: + + Contracts.cs + +Abstract: + + Z3 Managed API: Dummy Code Contracts class for .NET + frameworks that don't support them (e.g., CoreCLR). + +Author: + + Christoph Wintersteiger (cwinter) 2016-10-06 + +Notes: + +--*/ + +namespace System.Diagnostics.Contracts +{ + public class ContractClass : Attribute + { + public ContractClass(Type t) { } + } + + public class ContractClassFor : Attribute + { + public ContractClassFor(Type t) { } + } + + public class ContractInvariantMethod : Attribute + { + public ContractInvariantMethod() { } + } + + public class ContractVerification : Attribute + { + public ContractVerification(bool b) { } + } + + public class Pure : Attribute { } + + public static class Contract + { + [Conditional("false")] + public static void Ensures(bool b) { } + [Conditional("false")] + public static void Requires(bool b) { } + [Conditional("false")] + public static void Assume(bool b, string msg) { } + [Conditional("false")] + public static void Assert(bool b) { } + public static bool ForAll(bool b) { return true; } + public static bool ForAll(Object c, Func p) { return true; } + public static bool ForAll(int from, int to, Predicate p) { return true; } + [Conditional("false")] + public static void Invariant(bool b) { } + public static T[] Result() { return new T[1]; } + [Conditional("false")] + public static void EndContractBlock() { } + public static T ValueAtReturn(out T v) { T[] t = new T[1]; v = t[0]; return v; } + } +} \ No newline at end of file diff --git a/src/api/dotnet/core/README.txt b/src/api/dotnet/core/README.txt new file mode 100644 index 000000000..72331d7f9 --- /dev/null +++ b/src/api/dotnet/core/README.txt @@ -0,0 +1,9 @@ +Z3 API for .NET Core + +Z3's .NET API uses Code Contracts, which are not included in .NET Core. The +enclosed file called DummyContracts.cs provides stubs for the Code Contracts +functions, so that the API will compile, but not perform any contract +checking. To build this using .NET core, run (in this directory): + +dotnet restore +dotnet build project.json diff --git a/src/api/dotnet/core/project.json b/src/api/dotnet/core/project.json new file mode 100644 index 000000000..d54b6877b --- /dev/null +++ b/src/api/dotnet/core/project.json @@ -0,0 +1,22 @@ +{ + "version": "1.0.0-*", + "buildOptions": { + "debugType": "portable", + "emitEntryPoint": false, + "outputName": "Microsoft.Z3", + "compile": [ "../*.cs", "*.cs" ], + "define": ["DOTNET_CORE"] + }, + "dependencies": { }, + "frameworks": { + "netcoreapp1.0": { + "dependencies": { + "Microsoft.NETCore.App": { + "type": "platform", + "version": "1.0.1" + } + }, + "imports": "dnxcore50" + } + } +} diff --git a/src/api/dotnet/dotnet35/Example/App.config b/src/api/dotnet/dotnet35/Example/App.config new file mode 100644 index 000000000..88fa4027b --- /dev/null +++ b/src/api/dotnet/dotnet35/Example/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/api/dotnet/dotnet35/Example/Example.csproj b/src/api/dotnet/dotnet35/Example/Example.csproj new file mode 100644 index 000000000..2b096ed40 --- /dev/null +++ b/src/api/dotnet/dotnet35/Example/Example.csproj @@ -0,0 +1,78 @@ + + + + + Debug + AnyCPU + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812} + Exe + Properties + Example + Example + v4.5.2 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + TRACE;DEBUG;FRAMEWORK_LT_4 + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;FRAMEWORK_LT_4 + prompt + 4 + + + true + bin\x64\Debug\ + TRACE;DEBUG;FRAMEWORK_LT_4 + full + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\Release\ + TRACE;FRAMEWORK_LT_4 + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + + Program.cs + + + + + + + + + {ec3db697-b734-42f7-9468-5b62821eeb5a} + Microsoft.Z3.NET35 + + + + + \ No newline at end of file diff --git a/src/api/dotnet/dotnet35/Example/Properties/AssemblyInfo.cs b/src/api/dotnet/dotnet35/Example/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..ed0d8454f --- /dev/null +++ b/src/api/dotnet/dotnet35/Example/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Example")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Example")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("2a8e577b-7b6d-4ca9-832a-ca2eec314812")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.csproj b/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.csproj new file mode 100644 index 000000000..d278b4f1d --- /dev/null +++ b/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.csproj @@ -0,0 +1,347 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {EC3DB697-B734-42F7-9468-5B62821EEB5A} + Library + Properties + Microsoft.Z3 + Microsoft.Z3 + v3.5 + 512 + + + 0 + + + true + full + false + Debug\ + TRACE;DEBUG;FRAMEWORK_LT_4 + prompt + 4 + true + Debug\Microsoft.Z3.XML + False + False + True + False + False + True + False + True + True + False + False + False + True + False + False + False + True + False + False + True + True + True + False + False + + + + + + + True + Full + %28none%29 + 2 + + + pdbonly + true + Release\ + FRAMEWORK_LT_4 + prompt + 4 + true + Release\Microsoft.Z3.xml + x86 + + + true + + + + + + + false + + + true + bin\x64\Debug\ + TRACE;DEBUG;FRAMEWORK_LT_4 + true + Debug\Microsoft.Z3.XML + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + FRAMEWORK_LT_4 + true + Release\Microsoft.Z3.xml + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + + packages\Code.Contract.1.0.0\lib\net35\Microsoft.Contracts.dll + True + + + + + + + AlgebraicNum.cs + + + ApplyResult.cs + + + ArithExpr.cs + + + ArithSort.cs + + + ArrayExpr.cs + + + ArraySort.cs + + + AST.cs + + + ASTMap.cs + + + ASTVector.cs + + + BitVecExpr.cs + + + BitVecNum.cs + + + BitVecSort.cs + + + BoolExpr.cs + + + BoolSort.cs + + + Constructor.cs + + + ConstructorList.cs + + + Context.cs + + + DatatypeExpr.cs + + + DatatypeSort.cs + + + Deprecated.cs + + + Enumerations.cs + + + EnumSort.cs + + + Expr.cs + + + FiniteDomainExpr.cs + + + FiniteDomainNum.cs + + + FiniteDomainSort.cs + + + Fixedpoint.cs + + + FPExpr.cs + + + FPNum.cs + + + FPRMExpr.cs + + + FPRMNum.cs + + + FPRMSort.cs + + + FPSort.cs + + + FuncDecl.cs + + + FuncInterp.cs + + + Global.cs + + + Goal.cs + + + IDecRefQueue.cs + + + InterpolationContext.cs + + + IntExpr.cs + + + IntNum.cs + + + IntSort.cs + + + IntSymbol.cs + + + ListSort.cs + + + Log.cs + + + Model.cs + + + Native.cs + + + Optimize.cs + + + ParamDescrs.cs + + + Params.cs + + + Pattern.cs + + + Probe.cs + + + Quantifier.cs + + + RatNum.cs + + + RealExpr.cs + + + RealSort.cs + + + ReExpr.cs + + + RelationSort.cs + + + ReSort.cs + + + SeqExpr.cs + + + SeqSort.cs + + + SetSort.cs + + + Solver.cs + + + Sort.cs + + + Statistics.cs + + + Status.cs + + + StringSymbol.cs + + + Symbol.cs + + + Tactic.cs + + + TupleSort.cs + + + UninterpretedSort.cs + + + Version.cs + + + Z3Exception.cs + + + Z3Object.cs + + + + + + + + + + + \ No newline at end of file diff --git a/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.sln b/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.sln new file mode 100644 index 000000000..b6e252684 --- /dev/null +++ b/src/api/dotnet/dotnet35/Microsoft.Z3.NET35.sln @@ -0,0 +1,48 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Z3.NET35", "Microsoft.Z3.NET35.csproj", "{EC3DB697-B734-42F7-9468-5B62821EEB5A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{2A8E577B-7B6D-4CA9-832A-CA2EEC314812}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|x64.ActiveCfg = Debug|Any CPU + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|x64.Build.0 = Debug|Any CPU + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|x86.ActiveCfg = Debug|Any CPU + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Debug|x86.Build.0 = Debug|Any CPU + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|Any CPU.Build.0 = Release|Any CPU + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|x64.ActiveCfg = Release|x64 + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|x64.Build.0 = Release|x64 + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|x86.ActiveCfg = Release|Any CPU + {EC3DB697-B734-42F7-9468-5B62821EEB5A}.Release|x86.Build.0 = Release|Any CPU + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|x64.ActiveCfg = Debug|Any CPU + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|x64.Build.0 = Debug|Any CPU + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|x86.ActiveCfg = Debug|Any CPU + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Debug|x86.Build.0 = Debug|Any CPU + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|Any CPU.Build.0 = Release|Any CPU + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|x64.ActiveCfg = Release|x64 + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|x64.Build.0 = Release|x64 + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|x86.ActiveCfg = Release|Any CPU + {2A8E577B-7B6D-4CA9-832A-CA2EEC314812}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs b/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..fb4319002 --- /dev/null +++ b/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs @@ -0,0 +1,38 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Permissions; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Z3 .NET Interface")] +[assembly: AssemblyDescription(".NET Interface to the Z3 Theorem Prover")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyProduct("Z3")] +[assembly: AssemblyCopyright("Copyright (C) 2006-2015 Microsoft Corporation")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4853ed71-2078-40f4-8117-bc46646bce0e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("4.2.0.0")] +[assembly: AssemblyVersion("4.5.1.6031")] +[assembly: AssemblyFileVersion("4.5.1.6031")] diff --git a/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs.in b/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs.in new file mode 100644 index 000000000..e5a85f16f --- /dev/null +++ b/src/api/dotnet/dotnet35/Properties/AssemblyInfo.cs.in @@ -0,0 +1,38 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Permissions; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Z3 .NET Interface")] +[assembly: AssemblyDescription(".NET Interface to the Z3 Theorem Prover")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyProduct("Z3")] +[assembly: AssemblyCopyright("Copyright (C) 2006-2015 Microsoft Corporation")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4853ed71-2078-40f4-8117-bc46646bce0e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("4.2.0.0")] +[assembly: AssemblyVersion("@VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_REVISION@")] +[assembly: AssemblyFileVersion("@VER_MAJOR@.@VER_MINOR@.@VER_BUILD@.@VER_REVISION@")] diff --git a/src/api/dotnet/Readme.NET35 b/src/api/dotnet/dotnet35/Readme.NET35 similarity index 87% rename from src/api/dotnet/Readme.NET35 rename to src/api/dotnet/dotnet35/Readme.NET35 index 73743fd15..f8c2958ee 100644 --- a/src/api/dotnet/Readme.NET35 +++ b/src/api/dotnet/dotnet35/Readme.NET35 @@ -6,4 +6,5 @@ In the project properties of Microsoft.Z3.csproj: - Under 'Application': Change Target framework to .NET Framework 3.5 - Under 'Build': Add FRAMEWORK_LT_4 to the condidional compilation symbols - Remove the reference to System.Numerics -- Install the NuGet Package "Microsoft Code Contracts for Net3.5" +- Install the NuGet Package "Microsoft Code Contracts for Net3.5": + In the Package Manager Console enter Install-Package Code.Contract diff --git a/src/api/dotnet/dotnet35/packages.config b/src/api/dotnet/dotnet35/packages.config new file mode 100644 index 000000000..daa06aed7 --- /dev/null +++ b/src/api/dotnet/dotnet35/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/api/java/AST.java b/src/api/java/AST.java index e84dc8c81..e1cde837f 100644 --- a/src/api/java/AST.java +++ b/src/api/java/AST.java @@ -175,15 +175,8 @@ public class AST extends Z3Object implements Comparable * A string representation of the AST. **/ @Override - public String toString() - { - try - { - return Native.astToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + public String toString() { + return Native.astToString(getContext().nCtx(), getNativeObject()); } /** @@ -194,34 +187,18 @@ public class AST extends Z3Object implements Comparable return Native.astToString(getContext().nCtx(), getNativeObject()); } - AST(Context ctx) - { - super(ctx); - } - - AST(Context ctx, long obj) - { + AST(Context ctx, long obj) { super(ctx, obj); } @Override - void incRef(long o) - { - // Console.WriteLine("AST IncRef()"); - if (getContext() == null || o == 0) - return; - getContext().getASTDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.incRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - // Console.WriteLine("AST DecRef()"); - if (getContext() == null || o == 0) - return; - getContext().getASTDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getASTDRQ().storeReference(getContext(), this); } static AST create(Context ctx, long obj) diff --git a/src/api/java/ASTDecRefQueue.java b/src/api/java/ASTDecRefQueue.java index 32f6a5d0e..b0a6fa217 100644 --- a/src/api/java/ASTDecRefQueue.java +++ b/src/api/java/ASTDecRefQueue.java @@ -17,39 +17,15 @@ Notes: package com.microsoft.z3; -class ASTDecRefQueue extends IDecRefQueue +class ASTDecRefQueue extends IDecRefQueue { public ASTDecRefQueue() { super(); } - public ASTDecRefQueue(int move_limit) - { - super(move_limit); - } - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.incRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } - - @Override - protected void decRef(Context ctx, long obj) - { - try - { - Native.decRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + protected void decRef(Context ctx, long obj) { + Native.decRef(ctx.nCtx(), obj); } }; diff --git a/src/api/java/ASTMap.java b/src/api/java/ASTMap.java index 7bc485bf5..916811cec 100644 --- a/src/api/java/ASTMap.java +++ b/src/api/java/ASTMap.java @@ -20,8 +20,7 @@ package com.microsoft.z3; /** * Map from AST to AST **/ -class ASTMap extends Z3Object -{ +class ASTMap extends Z3Object { /** * Checks whether the map contains the key {@code k}. * @param k An AST @@ -104,13 +103,7 @@ class ASTMap extends Z3Object @Override public String toString() { - try - { - return Native.astMapToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + return Native.astMapToString(getContext().nCtx(), getNativeObject()); } ASTMap(Context ctx, long obj) @@ -124,16 +117,12 @@ class ASTMap extends Z3Object } @Override - void incRef(long o) - { - getContext().getASTMapDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.astMapIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getASTMapDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getASTMapDRQ().storeReference(getContext(), this); } } diff --git a/src/api/java/ASTVector.java b/src/api/java/ASTVector.java index ab99e2aee..4d9ab291a 100644 --- a/src/api/java/ASTVector.java +++ b/src/api/java/ASTVector.java @@ -20,8 +20,7 @@ package com.microsoft.z3; /** * Vectors of ASTs. **/ -public class ASTVector extends Z3Object -{ +public class ASTVector extends Z3Object { /** * The size of the vector **/ @@ -88,15 +87,8 @@ public class ASTVector extends Z3Object * Retrieves a string representation of the vector. **/ @Override - public String toString() - { - try - { - return Native.astVectorToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + public String toString() { + return Native.astVectorToString(getContext().nCtx(), getNativeObject()); } ASTVector(Context ctx, long obj) @@ -110,19 +102,15 @@ public class ASTVector extends Z3Object } @Override - void incRef(long o) - { - getContext().getASTVectorDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.astVectorIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getASTVectorDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getASTVectorDRQ().storeReference(getContext(), this); } - + /** * Translates the AST vector into an AST[] * */ diff --git a/src/api/java/ApplyResult.java b/src/api/java/ApplyResult.java index 84c63e966..6fafbd888 100644 --- a/src/api/java/ApplyResult.java +++ b/src/api/java/ApplyResult.java @@ -21,8 +21,7 @@ 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 -{ +public class ApplyResult extends Z3Object { /** * The number of Subgoals. **/ @@ -64,15 +63,8 @@ public class ApplyResult extends Z3Object * A string representation of the ApplyResult. **/ @Override - public String toString() - { - try - { - return Native.applyResultToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + public String toString() { + return Native.applyResultToString(getContext().nCtx(), getNativeObject()); } ApplyResult(Context ctx, long obj) @@ -81,16 +73,12 @@ public class ApplyResult extends Z3Object } @Override - void incRef(long o) - { - getContext().getApplyResultDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.applyResultIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getApplyResultDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getApplyResultDRQ().storeReference(getContext(), this); } } diff --git a/src/api/java/ApplyResultDecRefQueue.java b/src/api/java/ApplyResultDecRefQueue.java index 63f315ecd..e1a660781 100644 --- a/src/api/java/ApplyResultDecRefQueue.java +++ b/src/api/java/ApplyResultDecRefQueue.java @@ -17,39 +17,15 @@ Notes: package com.microsoft.z3; -class ApplyResultDecRefQueue extends IDecRefQueue +class ApplyResultDecRefQueue extends IDecRefQueue { public ApplyResultDecRefQueue() { super(); } - public ApplyResultDecRefQueue(int move_limit) - { - super(move_limit); - } - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.applyResultIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } - - @Override - protected void decRef(Context ctx, long obj) - { - try - { - Native.applyResultDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + protected void decRef(Context ctx, long obj) { + Native.applyResultDecRef(ctx.nCtx(), obj); } }; diff --git a/src/api/java/AstMapDecRefQueue.java b/src/api/java/AstMapDecRefQueue.java index 1598f75e7..6c96970b7 100644 --- a/src/api/java/AstMapDecRefQueue.java +++ b/src/api/java/AstMapDecRefQueue.java @@ -17,39 +17,14 @@ Notes: package com.microsoft.z3; -class ASTMapDecRefQueue extends IDecRefQueue -{ +class ASTMapDecRefQueue extends IDecRefQueue { public ASTMapDecRefQueue() { super(); } - public ASTMapDecRefQueue(int move_limit) - { - super(move_limit); - } - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.astMapIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + protected void decRef(Context ctx, long obj) { + Native.astMapDecRef(ctx.nCtx(), obj); } - - @Override - protected void decRef(Context ctx, long obj) - { - try - { - Native.astMapDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } -}; +} diff --git a/src/api/java/AstVectorDecRefQueue.java b/src/api/java/AstVectorDecRefQueue.java index a63d808d3..e7ce3e33e 100644 --- a/src/api/java/AstVectorDecRefQueue.java +++ b/src/api/java/AstVectorDecRefQueue.java @@ -17,39 +17,14 @@ Notes: package com.microsoft.z3; -class ASTVectorDecRefQueue extends IDecRefQueue -{ +class ASTVectorDecRefQueue extends IDecRefQueue { public ASTVectorDecRefQueue() { super(); } - public ASTVectorDecRefQueue(int move_limit) - { - super(move_limit); - } - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.astVectorIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + protected void decRef(Context ctx, long obj) { + Native.astVectorDecRef(ctx.nCtx(), obj); } - - @Override - protected void decRef(Context ctx, long obj) - { - try - { - Native.astVectorDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } -}; +} diff --git a/src/api/java/BitVecNum.java b/src/api/java/BitVecNum.java index 5a62f8087..d6c176855 100644 --- a/src/api/java/BitVecNum.java +++ b/src/api/java/BitVecNum.java @@ -66,13 +66,7 @@ public class BitVecNum extends BitVecExpr @Override public String toString() { - try - { - return Native.getNumeralString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + return Native.getNumeralString(getContext().nCtx(), getNativeObject()); } BitVecNum(Context ctx, long obj) diff --git a/src/api/java/BoolExpr.java b/src/api/java/BoolExpr.java index 02eabd2b5..dc75b2e7c 100644 --- a/src/api/java/BoolExpr.java +++ b/src/api/java/BoolExpr.java @@ -20,15 +20,7 @@ package com.microsoft.z3; /** * Boolean expressions **/ -public class BoolExpr extends Expr -{ - /** - * Constructor for BoolExpr - **/ - protected BoolExpr(Context ctx) - { - super(ctx); - } +public class BoolExpr extends Expr { /** * Constructor for BoolExpr diff --git a/src/api/java/Constructor.java b/src/api/java/Constructor.java index a6c2856d4..87ab86c3f 100644 --- a/src/api/java/Constructor.java +++ b/src/api/java/Constructor.java @@ -20,8 +20,14 @@ package com.microsoft.z3; /** * Constructors are used for datatype sorts. **/ -public class Constructor extends Z3Object -{ +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 @@ -78,29 +84,19 @@ public class Constructor extends Z3Object return t; } - /** - * Destructor. - * @throws Throwable - * @throws Z3Exception on error - **/ - protected void finalize() throws Throwable - { - try { - Native.delConstructor(getContext().nCtx(), getNativeObject()); - } finally { - super.finalize(); - } + @Override + void incRef() { + // Datatype constructors are not reference counted. } - private int n = 0; + @Override + void addToReferenceQueue() { + getContext().getConstructorDRQ().storeReference(getContext(), this); + } - Constructor(Context ctx, Symbol name, Symbol recognizer, - Symbol[] fieldNames, Sort[] sorts, int[] sortRefs) - - { - super(ctx); - - n = AST.arrayLength(fieldNames); + 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( @@ -112,9 +108,10 @@ public class Constructor extends Z3Object if (sortRefs == null) sortRefs = new int[n]; - setNativeObject(Native.mkConstructor(ctx.nCtx(), name.getNativeObject(), + long nativeObj = Native.mkConstructor(ctx.nCtx(), name.getNativeObject(), recognizer.getNativeObject(), n, Symbol.arrayToNative(fieldNames), - Sort.arrayToNative(sorts), sortRefs)); + Sort.arrayToNative(sorts), sortRefs); + return new Constructor(ctx, n, nativeObj); } } diff --git a/src/api/java/ConstructorDecRefQueue.java b/src/api/java/ConstructorDecRefQueue.java new file mode 100644 index 000000000..5003dde5f --- /dev/null +++ b/src/api/java/ConstructorDecRefQueue.java @@ -0,0 +1,12 @@ +package com.microsoft.z3; + +public class ConstructorDecRefQueue extends IDecRefQueue { + public ConstructorDecRefQueue() { + super(); + } + + @Override + protected void decRef(Context ctx, long obj) { + Native.delConstructor(ctx.nCtx(), obj); + } +} diff --git a/src/api/java/ConstructorList.java b/src/api/java/ConstructorList.java index fe3fae1ac..c79e08d9e 100644 --- a/src/api/java/ConstructorList.java +++ b/src/api/java/ConstructorList.java @@ -20,32 +20,26 @@ package com.microsoft.z3; /** * Lists of constructors **/ -public class ConstructorList extends Z3Object -{ - /** - * Destructor. - * @throws Throwable - * @throws Z3Exception on error - **/ - protected void finalize() throws Throwable - { - try { - Native.delConstructorList(getContext().nCtx(), getNativeObject()); - } finally { - super.finalize(); - } - } +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); - - setNativeObject(Native.mkConstructorList(getContext().nCtx(), + super(ctx, Native.mkConstructorList(ctx.nCtx(), constructors.length, Constructor.arrayToNative(constructors))); } diff --git a/src/api/java/ConstructorListDecRefQueue.java b/src/api/java/ConstructorListDecRefQueue.java new file mode 100644 index 000000000..1a2460731 --- /dev/null +++ b/src/api/java/ConstructorListDecRefQueue.java @@ -0,0 +1,12 @@ +package com.microsoft.z3; + +public class ConstructorListDecRefQueue extends IDecRefQueue { + public ConstructorListDecRefQueue() { + super(); + } + + @Override + protected void decRef(Context ctx, long obj) { + Native.delConstructorList(ctx.nCtx(), obj); + } +} diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 9fa17fcd4..d50968a32 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -17,34 +17,40 @@ Notes: package com.microsoft.z3; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; +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. **/ -public class Context extends IDisposable -{ - /** - * Constructor. - **/ - public Context() - { - super(); - synchronized (creation_lock) { +public class Context implements AutoCloseable { + private final long m_ctx; + static final Object creation_lock = new Object(); + + public Context () { + synchronized (creation_lock) { m_ctx = Native.mkContextRc(0); - initContext(); + init(); } } + protected Context (long m_ctx) { + synchronized (creation_lock) { + this.m_ctx = m_ctx; + init(); + } + } + + /** * Constructor. * Remarks: - * The following parameters can be set: + * The following parameters can be set: * - proof (Boolean) Enable proof generation - * - debug_ref_count (Boolean) Enable debug support for Z3_ast reference counting + * - 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 @@ -53,22 +59,26 @@ public class Context extends IDisposable * - 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 + * 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) - { - super(); - synchronized (creation_lock) { + public Context(Map settings) { + synchronized (creation_lock) { long cfg = Native.mkConfig(); - for (Map.Entry kv : settings.entrySet()) + for (Map.Entry kv : settings.entrySet()) { Native.setParamValue(cfg, kv.getKey(), kv.getValue()); + } m_ctx = Native.mkContextRc(cfg); Native.delConfig(cfg); - initContext(); + 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. @@ -242,7 +252,7 @@ public class Context extends IDisposable checkContextMatch(name); checkContextMatch(fieldNames); checkContextMatch(fieldSorts); - return new TupleSort(this, name, (int) fieldNames.length, fieldNames, + return new TupleSort(this, name, fieldNames.length, fieldNames, fieldSorts); } @@ -319,8 +329,7 @@ public class Context extends IDisposable Symbol[] fieldNames, Sort[] sorts, int[] sortRefs) { - - return new Constructor(this, name, recognizer, fieldNames, sorts, + return of(this, name, recognizer, fieldNames, sorts, sortRefs); } @@ -329,10 +338,8 @@ public class Context extends IDisposable **/ public Constructor mkConstructor(String name, String recognizer, String[] fieldNames, Sort[] sorts, int[] sortRefs) - { - - return new Constructor(this, mkSymbol(name), mkSymbol(recognizer), + return of(this, mkSymbol(name), mkSymbol(recognizer), mkSymbols(fieldNames), sorts, sortRefs); } @@ -525,7 +532,7 @@ public class Context extends IDisposable throw new Z3Exception("Cannot create a pattern from zero terms"); long[] termsNative = AST.arrayToNative(terms); - return new Pattern(this, Native.mkPattern(nCtx(), (int) terms.length, + return new Pattern(this, Native.mkPattern(nCtx(), terms.length, termsNative)); } @@ -688,7 +695,7 @@ public class Context extends IDisposable public BoolExpr mkDistinct(Expr... args) { checkContextMatch(args); - return new BoolExpr(this, Native.mkDistinct(nCtx(), (int) args.length, + return new BoolExpr(this, Native.mkDistinct(nCtx(), args.length, AST.arrayToNative(args))); } @@ -756,7 +763,7 @@ public class Context extends IDisposable public BoolExpr mkAnd(BoolExpr... t) { checkContextMatch(t); - return new BoolExpr(this, Native.mkAnd(nCtx(), (int) t.length, + return new BoolExpr(this, Native.mkAnd(nCtx(), t.length, AST.arrayToNative(t))); } @@ -766,7 +773,7 @@ public class Context extends IDisposable public BoolExpr mkOr(BoolExpr... t) { checkContextMatch(t); - return new BoolExpr(this, Native.mkOr(nCtx(), (int) t.length, + return new BoolExpr(this, Native.mkOr(nCtx(), t.length, AST.arrayToNative(t))); } @@ -777,7 +784,7 @@ public class Context extends IDisposable { checkContextMatch(t); return (ArithExpr) Expr.create(this, - Native.mkAdd(nCtx(), (int) t.length, AST.arrayToNative(t))); + Native.mkAdd(nCtx(), t.length, AST.arrayToNative(t))); } /** @@ -787,7 +794,7 @@ public class Context extends IDisposable { checkContextMatch(t); return (ArithExpr) Expr.create(this, - Native.mkMul(nCtx(), (int) t.length, AST.arrayToNative(t))); + Native.mkMul(nCtx(), t.length, AST.arrayToNative(t))); } /** @@ -797,7 +804,7 @@ public class Context extends IDisposable { checkContextMatch(t); return (ArithExpr) Expr.create(this, - Native.mkSub(nCtx(), (int) t.length, AST.arrayToNative(t))); + Native.mkSub(nCtx(), t.length, AST.arrayToNative(t))); } /** @@ -1814,7 +1821,7 @@ public class Context extends IDisposable { checkContextMatch(args); return (ArrayExpr)Expr.create(this, - Native.mkSetUnion(nCtx(), (int) args.length, + Native.mkSetUnion(nCtx(), args.length, AST.arrayToNative(args))); } @@ -1825,7 +1832,7 @@ public class Context extends IDisposable { checkContextMatch(args); return (ArrayExpr)Expr.create(this, - Native.mkSetIntersect(nCtx(), (int) args.length, + Native.mkSetIntersect(nCtx(), args.length, AST.arrayToNative(args))); } @@ -1912,7 +1919,7 @@ public class Context extends IDisposable public SeqExpr MkConcat(SeqExpr... t) { checkContextMatch(t); - return new SeqExpr(this, Native.mkSeqConcat(nCtx(), (int)t.length, AST.arrayToNative(t))); + return new SeqExpr(this, Native.mkSeqConcat(nCtx(), t.length, AST.arrayToNative(t))); } @@ -2040,7 +2047,7 @@ public class Context extends IDisposable public ReExpr MkConcat(ReExpr... t) { checkContextMatch(t); - return new ReExpr(this, Native.mkReConcat(nCtx(), (int)t.length, AST.arrayToNative(t))); + return new ReExpr(this, Native.mkReConcat(nCtx(), t.length, AST.arrayToNative(t))); } /** @@ -2049,7 +2056,7 @@ public class Context extends IDisposable public ReExpr MkUnion(ReExpr... t) { checkContextMatch(t); - return new ReExpr(this, Native.mkReUnion(nCtx(), (int)t.length, AST.arrayToNative(t))); + return new ReExpr(this, Native.mkReUnion(nCtx(), t.length, AST.arrayToNative(t))); } @@ -2258,7 +2265,7 @@ public class Context extends IDisposable Symbol quantifierID, Symbol skolemID) { - return new Quantifier(this, true, sorts, names, body, weight, patterns, + return Quantifier.of(this, true, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } @@ -2271,7 +2278,7 @@ public class Context extends IDisposable Symbol skolemID) { - return new Quantifier(this, true, boundConstants, body, weight, + return Quantifier.of(this, true, boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } @@ -2284,7 +2291,7 @@ public class Context extends IDisposable Symbol quantifierID, Symbol skolemID) { - return new Quantifier(this, false, sorts, names, body, weight, + return Quantifier.of(this, false, sorts, names, body, weight, patterns, noPatterns, quantifierID, skolemID); } @@ -2297,7 +2304,7 @@ public class Context extends IDisposable Symbol skolemID) { - return new Quantifier(this, false, boundConstants, body, weight, + return Quantifier.of(this, false, boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } @@ -3814,7 +3821,7 @@ public class Context extends IDisposable * 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 + * @see #unwrapAST * @param nativeObject The native pointer to wrap. **/ public AST wrapAST(long nativeObject) @@ -3869,19 +3876,12 @@ public class Context extends IDisposable Native.updateParamValue(nCtx(), id, value); } - protected long m_ctx = 0; - protected static final Object creation_lock = new Object(); long nCtx() { return m_ctx; } - void initContext() - { - setPrintMode(Z3_ast_print_mode.Z3_PRINT_SMTLIB2_COMPLIANT); - Native.setInternalErrorHandler(nCtx()); - } void checkContextMatch(Z3Object other) { @@ -3910,157 +3910,142 @@ public class Context extends IDisposable } private ASTDecRefQueue m_AST_DRQ = new ASTDecRefQueue(); - private ASTMapDecRefQueue m_ASTMap_DRQ = new ASTMapDecRefQueue(10); - private ASTVectorDecRefQueue m_ASTVector_DRQ = new ASTVectorDecRefQueue(10); - private ApplyResultDecRefQueue m_ApplyResult_DRQ = new ApplyResultDecRefQueue(10); - private FuncInterpEntryDecRefQueue m_FuncEntry_DRQ = new FuncInterpEntryDecRefQueue(10); - private FuncInterpDecRefQueue m_FuncInterp_DRQ = new FuncInterpDecRefQueue(10); - private GoalDecRefQueue m_Goal_DRQ = new GoalDecRefQueue(10); - private ModelDecRefQueue m_Model_DRQ = new ModelDecRefQueue(10); - private ParamsDecRefQueue m_Params_DRQ = new ParamsDecRefQueue(10); - private ParamDescrsDecRefQueue m_ParamDescrs_DRQ = new ParamDescrsDecRefQueue(10); - private ProbeDecRefQueue m_Probe_DRQ = new ProbeDecRefQueue(10); - private SolverDecRefQueue m_Solver_DRQ = new SolverDecRefQueue(10); - private StatisticsDecRefQueue m_Statistics_DRQ = new StatisticsDecRefQueue(10); - private TacticDecRefQueue m_Tactic_DRQ = new TacticDecRefQueue(10); - private FixedpointDecRefQueue m_Fixedpoint_DRQ = new FixedpointDecRefQueue(10); - private OptimizeDecRefQueue m_Optimize_DRQ = new OptimizeDecRefQueue(10); + 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 getASTDRQ() + public IDecRefQueue getConstructorDRQ() { + return m_Constructor_DRQ; + } + + public IDecRefQueue getConstructorListDRQ() { + return m_ConstructorList_DRQ; + } + + public IDecRefQueue getASTDRQ() { return m_AST_DRQ; } - public IDecRefQueue getASTMapDRQ() + public IDecRefQueue getASTMapDRQ() { return m_ASTMap_DRQ; } - public IDecRefQueue getASTVectorDRQ() + public IDecRefQueue getASTVectorDRQ() { return m_ASTVector_DRQ; } - public IDecRefQueue getApplyResultDRQ() + public IDecRefQueue getApplyResultDRQ() { return m_ApplyResult_DRQ; } - public IDecRefQueue getFuncEntryDRQ() + public IDecRefQueue getFuncEntryDRQ() { return m_FuncEntry_DRQ; } - public IDecRefQueue getFuncInterpDRQ() + public IDecRefQueue getFuncInterpDRQ() { return m_FuncInterp_DRQ; } - public IDecRefQueue getGoalDRQ() + public IDecRefQueue getGoalDRQ() { return m_Goal_DRQ; } - public IDecRefQueue getModelDRQ() + public IDecRefQueue getModelDRQ() { return m_Model_DRQ; } - public IDecRefQueue getParamsDRQ() + public IDecRefQueue getParamsDRQ() { return m_Params_DRQ; } - public IDecRefQueue getParamDescrsDRQ() + public IDecRefQueue getParamDescrsDRQ() { return m_ParamDescrs_DRQ; } - public IDecRefQueue getProbeDRQ() + public IDecRefQueue getProbeDRQ() { return m_Probe_DRQ; } - public IDecRefQueue getSolverDRQ() + public IDecRefQueue getSolverDRQ() { return m_Solver_DRQ; } - public IDecRefQueue getStatisticsDRQ() + public IDecRefQueue getStatisticsDRQ() { return m_Statistics_DRQ; } - public IDecRefQueue getTacticDRQ() + public IDecRefQueue getTacticDRQ() { return m_Tactic_DRQ; } - public IDecRefQueue getFixedpointDRQ() + public IDecRefQueue getFixedpointDRQ() { return m_Fixedpoint_DRQ; } - public IDecRefQueue getOptimizeDRQ() + public IDecRefQueue getOptimizeDRQ() { return m_Optimize_DRQ; } - protected AtomicInteger m_refCount = new AtomicInteger(0); - - /** - * Finalizer. - * @throws Throwable - **/ - protected void finalize() throws Throwable - { - try { - dispose(); - } - catch (Throwable t) { - throw t; - } - finally { - super.finalize(); - } - } - /** * Disposes of the context. **/ - public void dispose() + @Override + public void close() { - m_AST_DRQ.clear(this); - m_ASTMap_DRQ.clear(this); - m_ASTVector_DRQ.clear(this); - m_ApplyResult_DRQ.clear(this); - m_FuncEntry_DRQ.clear(this); - m_FuncInterp_DRQ.clear(this); - m_Goal_DRQ.clear(this); - m_Model_DRQ.clear(this); - m_Params_DRQ.clear(this); - m_Probe_DRQ.clear(this); - m_Solver_DRQ.clear(this); - m_Optimize_DRQ.clear(this); - m_Statistics_DRQ.clear(this); - m_Tactic_DRQ.clear(this); - m_Fixedpoint_DRQ.clear(this); + 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) { - if (m_refCount.get() == 0 && m_ctx != 0) { - try { - Native.delContext(m_ctx); - } catch (Z3Exception e) { - // OK? - System.out.println("Context deletion failed; memory leak possible."); - } - m_ctx = 0; - } + Native.delContext(m_ctx); } } } diff --git a/src/api/java/EnumSort.java b/src/api/java/EnumSort.java index bb60eef56..ce2f8d578 100644 --- a/src/api/java/EnumSort.java +++ b/src/api/java/EnumSort.java @@ -92,13 +92,9 @@ public class EnumSort extends Sort EnumSort(Context ctx, Symbol name, Symbol[] enumNames) { - super(ctx, 0); - - int n = enumNames.length; - long[] n_constdecls = new long[n]; - long[] n_testers = new long[n]; - setNativeObject(Native.mkEnumerationSort(ctx.nCtx(), - name.getNativeObject(), n, Symbol.arrayToNative(enumNames), - n_constdecls, n_testers)); + super(ctx, Native.mkEnumerationSort(ctx.nCtx(), + name.getNativeObject(), enumNames.length, + Symbol.arrayToNative(enumNames), + new long[enumNames.length], new long[enumNames.length])); } }; diff --git a/src/api/java/Expr.java b/src/api/java/Expr.java index b24d3ac62..ea3fd2147 100644 --- a/src/api/java/Expr.java +++ b/src/api/java/Expr.java @@ -120,13 +120,13 @@ public class Expr extends AST * @param args arguments * @throws Z3Exception on error **/ - public void update(Expr[] args) + public Expr update(Expr[] args) { getContext().checkContextMatch(args); if (isApp() && args.length != getNumArgs()) { throw new Z3Exception("Number of arguments does not match"); } - setNativeObject(Native.updateTerm(getContext().nCtx(), getNativeObject(), + return new Expr(getContext(), Native.updateTerm(getContext().nCtx(), getNativeObject(), args.length, Expr.arrayToNative(args))); } @@ -2091,36 +2091,23 @@ public class Expr extends AST **/ public int getIndex() { - if (!isVar()) + if (!isVar()) { throw new Z3Exception("Term is not a bound variable."); + } return Native.getIndexValue(getContext().nCtx(), getNativeObject()); } - /** - * Constructor for Expr - **/ - protected Expr(Context ctx) - { - super(ctx); - { - } - } - /** * Constructor for Expr * @throws Z3Exception on error **/ - protected Expr(Context ctx, long obj) - { + protected Expr(Context ctx, long obj) { super(ctx, obj); - { - } } @Override - void checkNativeObject(long obj) - { + 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()) { diff --git a/src/api/java/FPNum.java b/src/api/java/FPNum.java index 69a44c559..813e82889 100644 --- a/src/api/java/FPNum.java +++ b/src/api/java/FPNum.java @@ -20,7 +20,7 @@ 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 @@ -28,11 +28,20 @@ public class FPNum extends FPExpr */ public boolean getSign() { Native.IntPtr res = new Native.IntPtr(); - if (Native.fpaGetNumeralSign(getContext().nCtx(), getNativeObject(), res) ^ true) + 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 @@ -53,29 +62,121 @@ public class FPNum extends FPExpr public long getSignificandUInt64() { Native.LongPtr res = new Native.LongPtr(); - if (Native.fpaGetNumeralSignificandUint64(getContext().nCtx(), getNativeObject(), res) ^ true) + 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() { - return Native.fpaGetNumeralExponentString(getContext().nCtx(), getNativeObject()); + 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() { + public long getExponentInt64(boolean biased) { Native.LongPtr res = new Native.LongPtr(); - if (Native.fpaGetNumeralExponentInt64(getContext().nCtx(), getNativeObject(), res) ^ true) + 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) { @@ -87,13 +188,6 @@ public class FPNum extends FPExpr */ public String toString() { - try - { - return Native.getNumeralString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } - } - + return Native.getNumeralString(getContext().nCtx(), getNativeObject()); + } } diff --git a/src/api/java/FiniteDomainNum.java b/src/api/java/FiniteDomainNum.java index e53ed22b2..68467e408 100644 --- a/src/api/java/FiniteDomainNum.java +++ b/src/api/java/FiniteDomainNum.java @@ -68,12 +68,6 @@ public class FiniteDomainNum extends FiniteDomainExpr @Override public String toString() { - try - { - return Native.getNumeralString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + return Native.getNumeralString(getContext().nCtx(), getNativeObject()); } } diff --git a/src/api/java/Fixedpoint.java b/src/api/java/Fixedpoint.java index 14fb3a44a..ad6d5a658 100644 --- a/src/api/java/Fixedpoint.java +++ b/src/api/java/Fixedpoint.java @@ -87,12 +87,11 @@ public class Fixedpoint extends Z3Object /** * Add rule into the fixedpoint solver. - * + * + * @param name Nullable rule name. * @throws Z3Exception **/ - public void addRule(BoolExpr rule, Symbol name) - { - + public void addRule(BoolExpr rule, Symbol name) { getContext().checkContextMatch(rule); Native.fixedpointAddRule(getContext().nCtx(), getNativeObject(), rule.getNativeObject(), AST.getNativeObject(name)); @@ -103,11 +102,10 @@ public class Fixedpoint extends Z3Object * * @throws Z3Exception **/ - public void addFact(FuncDecl pred, int ... args) - { + public void addFact(FuncDecl pred, int ... args) { getContext().checkContextMatch(pred); Native.fixedpointAddFact(getContext().nCtx(), getNativeObject(), - pred.getNativeObject(), (int) args.length, args); + pred.getNativeObject(), args.length, args); } /** @@ -119,9 +117,7 @@ public class Fixedpoint extends Z3Object * * @throws Z3Exception **/ - public Status query(BoolExpr query) - { - + public Status query(BoolExpr query) { getContext().checkContextMatch(query); Z3_lbool r = Z3_lbool.fromInt(Native.fixedpointQuery(getContext().nCtx(), getNativeObject(), query.getNativeObject())); @@ -144,9 +140,7 @@ public class Fixedpoint extends Z3Object * * @throws Z3Exception **/ - public Status query(FuncDecl[] relations) - { - + public Status query(FuncDecl[] relations) { getContext().checkContextMatch(relations); Z3_lbool r = Z3_lbool.fromInt(Native.fixedpointQueryRelations(getContext() .nCtx(), getNativeObject(), AST.arrayLength(relations), AST @@ -166,8 +160,7 @@ public class Fixedpoint extends Z3Object * Creates a backtracking point. * @see #pop **/ - public void push() - { + public void push() { Native.fixedpointPush(getContext().nCtx(), getNativeObject()); } @@ -178,19 +171,17 @@ public class Fixedpoint extends Z3Object * * @see #push **/ - public void pop() - { + public void pop() { Native.fixedpointPop(getContext().nCtx(), getNativeObject()); } /** * Update named rule into in the fixedpoint solver. - * + * + * @param name Nullable rule name. * @throws Z3Exception **/ - public void updateRule(BoolExpr rule, Symbol name) - { - + public void updateRule(BoolExpr rule, Symbol name) { getContext().checkContextMatch(rule); Native.fixedpointUpdateRule(getContext().nCtx(), getNativeObject(), rule.getNativeObject(), AST.getNativeObject(name)); @@ -255,14 +246,8 @@ public class Fixedpoint extends Z3Object @Override public String toString() { - try - { - return Native.fixedpointToString(getContext().nCtx(), getNativeObject(), + return Native.fixedpointToString(getContext().nCtx(), getNativeObject(), 0, null); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } } /** @@ -355,16 +340,15 @@ public class Fixedpoint extends Z3Object } @Override - void incRef(long o) - { - getContext().getFixedpointDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.fixedpointIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getFixedpointDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getFixedpointDRQ().storeReference(getContext(), this); } + + @Override + void checkNativeObject(long obj) { } } diff --git a/src/api/java/FixedpointDecRefQueue.java b/src/api/java/FixedpointDecRefQueue.java index e65538a30..69ed82092 100644 --- a/src/api/java/FixedpointDecRefQueue.java +++ b/src/api/java/FixedpointDecRefQueue.java @@ -17,39 +17,15 @@ Notes: package com.microsoft.z3; -class FixedpointDecRefQueue extends IDecRefQueue -{ +class FixedpointDecRefQueue extends IDecRefQueue { public FixedpointDecRefQueue() { super(); } - public FixedpointDecRefQueue(int move_limit) - { - super(move_limit); - } - - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.fixedpointIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } - @Override protected void decRef(Context ctx, long obj) { - try - { - Native.fixedpointDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + Native.fixedpointDecRef(ctx.nCtx(), obj); } }; diff --git a/src/api/java/FuncDecl.java b/src/api/java/FuncDecl.java index 301978c44..273e853c0 100644 --- a/src/api/java/FuncDecl.java +++ b/src/api/java/FuncDecl.java @@ -47,13 +47,7 @@ public class FuncDecl extends AST @Override public String toString() { - try - { - return Native.funcDeclToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + return Native.funcDeclToString(getContext().nCtx(), getNativeObject()); } /** diff --git a/src/api/java/FuncInterp.java b/src/api/java/FuncInterp.java index 72f6bb25d..b5873d98e 100644 --- a/src/api/java/FuncInterp.java +++ b/src/api/java/FuncInterp.java @@ -22,20 +22,20 @@ package com.microsoft.z3; * Each entry in the finite map represents the value of a function given a set * of arguments. **/ -public class FuncInterp extends Z3Object -{ +public class FuncInterp extends Z3Object { + /** * An Entry object represents an element in the finite map used to encode a * function interpretation. **/ - public class Entry extends Z3Object - { + 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(), @@ -45,7 +45,7 @@ public class FuncInterp extends Z3Object /** * The number of arguments of the entry. * @throws Z3Exception on error - **/ + **/ public int getNumArgs() { return Native.funcEntryGetNumArgs(getContext().nCtx(), getNativeObject()); @@ -53,10 +53,10 @@ public class FuncInterp extends Z3Object /** * The arguments of the function entry. - * + * * @throws Z3Exception * @throws Z3Exception on error - **/ + **/ public Expr[] getArgs() { int n = getNumArgs(); @@ -73,37 +73,26 @@ public class FuncInterp extends Z3Object @Override public String toString() { - try - { - int n = getNumArgs(); - String res = "["; - Expr[] args = getArgs(); - for (int i = 0; i < n; i++) - res += args[i] + ", "; - return res + getValue() + "]"; - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + 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) - { + Entry(Context ctx, long obj) { super(ctx, obj); } @Override - void incRef(long o) - { - getContext().getFuncEntryDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.funcEntryIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getFuncEntryDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getFuncEntryDRQ().storeReference(getContext(), this); } } @@ -161,33 +150,27 @@ public class FuncInterp extends Z3Object **/ public String toString() { - try + String res = ""; + res += "["; + for (Entry e : getEntries()) { - 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++) { - 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() + ", "; + if (i != 0) + res += ", "; + res += args[i]; } - res += "else -> " + getElse(); - res += "]"; - return res; - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); + if (n > 1) + res += "]"; + res += " -> " + e.getValue() + ", "; } + res += "else -> " + getElse(); + res += "]"; + return res; } FuncInterp(Context ctx, long obj) @@ -196,16 +179,12 @@ public class FuncInterp extends Z3Object } @Override - void incRef(long o) - { - getContext().getFuncInterpDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.funcInterpIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getFuncInterpDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getFuncInterpDRQ().storeReference(getContext(), this); } } diff --git a/src/api/java/FuncInterpDecRefQueue.java b/src/api/java/FuncInterpDecRefQueue.java index c888e9e3d..d8715bd0e 100644 --- a/src/api/java/FuncInterpDecRefQueue.java +++ b/src/api/java/FuncInterpDecRefQueue.java @@ -17,39 +17,15 @@ Notes: package com.microsoft.z3; -class FuncInterpDecRefQueue extends IDecRefQueue +class FuncInterpDecRefQueue extends IDecRefQueue { public FuncInterpDecRefQueue() { super(); } - public FuncInterpDecRefQueue(int move_limit) - { - super(move_limit); - } - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.funcInterpIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } - - @Override - protected void decRef(Context ctx, long obj) - { - try - { - Native.funcInterpDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + protected void decRef(Context ctx, long obj) { + Native.funcInterpDecRef(ctx.nCtx(), obj); } }; diff --git a/src/api/java/FuncInterpEntryDecRefQueue.java b/src/api/java/FuncInterpEntryDecRefQueue.java index 7dfdaa27d..a4d8a0690 100644 --- a/src/api/java/FuncInterpEntryDecRefQueue.java +++ b/src/api/java/FuncInterpEntryDecRefQueue.java @@ -17,39 +17,14 @@ Notes: package com.microsoft.z3; -class FuncInterpEntryDecRefQueue extends IDecRefQueue -{ +class FuncInterpEntryDecRefQueue extends IDecRefQueue { public FuncInterpEntryDecRefQueue() { super(); } - public FuncInterpEntryDecRefQueue(int move_limit) - { - super(move_limit); - } - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.funcEntryIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + protected void decRef(Context ctx, long obj) { + Native.funcEntryDecRef(ctx.nCtx(), obj); } - - @Override - protected void decRef(Context ctx, long obj) - { - try - { - Native.funcEntryDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } -}; +} diff --git a/src/api/java/Goal.java b/src/api/java/Goal.java index 46b04f6bf..25b1fe511 100644 --- a/src/api/java/Goal.java +++ b/src/api/java/Goal.java @@ -23,8 +23,7 @@ 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 -{ +public class Goal extends Z3Object { /** * The precision of the goal. * Remarks: Goals can be transformed using over @@ -211,15 +210,8 @@ public class Goal extends Z3Object * * @return A string representation of the Goal. **/ - public String toString() - { - try - { - return Native.goalToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + public String toString() { + return Native.goalToString(getContext().nCtx(), getNativeObject()); } /** @@ -229,11 +221,11 @@ public class Goal extends Z3Object **/ public BoolExpr AsBoolExpr() { int n = size(); - if (n == 0) + if (n == 0) { return getContext().mkTrue(); - else if (n == 1) + } else if (n == 1) { return getFormulas()[0]; - else { + } else { return getContext().mkAnd(getFormulas()); } } @@ -243,23 +235,18 @@ public class Goal extends Z3Object super(ctx, obj); } - Goal(Context ctx, boolean models, boolean unsatCores, boolean proofs) - - { + Goal(Context ctx, boolean models, boolean unsatCores, boolean proofs) { super(ctx, Native.mkGoal(ctx.nCtx(), (models), (unsatCores), (proofs))); } - void incRef(long o) - { - getContext().getGoalDRQ().incAndClear(getContext(), o); - super.incRef(o); + @Override + void incRef() { + Native.goalIncRef(getContext().nCtx(), getNativeObject()); } - void decRef(long o) - { - getContext().getGoalDRQ().add(o); - super.decRef(o); + @Override + void addToReferenceQueue() { + getContext().getGoalDRQ().storeReference(getContext(), this); } - } diff --git a/src/api/java/GoalDecRefQueue.java b/src/api/java/GoalDecRefQueue.java index be921241b..90bad1fb1 100644 --- a/src/api/java/GoalDecRefQueue.java +++ b/src/api/java/GoalDecRefQueue.java @@ -17,39 +17,14 @@ Notes: package com.microsoft.z3; -class GoalDecRefQueue extends IDecRefQueue -{ +class GoalDecRefQueue extends IDecRefQueue { public GoalDecRefQueue() { super(); } - public GoalDecRefQueue(int move_limit) - { - super(move_limit); - } - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.goalIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } - - @Override - protected void decRef(Context ctx, long obj) - { - try - { + protected void decRef(Context ctx, long obj) { Native.goalDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } } -}; +} diff --git a/src/api/java/IDecRefQueue.java b/src/api/java/IDecRefQueue.java index 1a99a3d92..4b515a3b6 100644 --- a/src/api/java/IDecRefQueue.java +++ b/src/api/java/IDecRefQueue.java @@ -17,55 +17,67 @@ Notes: package com.microsoft.z3; -import java.util.LinkedList; +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.util.IdentityHashMap; +import java.util.Map; -public abstract class IDecRefQueue -{ - protected final Object m_lock = new Object(); - protected LinkedList m_queue = new LinkedList(); - protected int m_move_limit; +/** + * 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() - { - m_move_limit = 1024; - } - - protected IDecRefQueue(int move_limit) - { - m_move_limit = move_limit; - } - - public void setLimit(int l) { m_move_limit = l; } - - protected abstract void incRef(Context ctx, long obj); + 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); - protected void incAndClear(Context ctx, long o) - { - incRef(ctx, o); - if (m_queue.size() >= m_move_limit) - clear(ctx); + public void storeReference(Context ctx, T obj) { + PhantomReference ref = new PhantomReference<>(obj, referenceQueue); + referenceMap.put(ref, obj.getNativeObject()); + clear(ctx); } - protected void add(long o) + /** + * Clean all references currently in {@code referenceQueue}. + */ + protected void clear(Context ctx) { - if (o == 0) - return; - - synchronized (m_lock) - { - m_queue.add(o); + Reference ref; + while ((ref = referenceQueue.poll()) != null) { + long z3ast = referenceMap.remove(ref); + decRef(ctx, z3ast); } } - protected void clear(Context ctx) - { - synchronized (m_lock) - { - for (Long o : m_queue) - decRef(ctx, o); - m_queue.clear(); + /** + * 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); } } } diff --git a/src/api/java/IDisposable.java b/src/api/java/IDisposable.java deleted file mode 100644 index dae8a7262..000000000 --- a/src/api/java/IDisposable.java +++ /dev/null @@ -1,25 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - IDisposable.java - -Abstract: - - Compatability interface (C# -> Java) - -Author: - - Christoph Wintersteiger (cwinter) 2012-03-16 - -Notes: - ---*/ - -package com.microsoft.z3; - -public abstract class IDisposable -{ - public abstract void dispose(); -} diff --git a/src/api/java/IntNum.java b/src/api/java/IntNum.java index 71d878311..d3a5b456f 100644 --- a/src/api/java/IntNum.java +++ b/src/api/java/IntNum.java @@ -63,14 +63,7 @@ public class IntNum extends IntExpr /** * Returns a string representation of the numeral. **/ - public String toString() - { - try - { - return Native.getNumeralString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + public String toString() { + return Native.getNumeralString(getContext().nCtx(), getNativeObject()); } } diff --git a/src/api/java/InterpolationContext.java b/src/api/java/InterpolationContext.java index 47a128643..99a63821f 100644 --- a/src/api/java/InterpolationContext.java +++ b/src/api/java/InterpolationContext.java @@ -17,11 +17,10 @@ Notes: package com.microsoft.z3; -import java.util.Map; -import java.lang.String; - import com.microsoft.z3.enumerations.Z3_lbool; +import java.util.Map; + /** * The InterpolationContext is suitable for generation of interpolants. * @@ -33,13 +32,13 @@ public class InterpolationContext extends Context /** * Constructor. **/ - public InterpolationContext() + public static InterpolationContext mkContext() { - super(); + long m_ctx; synchronized(creation_lock) { m_ctx = Native.mkInterpolationContext(0); - initContext(); } + return new InterpolationContext(m_ctx); } /** @@ -49,17 +48,21 @@ public class InterpolationContext extends Context * Remarks: * @see Context#Context **/ - public InterpolationContext(Map settings) + public static InterpolationContext mkContext(Map settings) { - super(); + long m_ctx; synchronized(creation_lock) { long cfg = Native.mkConfig(); for (Map.Entry kv : settings.entrySet()) Native.setParamValue(cfg, kv.getKey(), kv.getValue()); m_ctx = Native.mkInterpolationContext(cfg); Native.delConfig(cfg); - initContext(); } + return new InterpolationContext(m_ctx); + } + + private InterpolationContext(long m_ctx) { + super(m_ctx); } /** diff --git a/src/api/java/ListSort.java b/src/api/java/ListSort.java index 705e93579..0ff2c36cf 100644 --- a/src/api/java/ListSort.java +++ b/src/api/java/ListSort.java @@ -17,6 +17,8 @@ Notes: package com.microsoft.z3; +import com.microsoft.z3.Native.LongPtr; + /** * List sorts. **/ @@ -88,14 +90,9 @@ public class ListSort extends Sort ListSort(Context ctx, Symbol name, Sort elemSort) { - super(ctx, 0); - - Native.LongPtr inil = new Native.LongPtr(), iisnil = new Native.LongPtr(); - Native.LongPtr icons = new Native.LongPtr(), iiscons = new Native.LongPtr(); - Native.LongPtr ihead = new Native.LongPtr(), itail = new Native.LongPtr(); - - setNativeObject(Native.mkListSort(ctx.nCtx(), name.getNativeObject(), - elemSort.getNativeObject(), inil, iisnil, icons, iiscons, ihead, - itail)); + super(ctx, Native.mkListSort(ctx.nCtx(), name.getNativeObject(), + elemSort.getNativeObject(), + new LongPtr(), new Native.LongPtr(), new LongPtr(), + new LongPtr(), new LongPtr(), new LongPtr())); } }; diff --git a/src/api/java/Model.java b/src/api/java/Model.java index 0695d7cf1..60abb001d 100644 --- a/src/api/java/Model.java +++ b/src/api/java/Model.java @@ -22,8 +22,7 @@ import com.microsoft.z3.enumerations.Z3_sort_kind; /** * A Model contains interpretations (assignments) of constants and functions. **/ -public class Model extends Z3Object -{ +public class Model extends Z3Object { /** * Retrieves the interpretation (the assignment) of {@code a} in * the model. @@ -283,15 +282,8 @@ public class Model extends Z3Object * @return A string representation of the model. **/ @Override - public String toString() - { - try - { - return Native.modelToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + public String toString() { + return Native.modelToString(getContext().nCtx(), getNativeObject()); } Model(Context ctx, long obj) @@ -300,16 +292,12 @@ public class Model extends Z3Object } @Override - void incRef(long o) - { - getContext().getModelDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.modelIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getModelDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getModelDRQ().storeReference(getContext(), this); } } diff --git a/src/api/java/ModelDecRefQueue.java b/src/api/java/ModelDecRefQueue.java index b97add310..f1b7c3fdd 100644 --- a/src/api/java/ModelDecRefQueue.java +++ b/src/api/java/ModelDecRefQueue.java @@ -17,39 +17,14 @@ Notes: package com.microsoft.z3; -class ModelDecRefQueue extends IDecRefQueue -{ +class ModelDecRefQueue extends IDecRefQueue { public ModelDecRefQueue() { super(); } - public ModelDecRefQueue(int move_limit) - { - super(move_limit); - } - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.modelIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + protected void decRef(Context ctx, long obj) { + Native.modelDecRef(ctx.nCtx(), obj); } - - @Override - protected void decRef(Context ctx, long obj) - { - try - { - Native.modelDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } -}; +} diff --git a/src/api/java/Optimize.java b/src/api/java/Optimize.java index 5dfe8fcf4..ea100d1ca 100644 --- a/src/api/java/Optimize.java +++ b/src/api/java/Optimize.java @@ -266,17 +266,12 @@ public class Optimize extends Z3Object } @Override - void incRef(long o) - { - getContext().getOptimizeDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.optimizeIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getOptimizeDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getOptimizeDRQ().storeReference(getContext(), this); } - } diff --git a/src/api/java/OptimizeDecRefQueue.java b/src/api/java/OptimizeDecRefQueue.java index 795a8a399..0acf20068 100644 --- a/src/api/java/OptimizeDecRefQueue.java +++ b/src/api/java/OptimizeDecRefQueue.java @@ -17,39 +17,14 @@ Notes: package com.microsoft.z3; -class OptimizeDecRefQueue extends IDecRefQueue -{ +class OptimizeDecRefQueue extends IDecRefQueue { public OptimizeDecRefQueue() { super(); } - public OptimizeDecRefQueue(int move_limit) - { - super(move_limit); - } - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.fixedpointIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } - - @Override - protected void decRef(Context ctx, long obj) - { - try - { - Native.fixedpointDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + protected void decRef(Context ctx, long obj) { + Native.optimizeDecRef(ctx.nCtx(), obj); } }; diff --git a/src/api/java/ParamDescrs.java b/src/api/java/ParamDescrs.java index 8f8c6df0b..0008515e3 100644 --- a/src/api/java/ParamDescrs.java +++ b/src/api/java/ParamDescrs.java @@ -22,8 +22,7 @@ import com.microsoft.z3.enumerations.Z3_param_kind; /** * A ParamDescrs describes a set of parameters. **/ -public class ParamDescrs extends Z3Object -{ +public class ParamDescrs extends Z3Object { /** * validate a set of parameters. **/ @@ -82,15 +81,8 @@ public class ParamDescrs extends Z3Object * Retrieves a string representation of the ParamDescrs. **/ @Override - public String toString() - { - try - { - return Native.paramDescrsToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + public String toString() { + return Native.paramDescrsToString(getContext().nCtx(), getNativeObject()); } ParamDescrs(Context ctx, long obj) @@ -99,16 +91,12 @@ public class ParamDescrs extends Z3Object } @Override - void incRef(long o) - { - getContext().getParamDescrsDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.paramDescrsIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getParamDescrsDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getParamDescrsDRQ().storeReference(getContext(), this); } } diff --git a/src/api/java/ParamDescrsDecRefQueue.java b/src/api/java/ParamDescrsDecRefQueue.java index e3515bff6..ee3257db9 100644 --- a/src/api/java/ParamDescrsDecRefQueue.java +++ b/src/api/java/ParamDescrsDecRefQueue.java @@ -17,39 +17,15 @@ Notes: package com.microsoft.z3; -class ParamDescrsDecRefQueue extends IDecRefQueue -{ +class ParamDescrsDecRefQueue extends IDecRefQueue { public ParamDescrsDecRefQueue() { super(); } - public ParamDescrsDecRefQueue(int move_limit) - { - super(move_limit); - } - - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.paramDescrsIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } - @Override protected void decRef(Context ctx, long obj) { - try - { - Native.paramDescrsDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + Native.paramDescrsDecRef(ctx.nCtx(), obj); } -}; +} diff --git a/src/api/java/Params.java b/src/api/java/Params.java index 25d009b12..a76dd3cab 100644 --- a/src/api/java/Params.java +++ b/src/api/java/Params.java @@ -21,8 +21,7 @@ package com.microsoft.z3; /** * A ParameterSet represents a configuration in the form of Symbol/value pairs. **/ -public class Params extends Z3Object -{ +public class Params extends Z3Object { /** * Adds a parameter setting. **/ @@ -115,13 +114,7 @@ public class Params extends Z3Object @Override public String toString() { - try - { - return Native.paramsToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + return Native.paramsToString(getContext().nCtx(), getNativeObject()); } Params(Context ctx) @@ -129,17 +122,14 @@ public class Params extends Z3Object super(ctx, Native.mkParams(ctx.nCtx())); } + @Override - void incRef(long o) - { - getContext().getParamsDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.paramsIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getParamsDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getParamsDRQ().storeReference(getContext(), this); } } diff --git a/src/api/java/ParamsDecRefQueue.java b/src/api/java/ParamsDecRefQueue.java index f989f8015..349713f67 100644 --- a/src/api/java/ParamsDecRefQueue.java +++ b/src/api/java/ParamsDecRefQueue.java @@ -17,39 +17,14 @@ Notes: package com.microsoft.z3; -class ParamsDecRefQueue extends IDecRefQueue -{ +class ParamsDecRefQueue extends IDecRefQueue { public ParamsDecRefQueue() { super(); } - public ParamsDecRefQueue(int move_limit) - { - super(move_limit); - } - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.paramsIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + protected void decRef(Context ctx, long obj) { + Native.paramsDecRef(ctx.nCtx(), obj); } - - @Override - protected void decRef(Context ctx, long obj) - { - try - { - Native.paramsDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } -}; +} diff --git a/src/api/java/Pattern.java b/src/api/java/Pattern.java index eb12b6448..852ffcd0f 100644 --- a/src/api/java/Pattern.java +++ b/src/api/java/Pattern.java @@ -53,13 +53,7 @@ public class Pattern extends AST @Override public String toString() { - try - { - return Native.patternToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + return Native.patternToString(getContext().nCtx(), getNativeObject()); } Pattern(Context ctx, long obj) diff --git a/src/api/java/Probe.java b/src/api/java/Probe.java index bcaa76ce6..a36f3b64b 100644 --- a/src/api/java/Probe.java +++ b/src/api/java/Probe.java @@ -25,8 +25,7 @@ package com.microsoft.z3; * also be obtained using the command {@code (help-tactic)} in the SMT 2.0 * front-end. **/ -public class Probe extends Z3Object -{ +public class Probe extends Z3Object { /** * Execute the probe over the goal. * @@ -46,22 +45,17 @@ public class Probe extends Z3Object super(ctx, obj); } - Probe(Context ctx, String name) - { + Probe(Context ctx, String name) { super(ctx, Native.mkProbe(ctx.nCtx(), name)); } @Override - void incRef(long o) - { - getContext().getProbeDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.probeIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getProbeDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getProbeDRQ().storeReference(getContext(), this); } } diff --git a/src/api/java/ProbeDecRefQueue.java b/src/api/java/ProbeDecRefQueue.java index 368bd5bba..b25446c0c 100644 --- a/src/api/java/ProbeDecRefQueue.java +++ b/src/api/java/ProbeDecRefQueue.java @@ -17,39 +17,16 @@ Notes: package com.microsoft.z3; -class ProbeDecRefQueue extends IDecRefQueue +class ProbeDecRefQueue extends IDecRefQueue { public ProbeDecRefQueue() { super(); } - public ProbeDecRefQueue(int move_limit) - { - super(move_limit); - } - - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.probeIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } - @Override protected void decRef(Context ctx, long obj) { - try - { - Native.probeDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + Native.probeDecRef(ctx.nCtx(), obj); } }; diff --git a/src/api/java/Quantifier.java b/src/api/java/Quantifier.java index 1b425d3e4..bc2537107 100644 --- a/src/api/java/Quantifier.java +++ b/src/api/java/Quantifier.java @@ -145,68 +145,83 @@ public class Quantifier extends BoolExpr .nCtx(), getNativeObject())); } - Quantifier(Context ctx, boolean isForall, Sort[] sorts, Symbol[] names, + /** + * 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) - { - super(ctx, 0); + Symbol quantifierID, Symbol skolemID) { + ctx.checkContextMatch(patterns); + ctx.checkContextMatch(noPatterns); + ctx.checkContextMatch(sorts); + ctx.checkContextMatch(names); + ctx.checkContextMatch(body); - getContext().checkContextMatch(patterns); - getContext().checkContextMatch(noPatterns); - getContext().checkContextMatch(sorts); - getContext().checkContextMatch(names); - getContext().checkContextMatch(body); - - if (sorts.length != names.length) + if (sorts.length != names.length) { throw new Z3Exception( "Number of sorts does not match number of names"); + } - if (noPatterns == null && quantifierID == null && skolemID == null) - { - setNativeObject(Native.mkQuantifier(ctx.nCtx(), (isForall), weight, AST.arrayLength(patterns), AST + 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 - { - setNativeObject(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())); + .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); } - Quantifier(Context ctx, boolean isForall, Expr[] bound, Expr body, + + /** + * @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) - { - super(ctx, 0); + Symbol quantifierID, Symbol skolemID) { + ctx.checkContextMatch(noPatterns); + ctx.checkContextMatch(patterns); + ctx.checkContextMatch(body); - getContext().checkContextMatch(noPatterns); - getContext().checkContextMatch(patterns); - // Context().CheckContextMatch(bound); - getContext().checkContextMatch(body); - - if (noPatterns == null && quantifierID == null && skolemID == null) - { - setNativeObject(Native.mkQuantifierConst(ctx.nCtx(), + 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 - { - setNativeObject(Native.mkQuantifierConstEx(ctx.nCtx(), + 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())); + AST.arrayToNative(noPatterns), body.getNativeObject()); } + return new Quantifier(ctx, nativeObj); } Quantifier(Context ctx, long obj) @@ -215,11 +230,11 @@ public class Quantifier extends BoolExpr } @Override - void checkNativeObject(long obj) - { + void checkNativeObject(long obj) { if (Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_QUANTIFIER_AST - .toInt()) + .toInt()) { throw new Z3Exception("Underlying object is not a quantifier"); + } super.checkNativeObject(obj); } } diff --git a/src/api/java/RatNum.java b/src/api/java/RatNum.java index f44823a2b..2bf1b28dd 100644 --- a/src/api/java/RatNum.java +++ b/src/api/java/RatNum.java @@ -75,15 +75,8 @@ public class RatNum extends RealExpr * Returns a string representation of the numeral. **/ @Override - public String toString() - { - try - { - return Native.getNumeralString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + public String toString() { + return Native.getNumeralString(getContext().nCtx(), getNativeObject()); } RatNum(Context ctx, long obj) diff --git a/src/api/java/Solver.java b/src/api/java/Solver.java index ea76637c8..a98fcbf94 100644 --- a/src/api/java/Solver.java +++ b/src/api/java/Solver.java @@ -22,8 +22,7 @@ import com.microsoft.z3.enumerations.Z3_lbool; /** * Solvers. **/ -public class Solver extends Z3Object -{ +public class Solver extends Z3Object { /** * A string that describes all available solver parameters. **/ @@ -127,13 +126,13 @@ public class Solver extends Z3Object * using the Boolean constants in ps. * * Remarks: - * This API is an alternative to {@link check} with assumptions for + * 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} + * of the Boolean variables provided using {@code #assertAndTrack} * and the Boolean literals - * provided using {@link check} with assumptions. + * provided using {@link #check()} with assumptions. **/ public void assertAndTrack(BoolExpr[] constraints, BoolExpr[] ps) { @@ -154,13 +153,13 @@ public class Solver extends Z3Object * using the Boolean constant p. * * Remarks: - * This API is an alternative to {@link check} with assumptions for + * 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} + * of the Boolean variables provided using {@link #assertAndTrack} * and the Boolean literals - * provided using {@link check} with assumptions. + * provided using {@link #check} with assumptions. */ public void assertAndTrack(BoolExpr constraint, BoolExpr p) { @@ -323,14 +322,8 @@ public class Solver extends Z3Object @Override public String toString() { - try - { - return Native - .solverToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + return Native + .solverToString(getContext().nCtx(), getNativeObject()); } Solver(Context ctx, long obj) @@ -339,16 +332,12 @@ public class Solver extends Z3Object } @Override - void incRef(long o) - { - getContext().getSolverDRQ().incAndClear(getContext(), o); - super.incRef(o); + void incRef() { + Native.solverIncRef(getContext().nCtx(), getNativeObject()); } @Override - void decRef(long o) - { - getContext().getSolverDRQ().add(o); - super.decRef(o); + void addToReferenceQueue() { + getContext().getSolverDRQ().storeReference(getContext(), this); } } diff --git a/src/api/java/SolverDecRefQueue.java b/src/api/java/SolverDecRefQueue.java index f4d5fb139..efa15d939 100644 --- a/src/api/java/SolverDecRefQueue.java +++ b/src/api/java/SolverDecRefQueue.java @@ -17,36 +17,11 @@ Notes: package com.microsoft.z3; -class SolverDecRefQueue extends IDecRefQueue -{ +class SolverDecRefQueue extends IDecRefQueue { public SolverDecRefQueue() { super(); } - public SolverDecRefQueue(int move_limit) - { - super(move_limit); - } - @Override - protected void incRef(Context ctx, long obj) - { - try - { - Native.solverIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + protected void decRef(Context ctx, long obj) { + Native.solverDecRef(ctx.nCtx(), obj); } - - @Override - protected void decRef(Context ctx, long obj) - { - try - { - Native.solverDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } -}; +} diff --git a/src/api/java/Sort.java b/src/api/java/Sort.java index e30e0b8b3..0763a69a3 100644 --- a/src/api/java/Sort.java +++ b/src/api/java/Sort.java @@ -82,15 +82,9 @@ public class Sort extends AST /** * A string representation of the sort. **/ - public String toString() - { - try - { - return Native.sortToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + @Override + public String toString() { + return Native.sortToString(getContext().nCtx(), getNativeObject()); } /** @@ -101,6 +95,7 @@ public class Sort extends AST super(ctx, obj); } + @Override void checkNativeObject(long obj) { if (Native.getAstKind(getContext().nCtx(), obj) != Z3_ast_kind.Z3_SORT_AST diff --git a/src/api/java/Statistics.java b/src/api/java/Statistics.java index 5af0cf863..356cbeadb 100644 --- a/src/api/java/Statistics.java +++ b/src/api/java/Statistics.java @@ -20,8 +20,7 @@ package com.microsoft.z3; /** * Objects of this class track statistical information about solvers. **/ -public class Statistics extends Z3Object -{ +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} @@ -84,15 +83,9 @@ public class Statistics extends Z3Object /** * The string representation of the Entry. **/ - public String toString() - { - try - { - return Key + ": " + getValueString(); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + @Override + public String toString() { + return Key + ": " + getValueString(); } private boolean m_is_int = false; @@ -118,15 +111,10 @@ public class Statistics extends Z3Object /** * A string representation of the statistical data. **/ + @Override public String toString() { - try - { - return Native.statsToString(getContext().nCtx(), getNativeObject()); - } catch (Z3Exception e) - { - return "Z3Exception: " + e.getMessage(); - } + return Native.statsToString(getContext().nCtx(), getNativeObject()); } /** @@ -201,15 +189,13 @@ public class Statistics extends Z3Object super(ctx, obj); } - void incRef(long o) - { - getContext().getStatisticsDRQ().incAndClear(getContext(), o); - super.incRef(o); + @Override + void incRef() { + getContext().getStatisticsDRQ().storeReference(getContext(), this); } - void decRef(long o) - { - getContext().getStatisticsDRQ().add(o); - super.decRef(o); + @Override + void addToReferenceQueue() { + Native.statsIncRef(getContext().nCtx(), getNativeObject()); } } diff --git a/src/api/java/StatisticsDecRefQueue.java b/src/api/java/StatisticsDecRefQueue.java index 89c66a746..ed698e4ca 100644 --- a/src/api/java/StatisticsDecRefQueue.java +++ b/src/api/java/StatisticsDecRefQueue.java @@ -17,37 +17,14 @@ Notes: package com.microsoft.z3; -class StatisticsDecRefQueue extends IDecRefQueue -{ +class StatisticsDecRefQueue extends IDecRefQueue { public StatisticsDecRefQueue() { super(); } - public StatisticsDecRefQueue(int move_limit) - { - super(move_limit); + @Override + protected void decRef(Context ctx, long obj) { + Native.statsDecRef(ctx.nCtx(), obj); } - - protected void incRef(Context ctx, long obj) - { - try - { - Native.statsIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } - - protected void decRef(Context ctx, long obj) - { - try - { - Native.statsDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } -}; +} diff --git a/src/api/java/StringSymbol.java b/src/api/java/StringSymbol.java index 8470e1cd4..576737ea7 100644 --- a/src/api/java/StringSymbol.java +++ b/src/api/java/StringSymbol.java @@ -48,9 +48,9 @@ public class StringSymbol extends Symbol void checkNativeObject(long obj) { if (Native.getSymbolKind(getContext().nCtx(), obj) != Z3_symbol_kind.Z3_STRING_SYMBOL - .toInt()) + .toInt()) { throw new Z3Exception("Symbol is not of String kind"); - + } super.checkNativeObject(obj); } } diff --git a/src/api/java/Symbol.java b/src/api/java/Symbol.java index beeaebb69..139894be1 100644 --- a/src/api/java/Symbol.java +++ b/src/api/java/Symbol.java @@ -22,8 +22,7 @@ import com.microsoft.z3.enumerations.Z3_symbol_kind; /** * Symbols are used to name several term and type constructors. **/ -public class Symbol extends Z3Object -{ +public class Symbol extends Z3Object { /** * The kind of the symbol (int or string) **/ @@ -62,19 +61,13 @@ public class Symbol extends Z3Object * A string representation of the symbol. **/ @Override - public String toString() - { - try - { - if (isIntSymbol()) - return Integer.toString(((IntSymbol) this).getInt()); - else if (isStringSymbol()) - return ((StringSymbol) this).getString(); - else - return "Z3Exception: Unknown symbol kind encountered."; - } catch (Z3Exception ex) - { - return "Z3Exception: " + ex.getMessage(); + 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."; } } @@ -86,6 +79,17 @@ public class Symbol extends Z3Object 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))) diff --git a/src/api/java/Tactic.java b/src/api/java/Tactic.java index 786f8a6ec..11d02ca73 100644 --- a/src/api/java/Tactic.java +++ b/src/api/java/Tactic.java @@ -24,8 +24,7 @@ package com.microsoft.z3; * also be obtained using the command {@code (help-tactic)} in the SMT 2.0 * front-end. **/ -public class Tactic extends Z3Object -{ +public class Tactic extends Z3Object { /** * A string containing a description of parameters accepted by the tactic. **/ @@ -92,15 +91,13 @@ public class Tactic extends Z3Object super(ctx, Native.mkTactic(ctx.nCtx(), name)); } - void incRef(long o) - { - getContext().getTacticDRQ().incAndClear(getContext(), o); - super.incRef(o); + @Override + void incRef() { + Native.tacticIncRef(getContext().nCtx(), getNativeObject()); } - void decRef(long o) - { - getContext().getTacticDRQ().add(o); - super.decRef(o); + @Override + void addToReferenceQueue() { + getContext().getTacticDRQ().storeReference(getContext(), this); } } diff --git a/src/api/java/TacticDecRefQueue.java b/src/api/java/TacticDecRefQueue.java index 026760e46..8f151f25c 100644 --- a/src/api/java/TacticDecRefQueue.java +++ b/src/api/java/TacticDecRefQueue.java @@ -17,37 +17,15 @@ Notes: package com.microsoft.z3; -class TacticDecRefQueue extends IDecRefQueue -{ +class TacticDecRefQueue extends IDecRefQueue { public TacticDecRefQueue() { super(); } - public TacticDecRefQueue(int move_limit) - { - super(move_limit); - } - - protected void incRef(Context ctx, long obj) - { - try - { - Native.tacticIncRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } - } - + @Override protected void decRef(Context ctx, long obj) { - try - { - Native.tacticDecRef(ctx.nCtx(), obj); - } catch (Z3Exception e) - { - // OK. - } + Native.tacticDecRef(ctx.nCtx(), obj); } -}; +} diff --git a/src/api/java/TupleSort.java b/src/api/java/TupleSort.java index 1594b874d..ede20d260 100644 --- a/src/api/java/TupleSort.java +++ b/src/api/java/TupleSort.java @@ -59,11 +59,9 @@ public class TupleSort extends Sort TupleSort(Context ctx, Symbol name, int numFields, Symbol[] fieldNames, Sort[] fieldSorts) { - super(ctx, 0); - - Native.LongPtr t = new Native.LongPtr(); - setNativeObject(Native.mkTupleSort(ctx.nCtx(), name.getNativeObject(), + super(ctx, Native.mkTupleSort(ctx.nCtx(), name.getNativeObject(), numFields, Symbol.arrayToNative(fieldNames), - AST.arrayToNative(fieldSorts), t, new long[numFields])); + AST.arrayToNative(fieldSorts), new Native.LongPtr(), + new long[numFields])); } }; diff --git a/src/api/java/Version.java b/src/api/java/Version.java index 939a3ca5c..0579e2b05 100644 --- a/src/api/java/Version.java +++ b/src/api/java/Version.java @@ -63,6 +63,14 @@ public class Version return revision.value; } + /** + * A full version string + **/ + public static String getFullVersion() + { + return Native.getFullVersion(); + } + /** * A string representation of the version information. **/ diff --git a/src/api/java/Z3Object.java b/src/api/java/Z3Object.java index dc1feecbf..7c5f606d9 100644 --- a/src/api/java/Z3Object.java +++ b/src/api/java/Z3Object.java @@ -21,88 +21,43 @@ package com.microsoft.z3; * Internal base class for interfacing with native Z3 objects. Should not be * used externally. **/ -public class Z3Object extends IDisposable -{ - /** - * Finalizer. - * @throws Throwable - **/ - protected void finalize() throws Throwable - { - try { - dispose(); - } finally { - super.finalize(); - } - } +public abstract class Z3Object { - /** - * Disposes of the underlying native Z3 object. - **/ - public void dispose() - { - if (m_n_obj != 0) - { - decRef(m_n_obj); - m_n_obj = 0; - } + private final Context m_ctx; + private final long m_n_obj; - if (m_ctx != null) - { - if (m_ctx.m_refCount.decrementAndGet() == 0) - m_ctx.dispose(); - m_ctx = null; - } - } - - private Context m_ctx = null; - private long m_n_obj = 0; - - Z3Object(Context ctx) - { - ctx.m_refCount.incrementAndGet(); + Z3Object(Context ctx, long obj) { m_ctx = ctx; - } - - Z3Object(Context ctx, long obj) - { - ctx.m_refCount.incrementAndGet(); - m_ctx = ctx; - incRef(obj); + checkNativeObject(obj); m_n_obj = obj; + incRef(); + addToReferenceQueue(); } - void incRef(long o) - { - } + /** + * Add to ReferenceQueue for tracking reachability on the object and + * decreasing the reference count when the object is no longer reachable. + */ + abstract void addToReferenceQueue(); - void decRef(long o) - { - } + /** + * Increment reference count on {@code this}. + */ + abstract void incRef(); - void checkNativeObject(long obj) - { - } + /** + * 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; } - void setNativeObject(long value) - { - if (value != 0) - { - checkNativeObject(value); - incRef(value); - } - if (m_n_obj != 0) - { - decRef(m_n_obj); - } - m_n_obj = value; - } - static long getNativeObject(Z3Object s) { if (s == null) @@ -121,7 +76,7 @@ public class Z3Object extends IDisposable 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(); + an[i] = (a[i] == null) ? 0 : a[i].getNativeObject(); return an; } diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index bbbb9e74b..41d2226c5 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -25,6 +25,8 @@ 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 ^ "." ^ @@ -1323,10 +1325,20 @@ struct 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_significand_string = Z3native.fpa_get_numeral_significand_string - let get_numeral_significand_uint = Z3native.fpa_get_numeral_significand_uint64 + 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 @@ -1904,13 +1916,17 @@ struct let q = Z3native.optimize_get_model (gc x) x in if Z3native.is_null_model q then None else Some q - let get_lower (x:handle) (idx:int) = Z3native.optimize_get_lower (gc x.opt) x.opt idx - let get_upper (x:handle) (idx:int) = Z3native.optimize_get_upper (gc x.opt) x.opt idx + 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_statistics (gc x) x) end diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 9104b3080..818a635f7 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -80,6 +80,9 @@ sig (** 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 @@ -2138,21 +2141,54 @@ sig (** 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 a uint64. *) + significand does not fit into an int. *) val get_numeral_significand_uint : context -> Expr.expr -> bool * int - (** Return the exponent value of a floating-point numeral as a string *) - val get_numeral_exponent_string : context -> Expr.expr -> string + (** Indicates whether a floating-point numeral is a NaN. *) + val is_numeral_nan : context -> Expr.expr -> bool - (** Return the exponent value of a floating-point numeral as a signed integer *) - val get_numeral_exponent_int : context -> Expr.expr -> bool * int + (** 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 @@ -3020,29 +3056,22 @@ sig (** 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. *) + (** 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. *) + (** 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. *) @@ -3233,33 +3262,28 @@ sig (** Asssert a soft constraint. Supply integer weight and string that identifies a group - of soft constraints. - *) + of soft constraints. *) val add_soft : optimize -> Expr.expr -> string -> Symbol.symbol -> handle - (** Add maximization objective. - *) + (** Add maximization objective. *) val maximize : optimize -> Expr.expr -> handle - (** Add minimization objective. - *) + (** Add minimization objective. *) val minimize : optimize -> Expr.expr -> handle - (** Checks whether the assertions in the context are satisfiable and solves objectives. - *) + (** 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 -> int -> Expr.expr + val get_lower : handle -> Expr.expr (** Retrieve upper bound in current model for handle *) - val get_upper : handle -> int -> Expr.expr + val get_upper : handle -> Expr.expr - (** Creates a backtracking point. - {!pop} *) + (** Creates a backtracking point. {!pop} *) val push : optimize -> unit (** Backtrack one backtracking point. @@ -3275,6 +3299,27 @@ sig (** 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 diff --git a/src/api/ml/z3native_stubs.c.pre b/src/api/ml/z3native_stubs.c.pre index d6b2cdab4..5960d5095 100644 --- a/src/api/ml/z3native_stubs.c.pre +++ b/src/api/ml/z3native_stubs.c.pre @@ -227,6 +227,7 @@ void Z3_ast_finalize(value v) { 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) */ @@ -242,8 +243,8 @@ int Z3_ast_compare(value v1, value v2) { return +1; /* Comparison according to AST ids. */ - unsigned id1 = Z3_get_ast_id(a1->cp->ctx, a1->p); - unsigned id2 = Z3_get_ast_id(a2->cp->ctx, a2->p); + 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) @@ -255,7 +256,7 @@ int Z3_ast_compare(value v1, value v2) { int Z3_ast_compare_ext(value v1, value v2) { Z3_ast_plus * a1 = (Z3_ast_plus*)Data_custom_val(v1); unsigned id1; - int id2 = Val_int(v2); + unsigned id2 = (unsigned)Val_int(v2); if (a1->p == NULL && id2 == 0) return 0; if (a1->p == NULL) diff --git a/src/api/python/.gitignore b/src/api/python/.gitignore new file mode 100644 index 000000000..86e4bc9ce --- /dev/null +++ b/src/api/python/.gitignore @@ -0,0 +1,8 @@ +MANIFEST +dist +core +build +*.egg-info +bin +z3/lib +z3/include diff --git a/src/api/python/MANIFEST.in b/src/api/python/MANIFEST.in new file mode 100644 index 000000000..209e0cfa3 --- /dev/null +++ b/src/api/python/MANIFEST.in @@ -0,0 +1,4 @@ +include core/LICENSE.txt +recursive-include core/src * +recursive-include core/scripts * +recursive-include core/examples * diff --git a/src/api/python/setup.py b/src/api/python/setup.py new file mode 100644 index 000000000..ff3b0736d --- /dev/null +++ b/src/api/python/setup.py @@ -0,0 +1,169 @@ +import os +import sys +import shutil +import platform +import subprocess +import multiprocessing +import re +from setuptools import setup +from distutils.errors import LibError +from distutils.command.build import build as _build +from distutils.command.sdist import sdist as _sdist +from setuptools.command.develop import develop as _develop +from setuptools.command.bdist_egg import bdist_egg as _bdist_egg + + +build_env = dict(os.environ) +build_env['PYTHON'] = sys.executable +build_env['CXXFLAGS'] = "-std=c++11" + +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 +BUILD_DIR = os.path.join(SRC_DIR, 'build') # implicit in configure script +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') + +if sys.platform == 'darwin': + LIBRARY_FILE = "libz3.dylib" + EXECUTABLE_FILE = "z3" +elif sys.platform in ('win32', 'cygwin'): + LIBRARY_FILE = "libz3.dll" + EXECUTABLE_FILE = "z3.exe" +else: + LIBRARY_FILE = "libz3.so" + EXECUTABLE_FILE = "z3" + +def _clean_bins(): + """ + Clean up the binary files and headers that are installed along with the bindings + """ + shutil.rmtree(LIBS_DIR, ignore_errors=True) + shutil.rmtree(BINS_DIR, ignore_errors=True) + shutil.rmtree(HEADERS_DIR, ignore_errors=True) + +def _z3_version(): + 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(".*set_version\((.*), (.*), (.*), (.*)\).*", line) + if not n is None: + return n.group(1) + '.' + n.group(2) + '.' + n.group(3) + '.' + n.group(4) + return "?.?.?.?" + +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 osx + 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() + + if SRC_DIR == SRC_DIR_LOCAL: + shutil.copy(os.path.join(SRC_DIR, 'src', 'api', 'python', 'z3', 'z3core.py'), os.path.join(ROOT_DIR, 'z3')) + shutil.copy(os.path.join(SRC_DIR, 'src', 'api', 'python', '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) + os.mkdir(os.path.join(HEADERS_DIR, 'c++')) + shutil.copy(os.path.join(BUILD_DIR, LIBRARY_FILE), LIBS_DIR) + shutil.copy(os.path.join(BUILD_DIR, EXECUTABLE_FILE), BINS_DIR) + for fname in ('z3.h', 'z3_v1.h', 'z3_macros.h', 'z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_interp.h', 'z3_fpa.h', os.path.join('c++', 'z3++.h')): + shutil.copy(os.path.join(SRC_DIR, 'src', 'api', 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() + +class build(_build): + def run(self): + 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") + self.execute(_copy_sources, (), msg="Copying source files") + _sdist.run(self) + +# the build directory needs to exist +#try: os.makedirs(os.path.join(ROOT_DIR, 'build')) +#except OSError: pass + +setup( + name='z3-solver', + version=_z3_version(), + description='an efficient SMT solver library', + long_description='Z3 is a theorem prover from Microsoft Research with support for bitvectors, booleans, arrays, floating point numbers, strings, and other data types.\n\nFor documentation, please read http://z3prover.github.io/api/html/z3.html\n\nIn the event of technical difficulties related to configuration, compiliation, or installation, please submit issues to https://github.com/angr/angr-z3', + author="The Z3 Theorem Prover Project", + maintainer="Andrew Dutcher", + maintainer_email="andrew@andrewdutcher.com", + 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')] + }, + scripts=[os.path.join('bin', EXECUTABLE_FILE)], + cmdclass={'build': build, 'develop': develop, 'sdist': sdist, 'bdist_egg': bdist_egg}, +) diff --git a/src/api/python/z3/__init__.py b/src/api/python/z3/__init__.py new file mode 100644 index 000000000..f7aa29ab1 --- /dev/null +++ b/src/api/python/z3/__init__.py @@ -0,0 +1,12 @@ +from .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 diff --git a/src/api/python/z3.py b/src/api/python/z3/z3.py similarity index 93% rename from src/api/python/z3.py rename to src/api/python/z3/z3.py index 942201b0e..6729d99b5 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3/z3.py @@ -41,10 +41,11 @@ Z3 exceptions: ... print("failed: %s" % ex) failed: sort mismatch """ -from z3core import * -from z3types import * -from z3consts import * -from z3printer import * +from . import z3core +from .z3core import * +from .z3types import * +from .z3consts import * +from .z3printer import * from fractions import Fraction import sys import io @@ -79,6 +80,9 @@ def get_version(): 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): @@ -115,6 +119,8 @@ 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): + return [arg for arg in args[0]] else: return args except: # len is not necessarily defined when args is not a sequence (use reflection?) @@ -156,7 +162,7 @@ class Context: Z3_set_param_value(conf, str(key).upper(), _to_param_value(value)) prev = None for a in args: - if prev == None: + if prev is None: prev = a else: Z3_set_param_value(conf, str(prev), _to_param_value(a)) @@ -171,6 +177,7 @@ class Context: def __del__(self): self.lib.Z3_del_context(self.ctx) + self.ctx = None def ref(self): """Return a reference to the actual C pointer to the Z3 context.""" @@ -203,12 +210,12 @@ def main_ctx(): False """ global _main_ctx - if _main_ctx == None: + if _main_ctx is None: _main_ctx = Context() return _main_ctx def _get_ctx(ctx): - if ctx == None: + if ctx is None: return main_ctx() else: return ctx @@ -230,7 +237,7 @@ def set_param(*args, **kws): Z3_global_param_set(str(key).upper(), _to_param_value(value)) prev = None for a in args: - if prev == None: + if prev is None: prev = a else: Z3_global_param_set(str(prev), _to_param_value(a)) @@ -278,7 +285,8 @@ class AstRef(Z3PPObject): Z3_inc_ref(self.ctx.ref(), self.as_ast()) def __del__(self): - Z3_dec_ref(self.ctx.ref(), self.as_ast()) + if self.ctx.ref() is not None: + Z3_dec_ref(self.ctx.ref(), self.as_ast()) def __str__(self): return obj_to_string(self) @@ -292,6 +300,19 @@ class AstRef(Z3PPObject): 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 an string representing the AST node in s-expression notation. @@ -403,12 +424,12 @@ 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 == None: + if ctx is None: ctx = a.ctx else: if __debug__: _z3_assert(ctx == a.ctx, "Context mismatch") - if ctx == None: + if ctx is None: ctx = default_ctx return ctx @@ -520,7 +541,7 @@ class SortRef(AstRef): >>> p.sort() == IntSort() False """ - if other == None: + if other is None: return False return Z3_is_eq_sort(self.ctx_ref(), self.ast, other.ast) @@ -670,6 +691,30 @@ class FuncDeclRef(AstRef): """ 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. @@ -793,10 +838,10 @@ class ExprRef(AstRef): >>> b = Int('b') >>> a == b a == b - >>> a == None + >>> a is None False """ - if other == None: + 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) @@ -814,15 +859,18 @@ class ExprRef(AstRef): >>> b = Int('b') >>> a != b a != b - >>> a != None + >>> a is not None True """ - if other == None: + 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. @@ -940,7 +988,7 @@ def _to_expr_ref(a, ctx): def _coerce_expr_merge(s, a): if is_expr(a): s1 = a.sort() - if s == None: + if s is None: return s1 if s1.eq(s): return s @@ -966,6 +1014,7 @@ def _coerce_exprs(a, b, ctx=None): b = s.cast(b) return (a, b) + def _reduce(f, l, a): r = a for e in l: @@ -1156,7 +1205,7 @@ def Distinct(*args): args = _get_args(args) ctx = _ctx_from_ast_arg_list(args) if __debug__: - _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression") + _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) @@ -1251,7 +1300,7 @@ class BoolSortRef(SortRef): if isinstance(val, bool): return BoolVal(val, self.ctx) if __debug__: - _z3_assert(is_expr(val), "True, False or Z3 Boolean expression expected") + _z3_assert(is_expr(val), "True, False or Z3 Boolean expression expected. Received %s" % val) _z3_assert(self.eq(val.sort()), "Value cannot be converted into a Z3 Boolean value") return val @@ -1270,6 +1319,19 @@ class BoolRef(ExprRef): 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. @@ -1529,13 +1591,16 @@ def And(*args): 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 __debug__: - _z3_assert(ctx_args == None or ctx_args == ctx, "context mismatch") - _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression or probe") + _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: @@ -1564,8 +1629,8 @@ def Or(*args): args = _get_args(args) ctx_args = _ctx_from_ast_arg_list(args, ctx) if __debug__: - _z3_assert(ctx_args == None or ctx_args == ctx, "context mismatch") - _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression or probe") + _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: @@ -1780,7 +1845,7 @@ class QuantifierRef(BoolRef): """ if __debug__: _z3_assert(idx < self.num_vars(), "Invalid variable idx") - return SortRef(Z3_get_quantifier_bound_sort(self.ctx_ref(), self.ast, idx), self.ctx) + 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() @@ -1951,7 +2016,7 @@ class ArithSortRef(SortRef): if self.is_real(): return RealVal(val, self.ctx) if __debug__: - _z3_assert(False, "int, long, float, string (numeral), or Z3 Integer/Real expression expected") + _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). @@ -2600,6 +2665,19 @@ class RatNumRef(ArithRef): """ 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(), "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. @@ -4333,7 +4411,8 @@ class ScopedConstructor: self.c = c self.ctx = ctx def __del__(self): - Z3_del_constructor(self.ctx.ref(), self.c) + if self.ctx.ref() is not None: + Z3_del_constructor(self.ctx.ref(), self.c) class ScopedConstructorList: """Auxiliary object used to create Z3 datatypes.""" @@ -4341,7 +4420,8 @@ class ScopedConstructorList: self.c = c self.ctx = ctx def __del__(self): - Z3_del_constructor_list(self.ctx.ref(), self.c) + 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. @@ -4575,7 +4655,8 @@ class ParamsRef: Z3_params_inc_ref(self.ctx.ref(), self.params) def __del__(self): - Z3_params_dec_ref(self.ctx.ref(), self.params) + 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.""" @@ -4613,7 +4694,7 @@ def args2params(arguments, keywords, ctx=None): prev = None r = ParamsRef(ctx) for a in arguments: - if prev == None: + if prev is None: prev = a else: r.set(prev, a) @@ -4633,7 +4714,8 @@ class ParamDescrsRef: Z3_param_descrs_inc_ref(self.ctx.ref(), self.descr) def __del__(self): - Z3_param_descrs_dec_ref(self.ctx.ref(), self.descr) + 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`. @@ -4685,15 +4767,15 @@ class Goal(Z3PPObject): def __init__(self, models=True, unsat_cores=False, proofs=False, ctx=None, goal=None): if __debug__: - _z3_assert(goal == None or ctx != None, "If goal is different from None, then ctx must be also different from None") + _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 == None: + 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 __del__(self): - if self.goal != None: + 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): @@ -4945,17 +5027,17 @@ class AstVector(Z3PPObject): def __init__(self, v=None, ctx=None): self.vector = None - if v == 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 != None + assert ctx is not None self.ctx = ctx Z3_ast_vector_inc_ref(self.ctx.ref(), self.vector) def __del__(self): - if self.vector != None: + 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): @@ -5080,17 +5162,17 @@ class AstMap: def __init__(self, m=None, ctx=None): self.map = None - if m == 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 != None + assert ctx is not None self.ctx = ctx Z3_ast_map_inc_ref(self.ctx.ref(), self.map) def __del__(self): - if self.map != None: + 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): @@ -5205,7 +5287,8 @@ class FuncEntry: Z3_func_entry_inc_ref(self.ctx.ref(), self.entry) def __del__(self): - Z3_func_entry_dec_ref(self.ctx.ref(), self.entry) + 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. @@ -5306,11 +5389,11 @@ class FuncInterp(Z3PPObject): def __init__(self, f, ctx): self.f = f self.ctx = ctx - if self.f != None: + if self.f is not None: Z3_func_interp_inc_ref(self.ctx.ref(), self.f) def __del__(self): - if self.f != None: + 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): @@ -5414,13 +5497,14 @@ class ModelRef(Z3PPObject): """Model/Solution of a satisfiability problem (aka system of constraints).""" def __init__(self, m, ctx): - assert ctx != None + assert ctx is not None self.model = m self.ctx = ctx Z3_model_inc_ref(self.ctx.ref(), self.model) def __del__(self): - Z3_model_dec_ref(self.ctx.ref(), self.model) + if self.ctx.ref() is not None: + Z3_model_dec_ref(self.ctx.ref(), self.model) def __repr__(self): return obj_to_string(self) @@ -5695,7 +5779,8 @@ class Statistics: Z3_stats_inc_ref(self.ctx.ref(), self.stats) def __del__(self): - Z3_stats_dec_ref(self.ctx.ref(), self.stats) + if self.ctx.ref() is not None: + Z3_stats_dec_ref(self.ctx.ref(), self.stats) def __repr__(self): if in_html_mode(): @@ -5857,17 +5942,17 @@ 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): - assert solver == None or ctx != None + assert solver is None or ctx is not None self.ctx = _get_ctx(ctx) self.solver = None - if 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) def __del__(self): - if self.solver != None: + 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): @@ -5970,6 +6055,10 @@ class Solver(Z3PPObject): """ self.assert_exprs(*args) + def __iadd__(self, fml): + self.add(fml) + return self + def append(self, *args): """Assert constraints into the solver. @@ -6101,6 +6190,34 @@ class Solver(Z3PPObject): """ 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(Not(c), Not(c)), Implies(d, d), 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 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) @@ -6244,10 +6361,10 @@ class Fixedpoint(Z3PPObject): """Fixedpoint API provides methods for solving with recursive predicates""" def __init__(self, fixedpoint=None, ctx=None): - assert fixedpoint == None or ctx != None + assert fixedpoint is None or ctx is not None self.ctx = _get_ctx(ctx) self.fixedpoint = None - if fixedpoint == None: + if fixedpoint is None: self.fixedpoint = Z3_mk_fixedpoint(self.ctx.ref()) else: self.fixedpoint = fixedpoint @@ -6255,7 +6372,7 @@ class Fixedpoint(Z3PPObject): self.vars = [] def __del__(self): - if self.fixedpoint != None: + 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): @@ -6290,6 +6407,10 @@ class Fixedpoint(Z3PPObject): """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) @@ -6310,10 +6431,10 @@ class Fixedpoint(Z3PPObject): >>> s.query(b) sat """ - if name == None: + if name is None: name = "" name = to_symbol(name, self.ctx) - if body == None: + if body is None: head = self.abstract(head) Z3_fixedpoint_add_rule(self.ctx.ref(), self.fixedpoint, head.as_ast(), name) else: @@ -6361,7 +6482,7 @@ class Fixedpoint(Z3PPObject): def update_rule(self, head, body, name): """update rule""" - if name == None: + if name is None: name = "" name = to_symbol(name, self.ctx) body = _get_args(body) @@ -6613,7 +6734,7 @@ class Optimize(Z3PPObject): Z3_optimize_inc_ref(self.ctx.ref(), self.optimize) def __del__(self): - if self.optimize != None: + 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): @@ -6644,6 +6765,10 @@ class Optimize(Z3PPObject): """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 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 @@ -6653,9 +6778,11 @@ class Optimize(Z3PPObject): """ 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 == None: + 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) @@ -6702,6 +6829,22 @@ class Optimize(Z3PPObject): raise Z3Exception("Expecting objective handle returned by maximize/minimize") return obj.upper() + 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() @@ -6712,7 +6855,7 @@ class Optimize(Z3PPObject): return Z3_optimize_to_string(self.ctx.ref(), self.optimize) def statistics(self): - """Return statistics for the last `query()`. + """Return statistics for the last check`. """ return Statistics(Z3_optimize_get_statistics(self.ctx.ref(), self.optimize), self.ctx) @@ -6733,7 +6876,8 @@ class ApplyResult(Z3PPObject): Z3_apply_result_inc_ref(self.ctx.ref(), self.result) def __del__(self): - Z3_apply_result_dec_ref(self.ctx.ref(), self.result) + 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`. @@ -6860,7 +7004,7 @@ class Tactic: Z3_tactic_inc_ref(self.ctx.ref(), self.tactic) def __del__(self): - if self.tactic != None: + 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): @@ -7132,7 +7276,7 @@ class Probe: Z3_probe_inc_ref(self.ctx.ref(), self.probe) def __del__(self): - if self.probe != None: + 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): @@ -7453,11 +7597,11 @@ def Sum(*args): a__0 + a__1 + a__2 + a__3 + a__4 """ args = _get_args(args) - if __debug__: - _z3_assert(len(args) > 0, "Non empty list of arguments expected") + if len(args) == 0: + return 0 ctx = _ctx_from_ast_arg_list(args) - if __debug__: - _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression") + 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) @@ -7465,6 +7609,7 @@ def Sum(*args): _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. @@ -7478,11 +7623,11 @@ def Product(*args): a__0*a__1*a__2*a__3*a__4 """ args = _get_args(args) - if __debug__: - _z3_assert(len(args) > 0, "Non empty list of arguments expected") + if len(args) == 0: + return 1 ctx = _ctx_from_ast_arg_list(args) - if __debug__: - _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression") + 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) @@ -7501,32 +7646,72 @@ def AtMost(*args): _z3_assert(len(args) > 1, "Non empty list of arguments expected") ctx = _ctx_from_ast_arg_list(args) if __debug__: - _z3_assert(ctx != None, "At least one of the arguments must be a Z3 expression") + _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 __debug__: + _z3_assert(len(args) > 1, "Non empty list of arguments expected") + ctx = _ctx_from_ast_arg_list(args) + if __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 _pb_args_coeffs(args): + args = _get_args(args) + args, coeffs = zip(*args) + if __debug__: + _z3_assert(len(args) > 0, "Non empty list of arguments expected") + ctx = _ctx_from_ast_arg_list(args) + if __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)): + _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) """ - args = _get_args(args) - args, coeffs = zip(*args) - if __debug__: - _z3_assert(len(args) > 0, "Non empty list of arguments expected") - ctx = _ctx_from_ast_arg_list(args) - if __debug__: - _z3_assert(ctx != 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)): - _coeffs[i] = coeffs[i] + 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) + """ + ctx, sz, _args, _coeffs = _pb_args_coeffs(args) + return BoolRef(Z3_mk_pbge(ctx.ref(), sz, _args, _coeffs, k), ctx) + +def PbEq(args, k): + """Create a Pseudo-Boolean inequality k constraint. + + >>> a, b, c = Bools('a b c') + >>> f = PbEq(((a,1),(b,3),(c,2)), 3) + """ + 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`. @@ -7809,7 +7994,7 @@ def tree_interpolant(pat,p=None,ctx=None): ctx = _get_ctx(_ctx_from_ast_arg_list([f], ctx)) ptr = (AstVectorObj * 1)() mptr = (Model * 1)() - if p == None: + if p is None: p = ParamsRef(ctx) res = Z3_compute_interpolant(ctx.ref(),f.as_ast(),p.params,ptr,mptr) if res == Z3_L_FALSE: @@ -7844,7 +8029,7 @@ def binary_interpolant(a,b,p=None,ctx=None): """ f = And(Interpolant(a),b) ti = tree_interpolant(f,p,ctx) - return ti[0] if ti != None else None + return ti[0] if ti is not None else None def sequence_interpolant(v,p=None,ctx=None): """Compute interpolant for a sequence of formulas. @@ -7938,7 +8123,7 @@ def _coerce_fp_expr_list(alist, ctx): first_fp_sort = None for a in alist: if is_fp(a): - if first_fp_sort == None: + if first_fp_sort is None: first_fp_sort = a.sort() elif first_fp_sort == a.sort(): pass # OK, same as before @@ -8305,27 +8490,13 @@ def is_fprm_value(a): ### FP Numerals -class FPNumRef(FPRef): - def isNaN(self): - return self.decl().kind() == Z3_OP_FPA_NAN +class FPNumRef(FPRef): + """The sign of the numeral. - def isInf(self): - return self.decl().kind() == Z3_OP_FPA_PLUS_INF or self.decl().kind() == Z3_OP_FPA_MINUS_INF - - def isZero(self): - return self.decl().kind() == Z3_OP_FPA_PLUS_ZERO or self.decl().kind() == Z3_OP_FPA_MINUS_ZERO - - def isNegative(self): - k = self.decl().kind() - return (self.num_args() == 0 and (k == Z3_OP_FPA_MINUS_INF or k == Z3_OP_FPA_MINUS_ZERO)) or (self.sign() == True) - - """ - The sign of the numeral. - - >>> x = FPNumRef(+1.0, FPSort(8, 24)) + >>> x = FPVal(+1.0, FPSort(8, 24)) >>> x.sign() False - >>> x = FPNumRef(-1.0, FPSort(8, 24)) + >>> x = FPVal(-1.0, FPSort(8, 24)) >>> x.sign() True """ @@ -8335,49 +8506,104 @@ class FPNumRef(FPRef): 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. """ - The significand of the numeral. + def sign_as_bv(self): + return BitVecNumRef(Z3_fpa_get_numeral_sign_bv(self.ctx.ref(), self.as_ast()), self.ctx) - >>> x = FPNumRef(2.5, FPSort(8, 24)) + """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 exponent of the numeral. + """The significand of the numeral as a long. - >>> x = FPNumRef(2.5, FPSort(8, 24)) + >>> x = FPVal(2.5, FPSort(8, 24)) + >>> x.significand_as_long() + 1.25 + """ + def significand_as_long(self): + return Z3_fpa_get_numeral_significand_uint64(self.ctx.ref(), self.as_ast()) + + """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): - return Z3_fpa_get_numeral_exponent_string(self.ctx.ref(), self.as_ast()) + 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. + """The exponent of the numeral as a long. - >>> x = FPNumRef(2.5, FPSort(8, 24)) + >>> x = FPVal(2.5, FPSort(8, 24)) >>> x.exponent_as_long() 1 """ - def exponent_as_long(self): + 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): + 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 postitive.""" + 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 = FPNumRef(20, FPSort(8, 24)) + >>> x = FPVal(20, FPSort(8, 24)) >>> x.as_string() 1.25*(2**4) """ def as_string(self): - s = Z3_fpa_get_numeral_string(self.ctx.ref(), self.as_ast()) - return ("FPVal(%s, %s)" % (s, FPSortRef(self.sort()).as_string())) + 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. @@ -8417,7 +8643,7 @@ def FPSort(ebits, sbits, ctx=None): >>> eq(x, FP('x', FPSort(8, 24))) True """ - ctx = z3._get_ctx(ctx) + ctx = _get_ctx(ctx) return FPSortRef(Z3_mk_fpa_sort(ctx.ref(), ebits, sbits), ctx) def _to_float_str(val, exp=0): @@ -8529,7 +8755,7 @@ def FPVal(sig, exp=None, fps=None, ctx=None): >>> v = FPVal(20.0, FPSort(8, 24)) >>> v 1.25*(2**4) - >>> print("0x%.8x" % v.exponent_as_long()) + >>> print("0x%.8x" % v.exponent_as_long(False)) 0x00000004 >>> v = FPVal(2.25, FPSort(8, 24)) >>> v @@ -8548,10 +8774,10 @@ def FPVal(sig, exp=None, fps=None, ctx=None): if is_fp_sort(exp): fps = exp exp = None - elif fps == None: + elif fps is None: fps = _dflt_fps(ctx) _z3_assert(is_fp_sort(fps), "sort mismatch") - if exp == None: + if exp is None: exp = 0 val = _to_float_str(sig) if val == "NaN" or val == "nan": @@ -8603,7 +8829,7 @@ def FPs(names, fpsort, ctx=None): >>> fpMul(RNE(), fpAdd(RNE(), x, y), z) fpMul(RNE(), fpAdd(RNE(), x, y), z) """ - ctx = z3._get_ctx(ctx) + ctx = _get_ctx(ctx) if isinstance(names, str): names = names.split(" ") return [FP(name, fpsort, ctx) for name in names] @@ -8994,6 +9220,92 @@ def fpToFP(a1, a2=None, a3=None, ctx=None): 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 __debug__: @@ -9110,6 +9422,7 @@ class SeqSortRef(SortRef): False """ return Z3_is_string_sort(self.ctx_ref(), self.ast) + def StringSort(ctx=None): """Create a string sort @@ -9233,8 +9546,29 @@ def Empty(s): >>> e3 = Empty(SeqSort(IntSort())) >>> print(e3) seq.empty + >>> e4 = Empty(ReSort(SeqSort(IntSort()))) + >>> print(e4) + re.empty """ - return SeqRef(Z3_mk_seq_empty(s.ctx_ref(), s.ast), s.ctx) + 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 langauge + >>> e = Full(ReSort(SeqSort(IntSort()))) + >>> print(e) + re.all + >>> e1 = Full(ReSort(StringSort())) + >>> print(e1) + re.allchar + """ + 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""" @@ -9330,6 +9664,29 @@ def Length(s): 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") @@ -9350,10 +9707,10 @@ class ReSortRef(SortRef): def ReSort(s): if is_ast(s): - return ReSortRef(Z3_mk_re_sort(s.ctx.ref(), s.as_ast()), ctx) + 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())), ctx) + 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") @@ -9424,6 +9781,10 @@ def Option(re): """ 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")) @@ -9435,3 +9796,15 @@ def Star(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) diff --git a/src/api/python/z3num.py b/src/api/python/z3/z3num.py similarity index 98% rename from src/api/python/z3num.py rename to src/api/python/z3/z3num.py index b219829d3..b1af58dc3 100644 --- a/src/api/python/z3num.py +++ b/src/api/python/z3/z3num.py @@ -5,11 +5,13 @@ # # Author: Leonardo de Moura (leonardo) ############################################ -from z3 import * -from z3core import * -from z3printer import * +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 @@ -86,7 +88,7 @@ class Numeral: def __init__(self, num, ctx=None): if isinstance(num, Ast): self.ast = num - self.ctx = z3._get_ctx(ctx) + self.ctx = _get_ctx(ctx) elif isinstance(num, RatNumRef) or isinstance(num, AlgebraicNumRef): self.ast = num.ast self.ctx = num.ctx @@ -572,9 +574,4 @@ def isolate_roots(p, vs=[]): _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 ] - -if __name__ == "__main__": - import doctest - if doctest.testmod().failed: - exit(1) diff --git a/src/api/python/z3poly.py b/src/api/python/z3/z3poly.py similarity index 97% rename from src/api/python/z3poly.py rename to src/api/python/z3/z3poly.py index 0b8bf9457..169944291 100644 --- a/src/api/python/z3poly.py +++ b/src/api/python/z3/z3poly.py @@ -5,7 +5,8 @@ # # Author: Leonardo de Moura (leonardo) ############################################ -from z3 import * + +from .z3 import * def subresultants(p, q, x): """ @@ -32,6 +33,3 @@ if __name__ == "__main__": import doctest if doctest.testmod().failed: exit(1) - - - diff --git a/src/api/python/z3printer.py b/src/api/python/z3/z3printer.py similarity index 97% rename from src/api/python/z3printer.py rename to src/api/python/z3/z3printer.py index c8d69900a..aef71be2f 100644 --- a/src/api/python/z3printer.py +++ b/src/api/python/z3/z3printer.py @@ -6,10 +6,14 @@ # Author: Leonardo de Moura (leonardo) ############################################ import sys, io, z3 -from z3consts import * -from z3core import * +from .z3consts import * +from .z3core import * from ctypes import * +def _z3_assert(cond, msg): + if not cond: + raise Z3Exception(msg) + ############################## # # Configuration @@ -580,14 +584,14 @@ class Formatter: return to_format(a.as_string()) def pp_fprm_value(self, a): - z3._z3_assert(z3.is_fprm_value(a), 'expected FPRMNumRef') + _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._z3_assert(isinstance(a, z3.FPNumRef), 'type mismatch') + _z3_assert(isinstance(a, z3.FPNumRef), 'type mismatch') if not self.fpa_pretty: r = [] if (a.isNaN()): @@ -612,12 +616,12 @@ class Formatter: else: return to_format('+zero') else: - z3._z3_assert(z3.is_fp_value(a), 'expecting FP num ast') + _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) - exp = Z3_fpa_get_numeral_exponent_string(a.ctx_ref(), a.ast) r.append(to_format('FPVal(')) if sgnb and sgn.value != 0: r.append(to_format('-')) @@ -642,12 +646,12 @@ class Formatter: else: return to_format(_z3_op_to_fpa_pretty_str[Z3_OP_FPA_PLUS_ZERO]) else: - z3._z3_assert(z3.is_fp_value(a), 'expecting FP num ast') + _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) - exp = Z3_fpa_get_numeral_exponent_string(a.ctx_ref(), a.ast) if sgnb and sgn.value != 0: r.append(to_format('-')) r.append(to_format(sig)) @@ -659,7 +663,7 @@ class Formatter: def pp_fp(self, a, d, xs): - z3._z3_assert(isinstance(a, z3.FPRef), "type mismatch") + _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): @@ -674,7 +678,7 @@ class Formatter: if self.fpa_pretty: if self.is_infix(k) and n >= 3: rm = a.arg(0) - if z3.is_fprm_value(rm) and z3._dflt_rm(a.ctx).eq(rm): + 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 = [] @@ -1164,12 +1168,12 @@ def set_pp_option(k, v): return True val = getattr(_PP, k, None) if val != None: - z3._z3_assert(type(v) == type(val), "Invalid pretty print option value") + _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._z3_assert(type(v) == type(val), "Invalid pretty print option value") + _z3_assert(type(v) == type(val), "Invalid pretty print option value") setattr(_Formatter, k, v) return True return False @@ -1219,13 +1223,13 @@ def pp(a): print(a) def print_matrix(m): - z3._z3_assert(isinstance(m, list) or isinstance(m, tuple), "matrix expected") + _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._z3_assert(isinstance(r, list) or isinstance(r, tuple), "matrix expected") + _z3_assert(isinstance(r, list) or isinstance(r, tuple), "matrix expected") print('') for c in r: print('' % c) diff --git a/src/api/python/z3rcf.py b/src/api/python/z3/z3rcf.py similarity index 98% rename from src/api/python/z3rcf.py rename to src/api/python/z3/z3rcf.py index 84c724910..9d6f2f6ad 100644 --- a/src/api/python/z3rcf.py +++ b/src/api/python/z3/z3rcf.py @@ -9,9 +9,9 @@ # # Author: Leonardo de Moura (leonardo) ############################################ -from z3 import * -from z3core import * -from z3printer import * +from .z3 import * +from .z3core import * +from .z3printer import * from fractions import Fraction def _to_rcfnum(num, ctx=None): diff --git a/src/api/python/z3types.py b/src/api/python/z3/z3types.py similarity index 99% rename from src/api/python/z3types.py rename to src/api/python/z3/z3types.py index 44b19d33a..7cf61f49e 100644 --- a/src/api/python/z3types.py +++ b/src/api/python/z3/z3types.py @@ -6,7 +6,7 @@ # Author: Leonardo de Moura (leonardo) ############################################ -import ctypes, z3core +import ctypes class Z3Exception(Exception): def __init__(self, value): @@ -121,4 +121,3 @@ class FuncEntryObj(ctypes.c_void_p): class RCFNumObj(ctypes.c_void_p): def __init__(self, e): self._as_parameter_ = e def from_param(obj): return obj - diff --git a/src/api/python/z3util.py b/src/api/python/z3/z3util.py similarity index 99% rename from src/api/python/z3util.py rename to src/api/python/z3/z3util.py index 2494461cf..fe7e76b86 100644 --- a/src/api/python/z3util.py +++ b/src/api/python/z3/z3util.py @@ -11,7 +11,7 @@ Usage: import common_z3 as CM_Z3 """ -from z3 import * +from .z3 import * def vset(seq, idfun=None, as_list=True): # This functions preserves the order of arguments while removing duplicates. diff --git a/src/api/python/z3test.py b/src/api/python/z3test.py index 38939cebf..4554657e4 100644 --- a/src/api/python/z3test.py +++ b/src/api/python/z3test.py @@ -5,9 +5,16 @@ # # Author: Leonardo de Moura (leonardo) ############################################ -import z3, doctest +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) -r = doctest.testmod(z3) if r.failed != 0: - exit(1) + sys.exit(1) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 7494bcb17..aa4701f13 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -857,12 +857,18 @@ typedef enum - 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_FPA_RM_NEAREST_TIES_TO_EVEN: Floating-point rounding mode RNE - Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY: Floating-point rounding mode RNA @@ -947,6 +953,8 @@ typedef enum - Z3_OP_FPA_TO_IEEE_BV: Floating-point conversion to IEEE-754 bit-vector + - 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 { @@ -1057,6 +1065,7 @@ typedef enum { Z3_OP_EXT_ROTATE_LEFT, Z3_OP_EXT_ROTATE_RIGHT, + Z3_OP_BIT2BOOL, Z3_OP_INT2BV, Z3_OP_BV2INT, Z3_OP_CARRY, @@ -1145,12 +1154,22 @@ typedef enum { 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, // theory_str Z3_OP_STR_CONCAT, @@ -1168,8 +1187,10 @@ typedef enum { // 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, // Floating-Point Arithmetic Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN, @@ -1223,6 +1244,8 @@ typedef enum { Z3_OP_FPA_MIN_I, Z3_OP_FPA_MAX_I, + Z3_OP_INTERNAL, + Z3_OP_UNINTERPRETED } Z3_decl_kind; @@ -1805,7 +1828,7 @@ extern "C" { 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, unsigned __int64 size); + Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, __uint64 size); /** \brief Create an array type. @@ -1940,7 +1963,7 @@ extern "C" { The datatype may be recursive. Return the datatype sort. \param c logical context. - \param name name of datatype. + \param name name of datatype. \param num_constructors number of constructors passed in. \param constructors array of constructor containers. @@ -1999,9 +2022,9 @@ extern "C" { \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. - \param tester constructor test function declaration. - \param accessors array of accessor function declarations. + \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. def_API('Z3_query_constructor', VOID, (_in(CONTEXT), _in(CONSTRUCTOR), _in(UINT), _out(FUNC_DECL), _out(FUNC_DECL), _out_array(2, FUNC_DECL))) */ @@ -3074,7 +3097,8 @@ extern "C" { \brief Create a numeral of a given sort. \param c logical context. - \param numeral A string representing the numeral value in decimal notation. If the given sort is a real, then the numeral can be a rational, that is, a string of the form \ccode{[num]* / [num]*}. + \param numeral A string representing the numeral value in decimal notation. The string may be of the form \code{[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 \ccode{[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 @@ -3140,14 +3164,14 @@ extern "C" { /** \brief Create a numeral of a int, bit-vector, or finite-domain sort. - This function can be use to create numerals that fit in a machine unsigned __int64 integer. + This function can be use to create numerals that fit in a machine __uint64 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, unsigned __int64 v, Z3_sort ty); + Z3_ast Z3_API Z3_mk_unsigned_int64(Z3_context c, __uint64 v, Z3_sort ty); /*@}*/ @@ -3459,6 +3483,21 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_seq_index(Z3_context c, Z3_ast s, Z3_ast substr, Z3_ast offset); + /** + \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. @@ -3512,6 +3551,60 @@ extern "C" { */ 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 repated + between \c lo and \c hi times. The \c lo should be below \c hi with one exection: 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); + + /*@}*/ @@ -3867,7 +3960,7 @@ extern "C" { 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, unsigned __int64* r); + Z3_bool_opt Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, __uint64* r); /** \brief Return the domain of the given array sort. @@ -4046,10 +4139,19 @@ extern "C" { 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. @@ -4057,11 +4159,32 @@ extern "C" { 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 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 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 coeffs[], + int k); + /** \brief Convert a \c Z3_func_decl into \c Z3_ast. This is just type casting. @@ -4421,7 +4544,7 @@ extern "C" { /** \brief Similar to #Z3_get_numeral_string, but only succeeds if - the value can fit in a machine unsigned __int64 int. Return Z3_TRUE if the call succeeded. + the value can fit in a machine __uint64 int. Return Z3_TRUE if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST @@ -4429,7 +4552,7 @@ extern "C" { def_API('Z3_get_numeral_uint64', BOOL, (_in(CONTEXT), _in(AST), _out(UINT64))) */ - Z3_bool Z3_API Z3_get_numeral_uint64(Z3_context c, Z3_ast v, unsigned __int64* u); + Z3_bool Z3_API Z3_get_numeral_uint64(Z3_context c, Z3_ast v, __uint64* u); /** \brief Similar to #Z3_get_numeral_string, but only succeeds if @@ -5271,6 +5394,13 @@ extern "C" { Z3_string Z3_API Z3_get_error_msg(Z3_context c, Z3_error_code err); /*@}*/ + /** + \brief Return a string describing the given error code. + Retained function name for backwards compatibility within v4.1 + */ + Z3_string Z3_API Z3_get_error_msg_ex(Z3_context c, Z3_error_code err); + /*@}*/ + /** @name Miscellaneous */ /*@{*/ @@ -5281,6 +5411,13 @@ extern "C" { */ 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. + + 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 @@ -5967,7 +6104,7 @@ extern "C" { void Z3_API Z3_solver_assert_and_track(Z3_context c, Z3_solver s, Z3_ast a, Z3_ast p); /** - \brief Return the set of asserted formulas as a goal object. + \brief Return the set of asserted formulas on the solver. def_API('Z3_solver_get_assertions', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) */ @@ -6029,6 +6166,17 @@ extern "C" { 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 Retrieve the model for the last #Z3_solver_check or #Z3_solver_check_assumptions diff --git a/src/api/z3_fpa.h b/src/api/z3_fpa.h index 510fa0473..e92b728d7 100644 --- a/src/api/z3_fpa.h +++ b/src/api/z3_fpa.h @@ -253,9 +253,9 @@ extern "C" { 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 + 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. + of the arguments. The exponent is assumed to be in IEEE-754 biased representation. \param c logical context \param sgn sign @@ -822,6 +822,100 @@ extern "C" { */ 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))) + */ + Z3_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))) + */ + Z3_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))) + */ + Z3_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))) + */ + Z3_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))) + */ + Z3_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))) + */ + Z3_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))) + */ + Z3_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. @@ -858,23 +952,25 @@ extern "C" { Remarks: This function extracts the significand bits in `t`, without the hidden bit or normalization. Sets the Z3_INVALID_ARG error code if the - significand does not fit into a uint64. + significand does not fit into a uint64. NaN is an invalid argument. def_API('Z3_fpa_get_numeral_significand_uint64', BOOL, (_in(CONTEXT), _in(AST), _out(UINT64))) */ Z3_bool Z3_API Z3_fpa_get_numeral_significand_uint64(Z3_context c, Z3_ast t, __uint64 * n); /** - \brief Return the exponent value of a floating-point numeral as a string + \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))) + 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); + Z3_string Z3_API Z3_fpa_get_numeral_exponent_string(Z3_context c, Z3_ast t, Z3_bool biased); /** \brief Return the exponent value of a floating-point numeral as a signed 64-bit integer @@ -882,12 +978,28 @@ extern "C" { \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))) + def_API('Z3_fpa_get_numeral_exponent_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64), _in(BOOL))) */ - Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, __int64 * n); + Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, __int64 * n, Z3_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, Z3_bool biased); /** \brief Conversion of a floating-point term into a bit-vector term in IEEE 754-2008 format. diff --git a/src/api/z3_optimization.h b/src/api/z3_optimization.h index c59428d9e..f7a9a8fe9 100644 --- a/src/api/z3_optimization.h +++ b/src/api/z3_optimization.h @@ -71,7 +71,6 @@ extern "C" { */ 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 @@ -91,7 +90,6 @@ extern "C" { */ unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t); - /** \brief Create a backtracking point. @@ -197,6 +195,31 @@ extern "C" { */ 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. + + 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 - string containing SMT2 specification. + + 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. @@ -211,6 +234,26 @@ extern "C" { 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 (+ (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); + /*@}*/ /*@}*/ @@ -218,4 +261,4 @@ extern "C" { } #endif // __cplusplus -#endif \ No newline at end of file +#endif diff --git a/src/api/z3_replayer.cpp b/src/api/z3_replayer.cpp index b1baa6de2..68f279e3d 100644 --- a/src/api/z3_replayer.cpp +++ b/src/api/z3_replayer.cpp @@ -14,7 +14,7 @@ Author: Leonardo de Moura (leonardo) 2011-09-22 Notes: - + --*/ #include"vector.h" #include"map.h" @@ -30,7 +30,7 @@ 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 reference1"); + throw z3_replayer_exception("invalid argument reference"); } struct z3_replayer::imp { @@ -72,17 +72,17 @@ struct z3_replayer::imp { 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 reference2"); + throw z3_replayer_exception("invalid argument reference"); } if (m_args[pos].m_kind != k) { std::stringstream strm; - strm << "expecting " << kind2string(k) << " at position " + strm << "expecting " << kind2string(k) << " at position " << pos << " but got " << kind2string(m_args[pos].m_kind); throw z3_replayer_exception(strm.str().c_str()); } } - struct value { + struct value { value_kind m_kind; union { __int64 m_int; @@ -129,7 +129,7 @@ struct z3_replayer::imp { break; case DOUBLE: out << v.m_double; - break; + break; case STRING: out << v.m_str; break; @@ -160,7 +160,7 @@ struct z3_replayer::imp { char 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"); @@ -258,7 +258,7 @@ struct z3_replayer::imp { } bool is_double_char() const { - return curr() == '-' || curr() == '.' || ('0' <= curr() && curr() <= '9') || curr() == 'e' || curr() == 'E'; + return curr() == '-' || curr() == '.' || ('0' <= curr() && curr() <= '9') || curr() == 'e' || curr() == 'E'; } #if (!defined(strtof)) @@ -357,7 +357,7 @@ struct z3_replayer::imp { v.push_back(static_cast(m_args[i].m_uint)); } } - if (k == INT64) { + else if (k == INT64) { aidx = m_int_arrays.size(); nk = INT_ARRAY; m_int_arrays.push_back(svector()); @@ -376,7 +376,7 @@ struct z3_replayer::imp { } } else if (k == OBJECT) { - TRACE("z3_replayer_bug", + 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++) { @@ -415,9 +415,13 @@ struct z3_replayer::imp { if (c == EOF) return; switch (c) { + case 'V': + // version + next(); skip_blank(); read_string(); + break; case 'R': // reset - next(); + next(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "R\n";); reset(); break; @@ -428,7 +432,7 @@ struct z3_replayer::imp { if (m_ptr == 0) { m_args.push_back(0); } - else { + else { void * obj = 0; if (!m_heap.find(m_ptr, obj)) throw z3_replayer_exception("invalid pointer"); @@ -489,7 +493,7 @@ struct z3_replayer::imp { next(); skip_blank(); read_double(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "D " << m_double << "\n";); m_args.push_back(value(DOUBLE, m_double)); - break; + break; case 'p': case 's': case 'u': @@ -511,7 +515,7 @@ struct z3_replayer::imp { if (idx >= m_cmds.size()) throw z3_replayer_exception("invalid command"); try { - TRACE("z3_replayer_cmd", tout << m_cmds_names[idx] << "\n";); + TRACE("z3_replayer_cmd", tout << idx << ":" << m_cmds_names[idx] << "\n";); m_cmds[idx](m_owner); } catch (z3_error & ex) { @@ -692,15 +696,15 @@ struct z3_replayer::imp { 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); } diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index b72a55e34..410c50852 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -264,6 +264,10 @@ public: 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_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); } @@ -284,6 +288,18 @@ public: 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); @@ -300,8 +316,16 @@ public: MATCH_BINARY(is_idiv); MATCH_BINARY(is_power); - 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_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 { @@ -348,6 +372,9 @@ public: app * mk_int(int i) { return mk_numeral(rational(i), true); } + app * mk_real(int i) { + return mk_numeral(rational(i), 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); } @@ -422,5 +449,103 @@ public: expr_ref mk_add_simplify(unsigned sz, expr* const* args); }; + +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_ */ diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index 9634e17cb..ab21c4c88 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -522,7 +522,7 @@ void array_decl_plugin::get_sort_names(svector& sort_names, symbol 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) { + if (logic == symbol::null || logic == symbol("HORN")) { // 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)); diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index a822be37a..37ea297c2 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -80,7 +80,10 @@ void parameter::del_eh(ast_manager & m, family_id fid) { } else if (is_external()) { SASSERT(fid != null_family_id); - m.get_plugin(fid)->del(*this); + decl_plugin * plugin = m.get_plugin(fid); + if (plugin) { + plugin->del(*this); + } } } @@ -1391,6 +1394,22 @@ void ast_manager::init() { 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"))); @@ -1407,29 +1426,72 @@ ast_manager::~ast_manager() { } it = m_plugins.begin(); for (; it != end; ++it) { - if (*it) + if (*it) dealloc(*it); } - DEBUG_CODE({ - if (!m_ast_table.empty()) - std::cout << "ast_manager LEAKED: " << m_ast_table.size() << std::endl; - }); -#if 1 - DEBUG_CODE({ + 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; ast_table::iterator it_a = m_ast_table.begin(); ast_table::iterator end_a = m_ast_table.end(); for (; it_a != end_a; ++it_a) { - ast* a = (*it_a); - std::cout << "Leaked: "; - if (is_sort(a)) { - std::cout << to_sort(a)->get_name() << "\n"; + ast* n = (*it_a); + switch (n->get_kind()) { + case AST_SORT: { + sort_info* info = to_sort(n)->get_info(); + if (info != 0) { + mark_array_ref(mark, info->get_num_parameters(), info->get_parameters()); + } + break; } - else { - std::cout << mk_ll_pp(a, *this, false) << "id: " << a->get_id() << "\n"; + case AST_FUNC_DECL: { + func_decl_info* info = to_func_decl(n)->get_info(); + if (info != 0) { + 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; + } + } + it_a = m_ast_table.begin(); + for (; it_a != end_a; ++it_a) { + ast* n = *it_a; + 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); } - }); -#endif + } if (m_format_manager != 0) dealloc(m_format_manager); if (m_trace_stream_owner) { @@ -1813,8 +1875,8 @@ void ast_manager::delete_node(ast * n) { dec_array_ref(worklist, to_quantifier(n)->get_num_patterns(), to_quantifier(n)->get_patterns()); dec_array_ref(worklist, to_quantifier(n)->get_num_no_patterns(), to_quantifier(n)->get_no_patterns()); break; - default: - break; + default: + break; } if (m_debug_ref_count) { m_debug_free_indices.insert(n->m_id,0); @@ -1971,7 +2033,7 @@ bool ast_manager::check_sorts(ast const * n) const { return true; } catch (ast_exception & ex) { - warning_msg(ex.msg()); + warning_msg("%s", ex.msg()); return false; } } @@ -2270,6 +2332,22 @@ bool ast_manager::is_pattern(expr const * n) const { 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; +} + + quantifier * ast_manager::mk_quantifier(bool forall, 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, @@ -2569,6 +2647,8 @@ proof * ast_manager::mk_modus_ponens(proof * p1, proof * 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); @@ -2945,7 +3025,10 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro 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";); - SASSERT(num_proofs == cls_sz || (num_proofs == cls_sz + 1 && is_false(new_fact))); + // + // 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); diff --git a/src/ast/ast.h b/src/ast/ast.h index 0c85bad9c..066265bb8 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1848,6 +1848,8 @@ public: bool is_pattern(expr const * n) const; + bool is_pattern(expr const *n, ptr_vector &args); + public: quantifier * mk_quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index db2043320..023773f62 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -1180,6 +1180,21 @@ void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & pr(f, r); } +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(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) { ast_manager & m = env.get_manager(); @@ -1214,6 +1229,18 @@ std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environmen 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; +} + 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), @@ -1233,13 +1260,16 @@ mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent, unsigned num } std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) { - smt2_pp_environment_dbg env(p.m_manager); + smt2_pp_environment_dbg env(p.m_manager); 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 if (p.m_ast == 0) { + out << "null"; + } 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); @@ -1264,19 +1294,15 @@ std::ostream& operator<<(std::ostream& out, sort_ref const& e) { } std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e) { - for (unsigned i = 0; i < e.size(); ++i) { - out << mk_ismt2_pp(e[i], e.get_manager()); - if (i + 1 < e.size()) out << "; "; - } - return out; + 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, 0); } std::ostream& operator<<(std::ostream& out, app_ref_vector const& e) { - for (unsigned i = 0; i < e.size(); ++i) { - out << mk_ismt2_pp(e[i], e.get_manager()); - if (i + 1 < e.size()) out << "; "; - } - return out; + 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, 0); } std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e) { diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index eb15910bc..8ad33d6fc 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -732,7 +732,8 @@ void datatype_decl_plugin::get_op_names(svector & op_names, symbol datatype_util::datatype_util(ast_manager & m): m_manager(m), m_family_id(m.mk_family_id("datatype")), - m_asts(m) { + m_asts(m), + m_start(0) { } datatype_util::~datatype_util() { @@ -807,11 +808,11 @@ func_decl * datatype_util::get_non_rec_constructor_core(sort * ty, ptr_vector const * constructors = get_datatype_constructors(ty); - ptr_vector::const_iterator it = constructors->begin(); - ptr_vector::const_iterator end = constructors->end(); // step 1) - for (; it != end; ++it) { - func_decl * c = *it; + unsigned sz = constructors->size(); + ++m_start; + for (unsigned j = 0; j < sz; ++j) { + func_decl * c = (*constructors)[(j + m_start) % sz]; unsigned num_args = c->get_arity(); unsigned i = 0; for (; i < num_args; i++) { @@ -823,9 +824,8 @@ func_decl * datatype_util::get_non_rec_constructor_core(sort * ty, ptr_vectorbegin(); - for (; it != end; ++it) { - func_decl * c = *it; + for (unsigned j = 0; j < sz; ++j) { + func_decl * c = (*constructors)[(j + m_start) % sz]; TRACE("datatype_util_bug", tout << "non_rec_constructor c: " << c->get_name() << "\n";); unsigned num_args = c->get_arity(); unsigned i = 0; @@ -933,6 +933,25 @@ bool datatype_util::is_recursive(sort * ty) { return r; } + +bool datatype_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; +} + + void datatype_util::reset() { m_datatype2constructors.reset(); m_datatype2nonrec_constructor.reset(); @@ -941,9 +960,11 @@ void datatype_util::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; } /** diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h index 8ef6fb557..ba0bedd32 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.h @@ -173,9 +173,11 @@ class datatype_util { obj_map m_recognizer2constructor; obj_map m_accessor2constructor; obj_map m_is_recursive; + obj_map m_is_enum; ast_ref_vector m_asts; ptr_vector > m_vectors; - + unsigned m_start; + func_decl * get_non_rec_constructor_core(sort * ty, ptr_vector & forbidden_set); func_decl * get_constructor(sort * ty, unsigned c_id); @@ -184,6 +186,8 @@ public: ~datatype_util(); ast_manager & get_manager() const { return m_manager; } bool is_datatype(sort * 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_decl_of(f, m_family_id, OP_DT_RECOGNISER); } diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index 13416c086..f7d6d9d1c 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -324,7 +324,7 @@ namespace datalog { if (!is_rel_sort(r, sorts)) { return 0; } - unsigned index0; + unsigned index0 = 0; sort* last_sort = 0; SASSERT(num_params > 0); for (unsigned i = 0; i < num_params; ++i) { @@ -461,7 +461,7 @@ namespace datalog { return 0; } if (!ps.is_ast() || !is_sort(ps.get_ast()) || !is_fin_sort(to_sort(ps.get_ast()))) { - m_manager->raise_exception("second paramter should be a finite domain sort"); + m_manager->raise_exception("second parameter should be a finite domain sort"); return 0; } sort* s = to_sort(ps.get_ast()); diff --git a/src/ast/expr_functors.h b/src/ast/expr_functors.h index 32560e139..da2b43dea 100644 --- a/src/ast/expr_functors.h +++ b/src/ast/expr_functors.h @@ -31,6 +31,14 @@ public: 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. diff --git a/src/ast/fpa/bv2fpa_converter.cpp b/src/ast/fpa/bv2fpa_converter.cpp new file mode 100644 index 000000000..c113627f9 --- /dev/null +++ b/src/ast/fpa/bv2fpa_converter.cpp @@ -0,0 +1,558 @@ +/*++ + 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_smt2_pp.h" +#include"well_sorted.h" +#include"th_rewriter.h" +#include"fpa_rewriter.h" + +#include"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 (obj_map::iterator it = conv.m_const2bv.begin(); + it != conv.m_const2bv.end(); + it++) + { + m_const2bv.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map::iterator it = conv.m_rm_const2bv.begin(); + it != conv.m_rm_const2bv.end(); + it++) + { + m_rm_const2bv.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map::iterator it = conv.m_uf2bvuf.begin(); + it != conv.m_uf2bvuf.end(); + it++) + { + m_uf2bvuf.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value); + } + for (obj_map >::iterator it = conv.m_min_max_specials.begin(); + it != conv.m_min_max_specials.end(); + it++) { + m_specials.insert(it->m_key, it->m_value); + m.inc_ref(it->m_key); + m.inc_ref(it->m_value.first); + m.inc_ref(it->m_value.second); + } +} + +bv2fpa_converter::~bv2fpa_converter() { + dec_ref_map_key_values(m, m_const2bv); + dec_ref_map_key_values(m, m_rm_const2bv); + dec_ref_map_key_values(m, m_uf2bvuf); + for (obj_map >::iterator it = m_specials.begin(); + it != m_specials.end(); + it++) { + m.dec_ref(it->m_key); + m.dec_ref(it->m_value.first); + m.dec_ref(it->m_value.second); + } +} + +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, app * 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(m); + if (m_bv_util.is_numeral(bv)) + bv_num = bv; + else if (!mc->eval(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(0); + unsigned sz = 0; + + if (m_bv_util.is_numeral(bv_rm, bv_val, sz)) { + 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(); + } + } + + return res; +} + +expr_ref bv2fpa_converter::convert_bv2rm(model_core * mc, app * 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 (mc->eval(val->get_decl(), eval_v)) + res = convert_bv2rm(eval_v); + else + res = m_fpa_util.mk_round_toward_zero(); + } + + return res; +} + +expr_ref bv2fpa_converter::rebuild_floats(model_core * mc, sort * s, app * e) { + expr_ref result(m); + TRACE("bv2fpa", 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 == 0) + 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 == 0) + 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 (unsigned i = 0; i < a->get_num_args(); i++) + new_args.push_back(rebuild_floats(mc, a->get_decl()->get_domain()[i], to_app(a->get_arg(i)))); + result = m.mk_app(a->get_decl(), new_args.size(), new_args.c_ptr()); + } + + 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(f->get_range(), 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 = 0; + 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); + + if (bv_fi != 0) { + fpa_rewriter rw(m); + expr_ref ai(m); + result = alloc(func_interp, m, arity); + + 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]; + ai = rebuild_floats(mc, ft_dj, to_app(bv_aj)); + m_th_rw(ai); + new_args.push_back(ai); + } + + expr_ref bv_fres(m), ft_fres(m); + bv_fres = bv_fe->get_result(); + ft_fres = rebuild_floats(mc, rng, to_app(bv_fres)); + m_th_rw(ft_fres); + result->insert_new_entry(new_args.c_ptr(), ft_fres); + } + + app_ref bv_els(m); + expr_ref ft_els(m); + bv_els = (app*)bv_fi->get_else(); + ft_els = rebuild_floats(mc, rng, bv_els); + m_th_rw(ft_els); + result->set_else(ft_els); + } + + return result; +} + +void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model, obj_hashtable & seen) { + for (obj_map::iterator it = m_const2bv.begin(); + it != m_const2bv.end(); + it++) + { + func_decl * var = it->m_key; + app * val = to_app(it->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 == 0) { + 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(m); + 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 (obj_map::iterator it = m_rm_const2bv.begin(); + it != m_rm_const2bv.end(); + it++) + { + func_decl * var = it->m_key; + SASSERT(m_fpa_util.is_rm(var->get_range())); + expr * val = it->m_value; + SASSERT(m_fpa_util.is_bv2rm(val)); + expr * bvval = to_app(val)->get_arg(0); + expr_ref fv(m); + 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 (obj_map >::iterator it = m_specials.begin(); + it != m_specials.end(); + it++) { + func_decl * f = it->m_key; + app * pn_cnst = it->m_value.first; + app * np_cnst = it->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 (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) { + seen.insert(it->m_value); + + func_decl * f = it->m_key; + 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, it->m_value); + 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 var(m), val(m); + if (mc->eval(it->m_value, val)) + target_model->register_decl(f, val); + } + } + else { + func_interp * fmv = convert_func_interp(mc, f, it->m_value); + if (fmv) target_model->register_decl(f, fmv); + } + } +} + +void bv2fpa_converter::display(std::ostream & out) { + out << "(fpa2bv-model-converter"; + for (obj_map::iterator it = m_const2bv.begin(); + it != m_const2bv.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map::iterator it = m_rm_const2bv.begin(); + it != m_rm_const2bv.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value, m, indent) << ")"; + } + for (obj_map >::iterator it = m_specials.begin(); + it != m_specials.end(); + it++) { + const symbol & n = it->m_key->get_name(); + out << "\n (" << n << " "; + unsigned indent = n.size() + 4; + out << mk_ismt2_pp(it->m_value.first, m, indent) << "; " << + mk_ismt2_pp(it->m_value.second, m, indent) << ")"; + } + out << ")"; +} + +bv2fpa_converter * bv2fpa_converter::translate(ast_translation & translator) { + bv2fpa_converter * res = alloc(bv2fpa_converter, translator.to()); + for (obj_map::iterator it = m_const2bv.begin(); + it != m_const2bv.end(); + it++) + { + func_decl * k = translator(it->m_key); + expr * v = translator(it->m_value); + res->m_const2bv.insert(k, v); + translator.to().inc_ref(k); + translator.to().inc_ref(v); + } + for (obj_map::iterator it = m_rm_const2bv.begin(); + it != m_rm_const2bv.end(); + it++) + { + func_decl * k = translator(it->m_key); + expr * v = translator(it->m_value); + res->m_rm_const2bv.insert(k, v); + translator.to().inc_ref(k); + translator.to().inc_ref(v); + } + for (obj_map::iterator it = m_uf2bvuf.begin(); + it != m_uf2bvuf.end(); + it++) { + func_decl * k = translator(it->m_key); + func_decl * v = translator(it->m_value); + res->m_uf2bvuf.insert(k, v); + translator.to().inc_ref(k); + translator.to().inc_ref(v); + } + for (obj_map >::iterator it = m_specials.begin(); + it != m_specials.end(); + it++) { + func_decl * k = translator(it->m_key); + app * v1 = translator(it->m_value.first); + app * v2 = translator(it->m_value.second); + res->m_specials.insert(k, std::pair(v1, v2)); + translator.to().inc_ref(k); + translator.to().inc_ref(v1); + translator.to().inc_ref(v2); + } + return res; +} + +void bv2fpa_converter::convert(model_core * mc, model_core * float_mdl) { + TRACE("bv2fpa", 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; + }); + +} diff --git a/src/ast/fpa/bv2fpa_converter.h b/src/ast/fpa/bv2fpa_converter.h new file mode 100644 index 000000000..5150056c4 --- /dev/null +++ b/src/ast/fpa/bv2fpa_converter.h @@ -0,0 +1,74 @@ +/*++ + 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"fpa_decl_plugin.h" +#include"bv_decl_plugin.h" +#include"th_rewriter.h" +#include"model_core.h" +#include"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_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, app * bv); + expr_ref convert_bv2rm(expr * eval_v); + expr_ref convert_bv2rm(model_core * mc, app * val); + + void convert(model_core * mc, model_core * float_mdl); + 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, app * 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(0), new_float_fi(0), bv_fd(0), result(m) {} + }; + + array_model convert_array_func_interp(model_core * mc, func_decl * f, func_decl * bv_f); +}; + +#endif \ No newline at end of file diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 97d18bcf2..2c0ba1ce1 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -23,9 +23,9 @@ Notes: #include"th_rewriter.h" #include"fpa2bv_converter.h" +#include"fpa_rewriter.h" #define BVULT(X,Y,R) { expr_ref bvult_eq(m), bvult_not(m); m_simp.mk_eq(X, Y, bvult_eq); m_simp.mk_not(bvult_eq, bvult_not); expr_ref t(m); t = m_bv_util.mk_ule(X,Y); m_simp.mk_and(t, bvult_not, R); } -#define BVSLT(X,Y,R) { expr_ref bvslt_eq(m), bvslt_not(m); m_simp.mk_eq(X, Y, bvslt_eq); m_simp.mk_not(bvslt_eq, bvslt_not); expr_ref t(m); t = m_bv_util.mk_sle(X,Y); m_simp.mk_and(t, bvslt_not, R); } fpa2bv_converter::fpa2bv_converter(ast_manager & m) : m(m), @@ -33,7 +33,6 @@ fpa2bv_converter::fpa2bv_converter(ast_manager & m) : m_util(m), m_bv_util(m), m_arith_util(m), - m_array_util(m), m_dt_util(m), m_seq_util(m), m_mpf_manager(m_util.fm()), @@ -91,19 +90,28 @@ void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { } void fpa2bv_converter::mk_ite(expr * c, expr * t, expr * f, expr_ref & result) { - SASSERT(m_util.is_fp(t) && m_util.is_fp(f)); + if (m_util.is_fp(t) && m_util.is_fp(f)) { + expr *t_sgn, *t_sig, *t_exp; + expr *f_sgn, *f_sig, *f_exp; + split_fp(t, t_sgn, t_exp, t_sig); + split_fp(f, f_sgn, f_exp, f_sig); - expr *t_sgn, *t_sig, *t_exp; - expr *f_sgn, *f_sig, *f_exp; - 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); - 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); + 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) { @@ -125,10 +133,12 @@ void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * ar 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(); @@ -137,12 +147,12 @@ void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * ar mpf_exp_t const & exp = m_util.fm().exp(v); if (m_util.fm().is_nan(v)) - mk_nan(f, result); + mk_nan(s, result); else if (m_util.fm().is_inf(v)) { if (m_util.fm().sgn(v)) - mk_ninf(f, result); + mk_ninf(s, result); else - mk_pinf(f, result); + mk_pinf(s, result); } else { expr_ref bv_sgn(m), bv_sig(m), e(m), biased_exp(m); @@ -155,7 +165,6 @@ void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * ar 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;); - } } @@ -263,7 +272,7 @@ void fpa2bv_converter::mk_function(func_decl * f, unsigned num, expr * const * a rng = f->get_range(); fapp = m.mk_app(f, num, args); if (m_util.is_float(rng)) { - sort_ref bv_rng(m); + sort_ref bv_rng(m); expr_ref new_eq(m); unsigned ebits = m_util.get_ebits(rng); unsigned sbits = m_util.get_sbits(rng); @@ -289,7 +298,7 @@ void fpa2bv_converter::mk_function(func_decl * f, unsigned num, expr * const * a m_extra_assertions.push_back(new_eq); result = flt_app; } - else + else result = fapp; TRACE("fpa2bv", tout << "UF result: " << mk_ismt2_pp(result, m) << std::endl; ); @@ -1012,15 +1021,17 @@ void fpa2bv_converter::mk_rem(sort * s, expr_ref & x, expr_ref & y, expr_ref & r 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); + 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); @@ -1056,31 +1067,117 @@ void fpa2bv_converter::mk_rem(sort * s, expr_ref & x, expr_ref & y, expr_ref & r v5 = pzero; // exp(x) < exp(y) -> x + unsigned ebits = m_util.get_ebits(s); + unsigned sbits = m_util.get_sbits(s); expr * x_sgn, *x_sig, *x_exp; expr * y_sgn, *y_sig, *y_exp; split_fp(x, x_sgn, x_exp, x_sig); split_fp(y, y_sgn, y_exp, y_sig); - BVSLT(x_exp, y_exp, c6); + 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; - // else the actual remainder, r = x - y * n - expr_ref rne(m), nr(m), n(m), yn(m), r(m); - rne = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); - mk_div(s, rne, x, y, nr); - mk_round_to_integral(s, rne, nr, n); - mk_mul(s, rne, y, n, yn); - mk_sub(s, rne, x, yn, r); + 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); - expr_ref r_is_zero(m), x_sgn_ref(x_sgn, m), x_sgn_zero(m); - mk_is_zero(r, r_is_zero); - mk_zero(s, x_sgn_ref, x_sgn_zero); - mk_ite(r_is_zero, x_sgn_zero, r, v7); + 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); - dbg_decouple("fpa2bv_rem_nr", nr); - dbg_decouple("fpa2bv_rem_n", n); - dbg_decouple("fpa2bv_rem_yn", yn); - dbg_decouple("fpa2bv_rem_r", r); - dbg_decouple("fpa2bv_rem_v7", v7); + // 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 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 = 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)); + 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); + 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); + dbg_decouple("fpa2bv_rem_huge_rem", huge_rem); + + expr_ref rndd_sgn(m), rndd_exp(m), rndd_sig(m), rne_bv(m), rndd(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); + round(s, rne_bv, rndd_sgn, rndd_sig, rndd_exp, rndd); + + expr_ref y_half(m), ny_half(m), zero_e(m), two_e(m); + expr_ref y_half_is_zero(m), y_half_is_nz(m); + expr_ref r_ge_y_half(m), r_gt_ny_half(m), r_le_y_half(m), r_lt_ny_half(m); + expr_ref r_ge_zero(m), r_le_zero(m); + expr_ref rounded_sub_y(m), rounded_add_y(m); + mpf zero, two; + m_mpf_manager.set(two, ebits, sbits, 2); + m_mpf_manager.set(zero, ebits, sbits, 0); + mk_numeral(s, two, two_e); + mk_numeral(s, zero, zero_e); + mk_div(s, rne_bv, y, two_e, y_half); + mk_neg(s, y_half, ny_half); + mk_is_zero(y_half, y_half_is_zero); + y_half_is_nz = m.mk_not(y_half_is_zero); + + mk_float_ge(s, rndd, y_half, r_ge_y_half); + mk_float_gt(s, rndd, ny_half, r_gt_ny_half); + mk_float_le(s, rndd, y_half, r_le_y_half); + mk_float_lt(s, rndd, ny_half, r_lt_ny_half); + + mk_sub(s, rne_bv, rndd, y, rounded_sub_y); + mk_add(s, rne_bv, rndd, y, rounded_add_y); + + expr_ref sub_cnd(m), add_cnd(m); + sub_cnd = m.mk_and(y_half_is_nz, + m.mk_or(m.mk_and(y_is_pos, r_ge_y_half), + m.mk_and(y_is_neg, r_le_y_half))); + add_cnd = m.mk_and(y_half_is_nz, + m.mk_or(m.mk_and(y_is_pos, r_lt_ny_half), + m.mk_and(y_is_neg, r_gt_ny_half))); + + mk_ite(add_cnd, rounded_add_y, rndd, v7); + mk_ite(sub_cnd, rounded_sub_y, v7, v7); // And finally, we tie them together. mk_ite(c6, v6, v7, result); @@ -1102,9 +1199,15 @@ void fpa2bv_converter::mk_rem(sort * s, expr_ref & x, expr_ref & y, expr_ref & r void fpa2bv_converter::mk_abs(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); - expr * sgn, * s, * e; - split_fp(args[0], sgn, e, s); - result = m_util.mk_fp(m_bv_util.mk_numeral(0, 1), e, s); + 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 * sgn, *sig, *exp; + 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) { @@ -1178,10 +1281,10 @@ expr_ref fpa2bv_converter::mk_min_max_unspecified(func_decl * f, expr * x, expr // There is no "hardware interpretation" for fp.min/fp.max. std::pair decls(0, 0); - if (!m_specials.find(f, decls)) { + if (!m_min_max_specials.find(f, decls)) { decls.first = m.mk_fresh_const(0, m_bv_util.mk_sort(1)); decls.second = m.mk_fresh_const(0, m_bv_util.mk_sort(1)); - m_specials.insert(f, decls); + m_min_max_specials.insert(f, decls); m.inc_ref(f); m.inc_ref(decls.first); m.inc_ref(decls.second); @@ -1360,11 +1463,17 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, 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); - expr_ref ite_c(m), rm_is_not_to_neg(m); + 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_and(z_is_zero, rm_is_not_to_neg, ite_c); - mk_ite(ite_c, pzero, z, v7); + m_simp.mk_ite(rm_is_to_neg, nzero, pzero, zero_cond); + mk_ite(c71, zero_cond, z, v7); // else comes the fused multiplication. unsigned ebits = m_util.get_ebits(f->get_range()); @@ -1954,11 +2063,15 @@ void fpa2bv_converter::mk_round_to_integral(sort * s, expr_ref & rm, expr_ref & 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); +} - expr * x = args[0], * y = args[1]; - +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;); + 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); @@ -1992,9 +2105,13 @@ void fpa2bv_converter::mk_float_eq(func_decl * f, unsigned num, expr * const * a 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); +} - expr * x = args[0], * y = args[1]; - +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); @@ -2039,11 +2156,15 @@ void fpa2bv_converter::mk_float_lt(func_decl * f, unsigned num, expr * const * a 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); +} - expr * x = args[0], * y = args[1]; - +void fpa2bv_converter::mk_float_gt(sort * s, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref t3(m); - mk_float_le(f, num, args, t3); + 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); @@ -2060,17 +2181,31 @@ void fpa2bv_converter::mk_float_gt(func_decl * f, unsigned num, expr * const * a 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(f, num, args, a); - mk_float_eq(f, num, args, b); + 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(f, num, args, a); - mk_float_eq(f, num, args, b); + mk_float_gt(s, x, y, a); + mk_float_eq(s, x, y, b); m_simp.mk_or(a, b, result); } @@ -2146,6 +2281,7 @@ void fpa2bv_converter::mk_to_fp(func_decl * f, unsigned num, expr * const * args 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), @@ -2278,6 +2414,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r 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); @@ -2640,6 +2777,7 @@ void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * ar expr_ref unspec(m); unspec = mk_to_real_unspecified(ebits, sbits); + 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); @@ -2941,13 +3079,55 @@ void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * m_bv_util.mk_concat(m_bv_util.mk_numeral(-1, ebits), m_bv_util.mk_concat(m_bv_util.mk_numeral(0, sbits - 2), m_bv_util.mk_numeral(1, 1)))); - else - nanv = mk_to_ieee_bv_unspecified(ebits, sbits); + else { + app_ref unspec(m); + unspec = m_util.mk_internal_to_ieee_bv_unspecified(ebits, sbits); + mk_to_ieee_bv_unspecified(unspec->get_decl(), 0, 0, nanv); + } expr_ref sgn_e_s(m); sgn_e_s = m_bv_util.mk_concat(m_bv_util.mk_concat(sgn, e), s); m_simp.mk_ite(x_is_nan, nanv, 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 == 0); + unsigned ebits = f->get_parameter(0).get_int(); + unsigned sbits = f->get_parameter(1).get_int(); + + if (m_hi_fp_unspecified) { + result = m_bv_util.mk_concat(m_bv_util.mk_concat( + m_bv_util.mk_numeral(0, 1), + m_bv_util.mk_numeral(-1, ebits)), + m_bv_util.mk_numeral(1, sbits-1)); + } + else { + func_decl * fd; + if (m_uf2bvuf.find(f, fd)) + result = m.mk_const(fd); + else { + fd = m.mk_fresh_func_decl(0, 0, 0, f->get_range()); + m_uf2bvuf.insert(f, fd); + m.inc_ref(f); + m.inc_ref(fd); + result = m.mk_const(fd); + + 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_numeral(-1, ebits)); + m_extra_assertions.push_back(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(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)); } @@ -2980,11 +3160,14 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args // NaN, Inf, or negative (except -0) -> unspecified expr_ref c1(m), v1(m); - if (!is_signed) + if (!is_signed) { c1 = m.mk_or(x_is_nan, x_is_inf, m.mk_and(x_is_neg, m.mk_not(x_is_nzero))); - else + v1 = mk_to_ubv_unspecified(ebits, sbits, bv_sz); + } + else { c1 = m.mk_or(x_is_nan, x_is_inf); - v1 = mk_to_ubv_unspecified(ebits, sbits, bv_sz); + v1 = mk_to_sbv_unspecified(ebits, sbits, bv_sz); + } dbg_decouple("fpa2bv_to_bv_c1", c1); // +-Zero -> 0 @@ -3094,7 +3277,8 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args dbg_decouple("fpa2bv_to_bv_rnd", rnd); expr_ref unspec(m); - unspec = mk_to_ubv_unspecified(ebits, sbits, bv_sz); + unspec = is_signed ? mk_to_sbv_unspecified(ebits, sbits, bv_sz) : + mk_to_ubv_unspecified(ebits, sbits, bv_sz); result = m.mk_ite(rnd_has_overflown, unspec, rnd); result = m.mk_ite(c_in_limits, result, unspec); result = m.mk_ite(c2, v2, result); @@ -3115,101 +3299,85 @@ void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * arg mk_to_bv(f, num, args, true, result); } -expr_ref fpa2bv_converter::mk_to_ubv_unspecified(unsigned ebits, unsigned sbits, unsigned width) { - expr_ref result(m); +void fpa2bv_converter::mk_to_ubv_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 0); + unsigned width = m_bv_util.get_bv_size(f->get_range()); + if (m_hi_fp_unspecified) result = m_bv_util.mk_numeral(0, width); else { - app_ref unspec(m); - unspec = m_util.mk_internal_to_ubv_unspecified(ebits, sbits, width); - func_decl * unspec_fd = unspec->get_decl(); func_decl * fd; - if (!m_uf2bvuf.find(unspec_fd, fd)) { - app_ref bvc(m); - bvc = m.mk_fresh_const(0, unspec_fd->get_range()); - fd = bvc->get_decl(); - m_uf2bvuf.insert(unspec_fd, fd); - m.inc_ref(unspec_fd); + if (!m_uf2bvuf.find(f, fd)) { + fd = m.mk_fresh_func_decl(0, 0, 0, f->get_range()); + m_uf2bvuf.insert(f, fd); + m.inc_ref(f); m.inc_ref(fd); } result = m.mk_const(fd); } - return result; + + TRACE("fpa2bv_to_ubv_unspecified", tout << "result=" << mk_ismt2_pp(result, m) << std::endl;); + SASSERT(is_well_sorted(m, result)); +} + +expr_ref fpa2bv_converter::mk_to_ubv_unspecified(unsigned ebits, unsigned sbits, unsigned width) { + expr_ref res(m); + app_ref u(m); + u = m_util.mk_internal_to_ubv_unspecified(ebits, sbits, width); + mk_to_sbv_unspecified(u->get_decl(), 0, 0, res); + return res; +} + +void fpa2bv_converter::mk_to_sbv_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { + SASSERT(num == 0); + unsigned width = m_bv_util.get_bv_size(f->get_range()); + + if (m_hi_fp_unspecified) + result = m_bv_util.mk_numeral(0, width); + else { + func_decl * fd; + if (!m_uf2bvuf.find(f, fd)) { + fd = m.mk_fresh_func_decl(0, 0, 0, f->get_range()); + m_uf2bvuf.insert(f, fd); + m.inc_ref(f); + m.inc_ref(fd); + } + result = m.mk_const(fd); + } + + TRACE("fpa2bv_to_sbv_unspecified", tout << "result=" << mk_ismt2_pp(result, m) << std::endl;); + SASSERT(is_well_sorted(m, result)); } expr_ref fpa2bv_converter::mk_to_sbv_unspecified(unsigned ebits, unsigned sbits, unsigned width) { - expr_ref result(m); - if (m_hi_fp_unspecified) - result = m_bv_util.mk_numeral(0, width); - else { - app_ref unspec(m); - unspec = m_util.mk_internal_to_sbv_unspecified(ebits, sbits, width); - func_decl * unspec_fd = unspec->get_decl(); - func_decl * fd; - if (!m_uf2bvuf.find(unspec_fd, fd)) { - app_ref bvc(m); - bvc = m.mk_fresh_const(0, unspec_fd->get_range()); - fd = bvc->get_decl(); - m_uf2bvuf.insert(unspec_fd, fd); - m.inc_ref(unspec_fd); - m.inc_ref(fd); - } - result = m.mk_const(fd); - } - return result; + expr_ref res(m); + app_ref u(m); + u = m_util.mk_internal_to_sbv_unspecified(ebits, sbits, width); + mk_to_sbv_unspecified(u->get_decl(), 0, 0, res); + return res; } -expr_ref fpa2bv_converter::mk_to_real_unspecified(unsigned ebits, unsigned sbits) { - expr_ref result(m); +void fpa2bv_converter::mk_to_real_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { if (m_hi_fp_unspecified) result = m_arith_util.mk_numeral(rational(0), false); else { - app_ref unspec(m); - unspec = m_util.mk_internal_to_real_unspecified(ebits, sbits); - func_decl * unspec_fd = unspec->get_decl(); func_decl * fd; - if (!m_uf2bvuf.find(unspec_fd, fd)) { - app_ref bvc(m); - bvc = m.mk_fresh_const(0, unspec_fd->get_range()); - fd = bvc->get_decl(); - m_uf2bvuf.insert(unspec_fd, fd); - m.inc_ref(unspec_fd); + if (!m_uf2bvuf.find(f, fd)) { + fd = m.mk_fresh_func_decl(0, 0, 0, f->get_range()); + m_uf2bvuf.insert(f, fd); + m.inc_ref(f); m.inc_ref(fd); } result = m.mk_const(fd); - result = unspec; } - return result; } -expr_ref fpa2bv_converter::mk_to_ieee_bv_unspecified(unsigned ebits, unsigned sbits) { - expr_ref result(m); - - app_ref unspec(m); - unspec = m_util.mk_internal_to_ieee_bv_unspecified(ebits, sbits); - func_decl * unspec_fd = unspec->get_decl(); - func_decl * fd; - if (!m_uf2bvuf.find(unspec_fd, fd)) { - app_ref bvc(m); - bvc = m.mk_fresh_const(0, unspec_fd->get_range()); - fd = bvc->get_decl(); - m_uf2bvuf.insert(unspec_fd, fd); - m.inc_ref(unspec_fd); - m.inc_ref(fd); - } - result = m.mk_const(fd); - - app_ref mask(m), extra(m), result_and_mask(m); - mask = m_bv_util.mk_concat(m_bv_util.mk_numeral(0, 1), - m_bv_util.mk_concat(m_bv_util.mk_numeral(-1, ebits), - m_bv_util.mk_concat(m_bv_util.mk_numeral(0, sbits - 2), - m_bv_util.mk_numeral(1, 1)))); - expr * args[2] = { result, mask }; - result_and_mask = m.mk_app(m_bv_util.get_fid(), OP_BAND, 2, args); - extra = m.mk_eq(result_and_mask, mask); - m_extra_assertions.push_back(extra); - - return result; +expr_ref fpa2bv_converter::mk_to_real_unspecified(unsigned ebits, unsigned sbits) { + expr_ref res(m); + app_ref u(m); + u = m_util.mk_internal_to_real_unspecified(ebits, sbits); + mk_to_real_unspecified(u->get_decl(), 0, 0, res); + return res; } void fpa2bv_converter::mk_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -3518,11 +3686,11 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref // 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 sh(m), is_sh_zero(m), sl(m), sbits_s(m), short_shift(m); - zero_s = m_bv_util.mk_numeral(0, sbits-1); + 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_s, sh, is_sh_zero); + 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); @@ -3575,7 +3743,7 @@ void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { // CMW: This works only for quantifier-free formulas. if (m_util.is_fp(e)) { expr_ref new_bv(m); - expr *e_sgn, *e_sig, *e_exp; + expr *e_sgn, *e_sig, *e_exp; 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; @@ -3979,12 +4147,13 @@ void fpa2bv_converter::reset(void) { dec_ref_map_key_values(m, m_const2bv); dec_ref_map_key_values(m, m_rm_const2bv); dec_ref_map_key_values(m, m_uf2bvuf); - for (obj_map >::iterator it = m_specials.begin(); - it != m_specials.end(); + for (obj_map >::iterator it = m_min_max_specials.begin(); + it != m_min_max_specials.end(); it++) { m.dec_ref(it->m_key); m.dec_ref(it->m_value.first); m.dec_ref(it->m_value.second); } + m_min_max_specials.reset(); m_extra_assertions.reset(); } diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index d056a3642..34417b7fc 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -32,13 +32,17 @@ Notes: #include"basic_simplifier_plugin.h" class fpa2bv_converter { +public: + typedef obj_map > special_t; + typedef obj_map const2bv_t; + typedef obj_map uf2bvuf_t; + protected: ast_manager & m; basic_simplifier_plugin m_simp; fpa_util m_util; bv_util m_bv_util; arith_util m_arith_util; - array_util m_array_util; datatype_util m_dt_util; seq_util m_seq_util; mpf_manager & m_mpf_manager; @@ -46,13 +50,13 @@ protected: fpa_decl_plugin * m_plugin; bool m_hi_fp_unspecified; - obj_map m_const2bv; - obj_map m_rm_const2bv; - obj_map m_uf2bvuf; - - obj_map > m_specials; + const2bv_t m_const2bv; + const2bv_t m_rm_const2bv; + uf2bvuf_t m_uf2bvuf; + special_t m_min_max_specials; friend class fpa2bv_model_converter; + friend class bv2fpa_converter; public: fpa2bv_converter(ast_manager & m); @@ -67,9 +71,9 @@ public: 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; @@ -79,6 +83,7 @@ public: 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_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result); @@ -100,12 +105,18 @@ public: 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); @@ -122,12 +133,16 @@ public: 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_ubv_unspecified(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_sbv_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; } @@ -138,18 +153,15 @@ public: void mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_max_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - expr_ref mk_to_ubv_unspecified(unsigned ebits, unsigned sbits, unsigned width); - expr_ref mk_to_sbv_unspecified(unsigned ebits, unsigned sbits, unsigned width); - expr_ref mk_to_real_unspecified(unsigned ebits, unsigned sbits); - expr_ref mk_to_ieee_bv_unspecified(unsigned ebits, unsigned sbits); - void reset(void); void dbg_decouple(const char * prefix, expr_ref & e); expr_ref_vector m_extra_assertions; - bool is_special(func_decl * f) { return m_specials.contains(f); } - bool is_uf2bvuf(func_decl * f) { return m_uf2bvuf.contains(f); } + special_t const & get_min_max_specials() const { return m_min_max_specials; }; + 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); @@ -214,6 +226,10 @@ private: 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); + + expr_ref mk_to_ubv_unspecified(unsigned ebits, unsigned sbits, unsigned width); + expr_ref mk_to_sbv_unspecified(unsigned ebits, unsigned sbits, unsigned width); + expr_ref mk_to_real_unspecified(unsigned ebits, unsigned sbits); }; #endif diff --git a/src/ast/fpa/fpa2bv_rewriter.cpp b/src/ast/fpa/fpa2bv_rewriter.cpp index 0845393f4..725c0b043 100644 --- a/src/ast/fpa/fpa2bv_rewriter.cpp +++ b/src/ast/fpa/fpa2bv_rewriter.cpp @@ -86,10 +86,10 @@ br_status fpa2bv_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr * co return BR_DONE; } return BR_FAILED; - } + } else if (m().is_ite(f)) { SASSERT(num == 3); - if (m_conv.is_float(args[1])) { + 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; } @@ -103,7 +103,7 @@ br_status fpa2bv_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr * co } return BR_FAILED; } - + if (m_conv.is_float_family(f)) { switch (f->get_decl_kind()) { case OP_FPA_RM_NEAREST_TIES_TO_AWAY: @@ -143,9 +143,13 @@ br_status fpa2bv_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr * co 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_INTERNAL_TO_UBV_UNSPECIFIED: m_conv.mk_to_ubv_unspecified(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_INTERNAL_TO_SBV_UNSPECIFIED: m_conv.mk_to_sbv_unspecified(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_INTERNAL_TO_REAL_UNSPECIFIED: m_conv.mk_to_real_unspecified(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_INTERNAL_TO_IEEE_BV_UNSPECIFIED: m_conv.mk_to_ieee_bv_unspecified(f, num, args, result); return BR_DONE; case OP_FPA_MIN: m_conv.mk_min(f, num, args, result); return BR_REWRITE_FULL; case OP_FPA_MAX: m_conv.mk_max(f, num, args, result); return BR_REWRITE_FULL; @@ -157,19 +161,15 @@ br_status fpa2bv_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr * co case OP_FPA_INTERNAL_BVWRAP: case OP_FPA_INTERNAL_BV2RM: - - case OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED: - case OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED: - case OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED: - case OP_FPA_INTERNAL_TO_IEEE_BV_UNSPECIFIED: 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 + else { SASSERT(!m_conv.is_float_family(f)); if (m_conv.fu().contains_floats(f)) { diff --git a/src/ast/fpa_decl_plugin.cpp b/src/ast/fpa_decl_plugin.cpp index 6c8b7ac6f..d8bf70e80 100644 --- a/src/ast/fpa_decl_plugin.cpp +++ b/src/ast/fpa_decl_plugin.cpp @@ -665,14 +665,14 @@ func_decl * fpa_decl_plugin::mk_to_real(decl_kind k, unsigned num_parameters, pa 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 to_ieee_bv"); + 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("to_ieee_bv"); + symbol name("fp.to_ieee_bv"); return m_manager->mk_func_decl(name, 1, domain, bv_srt, func_decl_info(m_family_id, k)); } @@ -758,15 +758,15 @@ func_decl * fpa_decl_plugin::mk_internal_to_ieee_bv_unspecified( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (arity != 0) - m_manager->raise_exception("invalid number of arguments to to_ieee_bv_unspecified; expecting none"); + m_manager->raise_exception("invalid number of arguments to fp.to_ieee_bv_unspecified; expecting none"); if (num_parameters != 2) - m_manager->raise_exception("invalid number of parameters to to_ieee_bv_unspecified; expecting 2"); + m_manager->raise_exception("invalid number of parameters to fp.to_ieee_bv_unspecified; expecting 2"); if (!parameters[0].is_int() || !parameters[1].is_int()) - m_manager->raise_exception("invalid parameters type provided to to_ieee_bv_unspecified; expecting 2 integers"); + m_manager->raise_exception("invalid parameters type provided to fp.to_ieee_bv_unspecified; expecting 2 integers"); parameter width_p[1] = { parameter(parameters[0].get_int() + parameters[1].get_int()) }; sort * bv_srt = m_bv_plugin->mk_sort(m_bv_fid, 1, width_p); - return m_manager->mk_func_decl(symbol("to_ieee_bv_unspecified"), 0, domain, bv_srt, func_decl_info(m_family_id, k, num_parameters, parameters)); + return m_manager->mk_func_decl(symbol("fp.to_ieee_bv_unspecified"), 0, domain, bv_srt, func_decl_info(m_family_id, k, num_parameters, parameters)); } @@ -915,7 +915,8 @@ void fpa_decl_plugin::get_op_names(svector & op_names, symbol cons 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("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) { diff --git a/src/ast/fpa_decl_plugin.h b/src/ast/fpa_decl_plugin.h index ee7325014..cf341a07b 100644 --- a/src/ast/fpa_decl_plugin.h +++ b/src/ast/fpa_decl_plugin.h @@ -259,7 +259,7 @@ public: 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 * e) const { return is_app_of(e, m_fid, OP_FPA_FP); } + bool is_fp(expr * e) const { return is_app_of(e, m_fid, OP_FPA_FP); } unsigned get_ebits(sort * s) const; unsigned get_sbits(sort * s) const; @@ -288,19 +288,24 @@ public: 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) { + 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); + 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); @@ -377,28 +382,28 @@ public: app * mk_internal_to_ieee_bv_unspecified(unsigned ebits, unsigned sbits); app * mk_internal_to_real_unspecified(unsigned ebits, unsigned sbits); - bool is_bvwrap(expr * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_BVWRAP); } - bool is_bvwrap(func_decl * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_BVWRAP; } - bool is_bv2rm(expr * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_BV2RM); } - bool is_bv2rm(func_decl * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_BV2RM; } + bool is_bvwrap(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_BVWRAP); } + bool is_bvwrap(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_BVWRAP; } + bool is_bv2rm(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_BV2RM); } + bool is_bv2rm(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_BV2RM; } - bool is_min_interpreted(expr * e) { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_MIN_I); } - bool is_min_unspecified(expr * e) { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_MIN_UNSPECIFIED); } - bool is_max_interpreted(expr * e) { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_MAX_I); } - bool is_max_unspecified(expr * e) { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_MAX_UNSPECIFIED); } - bool is_to_ubv_unspecified(expr * e) { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED); } - bool is_to_sbv_unspecified(expr * e) { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED); } - bool is_to_ieee_bv_unspecified(expr * e) { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_TO_IEEE_BV_UNSPECIFIED); } - bool is_to_real_unspecified(expr * e) { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED); } + bool is_min_interpreted(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_MIN_I); } + bool is_min_unspecified(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_MIN_UNSPECIFIED); } + bool is_max_interpreted(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_MAX_I); } + bool is_max_unspecified(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_MAX_UNSPECIFIED); } + bool is_to_ubv_unspecified(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED); } + bool is_to_sbv_unspecified(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED); } + bool is_to_ieee_bv_unspecified(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_TO_IEEE_BV_UNSPECIFIED); } + bool is_to_real_unspecified(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED); } - bool is_min_interpreted(func_decl * f) { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_MIN_I; } - bool is_min_unspecified(func_decl * f) { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_MIN_UNSPECIFIED; } - bool is_max_interpreted(func_decl * f) { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_MAX_I; } - bool is_max_unspecified(func_decl * f) { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_MAX_UNSPECIFIED; } - bool is_to_ubv_unspecified(func_decl * f) { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED; } - bool is_to_sbv_unspecified(func_decl * f) { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED; } - bool is_to_ieee_bv_unspecified(func_decl * f) { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_TO_IEEE_BV_UNSPECIFIED; } - bool is_to_real_unspecified(func_decl * f) { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED; } + bool is_min_interpreted(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_MIN_I; } + bool is_min_unspecified(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_MIN_UNSPECIFIED; } + bool is_max_interpreted(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_MAX_I; } + bool is_max_unspecified(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_MAX_UNSPECIFIED; } + bool is_to_ubv_unspecified(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_TO_UBV_UNSPECIFIED; } + bool is_to_sbv_unspecified(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_TO_SBV_UNSPECIFIED; } + bool is_to_ieee_bv_unspecified(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_TO_IEEE_BV_UNSPECIFIED; } + bool is_to_real_unspecified(func_decl const * f) const { return f->get_family_id() == get_family_id() && f->get_decl_kind() == OP_FPA_INTERNAL_TO_REAL_UNSPECIFIED; } bool contains_floats(ast * a); }; diff --git a/src/ast/macros/macro_finder.cpp b/src/ast/macros/macro_finder.cpp index 35cb5891f..ee211c44f 100644 --- a/src/ast/macros/macro_finder.cpp +++ b/src/ast/macros/macro_finder.cpp @@ -22,7 +22,7 @@ Revision History: #include"ast_pp.h" #include"ast_ll_pp.h" -bool macro_finder::is_macro(expr * n, app * & head, expr * & def) { +bool macro_finder::is_macro(expr * n, app_ref & head, expr_ref & def) { if (!is_quantifier(n) || !to_quantifier(n)->is_forall()) return false; TRACE("macro_finder", tout << "processing: " << mk_pp(n, m_manager) << "\n";); @@ -171,23 +171,20 @@ bool macro_finder::expand_macros(unsigned num, expr * const * exprs, proof * con for (unsigned i = 0; i < num; i++) { expr * n = exprs[i]; proof * pr = m_manager.proofs_enabled() ? prs[i] : 0; - expr_ref new_n(m_manager); + expr_ref new_n(m_manager), def(m_manager); proof_ref new_pr(m_manager); m_macro_manager.expand_macros(n, pr, new_n, new_pr); - app * head = 0; - expr * def = 0; - app * t = 0; + app_ref head(m_manager), t(m_manager); 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" << mk_pp(new_n, m_manager) << "\n";); + 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_exprs, new_prs)) { - TRACE("macro_finder_found", tout << "found new arith macro:\n" << mk_pp(new_n, m_manager) << "\n";); + 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" << mk_pp(head, m_manager) << "\n" << mk_pp(t, m_manager) << "\n" << - mk_pp(def, m_manager) << "\n";); + TRACE("macro_finder_found", tout << "found new pseudo macro:\n" << head << "\n" << t << "\n" << def << "\n";); pseudo_predicate_macro2macro(m_manager, head, t, def, to_quantifier(new_n), new_pr, new_exprs, new_prs); found_new_macro = true; } diff --git a/src/ast/macros/macro_finder.h b/src/ast/macros/macro_finder.h index 541ec80b7..04ec11939 100644 --- a/src/ast/macros/macro_finder.h +++ b/src/ast/macros/macro_finder.h @@ -41,7 +41,7 @@ class macro_finder { bool expand_macros(unsigned num, expr * const * exprs, proof * const * prs, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); bool is_arith_macro(expr * n, proof * pr, expr_ref_vector & new_exprs, proof_ref_vector & new_prs); - bool is_macro(expr * n, app * & head, expr * & def); + bool is_macro(expr * n, app_ref & head, expr_ref & def); bool is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t); bool is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def); diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp index 75b4e55f4..b17e1ce28 100644 --- a/src/ast/macros/macro_manager.cpp +++ b/src/ast/macros/macro_manager.cpp @@ -106,7 +106,7 @@ bool macro_manager::insert(func_decl * f, quantifier * m, proof * pr) { if (!m_deps.insert(f, s)) { return false; } - + // add macro m_decl2macro.insert(f, m); m_decls.push_back(f); @@ -117,8 +117,8 @@ bool macro_manager::insert(func_decl * f, quantifier * m, proof * pr) { } 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. + + // Nothing's forbidden anymore; if something's bad, we detected it earlier. // mark_forbidden(m->get_expr()); return true; } @@ -144,7 +144,7 @@ namespace macro_manager_ns { \brief Mark all func_decls used in exprs as forbidden. */ void macro_manager::mark_forbidden(unsigned n, expr * const * exprs) { - expr_mark visited; + 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]); @@ -187,9 +187,9 @@ func_decl * macro_manager::get_macro_interpretation(unsigned i, expr_ref & inter app * head; expr * def; get_head_def(q, f, head, def); - TRACE("macro_bug", + TRACE("macro_bug", tout << f->get_name() << "\n" << mk_pp(head, m_manager) << "\n" << mk_pp(q, m_manager) << "\n";); - m_util.mk_macro_interpretation(head, def, interp); + m_util.mk_macro_interpretation(head, q->get_num_decls(), def, interp); return f; } @@ -237,7 +237,7 @@ void macro_manager::macro_expander::reduce1_quantifier(quantifier * q) { erase_patterns = true; } for (unsigned i = 0; !erase_patterns && i < q->get_num_no_patterns(); i++) { - if (q->get_no_pattern(i) != new_q->get_no_pattern(i)) + if (q->get_no_pattern(i) != new_q->get_no_pattern(i)) erase_patterns = true; } } @@ -254,7 +254,7 @@ bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref return false; app * n = to_app(_n); quantifier * q = 0; - func_decl * d = n->get_decl(); + func_decl * d = n->get_decl(); TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m) << "\nd:\n" << d->get_name() << "\n";); if (m_macro_manager.m_decl2macro.find(d, q)) { TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m) << "\n";); @@ -308,7 +308,7 @@ void macro_manager::expand_macros(expr * n, proof * pr, expr_ref & r, proof_ref if (r.get() == old_n.get()) return; old_n = r; - old_pr = new_pr; + old_pr = new_pr; } } else { diff --git a/src/ast/macros/macro_util.cpp b/src/ast/macros/macro_util.cpp index de55de632..99732871c 100644 --- a/src/ast/macros/macro_util.cpp +++ b/src/ast/macros/macro_util.cpp @@ -34,7 +34,7 @@ macro_util::macro_util(ast_manager & m, simplifier & s): m_simplifier(s), m_arith_simp(0), m_bv_simp(0), - m_basic_simp(0), + m_basic_simp(0), m_forbidden_set(0), m_curr_clause(0) { } @@ -64,23 +64,23 @@ basic_simplifier_plugin * macro_util::get_basic_simp() const { } bool macro_util::is_bv(expr * n) const { - return get_bv_simp()->is_bv(n); + return get_bv_simp()->is_bv(n); } bool macro_util::is_bv_sort(sort * s) const { - return get_bv_simp()->is_bv_sort(s); + return get_bv_simp()->is_bv_sort(s); } bool macro_util::is_add(expr * n) const { - return get_arith_simp()->is_add(n) || get_bv_simp()->is_add(n); + return get_arith_simp()->is_add(n) || get_bv_simp()->is_add(n); } bool macro_util::is_times_minus_one(expr * n, expr * & arg) const { return get_arith_simp()->is_times_minus_one(n, arg) || get_bv_simp()->is_times_minus_one(n, arg); } -bool macro_util::is_le(expr * n) const { - return get_arith_simp()->is_le(n) || get_bv_simp()->is_le(n); +bool macro_util::is_le(expr * n) const { + return get_arith_simp()->is_le(n) || get_bv_simp()->is_le(n); } bool macro_util::is_le_ge(expr * n) const { @@ -130,7 +130,7 @@ void macro_util::mk_add(unsigned num_args, expr * const * args, sort * s, expr_r /** \brief Return true if \c n is an application of the form - + (f x_{k_1}, ..., x_{k_n}) where f is uninterpreted @@ -147,7 +147,7 @@ bool macro_util::is_macro_head(expr * n, unsigned num_decls) const { 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)) + if (!is_var(c)) return false; unsigned idx = to_var(c)->get_idx(); if (idx >= num_decls || var2pos[idx] != -1) @@ -161,12 +161,12 @@ bool macro_util::is_macro_head(expr * n, unsigned num_decls) const { /** \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) + (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 @@ -176,11 +176,12 @@ bool macro_util::is_macro_head(expr * n, unsigned num_decls) const { def will contain t */ -bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const { +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) || m_manager.is_iff(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)) { + 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; @@ -189,14 +190,15 @@ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app * & head 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 @@ -206,11 +208,12 @@ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app * & head def will contain t */ -bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const { +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) || m_manager.is_iff(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)) { + 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; @@ -253,7 +256,7 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex if (!as->is_numeral(rhs)) return false; - + inv = false; ptr_buffer args; expr * h = 0; @@ -270,15 +273,15 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex for (unsigned i = 0; i < lhs_num_args; i++) { expr * arg = lhs_args[i]; expr * neg_arg; - if (h == 0 && - is_macro_head(arg, num_decls) && - !is_forbidden(to_app(arg)->get_decl()) && + if (h == 0 && + 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 == 0 && as->is_times_minus_one(arg, neg_arg) && - is_macro_head(neg_arg, num_decls) && - !is_forbidden(to_app(neg_arg)->get_decl()) && + 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; @@ -302,8 +305,8 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex /** \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 * & head, app * & t) { - if (!m_manager.is_eq(n)) +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); @@ -330,9 +333,9 @@ bool macro_util::is_pseudo_head(expr * n, unsigned num_decls, app * & head, app /** \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. + where t is a ground term, (f X) is the head. */ -bool macro_util::is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def) { +bool macro_util::is_pseudo_predicate_macro(expr * n, app_ref & head, app_ref & t, expr_ref & def) { if (!is_quantifier(n) || !to_quantifier(n)->is_forall()) return false; TRACE("macro_util", tout << "processing: " << mk_pp(n, m_manager) << "\n";); @@ -342,14 +345,14 @@ bool macro_util::is_pseudo_predicate_macro(expr * n, app * & head, app * & t, ex return false; expr * lhs = to_app(body)->get_arg(0); expr * rhs = to_app(body)->get_arg(1); - if (is_pseudo_head(lhs, num_decls, head, t) && - !is_forbidden(head->get_decl()) && + 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()) && + if (is_pseudo_head(rhs, num_decls, head, t) && + !is_forbidden(head->get_decl()) && !occurs(head->get_decl(), lhs)) { def = lhs; return true; @@ -360,7 +363,7 @@ bool macro_util::is_pseudo_predicate_macro(expr * n, app * & head, app * & t, ex /** \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. + 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: @@ -402,7 +405,7 @@ bool macro_util::is_quasi_macro_head(expr * n, unsigned num_decls) const { \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 { +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); @@ -428,6 +431,7 @@ void macro_util::quasi_macro_head_to_macro_head(app * qhead, unsigned num_decls, } get_basic_simp()->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; } /** @@ -437,10 +441,10 @@ void macro_util::quasi_macro_head_to_macro_head(app * qhead, unsigned num_decls, See normalize_expr */ -void macro_util::mk_macro_interpretation(app * head, expr * def, expr_ref & interp) const { +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, def, interp); + normalize_expr(head, num_decls, def, interp); } /** @@ -453,40 +457,36 @@ void macro_util::mk_macro_interpretation(app * head, expr * def, expr_ref & inte 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, expr * t, expr_ref & norm_t) const { - expr_ref_buffer var_mapping(m_manager); +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(); - unsigned max = num_args; - for (unsigned i = 0; i < num_args; i++) { - var * v = to_var(head->get_arg(i)); - if (v->get_idx() >= max) - max = v->get_idx() + 1; - } - TRACE("normalize_expr_bug", + 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)); - if (v->get_idx() != i) { + var * v = to_var(head->get_arg(i)); + unsigned vi = v->get_idx(); + SASSERT(vi < num_decls); + if (vi != i) { changed = true; - var * new_var = m_manager.mk_var(i, v->get_sort()); - CTRACE("normalize_expr_bug", v->get_idx() >= num_args, tout << mk_pp(v, m_manager) << ", num_args: " << num_args << "\n";); - SASSERT(v->get_idx() < max); - var_mapping.setx(max - v->get_idx() - 1, new_var); - } - else { - var_mapping.setx(max - i - 1, v); + 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); - TRACE("macro_util_bug", + 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) << "\nsubstitituion:\n"; + tout << "applying substitution to:\n" << mk_ll_pp(t, m_manager) << "\nsubstitution:\n"; for (unsigned i = 0; i < var_mapping.size(); i++) { - tout << "#" << i << " -> " << mk_pp(var_mapping[i], m_manager) << "\n"; + if (var_mapping[i] != 0) + tout << "#" << i << " -> " << mk_ll_pp(var_mapping[i], m_manager); }); subst(t, var_mapping.size(), var_mapping.c_ptr(), norm_t); } @@ -497,8 +497,8 @@ void macro_util::normalize_expr(app * head, expr * t, expr_ref & norm_t) const { // ----------------------------- // -// "Hint" support -// See comment at is_hint_atom +// "Hint" support +// See comment at is_hint_atom // for a definition of what a hint is. // // ----------------------------- @@ -510,7 +510,7 @@ bool is_hint_head(expr * n, ptr_buffer & vars) { 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); + expr * arg = to_app(n)->get_arg(i); if (is_var(arg)) vars.push_back(to_var(arg)); } @@ -546,7 +546,7 @@ bool vars_of_is_subset(expr * n, ptr_buffer const & vars) { } } else { - SASSERT(is_quantifier(curr)); + SASSERT(is_quantifier(curr)); return false; // do no support nested quantifier... being conservative. } } @@ -554,7 +554,7 @@ bool vars_of_is_subset(expr * n, ptr_buffer const & vars) { } /** - \brief (= lhs rhs) is a hint atom if + \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. */ @@ -565,7 +565,7 @@ bool is_hint_atom(expr * lhs, expr * rhs) { 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) { +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; @@ -587,21 +587,22 @@ void hint_to_macro_head(ast_manager & m, app * head, unsigned num_decls, app_ref 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(). + 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_hint", tout << "is_poly_hint n:\n" << mk_pp(n, m_manager) << "\nhead:\n" << mk_pp(head, m_manager) << "\nexception:\n"; + 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_hint", tout << "failed because head is not hint head\n";); + TRACE("macro_util", tout << "failed because head is not hint head\n";); return false; } func_decl * f = head->get_decl(); @@ -618,13 +619,13 @@ bool macro_util::is_poly_hint(expr * n, app * head, expr * exception) { 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_hint", tout << "failed because of:\n" << mk_pp(arg, m_manager) << "\n";); + TRACE("macro_util", tout << "failed because of:\n" << mk_pp(arg, m_manager) << "\n";); return false; } } - TRACE("macro_util_hint", tout << "succeeded\n";); + TRACE("macro_util", tout << "succeeded\n";); return true; - + } // ----------------------------- @@ -663,19 +664,19 @@ void macro_util::macro_candidates::insert(func_decl * f, expr * def, expr * cond // // ----------------------------- -void macro_util::insert_macro(app * head, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, macro_candidates & r) { +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, def, norm_def); + normalize_expr(head, num_decls, def, norm_def); if (cond != 0) - normalize_expr(head, cond, norm_cond); + 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, +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); @@ -690,11 +691,14 @@ void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def, } 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, def, new_cond, ineq, satisfy_atom, hint, r); + insert_macro(new_head, num_decls, def, new_cond, ineq, satisfy_atom, hint, r); } else { - insert_macro(head, def, cond, ineq, satisfy_atom, hint, r); + insert_macro(head, num_decls, def, cond, ineq, satisfy_atom, hint, r); } } @@ -777,8 +781,8 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a if (!is_app(arg)) continue; func_decl * f = to_app(arg)->get_decl(); - - bool _is_arith_macro = + + bool _is_arith_macro = is_quasi_macro_head(arg, num_decls) && !is_forbidden(f) && !poly_contains_head(lhs, f, arg) && @@ -799,14 +803,14 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a } else if (is_times_minus_one(arg, neg_arg) && is_app(neg_arg)) { f = to_app(neg_arg)->get_decl(); - bool _is_arith_macro = + 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); @@ -823,7 +827,7 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a } void macro_util::collect_arith_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) { - TRACE("macro_util_hint", tout << "collect_arith_macro_candidates:\n" << mk_pp(atom, m_manager) << "\n";); + 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); @@ -836,45 +840,47 @@ void macro_util::collect_arith_macro_candidates(expr * atom, unsigned num_decls, /** \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 } + + 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) { - if (m_manager.is_eq(atom) || m_manager.is_iff(atom)) { - expr * lhs = to_app(atom)->get_arg(0); - expr * rhs = to_app(atom)->get_arg(1); - if (is_quasi_macro_head(lhs, num_decls) && - !is_forbidden(to_app(lhs)->get_decl()) && + 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); @@ -885,9 +891,8 @@ void macro_util::collect_macro_candidates_core(expr * atom, unsigned num_decls, insert_quasi_macro(to_app(lhs), num_decls, rhs, 0, false, true, true, r); } - - if (is_quasi_macro_head(rhs, num_decls) && - !is_forbidden(to_app(rhs)->get_decl()) && + 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); diff --git a/src/ast/macros/macro_util.h b/src/ast/macros/macro_util.h index 2a1581162..033f6ecb4 100644 --- a/src/ast/macros/macro_util.h +++ b/src/ast/macros/macro_util.h @@ -74,9 +74,9 @@ private: void collect_arith_macros(expr * n, unsigned num_decls, unsigned max_macros, bool allow_cond_macros, macro_candidates & r); - void normalize_expr(app * head, expr * t, expr_ref & norm_t) const; - void insert_macro(app * head, 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, + 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. @@ -102,10 +102,10 @@ public: basic_simplifier_plugin * get_basic_simp() const; bool is_macro_head(expr * n, unsigned num_decls) const; - bool is_left_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const; - bool is_right_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const; - bool is_simple_macro(expr * n, unsigned num_decls, app * & head, expr * & def) const { - return is_left_simple_macro(n, num_decls, head, def) || is_right_simple_macro(n, num_decls, head, def); + 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; @@ -113,20 +113,20 @@ public: bool inv; return is_arith_macro(n, num_decls, head, def, inv); } - - bool is_pseudo_head(expr * n, unsigned num_decls, app * & head, app * & t); - bool is_pseudo_predicate_macro(expr * n, app * & head, app * & t, expr * & def); + + 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 quasi_macro_head_to_macro_head(app * qhead, unsigned & num_decls, app_ref & head, expr_ref & cond) const; - void mk_macro_interpretation(app * head, expr * def, expr_ref & interp) 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. + // Auxiliary goodness that allows us to manipulate BV and Arith polynomials. // bool is_bv(expr * n) const; bool is_bv_sort(sort * s) const; diff --git a/src/ast/normal_forms/defined_names.cpp b/src/ast/normal_forms/defined_names.cpp index 16e9098fc..c1d9b36a5 100644 --- a/src/ast/normal_forms/defined_names.cpp +++ b/src/ast/normal_forms/defined_names.cpp @@ -210,7 +210,7 @@ bool defined_names::impl::mk_name(expr * e, expr_ref & new_def, proof_ref & new_ TRACE("mk_definition_bug", tout << "name for expression is already cached..., returning false...\n";); n = n_ptr; if (m_manager.proofs_enabled()) { - proof * pr_ptr; + proof * pr_ptr = 0; m_expr2proof.find(e, pr_ptr); SASSERT(pr_ptr); pr = pr_ptr; diff --git a/src/ast/normal_forms/pull_quant.cpp b/src/ast/normal_forms/pull_quant.cpp index 5158439a7..74c7cafde 100644 --- a/src/ast/normal_forms/pull_quant.cpp +++ b/src/ast/normal_forms/pull_quant.cpp @@ -58,7 +58,7 @@ struct pull_quant::imp { } bool found_quantifier = false; - bool forall_children; + bool forall_children = false; for (unsigned i = 0; i < num_children; i++) { expr * child = children[i]; @@ -125,8 +125,8 @@ struct pull_quant::imp { // 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 ammount if var idx >= bound) - shift_amount, // shift2 (shift by this ammount if var idx < bound) + 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_manager) << diff --git a/src/ast/pattern/expr_pattern_match.cpp b/src/ast/pattern/expr_pattern_match.cpp index 86cad58f8..e18831650 100644 --- a/src/ast/pattern/expr_pattern_match.cpp +++ b/src/ast/pattern/expr_pattern_match.cpp @@ -388,7 +388,10 @@ expr_pattern_match::initialize(char const * spec_string) { 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); ptr_vector::const_iterator it = ctx.begin_assertions(); ptr_vector::const_iterator end = ctx.end_assertions(); diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index b93dd7657..64ec064a8 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -474,13 +474,15 @@ void pattern_inference::reset_pre_patterns() { m_pre_patterns.reset(); } - +#ifdef _TRACE static void dump_app_vector(std::ostream & out, ptr_vector const & v, ast_manager & m) { ptr_vector::const_iterator it = v.begin(); ptr_vector::const_iterator end = v.end(); for (; it != end; ++it) out << mk_pp(*it, m) << "\n"; } +#endif + bool pattern_inference::is_forbidden(app * n) const { func_decl const * decl = n->get_decl(); if (is_ground(n)) diff --git a/src/ast/pattern/pattern_inference_params.cpp b/src/ast/pattern/pattern_inference_params.cpp index 8adbdb9f1..b36d372f5 100644 --- a/src/ast/pattern/pattern_inference_params.cpp +++ b/src/ast/pattern/pattern_inference_params.cpp @@ -30,3 +30,18 @@ void pattern_inference_params::updt_params(params_ref const & _p) { 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); +} \ No newline at end of file diff --git a/src/ast/pattern/pattern_inference_params.h b/src/ast/pattern/pattern_inference_params.h index a941b7dd6..0dc413399 100644 --- a/src/ast/pattern/pattern_inference_params.h +++ b/src/ast/pattern/pattern_inference_params.h @@ -46,6 +46,8 @@ struct pattern_inference_params { } void updt_params(params_ref const & _p); + + void display(std::ostream & out) const; }; #endif /* PATTERN_INFERENCE_PARAMS_H_ */ diff --git a/src/ast/pb_decl_plugin.cpp b/src/ast/pb_decl_plugin.cpp index f4e3fdf97..09f020ca6 100644 --- a/src/ast/pb_decl_plugin.cpp +++ b/src/ast/pb_decl_plugin.cpp @@ -18,6 +18,7 @@ Revision History: --*/ #include "pb_decl_plugin.h" +#include "ast_util.h" pb_decl_plugin::pb_decl_plugin(): m_at_most_sym("at-most"), @@ -90,7 +91,7 @@ func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p } void pb_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { - if (logic == symbol::null) { + if (logic == symbol::null || logic == "QF_FD") { 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)); @@ -99,31 +100,61 @@ void pb_decl_plugin::get_op_names(svector & op_names, symbol const } } -app * pb_util::mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) { - vector params; - params.push_back(parameter(k)); - for (unsigned i = 0; i < num_args; ++i) { - params.push_back(parameter(coeffs[i])); +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(); } - return m.mk_app(m_fid, OP_PB_LE, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort()); + 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))); + for (unsigned i = 0; i < num_args; ++i) { + m_params.push_back(parameter(m_coeffs[i])); + } + 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) { - vector params; - params.push_back(parameter(k)); + normalize(num_args, coeffs, k); + m_params.reset(); + m_params.push_back(parameter(ceil(m_k))); for (unsigned i = 0; i < num_args; ++i) { - params.push_back(parameter(coeffs[i])); + m_params.push_back(parameter(m_coeffs[i])); } - return m.mk_app(m_fid, OP_PB_GE, params.size(), 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) { - vector params; - params.push_back(parameter(k)); - for (unsigned i = 0; i < num_args; ++i) { - params.push_back(parameter(coeffs[i])); + normalize(num_args, coeffs, k); + if (!m_k.is_int()) { + return m.mk_false(); } - return m.mk_app(m_fid, OP_PB_EQ, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort()); + 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 @@ -132,33 +163,18 @@ app * pb_util::mk_eq(unsigned num_args, rational const * coeffs, expr * const * // <=> // 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) { - vector coeffs; - rational k(_k); + normalize(num_args, _coeffs, _k); expr_ref_vector args(m); - expr* f; - rational d(denominator(k)); for (unsigned i = 0; i < num_args; ++i) { - coeffs.push_back(_coeffs[i]); - d = lcm(d, denominator(coeffs[i])); - if (m.is_not(_args[i], f)) { - args.push_back(f); - } - else { - args.push_back(m.mk_not(_args[i])); - } + args.push_back(mk_not(m, _args[i])); } - if (!d.is_one()) { - k *= d; - for (unsigned i = 0; i < num_args; ++i) { - coeffs[i] *= d; - } - } - k.neg(); - k += rational::one(); + m_k = floor(m_k); + m_k.neg(); + m_k += rational::one(); for (unsigned i = 0; i < num_args; ++i) { - k += coeffs[i]; + m_k += m_coeffs[i]; } - return mk_ge(num_args, coeffs.c_ptr(), args.c_ptr(), k); + return mk_ge(num_args, m_coeffs.c_ptr(), args.c_ptr(), m_k); } diff --git a/src/ast/pb_decl_plugin.h b/src/ast/pb_decl_plugin.h index d4264b9b9..0750dcac7 100644 --- a/src/ast/pb_decl_plugin.h +++ b/src/ast/pb_decl_plugin.h @@ -73,12 +73,18 @@ public: unsigned arity, sort * const * domain, sort * range); virtual void get_op_names(svector & op_names, symbol const & logic); + virtual bool is_considered_uninterpreted(func_decl * f) { 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; } diff --git a/src/ast/pp_params.pyg b/src/ast/pp_params.pyg index d831cada9..6b43cbea3 100644 --- a/src/ast/pp_params.pyg +++ b/src/ast/pp_params.pyg @@ -15,5 +15,6 @@ def_module_params('pp', ('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 widht'), + ('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'))) diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index c9a2d2379..81385c2af 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -162,8 +162,8 @@ bool arith_rewriter::div_polynomial(expr * t, numeral const & g, const_treatment } bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & result) { - numeral c; - if (!is_add(arg1) && is_numeral(arg2, c)) { + 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); @@ -193,6 +193,45 @@ bool arith_rewriter::is_bound(expr * arg1, expr * arg2, op_kind kind, expr_ref & case EQ: result = m_util.mk_eq(pp, k); return true; } } + expr* t1, *t2; + bool is_int; + 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; } @@ -946,12 +985,13 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) { numeral a; + expr* x; if (m_util.is_numeral(arg, a)) { result = m_util.mk_numeral(floor(a), true); return BR_DONE; } - else if (m_util.is_to_real(arg)) { - result = to_app(arg)->get_arg(0); + else if (m_util.is_to_real(arg, x)) { + result = x; return BR_DONE; } else { @@ -982,8 +1022,8 @@ br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) { new_args.push_back(m_util.mk_numeral(a, true)); } else { - SASSERT(m_util.is_to_real(c)); - new_args.push_back(to_app(c)->get_arg(0)); + VERIFY (m_util.is_to_real(c, x)); + new_args.push_back(x); } } SASSERT(num_args == new_args.size()); @@ -1196,11 +1236,17 @@ expr * arith_rewriter::mk_sin_value(rational const & k) { } br_status arith_rewriter::mk_sin_core(expr * arg, expr_ref & result) { - if (is_app_of(arg, get_fid(), OP_ASIN)) { + expr * m, *x; + if (m_util.is_asin(arg, x)) { // sin(asin(x)) == x - result = to_app(arg)->get_arg(0); + 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 @@ -1214,7 +1260,6 @@ br_status arith_rewriter::mk_sin_core(expr * arg, expr_ref & result) { 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)); @@ -1250,11 +1295,15 @@ br_status arith_rewriter::mk_sin_core(expr * arg, expr_ref & result) { } br_status arith_rewriter::mk_cos_core(expr * arg, expr_ref & result) { - if (is_app_of(arg, get_fid(), OP_ACOS)) { + expr* x; + if (m_util.is_acos(arg, x)) { // cos(acos(x)) == x - result = to_app(arg)->get_arg(0); + 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()) { @@ -1306,9 +1355,10 @@ br_status arith_rewriter::mk_cos_core(expr * arg, expr_ref & result) { } br_status arith_rewriter::mk_tan_core(expr * arg, expr_ref & result) { - if (is_app_of(arg, get_fid(), OP_ATAN)) { + expr* x; + if (m_util.is_atan(arg, x)) { // tan(atan(x)) == x - result = to_app(arg)->get_arg(0); + result = x; return BR_DONE; } @@ -1488,9 +1538,10 @@ br_status arith_rewriter::mk_atan_core(expr * arg, expr_ref & result) { } br_status arith_rewriter::mk_sinh_core(expr * arg, expr_ref & result) { - if (is_app_of(arg, get_fid(), OP_ASINH)) { + expr* x; + if (m_util.is_asinh(arg, x)) { // sinh(asinh(x)) == x - result = to_app(arg)->get_arg(0); + result = x; return BR_DONE; } expr * t; @@ -1503,12 +1554,12 @@ br_status arith_rewriter::mk_sinh_core(expr * arg, expr_ref & result) { } br_status arith_rewriter::mk_cosh_core(expr * arg, expr_ref & result) { - if (is_app_of(arg, get_fid(), OP_ACOSH)) { - // cosh(acosh(x)) == x - result = to_app(arg)->get_arg(0); + expr* t; + if (m_util.is_acosh(arg, t)) { + // cosh(acosh(t)) == t + result = t; return BR_DONE; } - expr * t; if (m_util.is_times_minus_one(arg, t)) { // cosh(-t) == cosh result = m_util.mk_cosh(t); @@ -1518,12 +1569,12 @@ br_status arith_rewriter::mk_cosh_core(expr * arg, expr_ref & result) { } br_status arith_rewriter::mk_tanh_core(expr * arg, expr_ref & result) { - if (is_app_of(arg, get_fid(), OP_ATANH)) { - // tanh(atanh(x)) == x - result = to_app(arg)->get_arg(0); + expr * t; + if (m_util.is_atanh(arg, t)) { + // tanh(atanh(t)) == t + result = t; return BR_DONE; } - expr * t; if (m_util.is_times_minus_one(arg, t)) { // tanh(-t) == -tanh(t) result = m_util.mk_uminus(m_util.mk_tanh(t)); diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index 1f4418fd5..6f6b5b62e 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -26,6 +26,7 @@ void array_rewriter::updt_params(params_ref const & _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_select_ite = false; } void array_rewriter::get_param_descrs(param_descrs & r) { @@ -201,6 +202,17 @@ br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, result = m().mk_app(f, num_args - 1, args + 1); return BR_REWRITE1; } + + expr* c, *th, *el; + if (m_expand_select_ite && m().is_ite(args[0], c, th, el)) { + 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; } diff --git a/src/ast/rewriter/array_rewriter.h b/src/ast/rewriter/array_rewriter.h index 10b7bcfda..4ff48d496 100644 --- a/src/ast/rewriter/array_rewriter.h +++ b/src/ast/rewriter/array_rewriter.h @@ -32,6 +32,7 @@ class array_rewriter { bool m_sort_store; bool m_expand_select_store; bool m_expand_store_eq; + bool m_expand_select_ite; template lbool compare_args(unsigned num_args, expr * const * args1, expr * const * args2); public: @@ -43,6 +44,8 @@ public: ast_manager & m() const { return m_util.get_manager(); } family_id get_fid() const { return m_util.get_family_id(); } + 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); diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_params.h b/src/ast/rewriter/bit_blaster/bit_blaster_params.h index ee32d005a..15ece0043 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_params.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_params.h @@ -22,7 +22,7 @@ Revision History: struct bit_blaster_params { bool m_bb_ext_gates; bool m_bb_quantifiers; - bit_blaster_params(): + bit_blaster_params() : m_bb_ext_gates(false), m_bb_quantifiers(false) { } @@ -32,6 +32,11 @@ struct bit_blaster_params { 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_ */ diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp index c260178ad..68f2a2b8e 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp @@ -66,9 +66,6 @@ struct blaster_cfg { void mk_nor(expr * a, expr * b, expr_ref & r) { m_rewriter.mk_nor(a, b, r); } }; -// CMW: GCC/LLVM do not like this definition because a symbol of the same name exists in assert_set_bit_blaster.o -// template class bit_blaster_tpl; - class blaster : public bit_blaster_tpl { bool_rewriter m_rewriter; bv_util m_util; @@ -165,6 +162,10 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { m_keyval_lim.push_back(m_keys.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()); @@ -621,9 +622,6 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); } }; -// CMW: GCC/LLVM do not like this definition because a symbol of the same name exists in assert_set_bit_blaster.o -// template class rewriter_tpl; - struct bit_blaster_rewriter::imp : public rewriter_tpl { blaster m_blaster; blaster_rewriter_cfg m_cfg; @@ -637,6 +635,7 @@ struct bit_blaster_rewriter::imp : public rewriter_tpl { } void push() { m_cfg.push(); } void pop(unsigned s) { m_cfg.pop(s); } + unsigned get_num_scopes() const { return m_cfg.get_num_scopes(); } }; bit_blaster_rewriter::bit_blaster_rewriter(ast_manager & m, params_ref const & p): @@ -680,3 +679,7 @@ void bit_blaster_rewriter::operator()(expr * e, expr_ref & result, proof_ref & r m_imp->operator()(e, result, result_proof); } +unsigned bit_blaster_rewriter::get_num_scopes() const { + return m_imp->get_num_scopes(); +} + diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h index b23daab3a..8db328ec8 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h @@ -37,6 +37,7 @@ public: void operator()(expr * e, expr_ref & result, proof_ref & result_proof); void push(); void pop(unsigned num_scopes); + unsigned get_num_scopes() const; }; #endif diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 71be3d1c2..f6597fbc5 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -59,9 +59,12 @@ br_status bool_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * co mk_implies(args[0], args[1], result); return BR_DONE; case OP_XOR: - SASSERT(num_args == 2); - mk_xor(args[0], args[1], result); - return BR_DONE; + 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; } @@ -456,6 +459,22 @@ bool bool_rewriter::simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, exp 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: @@ -473,6 +492,7 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ bool modified = false; bool forward = true; unsigned rounds = 0; + expr* narg; while (true) { rounds++; @@ -481,20 +501,13 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ verbose_stream() << "rounds: " << rounds << "\n"; #endif -#define PUSH_NEW_ARG(ARG) { \ - new_args.push_back(ARG); \ - if (m().is_not(ARG)) \ - neg_lits.mark(to_app(ARG)->get_arg(0)); \ - else \ - pos_lits.mark(ARG); \ -} #define PROCESS_ARG() \ { \ expr * arg = args[i]; \ - if (m().is_not(arg) && m().is_or(to_app(arg)->get_arg(0)) && \ - simp_nested_not_or(to_app(to_app(arg)->get_arg(0))->get_num_args(), \ - to_app(to_app(arg)->get_arg(0))->get_args(), \ + 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)) { \ @@ -515,11 +528,11 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ 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); \ + push_new_arg(arg_arg, new_args, neg_lits, pos_lits); \ } \ } \ else { \ - PUSH_NEW_ARG(arg); \ + push_new_arg(arg, new_args, neg_lits, pos_lits); \ } \ } @@ -528,7 +541,7 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ static unsigned counter = 0; counter++; if (counter % 10000 == 0) - verbose_stream() << "local-ctx-cost: " << m_local_ctx_cost << "\n"; + verbose_stream() << "local-ctx-cost: " << m_local_ctx_cost << " " << num_args << "\n"; #endif if (forward) { @@ -572,7 +585,7 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ */ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) { - expr* cond, *t, *e; + expr* cond = 0, *t = 0, *e = 0; VERIFY(m().is_ite(ite, cond, t, e)); SASSERT(m().is_value(val)); @@ -581,38 +594,40 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) TRACE("try_ite_value", tout << mk_ismt2_pp(t, m()) << " " << mk_ismt2_pp(e, m()) << " " << mk_ismt2_pp(val, m()) << "\n"; tout << t << " " << e << " " << val << "\n";); result = m().mk_false(); - } + } else if (t == val && e == val) { result = m().mk_true(); - } + } else if (t == val) { result = cond; } else { - SASSERT(e == val); + SASSERT(e == val); mk_not(cond, result); } return BR_DONE; } - if (m().is_value(t)) { - if (val == t) { - result = m().mk_or(cond, m().mk_eq(val, e)); + if (m_ite_extra_rules) { + if (m().is_value(t)) { + if (val == t) { + result = m().mk_or(cond, m().mk_eq(val, e)); + } + else { + mk_not(cond, result); + result = m().mk_and(result, m().mk_eq(val, e)); + } + return BR_REWRITE2; } - else { - mk_not(cond, result); - result = m().mk_and(result, m().mk_eq(val, e)); + if (m().is_value(e)) { + if (val == e) { + mk_not(cond, result); + result = m().mk_or(result, m().mk_eq(val, t)); + } + else { + result = m().mk_and(cond, m().mk_eq(val, t)); + } + return BR_REWRITE2; } - return BR_REWRITE2; - } - if (m().is_value(e)) { - if (val == e) { - mk_not(cond, result); - result = m().mk_or(result, m().mk_eq(val, t)); - } - else { - result = m().mk_and(cond, m().mk_eq(val, t)); - } - return BR_REWRITE2; } expr* cond2, *t2, *e2; if (m().is_ite(t, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) { diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index b309f8032..b1d2dec53 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -75,6 +75,8 @@ class bool_rewriter { 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; } diff --git a/src/ast/rewriter/bv_bounds.cpp b/src/ast/rewriter/bv_bounds.cpp new file mode 100644 index 000000000..76cdfbdbe --- /dev/null +++ b/src/ast/rewriter/bv_bounds.cpp @@ -0,0 +1,664 @@ +/*++ + Copyright (c) 2016 Microsoft Corporation + + Module Name: + + bv_bounds.cpp + + Abstract: + + + Author: + + Mikolas Janota (MikolasJanota) + + Revision History: +--*/ +#include"bv_bounds.h" +#include"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 (0) { + 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 (1) { + 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(NULL), *v2(NULL); + 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 (0) { + 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(NULL), *v2(NULL); + 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, numeral a, 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, numeral a, 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, numeral a, 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, 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, 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, numeral a, 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(NULL); + if (e == 0) { + 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 i, bv_bounds::interval 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(NULL); + 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 != NULL); + 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; +} diff --git a/src/ast/rewriter/bv_bounds.h b/src/ast/rewriter/bv_bounds.h new file mode 100644 index 000000000..eeefc2c11 --- /dev/null +++ b/src/ast/rewriter/bv_bounds.h @@ -0,0 +1,130 @@ + /*++ + 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.h" +#include"bv_decl_plugin.h" +#include"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, numeral u); // v <= u + bool bound_lo(app * v, numeral l); // l <= v + inline bool add_neg_bound(app * v, numeral a, numeral b); // not (a<=v<=b) + bool add_bound_signed(app * v, numeral a, numeral b, bool negate); + bool add_bound_unsigned(app * v, numeral a, 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, numeral a, 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, 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, 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(NULL), *rhs(NULL); + 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 */ diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index def05f014..0246f2b16 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -29,12 +29,18 @@ void bv_rewriter::updt_local_params(params_ref const & _p) { 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) { @@ -189,6 +195,12 @@ br_status bv_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons return mk_bv_comp(args[0], args[1], result); case OP_MKBV: return mk_mkbv(num_args, args, result); + case OP_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; } @@ -228,6 +240,198 @@ br_status bv_rewriter::mk_slt(expr * a, expr * b, expr_ref & result) { 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; @@ -246,7 +450,7 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref r2 = m_util.norm(r2, sz, is_signed); if (is_num1 && is_num2) { - result = r1 <= r2 ? m().mk_true() : m().mk_false(); + result = m().mk_bool_val(r1 <= r2); return BR_DONE; } @@ -303,6 +507,24 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref 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))) { // @@ -360,6 +582,88 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref 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 = NULL; + 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) : NULL; + } + expr * new_arg = NULL; + 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 = NULL; 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); @@ -463,6 +767,17 @@ br_status bv_rewriter::mk_extract(unsigned high, unsigned low, expr * arg, expr_ 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; + } + } + if (m().is_ite(arg)) { result = m().mk_ite(to_app(arg)->get_arg(0), m_mk_extract(high, low, to_app(arg)->get_arg(1)), @@ -836,6 +1151,22 @@ bool bv_rewriter::is_minus_one_core(expr * arg) const { 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))) { @@ -1040,6 +1371,7 @@ br_status bv_rewriter::mk_bv2int(expr * arg, expr_ref & result) { return BR_FAILED; } + br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_ref & result) { expr_ref_buffer new_args(m()); numeral v1; @@ -1100,6 +1432,8 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re return BR_DONE; } + + br_status bv_rewriter::mk_zero_extend(unsigned n, expr * arg, expr_ref & result) { if (n == 0) { result = arg; @@ -1526,6 +1860,35 @@ br_status bv_rewriter::mk_bv_not(expr * arg, expr_ref & result) { return BR_REWRITE1; } + if (m_bvnot_simpl) { + expr *s(0), *t(0); + 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; } @@ -1777,7 +2140,7 @@ br_status bv_rewriter::mk_bit2bool(expr * lhs, expr * rhs, expr_ref & result) { if (is_numeral(lhs)) { SASSERT(is_numeral(rhs)); - result = lhs == rhs ? m().mk_true() : m().mk_false(); + result = m().mk_bool_val(lhs == rhs); return BR_DONE; } @@ -2068,6 +2431,16 @@ br_status bv_rewriter::mk_mul_eq(expr * lhs, expr * rhs, expr_ref & result) { 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(); @@ -2119,6 +2492,25 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { 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()); @@ -2126,7 +2518,7 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { st = cancel_monomials(lhs, rhs, false, new_lhs, new_rhs); if (st != BR_FAILED) { if (is_numeral(new_lhs) && is_numeral(new_rhs)) { - result = new_lhs == new_rhs ? m().mk_true() : m().mk_false(); + result = m().mk_bool_val(new_lhs == new_rhs); return BR_DONE; } } @@ -2186,6 +2578,137 @@ br_status bv_rewriter::mk_mkbv(unsigned num, expr * const * args, expr_ref & res 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 numberals + 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; + } + } + } + } + + + } + 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; - diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index 7135c52ba..b5482e5fa 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -56,11 +56,16 @@ class bv_rewriter : public poly_rewriter { 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); @@ -70,13 +75,18 @@ class bv_rewriter : public poly_rewriter { 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); @@ -123,6 +133,9 @@ class bv_rewriter : public poly_rewriter { 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); @@ -136,6 +149,7 @@ class bv_rewriter : public poly_rewriter { 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), @@ -164,7 +178,9 @@ public: 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; } diff --git a/src/ast/rewriter/bv_rewriter_params.pyg b/src/ast/rewriter/bv_rewriter_params.pyg index 0f0163fb1..984a48b37 100644 --- a/src/ast/rewriter/bv_rewriter_params.pyg +++ b/src/ast/rewriter/bv_rewriter_params.pyg @@ -10,5 +10,10 @@ def_module_params(module_name='rewriter', ("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_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") )) diff --git a/src/ast/rewriter/bv_trailing.cpp b/src/ast/rewriter/bv_trailing.cpp index cf50ab0e2..f0b96a91f 100644 --- a/src/ast/rewriter/bv_trailing.cpp +++ b/src/ast/rewriter/bv_trailing.cpp @@ -36,11 +36,14 @@ struct bv_trailing::imp { : 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] = NULL; } virtual ~imp() { + TRACE("bv-trailing", tout << "dtor\n";); reset_cache(0); } @@ -67,10 +70,8 @@ struct bv_trailing::imp { } expr_ref out1(m); expr_ref out2(m); - DEBUG_CODE( - const unsigned rm1 = remove_trailing(e1, min, out1, TRAILING_DEPTH); - const unsigned rm2 = remove_trailing(e2, min, out2, TRAILING_DEPTH); - SASSERT(rm1 == min && rm2 == min);); + 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; @@ -339,6 +340,7 @@ struct bv_trailing::imp { if (depth == 0) return; if (m_count_cache[depth] == NULL) 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";); @@ -361,11 +363,13 @@ struct bv_trailing::imp { return true; } - void reset_cache(unsigned condition) { + void reset_cache(const unsigned condition) { SASSERT(m_count_cache[0] == NULL); for (unsigned i = 1; i <= TRAILING_DEPTH; ++i) { if (m_count_cache[i] == NULL) continue; - if (m_count_cache[i]->size() < condition) 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); diff --git a/src/ast/rewriter/enum2bv_rewriter.cpp b/src/ast/rewriter/enum2bv_rewriter.cpp new file mode 100644 index 000000000..cd0f54503 --- /dev/null +++ b/src/ast/rewriter/enum2bv_rewriter.cpp @@ -0,0 +1,293 @@ +/*++ +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"rewriter.h" +#include"rewriter_def.h" +#include"enum2bv_rewriter.h" +#include"ast_util.h" +#include"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 << "unabled nested data-type expression " << mk_pp(e, m); + throw rewriter_exception(strm.str().c_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().str().c_str(), 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) { + 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()) { + if (q->is_forall()) { + new_body_ref = m.mk_implies(mk_and(bounds), new_body_ref); + } + else { + bounds.push_back(new_body_ref); + new_body_ref = mk_and(bounds); + } + } + result = m.mk_quantifier(q->is_forall(), 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 = 0; + 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(0), + 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); } diff --git a/src/ast/rewriter/enum2bv_rewriter.h b/src/ast/rewriter/enum2bv_rewriter.h new file mode 100644 index 000000000..1b2c6160f --- /dev/null +++ b/src/ast/rewriter/enum2bv_rewriter.h @@ -0,0 +1,48 @@ +/*++ +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"datatype_decl_plugin.h" +#include"rewriter_types.h" +#include"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 diff --git a/src/ast/rewriter/fpa_rewriter.cpp b/src/ast/rewriter/fpa_rewriter.cpp index b8d9c232f..26487c5a4 100644 --- a/src/ast/rewriter/fpa_rewriter.cpp +++ b/src/ast/rewriter/fpa_rewriter.cpp @@ -18,11 +18,12 @@ Notes: --*/ #include"fpa_rewriter.h" #include"fpa_rewriter_params.hpp" +#include"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(true) { + m_hi_fp_unspecified(false) { updt_params(p); } @@ -98,7 +99,7 @@ br_status fpa_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con case OP_FPA_INTERNAL_MIN_UNSPECIFIED: case OP_FPA_INTERNAL_MAX_UNSPECIFIED: SASSERT(num_args == 2); st = BR_FAILED; break; - + case OP_FPA_INTERNAL_BVWRAP: SASSERT(num_args == 1); st = mk_bvwrap(args[0], result); break; case OP_FPA_INTERNAL_BV2RM: SASSERT(num_args == 1); st = mk_bv2rm(args[0], result); break; @@ -117,34 +118,40 @@ br_status fpa_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con br_status fpa_rewriter::mk_to_ubv_unspecified(unsigned ebits, unsigned sbits, unsigned width, expr_ref & result) { bv_util bu(m()); - if (m_hi_fp_unspecified) + if (m_hi_fp_unspecified) { // The "hardware interpretation" is 0. result = bu.mk_numeral(0, width); - else + return BR_DONE; + } + else { result = m_util.mk_internal_to_ubv_unspecified(ebits, sbits, width); - - return BR_DONE; + return BR_REWRITE1; + } } br_status fpa_rewriter::mk_to_sbv_unspecified(unsigned ebits, unsigned sbits, unsigned width, expr_ref & result) { bv_util bu(m()); - if (m_hi_fp_unspecified) + if (m_hi_fp_unspecified) { // The "hardware interpretation" is 0. result = bu.mk_numeral(0, width); - else + return BR_DONE; + } + else { result = m_util.mk_internal_to_sbv_unspecified(ebits, sbits, width); - - return BR_DONE; + return BR_REWRITE1; + } } br_status fpa_rewriter::mk_to_real_unspecified(unsigned ebits, unsigned sbits, expr_ref & result) { - if (m_hi_fp_unspecified) + if (m_hi_fp_unspecified) { // The "hardware interpretation" is 0. result = m_util.au().mk_numeral(rational(0), false); - else + return BR_DONE; + } + else { result = m_util.mk_internal_to_real_unspecified(ebits, sbits); - - return BR_DONE; + return BR_REWRITE1; + } } br_status fpa_rewriter::mk_to_fp(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { @@ -776,10 +783,8 @@ br_status fpa_rewriter::mk_to_ubv(func_decl * f, expr * arg1, expr * arg2, expr_ m_util.is_numeral(arg2, v)) { const mpf & x = v.get(); - if (m_fm.is_nan(v) || m_fm.is_inf(v) || m_fm.is_neg(v)) { - mk_to_ubv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result); - return BR_REWRITE_FULL; - } + if (m_fm.is_nan(v) || m_fm.is_inf(v) || m_fm.is_neg(v)) + return mk_to_ubv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result); bv_util bu(m()); scoped_mpq q(m_fm.mpq_manager()); @@ -789,11 +794,13 @@ br_status fpa_rewriter::mk_to_ubv(func_decl * f, expr * arg1, expr * arg2, expr_ rational ul, ll; ul = m_fm.m_powers2.m1(bv_sz); ll = rational(0); - if (r >= ll && r <= ul) + if (r >= ll && r <= ul) { result = bu.mk_numeral(r, bv_sz); + return BR_DONE; + } else - mk_to_ubv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result); - return BR_DONE; + return mk_to_ubv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result); + } return BR_FAILED; @@ -810,10 +817,8 @@ br_status fpa_rewriter::mk_to_sbv(func_decl * f, expr * arg1, expr * arg2, expr_ m_util.is_numeral(arg2, v)) { const mpf & x = v.get(); - if (m_fm.is_nan(v) || m_fm.is_inf(v)) { - mk_to_sbv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result); - return BR_REWRITE_FULL; - } + if (m_fm.is_nan(v) || m_fm.is_inf(v)) + return mk_to_sbv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result); bv_util bu(m()); scoped_mpq q(m_fm.mpq_manager()); @@ -823,46 +828,42 @@ br_status fpa_rewriter::mk_to_sbv(func_decl * f, expr * arg1, expr * arg2, expr_ rational ul, ll; ul = m_fm.m_powers2.m1(bv_sz - 1); ll = - m_fm.m_powers2(bv_sz - 1); - if (r >= ll && r <= ul) + if (r >= ll && r <= ul) { result = bu.mk_numeral(r, bv_sz); + return BR_DONE; + } else - mk_to_sbv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result); - return BR_DONE; + return mk_to_sbv_unspecified(x.get_ebits(), x.get_sbits(), bv_sz, result); } return BR_FAILED; } 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) { - result = bu.mk_concat(bu.mk_numeral(0, 1), - bu.mk_concat(bu.mk_numeral(-1, x.get_ebits()), - bu.mk_concat(bu.mk_numeral(0, x.get_sbits() - 2), - bu.mk_numeral(1, 1)))); + expr * args[4] = { bu.mk_numeral(0, 1), + bu.mk_numeral(-1, x.get_ebits()), + bu.mk_numeral(0, x.get_sbits() - 2), + bu.mk_numeral(1, 1) }; + result = bu.mk_concat(4, args); } - else { - app_ref unspec(m()), mask(m()), extra(m()); - unspec = m_util.mk_internal_to_ieee_bv_unspecified(x.get_ebits(), x.get_sbits()); - mask = bu.mk_concat(bu.mk_numeral(0, 1), - bu.mk_concat(bu.mk_numeral(-1, x.get_ebits()), - bu.mk_concat(bu.mk_numeral(0, x.get_sbits() - 2), - bu.mk_numeral(1, 1)))); - expr * args[2] = { unspec, mask }; - result = m().mk_app(bu.get_fid(), OP_BOR, 2, args); - } - return BR_REWRITE_FULL; + else + result = m_util.mk_internal_to_ieee_bv_unspecified(x.get_ebits(), x.get_sbits()); + + 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; } diff --git a/src/ast/rewriter/fpa_rewriter_params.pyg b/src/ast/rewriter/fpa_rewriter_params.pyg index 85973e744..f0cfbdf55 100644 --- a/src/ast/rewriter/fpa_rewriter_params.pyg +++ b/src/ast/rewriter/fpa_rewriter_params.pyg @@ -1,5 +1,5 @@ def_module_params(module_name='rewriter', class_name='fpa_rewriter_params', export=True, - params=(("hi_fp_unspecified", BOOL, True, "use the 'hardware interpretation' for unspecified values in fp.to_ubv, fp.to_sbv, and fp.to_real"), + 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"), )) diff --git a/src/ast/rewriter/pb2bv_rewriter.cpp b/src/ast/rewriter/pb2bv_rewriter.cpp new file mode 100644 index 000000000..37c87cd5b --- /dev/null +++ b/src/ast/rewriter/pb2bv_rewriter.cpp @@ -0,0 +1,477 @@ +/*++ +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"rewriter.h" +#include"rewriter_def.h" +#include"statistics.h" +#include"pb2bv_rewriter.h" +#include"sorting_network.h" +#include"ast_util.h" +#include"ast_pp.h" +#include"lbool.h" + + +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; + + struct card2bv_rewriter { + typedef expr* literal; + typedef ptr_vector literal_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; + + 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; + + } + + // + // 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(unsigned sz, expr * const* args, rational const & k) { + 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 << m_k << "\n";); + if (k.is_zero()) { + if (is_le != l_false) { + return expr_ref(m.mk_not(mk_or(m, sz, 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); + } + 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); + } + } + + expr_ref mk_bv(func_decl * f, unsigned sz, expr * const* args) { + decl_kind kind = f->get_decl_kind(); + rational k = pb.get_k(f); + m_coeffs.reset(); + for (unsigned i = 0; i < sz; ++i) { + m_coeffs.push_back(pb.get_coeff(f, i)); + } + SASSERT(!k.is_neg()); + switch (kind) { + case OP_PB_GE: + case OP_AT_LEAST_K: { + expr_ref_vector nargs(m); + nargs.append(sz, args); + dualize(f, nargs, k); + SASSERT(!k.is_neg()); + return mk_le_ge(sz, nargs.c_ptr(), k); + } + case OP_PB_LE: + case OP_AT_MOST_K: + return mk_le_ge(sz, args, k); + case OP_PB_EQ: + return mk_le_ge(sz, args, 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) + {} + + 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); + } + else if (au.is_le(f) && is_pb(args[0], args[1])) { + result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + } + else if (au.is_lt(f) && is_pb(args[0], args[1])) { + ++m_k; + result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + } + else if (au.is_ge(f) && is_pb(args[1], args[0])) { + result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + } + else if (au.is_gt(f) && is_pb(args[1], args[0])) { + ++m_k; + result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + } + else if (m.is_eq(f) && is_pb(args[0], args[1])) { + result = mk_le_ge(m_args.size(), m_args.c_ptr(), 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 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; + } + + void 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()) { + result = m_sort.le(full, pb.get_k(f).get_unsigned(), sz, args); + } + else if (pb.is_at_least_k(f) && pb.get_k(f).is_unsigned()) { + result = m_sort.ge(full, pb.get_k(f).get_unsigned(), sz, args); + } + else if (pb.is_eq(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + result = m_sort.eq(full, pb.get_k(f).get_unsigned(), sz, args); + } + else if (pb.is_le(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + result = m_sort.le(full, pb.get_k(f).get_unsigned(), sz, args); + } + else if (pb.is_ge(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + result = m_sort.ge(full, pb.get_k(f).get_unsigned(), sz, args); + } + else { + result = mk_bv(f, sz, args); + } + } + + // definitions used for sorting network + literal mk_false() { return m.mk_false(); } + literal mk_true() { return m.mk_true(); } + literal mk_max(literal a, literal b) { return trail(m.mk_or(a, b)); } + literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } + literal mk_not(literal a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } + + std::ostream& pp(std::ostream& out, literal lit) { return out << mk_ismt2_pp(lit, m); } + + literal trail(literal l) { + m_trail.push_back(l); + return l; + } + literal fresh() { + expr_ref fr(m.mk_fresh_const("sn", m.mk_bool_sort()), m); + m_imp.m_fresh.push_back(to_app(fr)->get_decl()); + return trail(fr); + } + + void mk_clause(unsigned n, literal const* lits) { + m_imp.m_lemmas.push_back(mk_or(m, n, 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 = 0; + return m_r.mk_app_core(f, num, args, result); + } + card2bv_rewriter_cfg(imp& i, ast_manager & m):m_r(i, m) {} + }; + + 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) {} + }; + + card_pb_rewriter m_rw; + + 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) { + } + + 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_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-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); } +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()(expr * e, expr_ref & result, proof_ref & result_proof) { (*m_imp)(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); } diff --git a/src/ast/rewriter/pb2bv_rewriter.h b/src/ast/rewriter/pb2bv_rewriter.h new file mode 100644 index 000000000..47d8361cb --- /dev/null +++ b/src/ast/rewriter/pb2bv_rewriter.h @@ -0,0 +1,46 @@ +/*++ +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"pb_decl_plugin.h" +#include"rewriter_types.h" +#include"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); + ast_manager & m() const; + unsigned get_num_steps() const; + void cleanup(); + func_decl_ref_vector const& fresh_constants() 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 collect_statistics(statistics & st) const; +}; + +#endif diff --git a/src/ast/rewriter/pb_rewriter.cpp b/src/ast/rewriter/pb_rewriter.cpp index eb85f8ec9..0fdbc858d 100644 --- a/src/ast/rewriter/pb_rewriter.cpp +++ b/src/ast/rewriter/pb_rewriter.cpp @@ -257,7 +257,12 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons all_unit &= m_coeffs.back().is_one(); } if (is_eq) { - result = m_util.mk_eq(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k); + if (sz == 0) { + result = k.is_zero()?m.mk_true():m.mk_false(); + } + else { + result = m_util.mk_eq(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k); + } } else if (all_unit && k.is_one()) { result = mk_or(m, sz, m_args.c_ptr()); @@ -277,6 +282,7 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons tout << tmp << "\n"; tout << result << "\n"; ); + TRACE("pb_validate", validate_rewrite(f, num_args, args, result);); diff --git a/src/ast/rewriter/rewriter.h b/src/ast/rewriter/rewriter.h index 401d00d89..fc596cabd 100644 --- a/src/ast/rewriter/rewriter.h +++ b/src/ast/rewriter/rewriter.h @@ -111,7 +111,7 @@ protected: void elim_reflex_prs(unsigned spos); public: rewriter_core(ast_manager & m, bool proof_gen); - ~rewriter_core(); + virtual ~rewriter_core(); ast_manager & m() const { return m_manager; } void reset(); void cleanup(); diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index eaf5713ee..61d177809 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -27,7 +27,7 @@ void rewriter_tpl::process_var(var * v) { SASSERT(v->get_sort() == m().get_sort(m_r)); if (ProofGen) { result_pr_stack().push_back(m_pr); - m_pr = 0; + m_pr = 0; } set_new_child_flag(v); TRACE("rewriter", tout << mk_ismt2_pp(v, m()) << " -> " << m_r << "\n";); @@ -39,11 +39,11 @@ void rewriter_tpl::process_var(var * v) { unsigned idx = v->get_idx(); if (idx < m_bindings.size()) { unsigned index = m_bindings.size() - idx - 1; - expr * r = m_bindings[index]; + var * r = (var*)(m_bindings[index]); if (r != 0) { 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_ref tmp(m()); m_shifter(r, shift_amount, tmp); @@ -103,8 +103,8 @@ template template bool rewriter_tpl::visit(expr * t, unsigned max_depth) { TRACE("rewriter_visit", tout << "visiting\n" << mk_ismt2_pp(t, m()) << "\n";); - expr * new_t; - proof * new_t_pr; + expr * new_t = 0; + proof * new_t_pr = 0; 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)); @@ -195,9 +195,9 @@ void rewriter_tpl::process_app(app * t, frame & fr) { // 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 && + 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); @@ -223,7 +223,7 @@ void rewriter_tpl::process_app(app * t, frame & fr) { } 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)); - TRACE("reduce_app", + TRACE("reduce_app", tout << mk_ismt2_pp(t, m()) << "\n"; tout << "st: " << st; if (m_r) tout << " --->\n" << mk_ismt2_pp(m_r, m()); @@ -296,11 +296,11 @@ void rewriter_tpl::process_app(app * t, frame & fr) { expr * def; proof * def_pr; quantifier * def_q; - // When get_macro succeeds, then + // When get_macro succeeds, then // we know that: // forall X. f(X) = def[X] // and def_pr is a proof for this quantifier. - // + // // Remark: def_q is only used for proof generation. // It is the quantifier forall X. f(X) = def[X] if (get_macro(f, def, def_q, def_pr)) { @@ -318,7 +318,7 @@ void rewriter_tpl::process_app(app * t, frame & fr) { if (ProofGen) { NOT_IMPLEMENTED_YET(); // We do not support the use of bindings in proof generation mode. - // Thus we have to apply the subsitution here, and + // Thus we have to apply the subsitution here, and // beta_reducer subst(m()); // subst.set_bindings(new_num_args, new_args); // expr_ref r2(m()); @@ -333,7 +333,7 @@ void rewriter_tpl::process_app(app * t, frame & fr) { unsigned i = num_args; while (i > 0) { --i; - m_bindings.push_back(new_args[i]); // num_args - i - 1]); + m_bindings.push_back(new_args[i]); // num_args - i - 1]); m_shifts.push_back(sz); } result_stack().push_back(def); @@ -465,7 +465,7 @@ void rewriter_tpl::process_quantifier(quantifier * q, frame & fr) { } else { new_pats = q->get_patterns(); - new_no_pats = q->get_no_patterns(); + new_no_pats = q->get_no_patterns(); } if (ProofGen) { quantifier * new_q = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_body); @@ -559,7 +559,7 @@ 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]) + if (m_bindings[i]) out << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << "\n"; } } @@ -596,6 +596,7 @@ 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()); @@ -630,6 +631,7 @@ 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()); diff --git a/src/ast/rewriter/rewriter_params.pyg b/src/ast/rewriter/rewriter_params.pyg index 3a8ed5d5e..5bd17f556 100644 --- a/src/ast/rewriter/rewriter_params.pyg +++ b/src/ast/rewriter/rewriter_params.pyg @@ -7,5 +7,6 @@ def_module_params('rewriter', ("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."))) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 04acd7ee1..26c3e23e4 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -86,20 +86,25 @@ public: expr_ref fml(m.mk_true(), m); return sym_expr::mk_pred(fml, m.mk_bool_sort()); } - virtual T mk_and(T x, T y) { - 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()); - } - } - 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_true(fml1)) return y; + virtual T mk_and(T x, T y) { + 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()); + } + } + + 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; expr_ref fml(m.mk_and(fml1, fml2), m); return sym_expr::mk_pred(fml, x->get_sort()); @@ -166,6 +171,11 @@ public: expr_ref fml(m.mk_not(x->accept(v)), m); return sym_expr::mk_pred(fml, x->get_sort()); } + + /*virtual vector, T>> generate_min_terms(vector constraints){ + + return 0; + }*/ }; re2automaton::re2automaton(ast_manager& m): m(m), u(m), bv(m), m_ba(0), m_sa(0) {} @@ -233,46 +243,8 @@ eautomaton* re2automaton::re2aut(expr* e) { TRACE("seq", tout << "Range expression is not handled: " << mk_pp(e, m) << "\n";); } } - else if (u.re.is_complement(e, e0)) { - // TBD non-standard semantics of complementation. - if (u.re.is_range(e0, e1, e2) && 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]; - unsigned nb = s1.num_bits(); - sort_ref s(bv.mk_sort(nb), m); - expr_ref v(m.mk_var(0, s), m); - expr_ref _start(bv.mk_numeral(start, nb), m); - expr_ref _stop(bv.mk_numeral(stop, nb), m); - expr_ref _pred(m.mk_not(m.mk_and(bv.mk_ule(_start, v), bv.mk_ule(v, _stop))), m); - a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred, s)); - display_expr1 disp(m); - TRACE("seq", tout << mk_pp(e, m) << "\n"; a->display(tout, disp);); - return a.detach(); - } - else if (u.re.is_to_re(e0, e1) && u.str.is_string(e1, s1) && s1.length() == 1) { - unsigned nb = s1.num_bits(); - sort_ref s(bv.mk_sort(nb), m); - expr_ref v(m.mk_var(0, s), m); - expr_ref _ch(bv.mk_numeral(s1[0], nb), m); - expr_ref _pred(m.mk_not(m.mk_eq(v, _ch)), m); - a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred, s)); - display_expr1 disp(m); - TRACE("seq", tout << mk_pp(e, m) << "\n"; a->display(tout, disp);); - return a.detach(); - } - else if (u.re.is_to_re(e0, e1) && u.str.is_unit(e1, e2)) { - sort* s = m.get_sort(e2); - expr_ref v(m.mk_var(0, s), m); - expr_ref _pred(m.mk_not(m.mk_eq(v, e2)), m); - a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred, s)); - display_expr1 disp(m); - TRACE("seq", tout << mk_pp(e, m) << "\n"; a->display(tout, disp);); - return a.detach(); - } - else { - TRACE("seq", tout << "Complement 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); @@ -303,7 +275,7 @@ eautomaton* re2automaton::re2aut(expr* e) { } else if (u.re.is_full(e)) { expr_ref tt(m.mk_true(), m); - sort* seq_s, *char_s; + sort *seq_s = 0, *char_s = 0; 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); @@ -363,6 +335,9 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con SASSERT(num_args == 1); return mk_re_opt(args[0], result); case OP_RE_CONCAT: + if (num_args == 1) { + result = args[0]; return BR_DONE; + } SASSERT(num_args == 2); return mk_re_concat(args[0], args[1], result); case OP_RE_UNION: @@ -549,7 +524,7 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { result = m().mk_bool_val(c.contains(d)); return BR_DONE; } - // check if subsequence of b is in a. + // check if subsequence of a is in b. expr_ref_vector as(m()), bs(m()); m_util.str.get_concat(a, as); m_util.str.get_concat(b, bs); @@ -612,6 +587,12 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { SASSERT(sz > offs); result = m_util.str.mk_contains(m_util.str.mk_concat(sz-offs, as.c_ptr()+offs), b); return BR_REWRITE2; + } + + expr* x, *y, *z; + if (m_util.str.is_extract(b, x, y, z) && x == a) { + result = m().mk_true(); + return BR_DONE; } return BR_FAILED; @@ -665,6 +646,14 @@ br_status seq_rewriter::mk_seq_replace(expr* a, expr* b, expr* c, expr_ref& resu result = a; return BR_DONE; } + if (m_util.str.is_string(b, s2) && s2.length() == 0) { + result = m_util.str.mk_concat(a, c); + return BR_REWRITE1; + } + if (m_util.str.is_string(a, s1) && s1.length() == 0) { + result = a; + return BR_DONE; + } return BR_FAILED; } @@ -794,7 +783,7 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) { bool isc1 = false; bool isc2 = false; - expr* a1, *a2, *b1, *b2; + expr *a1 = 0, *a2 = 0, *b1 = 0, *b2 = 0; if (m_util.str.is_concat(a, a1, a2) && m_util.str.is_string(a2, s1)) { isc1 = true; } @@ -910,6 +899,11 @@ br_status seq_rewriter::mk_str_stoi(expr* a, expr_ref& result) { result = m_autil.mk_numeral(r, true); return BR_DONE; } + expr* b; + if (m_util.str.is_itos(a, b)) { + result = b; + return BR_DONE; + } return BR_FAILED; } @@ -1321,7 +1315,7 @@ br_status seq_rewriter::mk_re_plus(expr* a, expr_ref& result) { } br_status seq_rewriter::mk_re_opt(expr* a, expr_ref& result) { - sort* s; + sort* s = 0; 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; @@ -1511,10 +1505,15 @@ bool seq_rewriter::reduce_eq(expr_ref_vector& ls, expr_ref_vector& rs, expr_ref_ lchange = true; } - bool is_sat; + 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(); @@ -1679,6 +1678,56 @@ bool seq_rewriter::min_length(unsigned n, expr* const* es, unsigned& len) { return bounded; } +bool seq_rewriter::is_string(unsigned n, expr* const* es, zstring& s) const { + zstring s1; + expr* e; + bv_util bv(m()); + rational val; + unsigned sz; + for (unsigned i = 0; i < n; ++i) { + if (m_util.str.is_string(es[i], s1)) { + s = s + s1; + } + else if (m_util.str.is_unit(es[i], e) && bv.is_numeral(e, val, sz)) { + s = s + zstring(val.get_unsigned()); + } + else { + 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 && 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; diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index 040bae1b4..2b434f475 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -125,15 +125,20 @@ class seq_rewriter { 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); + 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) { @@ -144,6 +149,9 @@ public: 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); } + + 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); diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index a56ca91d8..86772cdb4 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -194,6 +194,14 @@ struct th_rewriter_cfg : public default_rewriter_cfg { 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()) @@ -700,6 +708,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { return false; } + }; template class rewriter_tpl; @@ -713,6 +722,10 @@ struct th_rewriter::imp : public rewriter_tpl { 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): @@ -798,3 +811,7 @@ void th_rewriter::reset_used_dependencies() { 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); +} diff --git a/src/ast/rewriter/th_rewriter.h b/src/ast/rewriter/th_rewriter.h index bd5ce9c66..6aa4bb3da 100644 --- a/src/ast/rewriter/th_rewriter.h +++ b/src/ast/rewriter/th_rewriter.h @@ -25,6 +25,8 @@ Notes: class expr_substitution; +class expr_solver; + class th_rewriter { struct imp; imp * m_imp; @@ -58,6 +60,9 @@ public: // 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 diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 779096038..37daedea1 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -144,6 +144,9 @@ zstring zstring::replace(zstring const& src, zstring const& dst) const { if (length() < src.length()) { return zstring(*this); } + if (src.length() == 0) { + return zstring(*this); + } bool found = false; for (unsigned i = 0; i < length(); ++i) { bool eq = !found && i + src.length() <= length(); @@ -552,7 +555,7 @@ func_decl* seq_decl_plugin::mk_assoc_fun(decl_kind k, unsigned arity, sort* cons } match_right_assoc(*m_sigs[k], arity, domain, range, rng); func_decl_info info(m_family_id, k_seq); - info.set_right_associative(); + info.set_right_associative(true); return m.mk_func_decl(m_sigs[(rng == m_string)?k_string:k_seq]->m_name, rng, rng, rng, info); } @@ -598,7 +601,7 @@ func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, 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, 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_REGEXP_EMPTY: @@ -614,7 +617,7 @@ func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, 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, 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: switch (arity) { @@ -822,16 +825,6 @@ app* seq_util::str::mk_char(char ch) { return mk_char(s, 0); } -bool seq_util::str::is_char(expr* n, zstring& c) const { - if (u.is_char(n)) { - c = zstring(to_app(n)->get_decl()->get_parameter(0).get_symbol().bare_str()); - return true; - } - else { - return false; - } -} - 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()); @@ -868,7 +861,7 @@ app* seq_util::re::mk_full(sort* s) { return m.mk_app(m_fid, OP_RE_FULL_SET, 0, 0, 0, 0, s); } -app* seq_util::re::mk_empty(sort* s) { +app* seq_util::re::mk_empty(sort* s) { return m.mk_app(m_fid, OP_RE_EMPTY_SET, 0, 0, 0, 0, s); } diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 78672182a..fbbcba5de 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -250,7 +250,6 @@ public: } bool is_string(expr const* n, zstring& s) const; - bool is_char(expr* 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); diff --git a/src/ast/simplifier/arith_simplifier_params.cpp b/src/ast/simplifier/arith_simplifier_params.cpp index a3fabe02f..8584cdae0 100644 --- a/src/ast/simplifier/arith_simplifier_params.cpp +++ b/src/ast/simplifier/arith_simplifier_params.cpp @@ -24,3 +24,10 @@ void arith_simplifier_params::updt_params(params_ref const & _p) { m_arith_expand_eqs = p.arith_expand_eqs(); m_arith_process_all_eqs = p.arith_process_all_eqs(); } + +#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; + +void arith_simplifier_params::display(std::ostream & out) const { + DISPLAY_PARAM(m_arith_expand_eqs); + DISPLAY_PARAM(m_arith_process_all_eqs); +} \ No newline at end of file diff --git a/src/ast/simplifier/arith_simplifier_params.h b/src/ast/simplifier/arith_simplifier_params.h index 2ff8fe2c0..6186ee4a2 100644 --- a/src/ast/simplifier/arith_simplifier_params.h +++ b/src/ast/simplifier/arith_simplifier_params.h @@ -30,6 +30,8 @@ struct arith_simplifier_params { } void updt_params(params_ref const & _p); + + void display(std::ostream & out) const; }; #endif /* ARITH_SIMPLIFIER_PARAMS_H_ */ diff --git a/src/ast/simplifier/arith_simplifier_plugin.cpp b/src/ast/simplifier/arith_simplifier_plugin.cpp index 588508f4f..ef320578a 100644 --- a/src/ast/simplifier/arith_simplifier_plugin.cpp +++ b/src/ast/simplifier/arith_simplifier_plugin.cpp @@ -43,8 +43,7 @@ bool arith_simplifier_plugin::is_neg_poly(expr * t) const { 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)) + if (is_numeral(t, r)) return r.is_neg(); } return false; diff --git a/src/ast/simplifier/bit2int.cpp b/src/ast/simplifier/bit2int.cpp index ab4193486..08c3da774 100644 --- a/src/ast/simplifier/bit2int.cpp +++ b/src/ast/simplifier/bit2int.cpp @@ -273,7 +273,7 @@ void bit2int::visit(app* n) { // bv2int(x) <= z - bv2int(y) -> bv2int(x) + bv2int(y) <= z // - expr* e1, *e2; + expr* e1 = 0, *e2 = 0; expr_ref tmp1(m_manager), tmp2(m_manager); expr_ref tmp3(m_manager); expr_ref pos1(m_manager), neg1(m_manager); diff --git a/src/ast/simplifier/bv_simplifier_params.cpp b/src/ast/simplifier/bv_simplifier_params.cpp index 5d6cd363f..1ed263aa6 100644 --- a/src/ast/simplifier/bv_simplifier_params.cpp +++ b/src/ast/simplifier/bv_simplifier_params.cpp @@ -27,3 +27,10 @@ void bv_simplifier_params::updt_params(params_ref const & _p) { m_bv2int_distribute = p.bv_bv2int_distribute(); } + +#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; + +void bv_simplifier_params::display(std::ostream & out) const { + DISPLAY_PARAM(m_hi_div0); + DISPLAY_PARAM(m_bv2int_distribute); +} \ No newline at end of file diff --git a/src/ast/simplifier/bv_simplifier_params.h b/src/ast/simplifier/bv_simplifier_params.h index 50015b7ca..dafa99065 100644 --- a/src/ast/simplifier/bv_simplifier_params.h +++ b/src/ast/simplifier/bv_simplifier_params.h @@ -30,6 +30,8 @@ struct bv_simplifier_params { } void updt_params(params_ref const & _p); + + void display(std::ostream & out) const; }; #endif /* BV_SIMPLIFIER_PARAMS_H_ */ diff --git a/src/ast/simplifier/poly_simplifier_plugin.cpp b/src/ast/simplifier/poly_simplifier_plugin.cpp index c5dc275fd..e5e74ca56 100644 --- a/src/ast/simplifier/poly_simplifier_plugin.cpp +++ b/src/ast/simplifier/poly_simplifier_plugin.cpp @@ -607,12 +607,25 @@ void poly_simplifier_plugin::append_to_monomial(expr * n, numeral & k, ptr_buffe k *= val; n = get_monomial_body(n); - if (is_mul(n)) { - for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) - result.push_back(to_app(n)->get_arg(i)); - } - else { - result.push_back(n); + unsigned hd = result.size(); + result.push_back(n); + while (hd < result.size()) { + n = result[hd]; + if (is_mul(n)) { + result[hd] = result.back(); + result.pop_back(); + for (unsigned i = 0; i < to_app(n)->get_num_args(); i++) { + result.push_back(to_app(n)->get_arg(i)); + } + } + else if (is_numeral(n, val)) { + k *= val; + result[hd] = result.back(); + result.pop_back(); + } + else { + ++hd; + } } } diff --git a/src/ast/simplifier/simplifier.cpp b/src/ast/simplifier/simplifier.cpp index 63731c679..6f7e62fd4 100644 --- a/src/ast/simplifier/simplifier.cpp +++ b/src/ast/simplifier/simplifier.cpp @@ -650,10 +650,12 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) { #define Grey 1 #define Black 2 +#ifdef Z3DEBUG static int get_color(obj_map & colors, expr * n) { obj_map::obj_map_entry * entry = colors.insert_if_not_there2(n, White); return entry->get_data().m_value; } +#endif static bool visit_ac_children(func_decl * f, expr * n, obj_map & colors, ptr_buffer & todo, ptr_buffer & result) { if (is_app_of(n, f)) { diff --git a/src/ast/substitution/substitution.cpp b/src/ast/substitution/substitution.cpp index be293c5a8..eea1938a6 100644 --- a/src/ast/substitution/substitution.cpp +++ b/src/ast/substitution/substitution.cpp @@ -146,7 +146,7 @@ void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, e bool has_new_args = false; for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(e)->get_arg(i); - expr * new_arg; + expr * new_arg = 0; VERIFY(m_apply_cache.find(expr_offset(arg, off), new_arg)); new_args.push_back(new_arg); diff --git a/src/ast/well_sorted.cpp b/src/ast/well_sorted.cpp index f9f04a2df..b0afe566b 100644 --- a/src/ast/well_sorted.cpp +++ b/src/ast/well_sorted.cpp @@ -44,7 +44,8 @@ struct well_sorted_proc { 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()) { + 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; @@ -66,7 +67,7 @@ struct well_sorted_proc { 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(strm.str().c_str()); + warning_msg("%s", strm.str().c_str()); m_error = true; return; } diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index f5e92fe57..d951f7710 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -29,6 +29,7 @@ Notes: #include"gparams.h" #include"env_params.h" #include"well_sorted.h" +#include"pp_params.hpp" class help_cmd : public cmd { svector m_cmds; @@ -161,14 +162,19 @@ ATOMIC_CMD(get_proof_cmd, "get-proof", "retrieve proof", { throw cmd_exception("proof is not well sorted"); } - // 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()); - // ctx.regular_stream() << mk_pp(pr, ctx.m()) << "\n"; - pp.display_smt2(ctx.regular_stream(), pr); - ctx.regular_stream() << std::endl; + 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; + } }); #define PRINT_CORE() \ @@ -625,7 +631,7 @@ public: ctx.regular_stream() << "(:status " << ctx.get_status() << ")" << std::endl; } else if (opt == m_reason_unknown) { - ctx.regular_stream() << "(:reason-unknown " << ctx.reason_unknown() << ")" << std::endl; + ctx.regular_stream() << "(:reason-unknown \"" << ctx.reason_unknown() << "\")" << std::endl; } else if (opt == m_all_statistics) { ctx.display_statistics(); @@ -753,6 +759,42 @@ public: } }; +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) {} + virtual char const * get_usage() const { return "(*) (*)"; } + virtual char const * get_descr(cmd_context & ctx) const { return "retrieve consequences that fix values for supplied variables"; } + virtual unsigned get_arity() const { return 2; } + virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_EXPR_LIST; } + virtual void set_next_arg(cmd_context & ctx, unsigned num, expr * const * tlist) { + if (m_count == 0) { + m_assumptions.append(num, tlist); + ++m_count; + } + else { + m_variables.append(num, tlist); + } + } + virtual void failure_cleanup(cmd_context & ctx) {} + virtual void execute(cmd_context & ctx) { + 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"; + } + virtual void prepare(cmd_context & ctx) { reset(ctx); } + + virtual void reset(cmd_context& ctx) { + m_assumptions.reset(); m_variables.reset(); m_count = 0; + } + virtual void finalize(cmd_context & ctx) {} +}; + // provides "help" for builtin cmds class builtin_cmd : public cmd { char const * m_usage; @@ -776,6 +818,7 @@ void install_basic_cmds(cmd_context & ctx) { 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.")); diff --git a/src/cmd_context/check_logic.cpp b/src/cmd_context/check_logic.cpp index a547ab616..02f66fc4d 100644 --- a/src/cmd_context/check_logic.cpp +++ b/src/cmd_context/check_logic.cpp @@ -22,8 +22,11 @@ Revision History: #include"bv_decl_plugin.h" #include"seq_decl_plugin.h" #include"str_decl_plugin.h" +#include"pb_decl_plugin.h" +#include"datatype_decl_plugin.h" #include"ast_pp.h" #include"for_each_expr.h" +#include struct check_logic::imp { ast_manager & m; @@ -33,6 +36,8 @@ struct check_logic::imp { array_util m_ar_util; seq_util m_seq_util; str_util m_str_util; + datatype_util m_dt_util; + pb_util m_pb_util; bool m_uf; // true if the logic supports uninterpreted functions bool m_arrays; // true if the logic supports arbitrary arrays bool m_bv_arrays; // true if the logic supports only bv arrays @@ -44,7 +49,7 @@ struct check_logic::imp { 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_str_util(m) { + imp(ast_manager & _m):m(_m), m_a_util(m), m_bv_util(m), m_ar_util(m), m_seq_util(m), m_str_util(m), m_dt_util(m), m_pb_util(m) { reset(); } @@ -180,10 +185,15 @@ struct check_logic::imp { m_reals = true; m_quantifiers = false; } + else if (logic == "QF_FD") { + m_bvs = true; + m_uf = true; + m_ints = true; + } else { m_unknown_logic = true; } - + m_logic = logic; } @@ -231,7 +241,7 @@ struct check_logic::imp { } } - void operator()(var * n) { + void operator()(var * n) { if (!m_quantifiers) fail("logic does not support quantifiers"); check_sort(m.get_sort(n)); @@ -273,7 +283,7 @@ struct check_logic::imp { } } } - + // check if the divisor is a numeral void check_div(app * n) { SASSERT(n->get_num_args() == 2); @@ -322,8 +332,8 @@ struct check_logic::imp { return false; non_numeral = arg; } - if (non_numeral == 0) - return true; + if (non_numeral == 0) + 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)) @@ -332,10 +342,10 @@ struct check_logic::imp { } return true; } - + bool is_diff_arg(expr * t) { if (is_diff_var(t)) - return true; + return true; if (is_numeral(t)) return true; if (m_a_util.is_add(t) || m_a_util.is_sub(t)) @@ -360,7 +370,7 @@ struct check_logic::imp { 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; + 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()) @@ -385,7 +395,7 @@ struct check_logic::imp { check_diff_arg(n); } } - + void operator()(app * n) { sort * s = m.get_sort(n); check_sort(s); @@ -409,18 +419,18 @@ struct check_logic::imp { 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... + // nothing to check... } else if (fid == m_ar_util.get_family_id()) { - // nothing to check... + // nothing to check... if (m_diff) check_diff_args(n); } else if (fid == m.get_basic_family_id()) { - // nothing to check... + // nothing to check... if (m_diff) { if (m.is_eq(n)) check_diff_predicate(n); @@ -434,15 +444,23 @@ struct check_logic::imp { else if (fid == m_seq_util.get_family_id()) { // nothing to check } - else if (fid == m_str_util.get_family_id()) { - // nothing to check - } + else if (fid == m_str_util.get_family_id()) { + // nothing to check + } + else if (fid == m_dt_util.get_family_id() && m_logic == "QF_FD") { + // nothing to check + } + else if (fid == m_pb_util.get_family_id() && m_logic == "QF_FD") { + // nothing to check + } else { - fail("logic does not support theory"); + std::stringstream strm; + strm << "logic does not support theory " << m.get_family_name(fid); + fail(strm.str().c_str()); } } - - void operator()(quantifier * n) { + + void operator()(quantifier * n) { if (!m_quantifiers) fail("logic does not support quantifiers"); } @@ -482,7 +500,7 @@ struct check_logic::imp { check_logic::check_logic() { m_imp = 0; } - + check_logic::~check_logic() { if (m_imp) dealloc(m_imp); diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 54a4e6cd5..4f1458318 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -46,6 +46,7 @@ Notes: #include"model_params.hpp" #include"th_rewriter.h" #include"tactic_exception.h" +#include"smt_logics.h" func_decls::func_decls(ast_manager & m, func_decl * f): m_decls(TAG(func_decl*, f, 0)) { @@ -507,93 +508,27 @@ void cmd_context::load_plugin(symbol const & name, bool install, svector>>>>>> upstream-master } + bool cmd_context::logic_has_array() const { - return !has_logic() || logic_has_array_core(m_logic); + return !has_logic() || smt_logics::logic_has_array(m_logic); } bool cmd_context::logic_has_datatype() const { - return !has_logic(); + return !has_logic() || smt_logics::logic_has_datatype(m_logic); } void cmd_context::init_manager_core(bool new_manager) { @@ -645,7 +584,7 @@ void cmd_context::init_manager_core(bool new_manager) { 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("seq"), alloc(seq_decl_plugin), logic_has_seq()); - register_plugin(symbol("pb"), alloc(pb_decl_plugin), !has_logic()); + 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("str"), alloc(str_decl_plugin), logic_has_str()); @@ -662,7 +601,12 @@ void cmd_context::init_manager_core(bool new_manager) { load_plugin(symbol("datatype"), logic_has_datatype(), fids); load_plugin(symbol("seq"), logic_has_seq(), fids); load_plugin(symbol("fpa"), logic_has_fpa(), fids); +<<<<<<< HEAD load_plugin(symbol("str"), logic_has_str(), fids); +======= + load_plugin(symbol("pb"), logic_has_pb(), fids); + +>>>>>>> upstream-master svector::iterator it = fids.begin(); svector::iterator end = fids.end(); for (; it != end; ++it) { @@ -710,38 +654,24 @@ void cmd_context::init_external_manager() { init_manager_core(false); } -bool cmd_context::supported_logic(symbol const & s) const { - return s == "QF_UF" || s == "UF" || - logic_has_arith_core(s) || logic_has_bv_core(s) || - logic_has_array_core(s) || logic_has_seq_core(s) || - logic_has_horn(s) || logic_has_fpa_core(s) || - s == "QF_S"; -} - bool cmd_context::set_logic(symbol const & s) { 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 (!supported_logic(s)) { + if (!smt_logics::supported_logic(s)) { return false; } m_logic = s; - if (is_logic("QF_RDL") || - is_logic("QF_LRA") || - is_logic("UFLRA") || - is_logic("LRA") || - is_logic("RDL") || - is_logic("QF_NRA") || - is_logic("QF_UFNRA") || - is_logic("QF_UFLRA")) + 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() == 0) - throw cmd_exception("state of the most recent check-sat command is not unknown"); + throw cmd_exception("state of the most recent check-sat command is not known"); return m_check_sat_result->reason_unknown(); } @@ -1512,6 +1442,31 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions } } +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.m_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); @@ -1641,6 +1596,7 @@ void cmd_context::validate_model() { scoped_ctrl_c ctrlc(eh); ptr_vector::const_iterator it = begin_assertions(); ptr_vector::const_iterator end = end_assertions(); + bool invalid_model = false; for (; it != end; ++it) { expr * a = *it; if (is_ground(a)) { @@ -1654,6 +1610,9 @@ void cmd_context::validate_model() { // 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_array, r); } @@ -1661,9 +1620,12 @@ void cmd_context::validate_model() { continue; } TRACE("model_validate", model_smt2_pp(tout, *this, *(md.get()), 0);); - throw cmd_exception("an invalid model was generated"); + invalid_model = true; } } + if (invalid_model) { + throw cmd_exception("an invalid model was generated"); + } } } diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 6225c3a69..39432e1c2 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -249,20 +249,14 @@ protected: void erase_psort_decl_core(symbol const & s); void erase_macro_core(symbol const & s); - bool logic_has_arith_core(symbol const & s) const; - bool logic_has_bv_core(symbol const & s) const; - bool logic_has_array_core(symbol const & s) const; - bool logic_has_seq_core(symbol const & s) const; - bool logic_has_fpa_core(symbol const & s) const; - bool logic_has_horn(symbol const& s) const; 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_str() const; - bool supported_logic(symbol const & s) 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;} @@ -393,6 +387,7 @@ public: 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); diff --git a/src/cmd_context/cmd_util.cpp b/src/cmd_context/cmd_util.cpp index 083493751..9774c75ff 100644 --- a/src/cmd_context/cmd_util.cpp +++ b/src/cmd_context/cmd_util.cpp @@ -6,7 +6,7 @@ Module Name: cmd_util.cpp Abstract: - Macros for definining new SMT2 front-end cmds. + Macros for defining new SMT2 front-end cmds. Author: diff --git a/src/cmd_context/cmd_util.h b/src/cmd_context/cmd_util.h index f0660af37..e575783f5 100644 --- a/src/cmd_context/cmd_util.h +++ b/src/cmd_context/cmd_util.h @@ -6,7 +6,7 @@ Module Name: cmd_util.h Abstract: - Macros for definining new SMT2 front-end cmds. + Macros for defining new SMT2 front-end cmds. Author: diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp index c482b4d09..53da91d1e 100644 --- a/src/cmd_context/interpolant_cmds.cpp +++ b/src/cmd_context/interpolant_cmds.cpp @@ -28,7 +28,6 @@ #include"mpq.h" #include"expr2var.h" #include"pp.h" -#include"pp_params.hpp" #include"iz3interp.h" #include"iz3checker.h" #include"iz3profiling.h" diff --git a/src/cmd_context/parametric_cmd.h b/src/cmd_context/parametric_cmd.h index 3e95832c4..cac5fe38e 100644 --- a/src/cmd_context/parametric_cmd.h +++ b/src/cmd_context/parametric_cmd.h @@ -72,6 +72,7 @@ public: // m_params.set_func_decl(m_last, f); // m_last = symbol::null; } + virtual void set_next_arg(cmd_context & ctx, sexpr * n) { UNREACHABLE(); } }; #endif diff --git a/src/cmd_context/simplify_cmd.cpp b/src/cmd_context/simplify_cmd.cpp index 1c249ce1f..8b354c8b7 100644 --- a/src/cmd_context/simplify_cmd.cpp +++ b/src/cmd_context/simplify_cmd.cpp @@ -24,9 +24,30 @@ Notes: #include"scoped_timer.h" #include"scoped_ctrl_c.h" #include"cancel_eh.h" +#include"seq_rewriter.h" #include class simplify_cmd : public parametric_cmd { + + class th_solver : public expr_solver { + cmd_context& m_ctx; + params_ref m_params; + ref m_solver; + public: + th_solver(cmd_context& ctx): m_ctx(ctx) {} + + virtual lbool check_sat(expr* e) { + 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,0); + m_solver->pop(1); + return r; + } + }; + expr * m_target; public: simplify_cmd(char const * name = "simplify"):parametric_cmd(name) {} @@ -70,10 +91,12 @@ public: 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); + unsigned rlimit = m_params.get_uint("rlimit", UINT_MAX); bool failed = false; cancel_eh eh(ctx.m().limit()); { diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index f044c9736..2f7b1ea6e 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -97,7 +97,7 @@ void help_tactic(cmd_context & ctx) { 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 ) excutes the given tactic for at most milliseconds, it fails if the execution takes more than milliseconds.\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"; @@ -165,7 +165,21 @@ public: } }; -typedef simple_check_sat_result check_sat_tactic_result; +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) { + } + + virtual void get_labels(svector & r) { + r.append(labels); + } + + virtual void add_labels(svector & r) { + labels.append(r); + } +}; class check_sat_using_tactict_cmd : public exec_given_tactic_cmd { public: @@ -189,6 +203,7 @@ public: ast_manager & m = ctx.m(); unsigned timeout = p.get_uint("timeout", ctx.params().m_timeout); unsigned rlimit = p.get_uint("rlimit", ctx.params().m_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);); @@ -208,7 +223,7 @@ public: cmd_context::scoped_watch sw(ctx); lbool r = l_undef; try { - r = check_sat(t, g, md, pr, core, reason_unknown); + 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) { @@ -288,9 +303,7 @@ public: #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."); -#ifndef _EXTERNAL_RELEASE p.insert("print_dependencies", CPK_BOOL, "(default: false) print dependencies when displaying the resultant set of goals."); -#endif exec_given_tactic_cmd::init_pdescrs(ctx, p); } diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp index b3a96aea4..1869b74ce 100644 --- a/src/duality/duality_solver.cpp +++ b/src/duality/duality_solver.cpp @@ -3099,7 +3099,7 @@ namespace Duality { // Maps nodes of derivation tree into old subtree hash_map cex_map; - virtual void ChooseExpand(const std::set &choices, std::set &best){ + virtual void ChooseExpand(const std::set &choices, std::set &best, bool, bool){ if(old_node == 0){ Heuristic::ChooseExpand(choices,best); return; diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp index 57188e5ca..d0f8c3a75 100755 --- a/src/interp/iz3base.cpp +++ b/src/interp/iz3base.cpp @@ -267,12 +267,15 @@ bool iz3base::is_sat(const std::vector &q, ast &_proof, std::vector &v p.set_bool("model", true); p.set_bool("unsat_core", true); scoped_ptr sf = mk_smt_solver_factory(); - ::solver *m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); - ::solver &s = *m_solver; + scoped_ptr< ::solver > solver = (*sf)(m(), p, true, true, true, ::symbol::null); + ::solver &s = *solver.get(); for(unsigned i = 0; i < q.size(); i++) s.assert_expr(to_expr(q[i].raw())); lbool res = s.check_sat(0,0); + if (m().canceled()) { + throw iz3_exception(Z3_CANCELED_MSG); + } if(res == l_false){ ::ast *proof = s.get_proof(); _proof = cook(proof); @@ -280,13 +283,17 @@ bool iz3base::is_sat(const std::vector &q, ast &_proof, std::vector &v else if(vars.size()) { model_ref(_m); s.get_model(_m); + if (!_m.get()) { + SASSERT(l_undef == res); + throw iz3_exception("interpolation cannot proceed without a model"); + } for(unsigned i = 0; i < vars.size(); i++){ expr_ref r(m()); _m.get()->eval(to_expr(vars[i].raw()),r,true); vars[i] = cook(r.get()); } } - dealloc(m_solver); + solver = 0; return res != l_false; } diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h index 85fbbdb3a..424b95359 100755 --- a/src/interp/iz3mgr.h +++ b/src/interp/iz3mgr.h @@ -45,7 +45,6 @@ #include"scoped_ctrl_c.h" #include"cancel_eh.h" #include"scoped_timer.h" -//# include"pp_params.hpp" /* A wrapper around an ast manager, providing convenience methods. */ diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index dd1ff87f5..2a68cba08 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -467,10 +467,17 @@ public: 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_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 s) const { + for (uint_set::iterator it = s.begin(), end = s.end(); it != end; ++it) { + if (is_final_state(*it)) + return true; + } + return false; + } bool is_epsilon_free() const { for (unsigned i = 0; i < m_delta.size(); ++i) { moves const& mvs = m_delta[i]; @@ -517,6 +524,13 @@ public: 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 states, moves& mvs, bool epsilon_closure = true) const { + for (uint_set::iterator it = states.begin(), end = states.end(); it != end; ++it) { + moves curr; + get_moves(*it, 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); } diff --git a/src/math/automata/boolean_algebra.h b/src/math/automata/boolean_algebra.h index 503878ef3..4f5527f5e 100644 --- a/src/math/automata/boolean_algebra.h +++ b/src/math/automata/boolean_algebra.h @@ -26,6 +26,7 @@ Revision History: 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; @@ -38,7 +39,8 @@ public: template class boolean_algebra : public positive_boolean_algebra { public: - virtual T mk_not(T x) = 0; + virtual ~boolean_algebra() {} + virtual T mk_not(T x) = 0; //virtual lbool are_equivalent(T x, T y) = 0; //virtual T simplify(T x) = 0; }; diff --git a/src/math/automata/symbolic_automata.h b/src/math/automata/symbolic_automata.h index cc3a0f5a8..5c2b4a2a4 100644 --- a/src/math/automata/symbolic_automata.h +++ b/src/math/automata/symbolic_automata.h @@ -104,8 +104,47 @@ public: 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 new_pred_neg(m_ba.mk_and(curr_pred, m_ba.mk_not(constraints[i])), m); + generate_min_terms_rec(constraints, min_terms, i + 1, curr_bv, new_pred_neg); + curr_bv.pop_back(); + } + } + }; + #endif diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index 2df5275c2..01476eb53 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -273,6 +273,96 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim 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 + + // adds non-final states of a to final if flipping and and final otherwise + if (a.is_final_configuration(set) != flip_acceptance) { + new_final_states.push_back(p_state_id); + } + + set.insert(a.init()); // Initial state as aset + s2id.insert(set, p_state_id++); // the index to the initial state is 0 + id2s.push_back(set); + + svector 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) { + 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; @@ -362,130 +452,11 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_produ } } -#if 0 -template -unsigned symbolic_automata::get_product_state_id(u2_map& pair2id, unsigned_pair const& p, unsigned& id) { - unsigned result = 0; - if (!pair2id.find(p, result)) { - result = id++; - pair2id.insert(p, result); - } - return result; -} -#endif + template typename symbolic_automata::automaton_t* symbolic_automata::mk_difference(automaton_t& a, automaton_t& b) { -#if 0 - map bs2id; // set of b-states to unique id - vector id2bs; // unique id to set of b-states - u2_map pair2id; // pair of states to new state id - unsigned sink_state = UINT_MAX; - uint_set bset; - moves_t new_moves; // moves in the resulting automaton - unsigned_vector new_final_states; // new final states - unsigned p_state_id = 0; // next state identifier - bs2id.insert(uint_set(), sink_state); // the sink state has no b-states - bset.insert(b.init()); // the initial state has a single initial b state - bs2id.insert(bset, 0); // the index to the initial b state is 0 - id2bs.push_back(bset); - if (!b.is_final_state(b.init()) && a.is_final_state(a.init())) { - new_final_states.push_back(p_state_id); - } - - svector todo; - unsigned_pair state(a.init(), 0); - todo.push_back(state); - pair2id.insert(state, p_state_id++); - - // or just make todo a vector whose indices coincide with state_id. - while (!todo.empty()) { - state = todo.back(); - unsigned state_id = pair2id[state]; - todo.pop_back(); - mvsA.reset(); - a.get_moves_from(state.first, mvsA, true); - if (state.second == sink_state) { - for (unsigned i = 0; i < mvsA.size(); ++i) { - unsigned_pair dst(mvsA[i].dst(), sink_state); - bool is_new = !pair2id.contains(dst); - unsigned dst_id = get_product_state_id(pair2id, dst, p_state_id); - new_moves.push_back(move_t(m, state_id, dst_id, mvsA[i].t())); - if (is_new && a.is_final_state(mvsA[i].dst())) { - new_final_states.push_back(dst_id); - todo.push_back(dst); - } - } - } - else { - get_moves_from(b, id2bs[state.second], mvsB); - generate_min_terms(mvsB, min_terms); - for (unsigned j = 0; j < min_terms.size(); ++j) { - for (unsigned i = 0; i < mvsA.size(); ++i) { - ref_t cond(m_ba.mk_and(mvsA[i].t(), min_terms[j].second), m); - switch (m_ba.is_sat(cond)) { - case l_false: - break; - case l_true: - ab_combinations.push_back(ab_comb(i, min_terms[j].first, cond)); - break; - case l_undef: - return 0; - } - } - } - - for (unsigned i = 0; i < ab_combinations.size(); ++i) { - move_t const& mvA = mvsA[ab_combinations[i].A]; - bset.reset(); - bool is_final = a.is_final_state(mvA.dst()); - for (unsigned j = 0; j < mvsB.size(); ++j) { - if (ab_combinations[i].B[j]) { - bset.insert(mvsB[j].dst()); - is_final &= !b.is_final_state(mvsB[j].dst()); - } - } - unsigned new_b; - if (bset.empty()) { - new_b = sink_state; - } - else if (!bs2id.find(bset, new_b)) { - new_b = id2bs.size(); - id2bs.push_back(bset); - bs2id.insert(bset, new_b); - } - unsigned_pair dst(mvA.dst(), new_b); - bool is_new = !pair2id.contains(dst); - dst_id = get_product_state_id(pair2id, dst, p_state_id); - move_t new_move(m, state_id, dst_id, ab_combinations[i].cond); - new_moves.push_back(new_move); - if (is_new) { - if (is_final) { - new_final_states.push_back(dst_id); - } - todo.push_back(dst); - } - } - } - } - - - if (new_final_states.empty()) { - return alloc(automaton_t, m); - } - - automaton_t* result = alloc(automaton_t, m, 0, new_final_states, new_moves); - -#if 0 - result->isEpsilonFree = true; - if (A.IsDeterministic) - result->isDeterministic = true; - result->EliminateDeadStates(); -#endif - return result; - -#endif - return 0; + return mk_product(a,mk_complement(b)); } #endif diff --git a/src/math/grobner/grobner.cpp b/src/math/grobner/grobner.cpp index 0c96dfde3..baa16b405 100644 --- a/src/math/grobner/grobner.cpp +++ b/src/math/grobner/grobner.cpp @@ -446,6 +446,7 @@ void grobner::merge_monomials(ptr_vector & monomials) { 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]; diff --git a/src/math/hilbert/heap_trie.h b/src/math/hilbert/heap_trie.h index ab55a44c3..e288bb076 100644 --- a/src/math/hilbert/heap_trie.h +++ b/src/math/hilbert/heap_trie.h @@ -283,7 +283,7 @@ public: ++m_stats.m_num_removes; // assumption: key is in table. node* n = m_root; - node* m; + node* m = 0; for (unsigned i = 0; i < num_keys(); ++i) { n->dec_ref(); VERIFY (to_trie(n)->find(get_key(keys, i), m)); diff --git a/src/math/polynomial/algebraic_numbers.cpp b/src/math/polynomial/algebraic_numbers.cpp index f4eb8a003..986c17664 100644 --- a/src/math/polynomial/algebraic_numbers.cpp +++ b/src/math/polynomial/algebraic_numbers.cpp @@ -1035,7 +1035,7 @@ namespace algebraic_numbers { unsigned num_rem = 0; // number of remaining sequences unsigned target_i = UINT_MAX; // index of sequence that is isolating - int target_lV, target_uV; + int target_lV = 0, target_uV = 0; for (unsigned i = 0; i < num_fs; i++) { if (seqs[i] == 0) continue; // sequence was discarded because it does not contain the root. @@ -1113,7 +1113,7 @@ namespace algebraic_numbers { unsigned num_rem = 0; // number of remaining sequences unsigned target_i = UINT_MAX; // index of sequence that is isolating - int target_lV, target_uV; + int target_lV = 0, target_uV = 0; for (unsigned i = 0; i < num_fs; i++) { if (seqs[i] == 0) continue; // sequence was discarded because it does not contain the root. diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index 93858b6be..1a4aa8304 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -25,7 +25,6 @@ Notes: #include"scoped_ptr_vector.h" #include"cooperate.h" #include"upolynomial_factorization.h" -#include"polynomial_factorization.h" #include"polynomial_primes.h" #include"permutation.h" #include"algebraic_numbers.h" diff --git a/src/math/polynomial/polynomial_factorization.cpp b/src/math/polynomial/polynomial_factorization.cpp deleted file mode 100644 index 4bf227d44..000000000 --- a/src/math/polynomial/polynomial_factorization.cpp +++ /dev/null @@ -1,1143 +0,0 @@ -/*++ -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. - ---*/ -#if 0 -// disabled for reorg - -#include"trace.h" -#include"util.h" -#include"polynomial_factorization.h" -#include"upolynomial_factorization_int.h" -#include"prime_generator.h" - -using namespace std; - -namespace polynomial { - -typedef upolynomial::manager::scoped_numeral scoped_numeral; - -/** - Generates a substitution of values for f -> f_univariate in order to reduce the factorization to the - univariate case. - - @param f multivariate polynomial (square-free, primitive, vars(f) > 1) - @param x the variable we want to keep as the univarate one - @param f_lc the leading coefficient of f in x - @param vars the vector of all variables from f witouth x - @param size the bound to use for selecting the values, i.e. |a_i| <= size - @param a the output values corresponding to vairables in (place place for x should be ignored) - @param f_univariate the output substitution -*/ -void generate_substitution_values( - polynomial_ref const & f, var x, polynomial_ref const & f_lc, var_vector const & vars, unsigned & size, - upolynomial::numeral_vector & a, upolynomial::manager upm, upolynomial::numeral_vector & f_u) { - - SASSERT(a.size() == vars.size()); - - TRACE("polynomial::factorization", tout << "polynomial::generate_substitution_values(f = " << f << ", f_lc = " << f_lc << ")";); - - // f = f_n x^n + ... + f_0, square-free and primitive - // this means - // f_lc = f_n - // since f is primitive, - // we are looking for numbers a_i such that - // (1) f_lc doesn't vanish - // (2) f_u = f(a_0, ..., a_n, x) is square-free - - manager & pm = f.m(); - numeral_manager & nm = pm.m(); - - // polynomial to use for subtituting into the lc(f) - polynomial_ref f_lc_subst(pm); - - // random generator - random_gen generator; - - // increase the size every once in a while (RETHINK THIS) - unsigned inc_size_c = 0; - unsigned inc_size_c_max = size; - - while (true) { - - // see if we should increase the size of the substitution - if ((++ inc_size_c) % inc_size_c_max == 0) { - size ++; - inc_size_c = 0; - inc_size_c_max *= 2; - } - - // the head coefficient we'll substitute in - f_lc_subst = f_lc; - - bool vanished = false; - for (unsigned i = 0; i < vars.size() && !vanished; ++ i) { - SASSERT(vars[i] != x); - - // the value for x_i - nm.set(a[i], (int)generator(2*size+1) - (int)size); - - // substitute - f_lc_subst = pm.substitute(f_lc_subst, x, a[i]); - - // did it vanish - vanished = pm.is_zero(f_lc_subst); - } - - if (vanished) { - // leading coefficient vanished, try again - continue; - } - - // substitute into f and get the univariate one - polynomial_ref f_subst(pm); - f_subst = pm.substitute(f, vars.size(), vars.c_ptr(), a.c_ptr()); - upm.to_numeral_vector(f_subst, f_u); - - // if the result is not square-free we try again - if (!upm.is_square_free(f_u)) - continue; - - // found it, break - break; - } -} - -/** - \brief Bound for the coefficients of the factorst of the multivariate polynomial f. R - Returns power of p -> p^e that covers the bound - - We use the gelfond bound here: - d_i: degree of x_i in f(x1, ..., x_n) - bound = |f| * 2^(\sum d_i - (n-1)/2)) -*/ -void multivariate_factor_coefficient_bound(polynomial_ref const & f, var x, numeral const & p, unsigned & e, numeral & p_e, var2degree & d) { - manager & pm = f.m(); - numeral_manager & nm = pm.m(); - - // compute g = lc(f)*f - polynomial_ref f_lc(pm), g(pm); - f_lc = pm.coeff(f, x, pm.degree(f, x)); - g = pm.mul(f, f_lc); - - // get the norm - scoped_numeral g_norm(nm); - pm.abs_norm(g, g_norm); - - // get the variable degrees - var_vector vars; - pm.vars(f, vars); - unsigned power = 0; - for (unsigned i = 0; i < vars.size(); ++ i) { - unsigned c_d = pm.degree(g, vars[i]); - d.set_degree(vars[i], c_d + 1); - power += c_d; - } - power = power - (vars.size()-1)/2; - - // compute the bound - scoped_numeral bound(nm); - nm.set(bound, 2); - nm.power(bound, power, bound); - nm.mul(g_norm, bound, bound); - - // return the first power of two that is bigger than the norm - e = 1; - nm.set(p_e, p); - while (nm.lt(p_e, bound)) { - nm.mul(p_e, p_e, p_e); - e *= 2; - } -} - -// check that A*S+B*T=C in zp mod ideal -bool check_solve(zp_manager & zp_pm, var2degree const & ideal, - zp_polynomial_ref const & A, zp_polynomial_ref const & B, zp_polynomial_ref const & C, - zp_polynomial_ref const & S, zp_polynomial_ref const & T) { - zp_polynomial_ref AS(zp_pm), BT(zp_pm), sum(zp_pm); - AS = zp_pm.mul(A, S); AS = zp_pm.mod_d(AS, ideal); - BT = zp_pm.mul(B, T); BT = zp_pm.mod_d(BT, ideal); - sum = zp_pm.add(AS, BT); - - TRACE("polynomial::factorization::multivariate", - tout << "zp_pm = Z_" << zp_pm.m().m().to_string(zp_pm.m().p()) << endl; - tout << "ideal = " << ideal << endl; - tout << "A = " << A << endl; - tout << "B = " << B << endl; - tout << "S = " << S << endl; - tout << "T = " << T << endl; - tout << "C = " << C << endl; - tout << "sum = " << sum << endl; - ); - - bool result = zp_pm.eq(sum, C); - return result; -} - -/** - Solve the equation A*S + B*T = C, given, AU + BV = 1, with deg(T) < deg(A) - S = U*C + tB - T = V*C - tA - we divide VC with A to get (T, t) -*/ -template -void solve(zp_manager & zp_pm, var x, var2degree const & ideal, - zp_polynomial_ref const & A, zp_polynomial_ref const & U, - zp_polynomial_ref const & B, zp_polynomial_ref const & V, - zp_polynomial_ref const & C, - output_manager & out_pm, - typename output_manager::polynomial_ref & S_out, - typename output_manager::polynomial_ref & T_out) { - TRACE("polynomial::factorization::multivariate", - tout << "polynomial::solve(" << endl; - tout << "zp_pm = Z_" << zp_pm.m().m().to_string(zp_pm.m().p()) << endl; - tout << "ideal = " << ideal << endl; - tout << "A = " << A << endl; - tout << "B = " << B << endl; - tout << "U = " << U << endl; - tout << "V = " << V << endl; - tout << "C = " << C << endl; - ); - - // solution is S = C*U + tB, T = C*V - tA - zp_polynomial_ref CV(zp_pm); - CV = zp_pm.mul(C, V); CV = zp_pm.mod_d(CV, ideal); - zp_polynomial_ref CU(zp_pm); - CU = zp_pm.mul(C, U); CU = zp_pm.mod_d(CU, ideal); - zp_polynomial_ref t(zp_pm), T(zp_pm); - zp_pm.exact_pseudo_division_mod_d(CV, A, x, ideal, t, T); - - zp_polynomial_ref tB(zp_pm); - tB = zp_pm.mul(t, B); tB = zp_pm.mod_d(tB, ideal); - zp_polynomial_ref S(zp_pm); - S = zp_pm.add(CU, tB); - - SASSERT(check_solve(zp_pm, ideal, A, B, C, S, T)); - - // convert to the other manager - S_out = convert(zp_pm, S, out_pm); - T_out = convert(zp_pm, T, out_pm); - - TRACE("polynomial::factorization::multivariate", - tout << "CU = " << CU << endl; - tout << "CV = " << CV << endl; - tout << "t = " << t << endl; - tout << "--- solution ---" << endl; - tout << "S = " << S_out << endl; - tout << "T = " << T_out << endl; - ); -} - -/** - A, B, U, V: multivariate polynomials in Z_p[x, ...], mod ..., also the output polynomials, y is not there - C: C = A*B in Z_p[x, ...] mod p, ... - - ideal: all the vars we care about in the ideal - - y, the variable we are lifting is not in ideal_vars, we will add it - - A monic, A*U+B*V = 1 - - p is not necessary prime, it's a power of a prime - - we're doing quadratic lifting here - - output: added y, i.e. - * all polynomials in Z_p[x, ..., y] mod (..., y^d) -*/ -void multivariate_hansel_lift_ideal( - zp_manager & zp_pm, var x, - zp_polynomial_ref const & C, - zp_polynomial_ref & A, zp_polynomial_ref & U, zp_polynomial_ref & B, zp_polynomial_ref & V, - var2degree & ideal, var y, unsigned d) { - numeral_manager & nm = zp_pm.m().m(); - - TRACE("polynomial::factorization::multivariate", - tout << "polynomial::multiratiate_hensel_lift_ideal" << endl; - tout << "zp_pm is Z_" << zp_pm.m().m().to_string(zp_pm.m().p()) << endl; - tout << "x = x" << x << endl; - tout << "y = x" << y << endl; - tout << "C = " << C << endl; - tout << "A = " << A << endl; - tout << "B = " << B << endl; - tout << "U = " << U << endl; - tout << "V = " << V << endl; - tout << "ideal = " << ideal << endl; - ); - - // constant 1 - scoped_numeral one(nm); - nm.set(one, 1); - zp_polynomial_ref one_p(zp_pm); - one_p = zp_pm.mk_const(one); - - SASSERT(zp_pm.degree(A, y) == 0 && zp_pm.degree(B, y) == 0 && zp_pm.degree(U, y) == 0 && zp_pm.degree(V, y) == 0); - - // update the ideal, and start with y - ideal.set_degree(y, 1); - unsigned current_d = 1; - zp_polynomial_ref current_power(zp_pm); - current_power = zp_pm.mk_polynomial(y); - - // lift quadratic until we are over the asked for - while (current_d < d) { - - TRACE("polynomial::factorization::multivariate", - tout << "zp_pm = Z_" << nm.to_string(zp_pm.m().p()) << endl; - tout << "ideal = " << ideal << endl; - tout << "C = " << C << endl; - tout << "A = " << A << endl; - tout << "B = " << B << endl; - ); - - // again, classic hensel: - // since C = A*B mod (p, ideal, y^k) we know that (C - A*B) = 0 mod (p, ideal, y^k) - // f = (C - A*B) mod (p, ideal, y^k) and thus divisible by y^current_d = current_power - zp_polynomial_ref f(zp_pm); - f = zp_pm.mul(A, B); - - TRACE("polynomial::factorization::multivariate", - tout << "zp_pm = Z_" << nm.to_string(zp_pm.m().p()) << endl; - tout << "ideal = " << ideal << endl; - tout << "C = " << C << endl; - tout << "A = " << A << endl; - tout << "B = " << B << endl; - tout << "f = " << f << endl; - ); - - f = zp_pm.sub(C, f); - f = zp_pm.exact_div(f, current_power); - f = zp_pm.mod_d(f, ideal); - - TRACE("polynomial::factorization::multivariate", - tout << "A = " << A << endl; - tout << "B = " << B << endl; - ); - - // get the S, T, solution to A*S+B*T = f, with d(T, x) < d(A, x) - // but we know that S = U*f + Bt, T = V*f - At, so we do division - zp_polynomial_ref S(zp_pm), T(zp_pm); - solve(zp_pm, x, ideal, A, U, B, V, f, zp_pm, S, T); - // now, lets lift A and B - // we want A1 = A + T*y^d, B1 = B + S*y^d with A1*B1 = C mod (ideal, y^(2*k)) - // hence A*B + y^d*(A*S+B*T) = C - S = zp_pm.mul(S, current_power); - T = zp_pm.mul(T, current_power); - A = zp_pm.add(A, T); - B = zp_pm.add(B, S); - - TRACE("polynomial::factorization::multivariate", - tout << "A = " << A << endl; - tout << "B = " << B << endl; - ); - - // again, classic quadratic hensel - // we need A*U1 + B*V1 = 1 mod (p, ideal, y^2), from above - // U1 = U + S*y^d, V1 = V + T*y^d, hence A*U + B*V + y^d(S + T) = 1 mod new ideal^2 - // we know that y^d divides (1-UA-BV) so we compute f = (1-UA-BV)/y^d - // UA + VB + y^d(SA + TB) = 1 (mod ideal^2) - // SA + TB = f (mod ideal) - // we solve for S, T again, and do as above - zp_polynomial_ref UA(zp_pm), BV(zp_pm); - f = zp_pm.mk_const(one); - UA = zp_pm.mul(U, A); - BV = zp_pm.mul(V, B); - f = zp_pm.sub(f, UA); f = zp_pm.sub(f, BV); - - TRACE("polynomial::factorization::multivariate", - tout << "ideal = " << ideal << endl; - tout << "current_power = " << current_power << endl; - tout << "UA = " << UA << endl; - tout << "BV = " << BV << endl; - tout << "f = " << f << endl; - tout << "x = x" << x << endl; - tout << "y = x" << y << endl; - ); - - f = zp_pm.exact_div(f, current_power); - f = zp_pm.mod_d(f, ideal); - - // get the S, T, solution to A*S+B*T = f, with d(T, x) < d(A, x) - solve(zp_pm, x, ideal, A, U, B, V, f, zp_pm, S, T); - // now, lets lift U and V - S = zp_pm.mul(S, current_power); - U = zp_pm.add(U, S); - T = zp_pm.mul(T, current_power); - V = zp_pm.add(V, T); - - // lift the ideal - current_d *= 2; - current_power = zp_pm.mul(current_power, current_power); - ideal.set_degree(y, current_d); - - // move, A, B, C, D into the ideal - A = zp_pm.mod_d(A, ideal); - B = zp_pm.mod_d(B, ideal); - S = zp_pm.mod_d(S, ideal); - T = zp_pm.mod_d(T, ideal); - - TRACE("polynomial::factorization::multivariate", - tout << "current_d = " << d << endl; - tout << "zp_pm is Z_" << zp_pm.m().m().to_string(zp_pm.m().p()) << endl; - tout << "x = x" << x << endl; - tout << "y = x" << y << endl; - tout << "C = " << C << endl; - tout << "A = " << A << endl; - tout << "B = " << B << endl; - tout << "U = " << U << endl; - tout << "V = " << V << endl; - tout << "ideal = " << ideal << endl; - ); - - SASSERT(check_solve(zp_pm, ideal, A, B, one_p, U, V)); - } -} - -template -bool are_equal_in( - manager_to_check pm, - typename manager_1::polynomial_ref const & A, - typename manager_2::polynomial_ref const & B) { - typename manager_to_check::polynomial_ref A_pm(pm), B_pm(pm); - - A_pm = convert(A.m(), A, pm); - B_pm = convert(B.m(), B, pm); - - bool equal = pm.eq(A_pm, B_pm); - return equal; -} - -/** - C: target multivariate polynomial mod ideal, p^e, the manager is in p^e - x: main variable - A, B, U, V: univariate polynomials in Z_p[x] such that U*A+B*V=1 mod ideal, these guys managers are in Z_p - - output: A_lifted, B_lifted, A = A_lifted mod ideal - A_lifted*B_lifted = f mod x_i^d_i, p^e -*/ -void multivariate_hansel_lift_zp( - manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, - zp_polynomial_ref const & C_pe, var x, unsigned e, - zp_polynomial_ref const & A_p, zp_polynomial_ref const & U_p, - zp_polynomial_ref const & B_p, zp_polynomial_ref const & V_p, - var2degree const & ideal, - zp_polynomial_ref & A_lifted, zp_polynomial_ref & B_lifted) { - TRACE("polynomial::factorization::multivariate", - tout << "polynomial::multiratiate_hensel_lift_zp:" << endl; - tout << "zp_pm = Z_" << zp_pm.m().m().to_string(zp_pm.m().p()) << endl; - tout << "zpe_pm = Z_" << zpe_pm.m().m().to_string(zpe_pm.m().p()) << endl; - tout << "x = x" << x << endl; - tout << "ideal = " << ideal << endl; - tout << "C_pe = " << C_pe << "," << endl; - tout << "A_p = " << A_p << "," << endl; - tout << "B_p = " << B_p << "," << endl; - ); - - // fixed zpe_pm - // upolynomial::zp_numeral_manager & zpe_nm = zpe_pm.m(); - // numeral const & pe = zpe_nm.p(); - // fixed zp_pm - upolynomial::zp_numeral_manager & zp_nm = zp_pm.m(); - numeral const & p = zp_nm.p(); - // regular numeral manager and mangager - numeral_manager & nm = zp_nm.m(); - - // sliding zpk_pm mod p^k - upolynomial::zp_numeral_manager zpk_nm(nm, p); - zp_manager zpk_pm(zpk_nm, &zp_pm.mm()); // in the end we copy the result over to zpe - unsigned k = 1; - upolynomial::scoped_numeral pk(nm); - nm.set(pk, zpk_nm.p()); - - // constant 1 - scoped_numeral one(nm); - nm.set(one, 1); - zp_polynomial_ref one_p(zpk_pm); - one_p = zpk_pm.mk_const(one); - - // lift until you get over the requested power of e - zp_polynomial_ref A_pk(zpk_pm), B_pk(zpk_pm), U_pk(zpk_pm), V_pk(zpk_pm); - - A_pk = convert(zp_pm, A_p, zpk_pm); - B_pk = convert(zp_pm, B_p, zpk_pm); - U_pk = convert(zp_pm, U_p, zpk_pm); - V_pk = convert(zp_pm, V_p, zpk_pm); - - TRACE("polynomial::factorization::multivariate", - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; - tout << "A_pk = " << A_pk << endl; - tout << "B_pk = " << B_pk << endl; - tout << "U_pk = " << U_pk << endl; - tout << "V_pk = " << V_pk << endl; - ); - - SASSERT(check_solve(zpk_pm, ideal, A_pk, B_pk, one_p, U_pk, V_pk)); - - while (k < e) { - - // standard hensel: - // (C - AB) and is divisible by p^k, so we compute f = (C - AB)/p^k mod ideal in Z[...] - zp_polynomial_ref f_pk(zpk_pm); - polynomial_ref A_pk_in_Z(pm), B_pk_in_Z(pm), AB_in_Z(pm), f_in_Z(pm); - f_in_Z = convert(zpe_pm, C_pe, pm); - A_pk_in_Z = convert(zpk_pm, A_pk, pm); - B_pk_in_Z = convert(zpk_pm, B_pk, pm); - AB_in_Z = pm.mul(A_pk_in_Z, B_pk_in_Z); - AB_in_Z = pm.mod_d(AB_in_Z, ideal); - f_in_Z = pm.sub(f_in_Z, AB_in_Z); - f_in_Z = pm.exact_div(f_in_Z, pk); - f_in_Z = pm.mod_d(f_in_Z, ideal); - f_pk = convert(pm, f_in_Z, zpk_pm); - - TRACE("polynomial::factorization::multivariate", - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; - tout << "f_pk = " << f_pk << endl; - ); - - // standard hensel we need to lift to p^(2k) - // we have U*A+V*B = 1, C = A*B, so p^k divides C - AB - // we want A1 = A + p^k*S, B1 = B + p^k*T, and also - // C - (A + p^k*S)*(B + p^k*T) = 0 mod (p^2k) - // C - A*B = p^k (T*A + S*B), i.e. - // f = (C - A*B)/p^k = (T*A + S*B), so we solve this equation in Z_p^k - polynomial_ref S_in_Z(pm), T_in_Z(pm); - solve(zpk_pm, x, ideal, A_pk, U_pk, B_pk, V_pk, f_pk, pm, T_in_Z, S_in_Z); - - TRACE("polynomial::factorization::multivariate", - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; - tout << "S_in_Z = " << S_in_Z << endl; - tout << "T_in_Z = " << T_in_Z << endl; - ); - - // lift A and B to, A = A + p^k*S, B = B + p^k*T - polynomial_ref A_next_in_Z(pm), B_next_in_Z(pm); - S_in_Z = pm.mul(pk, S_in_Z); - S_in_Z = pm.mod_d(S_in_Z, ideal); - A_next_in_Z = convert(zpk_pm, A_pk, pm); - A_next_in_Z = pm.add(A_next_in_Z, S_in_Z); - T_in_Z = pm.mul(pk, T_in_Z); - T_in_Z = pm.mod_d(T_in_Z, ideal); - B_next_in_Z = convert(zpk_pm, B_pk, pm); - B_next_in_Z = pm.add(B_next_in_Z, T_in_Z); - - TRACE("polynomial::factorization::multivariate", - tout << "pk = " << nm.to_string(pk) << endl; - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; - tout << "S_in_Z = " << S_in_Z << endl; - tout << "T_in_Z = " << T_in_Z << endl; - tout << "A_pk = " << A_pk << endl; - tout << "B_pk = " << B_pk << endl; - tout << "A_next_in_Z = " << A_next_in_Z << endl; - tout << "B_next_in_Z = " << B_next_in_Z << endl; - ); - - bool eq1 = are_equal_in(zpk_pm, A_next_in_Z, A_pk); - SASSERT(eq1); - bool eq2 = are_equal_in(zpk_pm, B_next_in_Z, B_pk); - SASSERT(eq2); - - // again, classic quadratic hensel - // we need A*U1 + B*V1 = 1 mod p^2k, from above - // U1 = U + p^k*S, V1 = V + p^k*T, hence A*U + B*V + p^k*(S + T) = 1 mod (p^2k) - // we know that p^k divides (1-UA-BV) so we compute f = (1-UA-BV)/p^k - // UA + VB + p^k(SA + TB) = 1 (mod p^k) - // SA + TB = f (mod ideal) - // we solve for S, T again, and do as above - polynomial_ref U_pk_in_Z(pm), V_pk_in_Z(pm), UA_in_Z(pm), BV_in_Z(pm); - U_pk_in_Z = convert(zpk_pm, U_pk, pm); - V_pk_in_Z = convert(zpk_pm, V_pk, pm); - f_in_Z = pm.mk_const(one); - UA_in_Z = pm.mul(U_pk_in_Z, A_next_in_Z); - UA_in_Z = pm.mod_d(UA_in_Z, ideal); - BV_in_Z = pm.mul(V_pk_in_Z, B_next_in_Z); - BV_in_Z = pm.mod_d(BV_in_Z, ideal); - f_in_Z = pm.sub(f_in_Z, UA_in_Z); - f_in_Z = pm.sub(f_in_Z, BV_in_Z); - - TRACE("polynomial::factorization::multivariate", - tout << "pk = " << nm.to_string(pk) << endl; - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; - tout << "U_pk_in_Z = " << U_pk_in_Z << endl; - tout << "V_pk_in_Z = " << V_pk_in_Z << endl; - tout << "UA_in_Z = " << UA_in_Z << endl; - tout << "BV_in_Z = " << BV_in_Z << endl; - tout << "f_in_Z = " << f_in_Z << endl; - ); - - f_in_Z = pm.exact_div(f_in_Z, pk); - f_pk = convert(pm, f_in_Z, zpk_pm); - - // get the S, T, solution to A*S+B*T = f, with d(T, x) < d(A, x) - solve(zpk_pm, x, ideal, A_pk, U_pk, B_pk, V_pk, f_pk, pm, S_in_Z, T_in_Z); - - TRACE("polynomial::factorization::multivariate", - tout << "pk = " << nm.to_string(pk) << endl; - tout << "zpk_pm = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; - tout << "S_in_Z = " << S_in_Z << endl; - tout << "T_in_Z = " << T_in_Z << endl; - ); - - // go to the next zpk - scoped_numeral next_pk(nm); - nm.mul(pk, pk, next_pk); - zpk_nm.set_p(next_pk); - - TRACE("polynomial::factorization::multivariate", - tout << "zp_pk = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; - ); - - // lift U - zp_polynomial_ref S_pk(zpk_pm); - S_in_Z = pm.mul(pk, S_in_Z); - S_pk = convert(pm, S_in_Z, zpk_pm); - - TRACE("polynomial::factorization::multivariate", - tout << "S_pk = " << S_pk << endl; - ); - - U_pk = zpk_pm.add(U_pk, S_pk); - // lift V - zp_polynomial_ref T_pk(zpk_pm); - T_in_Z = pm.mul(pk, T_in_Z); - T_pk = convert(pm, T_in_Z, zpk_pm); - - TRACE("polynomial::factorization::multivariate", - tout << "T_pk = " << T_pk << endl; - ); - - V_pk = zpk_pm.add(V_pk, T_pk); - - // lift A and B - TRACE("polynomial::factorization::multivariate", - tout << "A_pk_in_Z = " << A_pk_in_Z << endl; - tout << "B_pk_in_Z = " << B_pk_in_Z << endl; - ); - A_pk = convert(pm, A_pk_in_Z, zpk_pm); - B_pk = convert(pm, B_pk_in_Z, zpk_pm); - - // move to the next pk - k *= 2; - nm.set(pk, next_pk); - - TRACE("polynomial::factorization::multivariate", - tout << "zp_pk = Z_" << zpk_pm.m().m().to_string(zpk_pm.m().p()) << endl; - tout << "A_pk = " << A_pk << endl; - tout << "B_pk = " << B_pk << endl; - tout << "U_pk = " << U_pk << endl; - tout << "V_pk = " << V_pk << endl; - tout << "C_pe = " << C_pe << endl; - ); - - SASSERT(check_solve(zpk_pm, ideal, A_pk, B_pk, one_p, U_pk, V_pk)); - } - - // now convert to the non-sliding zpe_manager - SASSERT(k == e); - A_lifted = convert(zpk_pm, A_pk, zpe_pm); - B_lifted = convert(zpk_pm, B_pk, zpe_pm); -} - -/** - f: target multivariate polynomial mod x_i^d_i, p^e - x: main variable - all_vars: all variables (including x) - A, B, U, V: univariate polynomials in Z_p[x] such that U*A+B*V=1 from ext gcd - - output: A_lifted, B_lifted d(A) = d(A_lifted), A = A_lifted mod x_i^d_i, p - A_lifted*B_lifted = f mod x_i^d_i, p^e -*/ -void multivariate_hensel_lift( - manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, - zp_polynomial_ref const & f, var x, unsigned e, var_vector const & all_vars, - upolynomial::zp_manager & zp_upm, - upolynomial::numeral_vector const & U, upolynomial::numeral_vector const & A, - upolynomial::numeral_vector const & V, upolynomial::numeral_vector const & B, - var2degree & target_ideal, zp_polynomial_ref & A_lifted, zp_polynomial_ref & B_lifted) { - upolynomial::zp_numeral_manager & zp_nm = zp_upm.m(); - upolynomial::numeral_manager & nm = zp_nm.m(); - - TRACE("polynomial::factorization::multivariate", - tout << "polynomial::multiratiate_hensel_lift(" << endl; - tout << "f = " << f << "," << endl; - tout << "x = x" << x << "," << endl; - tout << "e = " << e << "," << endl; - tout << "U = "; zp_upm.display(tout, U); tout << "," << endl; - tout << "A = "; zp_upm.display(tout, A); tout << "," << endl; - tout << "V = "; zp_upm.display(tout, V); tout << "," << endl; - tout << "B = "; zp_upm.display(tout, B); tout << "," << endl; - tout << "target_ideal = " << target_ideal << "," << endl; - tout << "p = " << nm.to_string(zp_pm.m().p()) << endl; - tout << "pe = " << nm.to_string(zpe_pm.m().p()) << endl; - ); - - // multivariate versions of A, B, U, V that we keep lifting over ideal x_i^d_i - zp_polynomial_ref A_m_p(zp_pm), B_m_p(zp_pm), U_m_p(zp_pm), V_m_p(zp_pm); - A_m_p = zp_pm.to_polynomial(A, x); - B_m_p = zp_pm.to_polynomial(B, x); - U_m_p = zp_pm.to_polynomial(U, x); - V_m_p = zp_pm.to_polynomial(V, x); - - TRACE("polynomial::factorization::multivariate", - tout << "A_m_p = " << A_m_p << endl; - tout << "B_m_p = " << B_m_p << endl; - tout << "U_m_p = " << U_m_p << endl; - tout << "V_m_p = " << V_m_p << endl; - ); - - // the the target in Z_p[...] - zp_polynomial_ref C_m_p(zp_pm); - C_m_p = convert(zpe_pm, f, zp_pm); - - // lift each variable individually - var2degree lifted_ideal; - unsigned_vector lifted_degs; - for (unsigned i = 0; i < all_vars.size(); ++ i) { - if (all_vars[i] == x) { - // skip the main variable - continue; - } - // current variable and degree we are lifting to, y^(d_y), at least - var y = all_vars[i]; - // lift to y^(d_y) - multivariate_hansel_lift_ideal(zp_pm, x, C_m_p, A_m_p, U_m_p, B_m_p, V_m_p, lifted_ideal, y, target_ideal.degree(y)); - } - - TRACE("polynomial::factorization::multivariate", - tout << "A_m_p = " << A_m_p << endl; - tout << "B_m_p = " << B_m_p << endl; - tout << "U_m_p = " << U_m_p << endl; - tout << "V_m_p = " << V_m_p << endl; - tout << "lifted_ideal = " << lifted_ideal << endl; - ); - - // now lift it all to p^e - multivariate_hansel_lift_zp(pm, zp_pm, zpe_pm, f, x, e, A_m_p, U_m_p, B_m_p, V_m_p, lifted_ideal, A_lifted, B_lifted); -} - - -/** - f: multivariate polynomial - x: main variable - all_vars: all variables (including x) - f_u: f mod x_1, ..., x_n (excluding mod x), i.e. this is f(0, x), f_u is square_free - f_u_zp_factors: monic factors of f_u (mod p), pairvise gcd = 1 - - we're lifting the factors to mod x_1^d_1, ..., x_n&d_n (excliding x), mod p^e - i.e. such that f congruent to the new factors. output goes to f_zpe factors. -*/ -void multivariate_hensel_lift( - manager & pm, zp_manager & zp_pm, zp_manager & zpe_pm, - polynomial_ref const & f, var x, unsigned e, var_vector const & all_vars, - upolynomial::manager & upm, upolynomial::numeral_vector const & f_u, - upolynomial::zp_factors const & f_u_zp_factors, - var2degree & target_ideal, - zp_factors & f_zpe_factors) { - SASSERT(f_u_zp_factors.distinct_factors() > 1); - - TRACE("polynomial::factorization::multivariate", - tout << "polynomial::multivariate_hensel_lift(" << endl; - tout << "f = " << f << "," << endl; - tout << "x = x" << x << "," << endl; - tout << "e = " << e << "," << endl; - tout << "f_u = "; upm.display(tout, f_u); tout << "," << endl; - tout << "f_u_zp_factors" << f_u_zp_factors << "," << endl; - tout << "target_ideal = " << target_ideal << "," << endl; - tout << "f_zpe_factors = " << f_zpe_factors << ")" << endl; - ); - - // managers and all - numeral_manager & nm = pm.m(); - upolynomial::zp_manager & zp_upm = f_u_zp_factors.upm(); - // upolynomial::zp_numeral_manager & zp_nm = zp_upm.m(); - upolynomial::zp_numeral_manager & zpe_nm = zpe_pm.m(); - upolynomial::zp_manager zpe_upm(zpe_nm); - - // the targed product we want (mod x_i^d_i, mod p^e) - zp_polynomial_ref f_target_zpe(zpe_pm); - f_target_zpe = convert(pm, f, zpe_pm); - f_target_zpe = zpe_pm.mod_d(f_target_zpe, target_ideal); - - TRACE("polynomial::factorization::multivariate", - tout << "target_ideal = " << target_ideal << endl; - tout << "f_target_zpe = " << f_target_zpe << endl; - ); - - // we do the product by doing individual lifting like in the univarate case - zp_polynomial_ref B(zp_pm), C_p(zp_pm); - zp_polynomial_ref A_lifted(zpe_pm), B_lifted(zpe_pm); - upolynomial::scoped_numeral_vector B_u(nm), C_u(nm), tmp_u(nm); - upolynomial::scoped_numeral_vector U(nm), V(nm); - for (int i = 0, i_end = f_u_zp_factors.distinct_factors() - 1; i < i_end; ++ i) - { - // get the univarate ones to lift now - upolynomial::numeral_vector const & A_u = f_u_zp_factors[i]; - // current starting product is f_target_zpe(0, x) in *Z_p* - zp_upm.to_numeral_vector(f_target_zpe, x, C_u); - // we get the rest into B (mod p) - zp_upm.exact_div(C_u, A_u, B_u); - - TRACE("polynomial::factorization::multivariate", - tout << "p = " << nm.to_string(zp_upm.m().p()) << endl; - tout << "f_target_zpe = " << f_target_zpe << endl; - tout << "A_u = "; upm.display(tout, A_u); tout << endl; - tout << "B_u = "; upm.display(tout, B_u); tout << endl; - tout << "C_u = "; upm.display(tout, C_u); tout << endl; - ); - - // and get the U, V, such that A*U+B*V = 1 - zp_upm.ext_gcd(A_u, B_u, U, V, tmp_u); - - TRACE("polynomial::factorization::multivariate", - tout << "U = "; upm.display(tout, U); tout << endl; - tout << "V = "; upm.display(tout, V); tout << endl; - tout << "gcd = "; upm.display(tout, tmp_u); tout << endl; - ); - - // do the lifting for this pair - multivariate_hensel_lift(pm, zp_pm, zpe_pm, f_target_zpe, x, e, all_vars, zp_upm, U, A_u, V, B_u, target_ideal, A_lifted, B_lifted); - - // add the lifted A to the output - f_zpe_factors.push_back(A_lifted, 1); - // move to the new target by dividing with the lifted A - f_target_zpe = zpe_pm.exact_div(f_target_zpe, A_lifted); - } - - // add the last f_target - f_zpe_factors.push_back(f_target_zpe, 1); -} - -class mfactorization_combination_iterator : public upolynomial::factorization_combination_iterator_base { - - /** main variable */ - var m_x; - -public: - - mfactorization_combination_iterator(zp_factors const & factors, var x) - : upolynomial::factorization_combination_iterator_base(factors) - {} - - /** - \brief Filter the ones not in the degree set. - */ - bool filter_current() const { - return false; - } - - /** - \brief Returns the degree of the current selection. - */ - unsigned current_degree() const { - unsigned degree = 0; - zp_manager & pm = m_factors.pm(); - for (unsigned i = 0; i < left_size(); ++ i) { - degree += pm.degree(m_factors[m_current[i]], m_x); - } - return degree; - } - - void left(zp_polynomial_ref & out) const { - SASSERT(m_current_size > 0); - zp_manager & zp_pm = m_factors.pm(); - out = m_factors[m_current[0]]; - for (int i = 1; i < m_current_size; ++ i) { - out = zp_pm.mul(out, m_factors[m_current[i]]); - } - } - - void get_left_tail_coeff(numeral const & m, numeral & out) { - zp_manager & zp_pm = m_factors.pm(); - upolynomial::zp_numeral_manager & zp_nm = zp_pm.m(); - zp_nm.set(out, m); - for (int i = 0; i < m_current_size; ++ i) { - zp_nm.mul(out, zp_pm.numeral_tc(m_factors[m_current[i]]), out); - } - } - - void get_right_tail_coeff(numeral const & m, numeral & out) { - zp_manager & zp_pm = m_factors.pm(); - upolynomial::zp_numeral_manager & zp_nm = zp_pm.m(); - zp_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); - zp_nm.mul(out, zp_pm.numeral_tc(m_factors[current]), out); - current ++; - } else { - current ++; - selection_i ++; - } - } - } - } - - void right(zp_polynomial_ref & out) const { - SASSERT(m_current_size > 0); - zp_manager & zp_pm = m_factors.pm(); - upolynomial::zp_numeral_manager & zp_nm = zp_pm.m(); - - unsigned current = 0; - unsigned selection_i = 0; - - numeral one; - zp_nm.set(one, 1); - out = zp_pm.mk_const(one); - - // 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); - out = zp_pm.mul(out, m_factors[current]); - current ++; - } else { - current ++; - selection_i ++; - } - } - } - } -}; - - -// the multivariate factorization -bool factor_square_free_primitive(polynomial_ref const & f, factors & f_factors) { - - TRACE("polynomial::factorization", tout << "polynomial::factor_square_free_primitive(f = " << f << ", factors = " << f_factors << ")" << endl;); - - manager & pm = f.m(); - numeral_manager & nm = pm.m(); - - // to start with, maybe this should be part of input - var x = pm.max_var(f); - // get all the variables - var_vector vars, vars_no_x; - pm.vars(f, vars); - for(unsigned i = 0; i < vars.size(); ++ i) { - if (vars[i] != x) { - vars_no_x.push_back(vars[i]); - } - } - SASSERT(vars.size() > 1); - - // degree of the main variable - unsigned x_degree = pm.degree(f, x); - // the leading coefficient - polynomial_ref f_lc(pm); - f_lc = pm.coeff(f, x, x_degree); - - // the vector of values we substitute - upolynomial::scoped_numeral_vector a(nm); - - // the univariate polynomial - upolynomial::manager upm(nm); - upolynomial::scoped_numeral_vector f_u(upm); - - // generate the values to substitute and substitute them to get f_u(x) = f(a, x), the univariate version of f - unsigned size = 1; - a.resize(vars_no_x.size()); - for (unsigned i = 0; i < a.size(); ++ i) { nm.reset(a[i]); } - generate_substitution_values(f, x, f_lc, vars_no_x, size, a, upm, f_u); - - TRACE("polynomial::factorization::multivariate", - tout << "f_u = "; upm.display(tout, f_u); tout << endl; - tout << "substitution:" << endl; - for (unsigned i = 0; i < vars_no_x.size(); ++ i) { - tout << "x" << vars[i] << " -> " << nm.to_string(a[i]) << endl; - }); - - // the primitive part of f_u - scoped_numeral f_u_lc(nm); - upolynomial::scoped_numeral_vector f_u_pp(nm); - upm.get_primitive_and_content(f_u, f_u_pp, f_u_lc); - - TRACE("polynomial::factorization::multivariate", - tout << "f_u_lc" << nm.to_string(f_u_lc) << endl; - tout << "f_u_pp = "; upm.display(tout, f_u_pp); tout << endl; - ); - - // factor the univariate one - upolynomial::factors factors_u(upm); - upolynomial::factor_square_free(upm, f_u, factors_u); - - TRACE("polynomial::factorization::multivariate", - tout << "factors_u = " << factors_u << endl; - ); - - // if there is no univariate factors, it's irreducible - if (factors_u.distinct_factors() == 1) { - f_factors.push_back(f, 1); - return false; - } - - // translate f with a, so that we work modulo x_i^k and not (x_i - a_i)^k - polynomial_ref f_t(pm); - // Do the translation, we must have that a[x] = 0 - pm.translate(f, vars_no_x, a, f_t); - - TRACE("polynomial::factorization::multivariate", - tout << "f_t = " << f_t << endl; - ); - - // the zp manager stuff, we'll be changing the base - upolynomial::zp_numeral_manager zp_nm(nm, 2); - upolynomial::zp_manager zp_upm(zp_nm); - - // get the first prime number p that keeps f square-free - scoped_numeral p(nm); - prime_iterator prime_it; - upolynomial::scoped_numeral_vector f_u_pp_zp(nm); - do { - // create Z_p with the next prime - nm.set(p, prime_it.next()); - // translate to Zp[x] - zp_nm.set_p(p); - upolynomial::to_zp_manager(zp_upm, f_u_pp, f_u_pp_zp); - } while (!zp_upm.is_square_free(f_u_pp_zp)); - - TRACE("polynomial::factorization::multivariate", - tout << "p = " << p << endl; - tout << "f_t = " << f_t << endl; - ); - - // convert factors of f to factors modulo p (monic ones) - upolynomial::zp_factors factors_u_zp(zp_upm); - upolynomial::scoped_numeral_vector current_factor(nm); - for (unsigned i = 0; i < factors_u.distinct_factors(); ++ i) { - upolynomial::to_zp_manager(zp_upm, factors_u[i], current_factor); - zp_upm.mk_monic(current_factor); - factors_u_zp.push_back_swap(current_factor, 1); - } - - TRACE("polynomial::factorization::multivariate", - tout << "factors_u_zp = " << factors_u_zp << endl; - ); - - // compute factor coefficient bound (pover p^e) of the translated f with the leading coefficient added, i.e. - // f_t*lc(f_t, x) = f_t*lc(f, x) - unsigned e; - scoped_numeral p_e(nm); - var2degree target_ideal; - upolynomial::scoped_numeral f_t_lc(nm); - nm.set(f_t_lc, pm.numeral_lc(f_t, x)); - polynomial_ref f_t_with_lc(pm); - f_t_with_lc = pm.mul(f_t_lc, f_t); - multivariate_factor_coefficient_bound(f_t_with_lc, x, p, e, p_e, target_ideal); - - TRACE("polynomial::factorization::multivariate", - tout << "target_ideal = " << target_ideal << endl; - ); - - // do the multivariate lifting of the translated one f_t - upolynomial::zp_numeral_manager zpe_nm(nm, p_e); - zp_manager zpe_pm(zpe_nm, &pm.mm()); - zp_manager zp_pm(zp_nm, &pm.mm()); - zp_factors factors_zpe(zpe_pm); - multivariate_hensel_lift(pm, zp_pm, zpe_pm, f_t, x, e, vars, upm, f_u_pp_zp, factors_u_zp, target_ideal, factors_zpe); - - TRACE("polynomial::factorization::multivariate", - tout << "factors_zpe = " << factors_zpe << endl; - ); - - // try the factors from the lifted combinations - factors f_t_factors(pm); - bool remove = false; - mfactorization_combination_iterator it(factors_zpe, x); - unsigned max_degre = pm.degree(f_t, x) / 2; - zp_polynomial_ref zpe_trial_factor(zpe_pm); - while (it.next(remove)) { - // - // 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 meen that the rest is factorized, so we still take out - // the original factor - // - bool using_left = it.current_degree() <= max_degre; - if (using_left) { - // do a quick check first - it.left(zpe_trial_factor); - } else { - // do a quick check first - it.right(zpe_trial_factor); - } - // add the lc(f_pp) to the trial divisor - zpe_trial_factor = zpe_pm.mul(f_t_lc, zpe_trial_factor); - polynomial_ref trial_factor(pm), trial_factor_quo(pm); - trial_factor = convert(zpe_pm, zpe_trial_factor, pm); - bool true_factor = true; - trial_factor_quo = pm.exact_div(f_t_with_lc, trial_factor); - // 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 - upolynomial::scoped_numeral trial_factor_cont(nm); - pm.int_content(f_t, trial_factor_cont); - trial_factor = pm.exact_div(trial_factor, trial_factor_cont); - // add the factor - f_t_factors.push_back(trial_factor, 1); - // we continue with the int-primitive quotient (with the lc added back) - // but we also have to keep lc(f_t)*f_t - pm.int_content(trial_factor_quo, f_t_lc); // content - trial_factor_quo = pm.exact_div(trial_factor_quo, f_t_lc); - nm.set(f_t_lc, pm.numeral_lc(trial_factor_quo, x)); - f_t = pm.mul(f_t_lc, trial_factor_quo); - // but we also remove it from the iterator - remove = true; - } else { - // don't remove this combination - remove = false; - } - } - - // translate the factor back - for (unsigned i = 0; i < a.size(); ++ i) { - nm.neg(a[i]); - } - for (unsigned i = 0; i < f_t_factors.distinct_factors(); ++ i) { - polynomial_ref factor(pm); - pm.translate(f_t_factors[i], vars_no_x, a, factor); - f_factors.push_back(factor, 1); - } - - TRACE("polynomial::factorization", tout << "polynomial::factor_square_free_primitive(f = " << f << ") => " << f_factors << endl;); - return true; -} - -}; // end polynomial namespace - -#endif diff --git a/src/math/polynomial/polynomial_factorization.h b/src/math/polynomial/polynomial_factorization.h deleted file mode 100644 index f069121ba..000000000 --- a/src/math/polynomial/polynomial_factorization.h +++ /dev/null @@ -1,65 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - polynomial_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. - ---*/ - -#pragma once - -#if 0 -// Disabled for reorg -#include "polynomial.h" -#include "upolynomial.h" -#include "bit_vector.h" -#include "z3_exception.h" - -namespace polynomial { - - /** - \brief Something to throw when we are in trouble. - */ - class factorization_exception : public default_exception { - public: - factorization_exception(char const * msg) : default_exception(msg) {} - }; - - /** - \brief Factor the polynomial f from Z[x1, ..., x_n]. Returns the index of the last factor that is completely - factored. I.e., if the method returns m, then f_1, ..., f_m are true irreducible factors, and the rest might - be further reducible. - */ - unsigned factor(polynomial_ref & f, factors & factors); - - /** - \brief Factor the square-free primitive polynomial f from Z[x1, ..., x_n]. Returns true if the factorization - was sucesseful, i.e. it was completed and the result is complete. Otherwise the quarantee is that the all but - the last factor are irreducible. - */ - bool factor_square_free_primitive(polynomial_ref const & f, factors & factors); -} - -inline std::ostream & operator<<(std::ostream & out, polynomial::factors & factors) { - factors.display(out); - return out; -} - -#endif diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 06ba38b4a..c31eabb62 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -7,7 +7,7 @@ Module Name: Abstract: - Model-based optimization for linear real arithmetic. + Model-based optimization and projection for linear real, integer arithmetic. Author: @@ -26,6 +26,7 @@ std::ostream& operator<<(std::ostream& out, opt::ineq_type 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; } @@ -34,14 +35,12 @@ std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) { namespace opt { - model_based_opt::model_based_opt() - { + model_based_opt::model_based_opt() { m_rows.push_back(row()); } bool model_based_opt::invariant() { - // variables in each row are sorted. for (unsigned i = 0; i < m_rows.size(); ++i) { if (!invariant(i, m_rows[i])) { return false; @@ -50,19 +49,23 @@ namespace opt { return true; } +#define PASSERT(_e_) if (!(_e_)) { TRACE("opt", display(tout, r);); SASSERT(_e_); } + bool model_based_opt::invariant(unsigned index, row const& r) { - rational val = r.m_coeff; vector const& vars = r.m_vars; for (unsigned i = 0; i < vars.size(); ++i) { - var const& v = vars[i]; - SASSERT(i + 1 == vars.size() || v.m_id < vars[i+1].m_id); - SASSERT(!v.m_coeff.is_zero()); - val += v.m_coeff * m_var2value[v.m_id]; + // 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)); } - SASSERT(val == r.m_value); - SASSERT(r.m_type != t_eq || val.is_zero()); - SASSERT(index == 0 || r.m_type != t_lt || val.is_neg()); - SASSERT(index == 0 || r.m_type != t_le || !val.is_pos()); + + PASSERT(r.m_value == get_row_value(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; } @@ -90,20 +93,25 @@ namespace opt { // inf_eps model_based_opt::maximize() { SASSERT(invariant()); - unsigned_vector other; unsigned_vector bound_trail, bound_vars; + TRACE("opt", display(tout << "tableau\n");); while (!objective().m_vars.empty()) { - TRACE("opt", display(tout << "tableau\n");); var v = objective().m_vars.back(); unsigned x = v.m_id; rational const& coeff = v.m_coeff; unsigned bound_row_index; rational bound_coeff; - other.reset(); - if (find_bound(x, bound_row_index, bound_coeff, other, coeff.is_pos())) { + if (find_bound(x, bound_row_index, bound_coeff, coeff.is_pos())) { SASSERT(!bound_coeff.is_zero()); - for (unsigned i = 0; i < other.size(); ++i) { - resolve(bound_row_index, bound_coeff, other[i], x); + TRACE("opt", display(tout << "update: " << v << " ", objective()); + for (unsigned i = 0; i < m_above.size(); ++i) { + display(tout << "resolve: ", m_rows[m_above[i]]); + }); + for (unsigned i = 0; i < m_above.size(); ++i) { + resolve(bound_row_index, bound_coeff, m_above[i], x); + } + for (unsigned i = 0; i < m_below.size(); ++i) { + resolve(bound_row_index, bound_coeff, m_below[i], x); } // coeff*x + objective <= ub // a2*x + t2 <= 0 @@ -111,11 +119,13 @@ namespace opt { // objective + t2*coeff/a2 <= ub mul_add(false, m_objective_id, - coeff/bound_coeff, bound_row_index); - m_rows[bound_row_index].m_alive = false; + 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(); } } @@ -136,15 +146,33 @@ namespace opt { } + void model_based_opt::update_value(unsigned x, rational const& val) { + rational old_val = m_var2value[x]; + m_var2value[x] = val; + unsigned_vector const& row_ids = m_var2row_ids[x]; + for (unsigned i = 0; i < row_ids.size(); ++i) { + unsigned row_id = row_ids[i]; + 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) { - rational eps(0); for (unsigned i = bound_trail.size(); i > 0; ) { --i; unsigned x = bound_vars[i]; row& r = m_rows[bound_trail[i]]; rational val = r.m_coeff; - rational x_val; - rational x_coeff; + rational old_x_val = m_var2value[x]; + rational new_x_val; + rational x_coeff, eps(0); vector const& vars = r.m_vars; for (unsigned j = 0; j < vars.size(); ++j) { var const& v = vars[j]; @@ -155,28 +183,21 @@ namespace opt { val += m_var2value[v.m_id]*v.m_coeff; } } - TRACE("opt", display(tout << "v" << x << " val: " << val - << " coeff_x: " << x_coeff << " val_x: " << m_var2value[x] << " ", r); ); SASSERT(!x_coeff.is_zero()); - x_val = -val/x_coeff; - // - // - // ax + t < 0 - // <=> x < -t/a - // <=> x := -t/a - epsilon - // - if (r.m_type == t_lt) { - // Adjust epsilon to be - if (!x_val.is_zero() && (eps.is_zero() || eps >= abs(x_val))) { - eps = abs(x_val)/rational(2); - } - if (!r.m_value.is_zero() && (eps.is_zero() || eps >= abs(r.m_value))) { - eps = abs(r.m_value)/rational(2); - } + 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()) { - x_val -= eps; + new_x_val -= eps; } // // -ax + t < 0 @@ -185,27 +206,47 @@ namespace opt { // <=> x > t/a // <=> x := t/a + epsilon // - else if (x_coeff.is_neg()) { - x_val += eps; + else { + new_x_val += eps; } } - m_var2value[x] = x_val; - r.m_value = (x_val * x_coeff) + val; + 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; - TRACE("opt", display(tout << "v" << x << " val: " << val << " coeff_x: " - << x_coeff << " val_x: " << m_var2value[x] << " ", r); ); + r.m_value = get_row_value(r); SASSERT(invariant(bound_trail[i], r)); } + + // update and check bounds for all other affected rows. + for (unsigned i = bound_trail.size(); i > 0; ) { + --i; + unsigned x = bound_vars[i]; + unsigned_vector const& row_ids = m_var2row_ids[x]; + for (unsigned j = 0; j < row_ids.size(); ++j) { + unsigned row_id = row_ids[j]; + row & r = m_rows[row_id]; + r.m_value = get_row_value(r); + SASSERT(invariant(row_id, r)); + } + } + SASSERT(invariant()); } - bool model_based_opt::find_bound(unsigned x, unsigned& bound_row_index, rational& bound_coeff, unsigned_vector& other, bool is_pos) { + 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 i = 0; i < row_ids.size(); ++i) { unsigned row_id = row_ids[i]; + SASSERT(row_id != m_objective_id); if (visited.contains(row_id)) { continue; } @@ -226,24 +267,39 @@ namespace opt { else if ((value == lub_val && r.m_type == opt::t_lt) || (is_pos && value < lub_val) || (!is_pos && value > lub_val)) { - other.push_back(bound_row_index); + m_above.push_back(bound_row_index); lub_val = value; - bound_row_index = row_id; + bound_row_index = row_id; bound_coeff = a; } else { - other.push_back(row_id); + m_above.push_back(row_id); } } else { - r.m_alive = false; + m_below.push_back(row_id); } } } return bound_row_index != UINT_MAX; } - - rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) { + + 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::get_row_value(row const& r) const { + vector const& vars = r.m_vars; + rational val = r.m_coeff; + for (unsigned i = 0; i < vars.size(); ++i) { + var const& v = vars[i]; + val += v.m_coeff * m_var2value[v.m_id]; + } + return val; + } + + rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) const { row const& r = m_rows[row_id]; if (r.m_vars.empty()) { return rational::zero(); @@ -259,7 +315,7 @@ namespace opt { } if (id < var_id) { lo = mid + 1; - } + } else { hi = mid; } @@ -306,10 +362,160 @@ namespace opt { if (m_rows[row_dst].m_alive) { rational a2 = get_coefficient(row_dst, x); - mul_add(row_dst != m_objective_id && a1.is_pos() == a2.is_pos(), row_dst, -a2/a1, row_src); + 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()) { + 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); + } } } - + + // 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()); + + 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; + bool use_case1 = + (src_c * dst_val + dst_c * src_val + slack).is_nonpos() + || abs_src_c.is_one() + || abs_dst_c.is_one(); + + if (use_case1) { + // dst <- abs_src_c*dst + abs_dst_c*src - slack + mul(row_dst, abs_src_c); + sub(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 + // + + 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 (unsigned i = 0; i < src.size(); ++i) { + if (src[i].m_id != x) dst.push_back(src[i]); + } + } + + 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 (unsigned i = 0; i < r.m_vars.size(); ++i) { + r.m_vars[i].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::normalize(unsigned row_id) { + row& r = m_rows[row_id]; + if (r.m_vars.empty()) 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 // @@ -317,6 +523,7 @@ namespace opt { if (c.is_zero()) { return; } + m_new_vars.reset(); row& r1 = m_rows[row_id1]; row const& r2 = m_rows[row_id2]; @@ -403,12 +610,18 @@ namespace opt { else if (r.m_coeff.is_neg()) { out << r.m_coeff << " "; } - out << r.m_type << " 0; value: " << r.m_value << "\n"; + 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"; + } } - unsigned model_based_opt::add_var(rational const& value) { + 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); m_var2row_ids.push_back(unsigned_vector()); return v; } @@ -417,34 +630,75 @@ namespace opt { return m_var2value[var]; } - void model_based_opt::set_row(unsigned row_id, vector const& coeffs, rational const& c, ineq_type rel) { + 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 (unsigned i = 0; i < coeffs.size(); ++i) { val += m_var2value[coeffs[i].m_id] * coeffs[i].m_coeff; + SASSERT(!is_int(coeffs[i].m_id) || coeffs[i].m_coeff.is_int()); + is_int_row &= is_int(coeffs[i].m_id); } r.m_alive = true; r.m_coeff = c; r.m_value = val; r.m_type = rel; - SASSERT(invariant(row_id, r)); - } - - void model_based_opt::add_constraint(vector const& coeffs, rational const& c, ineq_type rel) { - rational val(c); - unsigned row_id = m_rows.size(); - m_rows.push_back(row()); - set_row(row_id, coeffs, c, rel); - for (unsigned i = 0; i < coeffs.size(); ++i) { - m_var2row_ids[coeffs[i].m_id].push_back(row_id); + 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 (unsigned i = 0; i < r.m_vars.size(); ++i) { + m_var2row_ids[r.m_vars[i].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 (unsigned i = 0; i < coeffs.size(); ++i) { + m_var2row_ids[coeffs[i].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, t_le); + set_row(m_objective_id, coeffs, c, rational::zero(), t_le); } void model_based_opt::get_live_rows(vector& rows) { @@ -475,7 +729,8 @@ namespace opt { // void model_based_opt::project(unsigned x) { unsigned_vector& lub_rows = m_lub; - unsigned_vector& glb_rows = m_glb; + 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; @@ -484,6 +739,8 @@ namespace opt { uint_set visited; lub_rows.reset(); glb_rows.reset(); + mod_rows.reset(); + bool lub_is_unit = false, glb_is_unit = false; // select the lub and glb. for (unsigned i = 0; i < row_ids.size(); ++i) { unsigned row_id = row_ids[i]; @@ -503,7 +760,10 @@ namespace opt { solve_for(row_id, x); return; } - if (a.is_pos()) { + 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 || @@ -513,6 +773,7 @@ namespace opt { lub_strict = r.m_type == t_lt; } lub_rows.push_back(row_id); + lub_is_unit &= a.is_one(); } else { SASSERT(a.is_neg()); @@ -525,36 +786,176 @@ namespace opt { glb_strict = r.m_type == t_lt; } glb_rows.push_back(row_id); + glb_is_unit &= a.is_minus_one(); } } - unsigned row_index = (lub_rows.size() <= glb_rows.size())? lub_index : glb_index; - glb_rows.append(lub_rows); + + if (!mod_rows.empty()) { + solve_mod(x, mod_rows); + return; + } + + unsigned lub_size = lub_rows.size(); + unsigned glb_size = glb_rows.size(); + unsigned row_index = (lub_size <= glb_size) ? lub_index : glb_index; + glb_rows.append(lub_rows); + + // There are only upper or only lower bounds. if (row_index == UINT_MAX) { for (unsigned i = 0; i < glb_rows.size(); ++i) { unsigned row_id = glb_rows[i]; SASSERT(m_rows[row_id].m_alive); SASSERT(!get_coefficient(row_id, x).is_zero()); - m_rows[row_id].m_alive = false; + retire_row(row_id); } + return; } - else { - rational coeff = get_coefficient(row_index, x); - for (unsigned i = 0; i < glb_rows.size(); ++i) { - unsigned row_id = glb_rows[i]; - if (row_id != row_index) { - resolve(row_index, coeff, row_id, 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_rows.size(); + rational coeff = get_coefficient(row_id1, x); + for (unsigned j = 0; j < glb_size; ++j) { + unsigned row_id2 = glb_rows[j]; + if (last) { + resolve(row_id1, coeff, row_id2, x); + } + else { + unsigned row_id3 = copy_row(row_id2); + resolve(row_id1, coeff, row_id3, x); + } } } - m_rows[row_index].m_alive = false; + for (unsigned i = 0; i < lub_size; ++i) { + retire_row(lub_rows[i]); + } + return; } + + // General case. + rational coeff = get_coefficient(row_index, x); + for (unsigned i = 0; i < glb_rows.size(); ++i) { + unsigned row_id = glb_rows[i]; + if (row_id != row_index) { + resolve(row_index, coeff, row_id, x); + } + } + retire_row(row_index); } + // + // 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 + // + + void model_based_opt::solve_mod(unsigned x, unsigned_vector const& mod_rows) { + SASSERT(!mod_rows.empty()); + rational D(1); + for (unsigned i = 0; i < mod_rows.size(); ++i) { + D = lcm(D, m_rows[mod_rows[i]].m_mod); + } + TRACE("opt", display(tout << "lcm: " << D << " tableau\n");); + rational val_x = m_var2value[x]; + rational u = mod(val_x, D); + SASSERT(u.is_nonneg() && u < D); + for (unsigned i = 0; i < mod_rows.size(); ++i) { + replace_var(mod_rows[i], x, u); + SASSERT(invariant(mod_rows[i], m_rows[mod_rows[i]])); + } + // + // 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 i = 0; i < row_ids.size(); ++i) { + unsigned row_id = row_ids[i]; + if (!visited.contains(row_id)) { + // x |-> D*y + u + replace_var(row_id, x, D, y, u); + visited.insert(row_id); + } + } + project(y); + } + + // 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 + void model_based_opt::solve_for(unsigned row_id1, unsigned x) { - rational a = get_coefficient(row_id1, x); - row& r1 = m_rows[row_id1]; + rational a = get_coefficient(row_id1, x), b; SASSERT(!a.is_zero()); - SASSERT(r1.m_type == t_eq); - SASSERT(r1.m_alive); + SASSERT(m_rows[row_id1].m_type == t_eq); + SASSERT(m_rows[row_id1].m_alive); + if (m_var2is_int[x] && !abs(a).is_one()) { + row& r1 = m_rows[row_id1]; + vector coeffs; + mk_coeffs_without(coeffs, r1.m_vars, x); + add_divides(coeffs, r1.m_coeff, abs(a)); + } unsigned_vector const& row_ids = m_var2row_ids[x]; uint_set visited; visited.insert(row_id1); @@ -562,15 +963,19 @@ namespace opt { unsigned row_id2 = row_ids[i]; if (!visited.contains(row_id2)) { visited.insert(row_id2); - resolve(row_id1, a, row_id2, x); + b = get_coefficient(row_id2, x); + if (!b.is_zero()) { + resolve(row_id1, a, row_id2, x); + } } } - r1.m_alive = false; + retire_row(row_id1); } void model_based_opt::project(unsigned num_vars, unsigned const* vars) { for (unsigned i = 0; i < num_vars; ++i) { project(vars[i]); + TRACE("opt", display(tout << "After projecting: v" << vars[i] << "\n");); } } diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index e4ba288c8..eb0bc2570 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -30,7 +30,8 @@ namespace opt { enum ineq_type { t_eq, t_lt, - t_le + t_le, + t_mod }; @@ -52,9 +53,11 @@ namespace opt { 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(); } }; private: @@ -63,36 +66,73 @@ namespace opt { 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; + 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, unsigned_vector& other, bool is_pos); + bool find_bound(unsigned x, unsigned& bound_index, rational& bound_coeff, bool is_pos); - rational get_coefficient(unsigned row_id, unsigned var_id); + rational get_coefficient(unsigned row_id, unsigned var_id) const; + + rational get_row_value(row const& r) const; void resolve(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 set_row(unsigned row_id, vector const& coeffs, rational const& c, ineq_type rel); - + 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 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); + void project(unsigned var); void solve_for(unsigned row_id, unsigned x); + void solve_mod(unsigned x, unsigned_vector const& mod_rows); + + 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); + unsigned add_var(rational const& value, bool is_int = false); // retrieve updated value of variable. rational get_value(unsigned var_id); @@ -101,6 +141,9 @@ namespace opt { // 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); @@ -132,5 +175,6 @@ namespace opt { std::ostream& operator<<(std::ostream& out, opt::ineq_type ie); +inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::var const v) { return out << "v" << v.m_id; } #endif diff --git a/src/math/simplex/simplex_def.h b/src/math/simplex/simplex_def.h index cb86e2a85..762e8ceb2 100644 --- a/src/math/simplex/simplex_def.h +++ b/src/math/simplex/simplex_def.h @@ -540,7 +540,7 @@ namespace simplex { var_t max = get_num_vars(); var_t result = max; row r = row(m_vars[x_i].m_base2row); - int n; + int n = 0; unsigned best_col_sz = UINT_MAX; int best_so_far = INT_MAX; diff --git a/src/math/subpaving/subpaving_t.h b/src/math/subpaving/subpaving_t.h index a6aa3cf32..ccef1a318 100644 --- a/src/math/subpaving/subpaving_t.h +++ b/src/math/subpaving/subpaving_t.h @@ -354,7 +354,7 @@ public: }; /** - \brief Watched element (aka occurence) can be: + \brief Watched element (aka occurrence) can be: - A clause - A definition (i.e., a variable) diff --git a/src/math/subpaving/tactic/subpaving_tactic.cpp b/src/math/subpaving/tactic/subpaving_tactic.cpp index 27e096777..2d0199223 100644 --- a/src/math/subpaving/tactic/subpaving_tactic.cpp +++ b/src/math/subpaving/tactic/subpaving_tactic.cpp @@ -35,6 +35,8 @@ class subpaving_tactic : public tactic { 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(); } diff --git a/src/model/model_core.cpp b/src/model/model_core.cpp index 3c10f8704..503abac2f 100644 --- a/src/model/model_core.cpp +++ b/src/model/model_core.cpp @@ -87,3 +87,21 @@ void model_core::register_decl(func_decl * d, func_interp * fi) { } } +void model_core::unregister_decl(func_decl * d) { + decl2expr::obj_map_entry * ec = m_interp.find_core(d); + if (ec && ec->get_data().m_value != 0) { + m_manager.dec_ref(ec->get_data().m_key); + m_manager.dec_ref(ec->get_data().m_value); + m_interp.remove(d); + m_const_decls.erase(d); + return; + } + + decl2finterp::obj_map_entry * ef = m_finterp.find_core(d); + if (ef && ef->get_data().m_value != 0) { + m_manager.dec_ref(ef->get_data().m_key); + dealloc(ef->get_data().m_value); + m_finterp.remove(d); + m_func_decls.erase(d); + } +} diff --git a/src/model/model_core.h b/src/model/model_core.h index 8527a0bca..c42451c5a 100644 --- a/src/model/model_core.h +++ b/src/model/model_core.h @@ -60,6 +60,7 @@ public: 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; diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index fd420bad6..06bbceb43 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -33,6 +33,7 @@ Revision History: #include"cooperate.h" #include"ast_pp.h" #include"ast_util.h" +#include"model_smt2_pp.h" struct evaluator_cfg : public default_rewriter_cfg { @@ -72,6 +73,8 @@ struct evaluator_cfg : public default_rewriter_cfg { 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); } @@ -407,6 +410,7 @@ struct evaluator_cfg : public default_rewriter_cfg { 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()); @@ -445,7 +449,9 @@ struct evaluator_cfg : public default_rewriter_cfg { } else_case = g->get_else(); if (!else_case) { - TRACE("model_evaluator", tout << "no else case " << mk_pp(a, m()) << "\n";); + TRACE("model_evaluator", tout << "no else case " << mk_pp(a, m()) << "\n"; + /*model_smt2_pp(tout, m(), m_model, 0);*/ + ); return false; } if (!is_ground(else_case)) { @@ -529,5 +535,12 @@ void model_evaluator::operator()(expr * t, expr_ref & result) { m_imp->operator()(t, result); } +expr_ref model_evaluator::operator()(expr * t) { + TRACE("model_evaluator", tout << mk_ismt2_pp(t, m()) << "\n";); + expr_ref result(m()); + m_imp->operator()(t, result); + return result; +} + diff --git a/src/model/model_evaluator.h b/src/model/model_evaluator.h index 7fafbe14d..3f4da5c96 100644 --- a/src/model/model_evaluator.h +++ b/src/model/model_evaluator.h @@ -41,6 +41,8 @@ public: void operator()(expr * t, expr_ref & r); + expr_ref operator()(expr* t); + void cleanup(params_ref const & p = params_ref()); void reset(params_ref const & p = params_ref()); diff --git a/src/model/model_implicant.cpp b/src/model/model_implicant.cpp index 44c70036c..0d37a04df 100644 --- a/src/model/model_implicant.cpp +++ b/src/model/model_implicant.cpp @@ -666,8 +666,8 @@ void model_implicant::eval_eq(app* e, expr* arg1, expr* arg2) { } void model_implicant::eval_basic(app* e) { - expr* arg1, *arg2; - expr *argCond, *argThen, *argElse, *arg; + expr* arg1 = 0, *arg2 = 0; + expr *argCond = 0, *argThen = 0, *argElse = 0, *arg = 0; bool has_x = false; unsigned arity = e->get_num_args(); switch(e->get_decl_kind()) { diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index a90d420cb..2ca74a73b 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -303,6 +303,7 @@ namespace datalog { 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(); } @@ -1000,14 +1001,20 @@ namespace datalog { if (is_quantifier(body)) { quantifier* q = to_quantifier(body); expr* e = q->get_expr(); - VERIFY(m.is_implies(e, body, e2)); - fml = m.mk_quantifier(false, q->get_num_decls(), - q->get_decl_sorts(), q->get_decl_names(), - body); + if (m.is_implies(e, body, e2)) { + fml = m.mk_quantifier(false, q->get_num_decls(), + q->get_decl_sorts(), q->get_decl_names(), + body); + } + else { + fml = body; + } } else { - VERIFY(m.is_implies(body, body, e2)); fml = body; + if (m.is_implies(body, body, e2)) { + fml = body; + } } queries.push_back(fml); } diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index 3d8b56beb..ba841c84f 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -119,7 +119,6 @@ namespace datalog { 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 display_profile(std::ostream& out) = 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; @@ -272,6 +271,7 @@ namespace datalog { 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; diff --git a/src/muz/base/dl_engine_base.h b/src/muz/base/dl_engine_base.h index f353fbf2e..9878f3a9e 100644 --- a/src/muz/base/dl_engine_base.h +++ b/src/muz/base/dl_engine_base.h @@ -66,7 +66,7 @@ namespace datalog { } virtual void reset_statistics() {} - virtual void display_profile(std::ostream& out) const {} + 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); diff --git a/src/muz/base/dl_rule_set.cpp b/src/muz/base/dl_rule_set.cpp index 5eb8e5f7e..e9b383b56 100644 --- a/src/muz/base/dl_rule_set.cpp +++ b/src/muz/base/dl_rule_set.cpp @@ -601,7 +601,7 @@ namespace datalog { return; } while (!m_stack_P.empty()) { - unsigned on_stack_num; + unsigned on_stack_num = 0; VERIFY( m_preorder_nums.find(m_stack_P.back(), on_stack_num) ); if (on_stack_num <= p_num) { break; @@ -710,7 +710,7 @@ namespace datalog { item_set::iterator eend=deps.end(); for (; eit!=eend; ++eit) { T * tgt = *eit; - unsigned tgt_comp; + unsigned tgt_comp = 0; VERIFY( m_component_nums.find(tgt, tgt_comp) ); //m_components[tgt_comp]==0 means the edge is intra-component. diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 78ce453ec..b1719577b 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -591,7 +591,7 @@ namespace datalog { /** - \brief Remove the first occurence of \c el from \c v and return \c true. If + \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. */ diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fixedpoint_params.pyg index 86cfb30ac..8e7d6a7cb 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fixedpoint_params.pyg @@ -131,6 +131,7 @@ def_module_params('fixedpoint', ('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 squarring"), diff --git a/src/muz/dataflow/dataflow.h b/src/muz/dataflow/dataflow.h index 1e52c5f93..d2be194eb 100644 --- a/src/muz/dataflow/dataflow.h +++ b/src/muz/dataflow/dataflow.h @@ -72,7 +72,7 @@ namespace datalog { } e->get_data().m_value->push_back(cur); } - if (cur->get_uninterpreted_tail_size() == 0) { + 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) { @@ -97,7 +97,7 @@ namespace datalog { } void step_bottom_up() { - for(todo_set::iterator I = m_todo[m_todo_idx].begin(), + for(todo_set::iterator I = m_todo[m_todo_idx].begin(), E = m_todo[m_todo_idx].end(); I!=E; ++I) { ptr_vector * rules; if (!m_body2rules.find(*I, rules)) @@ -236,7 +236,7 @@ namespace datalog { return m_facts.get(m_rule->get_decl(idx), Fact::null_fact); } unsigned size() const { - return m_rule->get_uninterpreted_tail_size(); + return m_rule->get_positive_tail_size(); } }; diff --git a/src/muz/ddnf/ddnf.cpp b/src/muz/ddnf/ddnf.cpp index e7babc719..6863a908f 100644 --- a/src/muz/ddnf/ddnf.cpp +++ b/src/muz/ddnf/ddnf.cpp @@ -836,7 +836,7 @@ namespace datalog { } void compile_eq(expr* e, expr_ref& result, var* v, unsigned hi, unsigned lo, expr* c) { - tbv* t; + tbv* t = 0; // TBD: hi, lo are ignored. VERIFY(m_expr2tbv.find(e, t)); var_ref w(m); diff --git a/src/muz/fp/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp index 41c0f77af..b51af7d53 100644 --- a/src/muz/fp/datalog_parser.cpp +++ b/src/muz/fp/datalog_parser.cpp @@ -480,7 +480,7 @@ protected: str2sort m_sort_dict; - // true if an error occured during the current call to the parse_stream + // true if an error occurred during the current call to the parse_stream // function bool m_error; public: @@ -896,7 +896,7 @@ protected: tok = m_lexer->next_token(); if (tok != TK_COLON) { tok = unexpected(tok, - "Expecting colon in declaration (first occurence of a predicate must be a declaration)"); + "Expecting colon in declaration (first occurrence of a predicate must be a declaration)"); return tok; } tok = m_lexer->next_token(); diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index 049c34343..49632b39c 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -527,13 +527,8 @@ static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_c 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)); - // #ifndef _EXTERNAL_RELEASE - // TODO: we need these! -#if 1 - ctx.insert(alloc(dl_push_cmd, dl_ctx)); // not exposed to keep command-extensions simple. + ctx.insert(alloc(dl_push_cmd, dl_ctx)); ctx.insert(alloc(dl_pop_cmd, dl_ctx)); -#endif - // #endif } void install_dl_cmds(cmd_context & ctx) { diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index 1c25b015d..587488fc9 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -274,7 +274,7 @@ namespace pdr { for (unsigned i = 0; i < src.size(); ) { expr * curr = src[i].get(); - unsigned stored_lvl; + unsigned stored_lvl = 0; VERIFY(m_prop2level.find(curr, stored_lvl)); SASSERT(stored_lvl >= src_level); bool assumes_level; @@ -575,7 +575,7 @@ namespace pdr { // Predicates that are variable representatives. Other predicates at // positions the variables occur are made equivalent with these. expr_ref_vector conj(m); - app_ref_vector& var_reprs = *(alloc(app_ref_vector, m)); + app_ref_vector var_reprs(m); ptr_vector aux_vars; unsigned ut_size = rule.get_uninterpreted_tail_size(); @@ -584,7 +584,9 @@ namespace pdr { init_atom(pts, rule.get_head(), var_reprs, conj, UINT_MAX); for (unsigned i = 0; i < ut_size; ++i) { if (rule.is_neg_tail(i)) { - throw default_exception("PDR does not support negated predicates in rule tails"); + char const* msg = "PDR does not supported negated predicates in rule tails"; + IF_VERBOSE(0, verbose_stream() << msg << "\n";); + throw default_exception(msg); } init_atom(pts, rule.get_tail(i), var_reprs, conj, i); } @@ -599,10 +601,16 @@ namespace pdr { flatten_and(tail); for (unsigned i = 0; i < tail.size(); ++i) { expr_ref tmp(m); - var_subst(m, false)(tail[i].get(), var_reprs.size(), (expr*const*)var_reprs.c_ptr(), tmp); + var_subst vs(m, false); + vs(tail[i].get(), var_reprs.size(), (expr*const*)var_reprs.c_ptr(), tmp); conj.push_back(tmp); TRACE("pdr", tout << mk_pp(tail[i].get(), m) << "\n" << mk_pp(tmp, m) << "\n";); - SASSERT(is_ground(tmp)); + if (!is_ground(tmp)) { + std::stringstream msg; + msg << "PDR cannot solve non-ground tails: " << tmp; + IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); + throw default_exception(msg.str()); + } } expr_ref fml = pm.mk_and(conj); th_rewriter rw(m); @@ -624,7 +632,7 @@ namespace pdr { m_rule2transition.insert(&rule, fml.get()); rules.push_back(&rule); } - m_rule2inst.insert(&rule, &var_reprs); + m_rule2inst.insert(&rule, alloc(app_ref_vector, var_reprs)); m_rule2vars.insert(&rule, aux_vars); TRACE("pdr", tout << rule.get_decl()->get_name() << "\n"; @@ -888,14 +896,16 @@ namespace pdr { void model_node::dequeue(model_node*& root) { - TRACE("pdr", tout << this << " " << state() << "\n";); + TRACE("pdr", tout << this << " root: " << root << " " << state() << "\n";); if (!m_next && !m_prev) return; SASSERT(m_next); SASSERT(m_prev); SASSERT(children().empty()); if (this == m_next) { - SASSERT(root == this); - root = 0; + SASSERT(m_prev == this); + if (root == this) { + root = 0; + } } else { m_next->m_prev = m_prev; @@ -967,7 +977,7 @@ namespace pdr { } void model_search::enqueue_leaf(model_node* n) { - TRACE("pdr_verbose", tout << n << " " << n->state() << " goal: " << m_goal << "\n";); + TRACE("pdr_verbose", tout << "node: " << n << " " << n->state() << " goal: " << m_goal << "\n";); SASSERT(n->is_open()); if (!m_goal) { m_goal = n; @@ -997,7 +1007,7 @@ namespace pdr { return m_cache[l]; } - void model_search::erase_children(model_node& n, bool backtrack) { + void model_search::erase_children(model_node& n, bool backtrack) { ptr_vector todo, nodes; todo.append(n.children()); remove_goal(n); @@ -1459,13 +1469,20 @@ namespace pdr { reset(); } - void context::reset() { - TRACE("pdr", tout << "\n";); - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); + void context::reset(decl2rel& rels) { + decl2rel::iterator it = rels.begin(), end = rels.end(); for (; it != end; ++it) { dealloc(it->m_value); } - m_rels.reset(); + rels.reset(); + } + + void context::reset(bool full) { + TRACE("pdr", tout << "reset\n";); + reset(m_rels); + if (full) { + reset(m_rels_tmp); + } m_search.reset(); m_query = 0; m_last_result = l_undef; @@ -1487,6 +1504,7 @@ namespace pdr { e->get_data().m_value->add_rule(pred_rules[i]); } } + TRACE("pdr", tout << "adding rules\n";); datalog::rule_set::iterator rit = rules.begin(), rend = rules.end(); for (; rit != rend; ++rit) { datalog::rule* r = *rit; @@ -1501,6 +1519,7 @@ namespace pdr { } } // Initialize use list dependencies + TRACE("pdr", tout << "initialize use list dependencies\n";); decl2rel::iterator it = rels.begin(), end = rels.end(); for (; it != end; ++it) { func_decl* pred = it->m_key; @@ -1514,9 +1533,11 @@ namespace pdr { } } + TRACE("pdr", tout << "initialize predicate transformers\n";); // Initialize the predicate transformers. it = rels.begin(), end = rels.end(); for (; it != end; ++it) { + SASSERT(it->m_value); pred_transformer& rel = *it->m_value; rel.initialize(rels); TRACE("pdr", rel.display(tout); ); @@ -1524,21 +1545,24 @@ namespace pdr { } void context::update_rules(datalog::rule_set& rules) { - decl2rel rels; + TRACE("pdr", tout << "update rules\n";); + reset(m_rels_tmp); init_core_generalizers(rules); - init_rules(rules, rels); - decl2rel::iterator it = rels.begin(), end = rels.end(); + init_rules(rules, m_rels_tmp); + decl2rel::iterator it = m_rels_tmp.begin(), end = m_rels_tmp.end(); for (; it != end; ++it) { pred_transformer* pt = 0; if (m_rels.find(it->m_key, pt)) { it->m_value->inherit_properties(*pt); } } - reset(); - it = rels.begin(), end = rels.end(); + reset(false); + it = m_rels_tmp.begin(), end = m_rels_tmp.end(); for (; it != end; ++it) { m_rels.insert(it->m_key, it->m_value); } + m_rels_tmp.reset(); + TRACE("pdr", tout << "done update rules\n";); } unsigned context::get_num_levels(func_decl* p) { @@ -1759,6 +1783,7 @@ namespace pdr { smt::kernel solver(m, get_fparams()); solver.assert_expr(tmp); lbool res = solver.check(); + TRACE("pdr", tout << tmp << " " << res << "\n";); if (res != l_false) { msg << "rule validation failed when checking: " << mk_pp(tmp, m); IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); @@ -1817,6 +1842,10 @@ namespace pdr { m_fparams.m_arith_mode = AS_UTVPI; m_fparams.m_arith_expand_eqs = true; } + else { + m_fparams.m_arith_mode = AS_ARITH; + m_fparams.m_arith_expand_eqs = false; + } } } if (m_params.pdr_use_convex_closure_generalizer()) { @@ -1861,6 +1890,7 @@ namespace pdr { } lbool context::solve() { + TRACE("pdr", tout << "solve\n";); m_last_result = l_undef; try { solve_impl(); @@ -2076,6 +2106,7 @@ namespace pdr { } case l_undef: { TRACE("pdr", tout << "unknown state: " << mk_pp(m_pm.mk_and(cube), m) << "\n";); + IF_VERBOSE(1, verbose_stream() << "unknown state\n";); throw unknown_exception(); } } diff --git a/src/muz/pdr/pdr_context.h b/src/muz/pdr/pdr_context.h index c3567bdd8..a32a65c48 100644 --- a/src/muz/pdr/pdr_context.h +++ b/src/muz/pdr/pdr_context.h @@ -328,6 +328,7 @@ namespace pdr { datalog::context* m_context; manager m_pm; decl2rel m_rels; // Map from relation predicate to fp-operator. + decl2rel m_rels_tmp; func_decl_ref m_query_pred; pred_transformer* m_query; mutable model_search m_search; @@ -370,6 +371,8 @@ namespace pdr { void reset_core_generalizers(); + void reset(decl2rel& rels); + void validate(); void validate_proof(); void validate_search(); @@ -410,8 +413,7 @@ namespace pdr { lbool solve(); - - void reset(); + void reset(bool full = true); void set_query(func_decl* q) { m_query_pred = q; } diff --git a/src/muz/pdr/pdr_dl_interface.cpp b/src/muz/pdr/pdr_dl_interface.cpp index 37f708009..5f4a200fc 100644 --- a/src/muz/pdr/pdr_dl_interface.cpp +++ b/src/muz/pdr/pdr_dl_interface.cpp @@ -19,12 +19,8 @@ Revision History: #include "dl_context.h" #include "dl_mk_coi_filter.h" -#include "dl_mk_interp_tail_simplifier.h" -#include "dl_mk_subsumption_checker.h" -#include "dl_mk_rule_inliner.h" #include "dl_rule.h" #include "dl_rule_transformer.h" -#include "smt2parser.h" #include "pdr_context.h" #include "pdr_dl_interface.h" #include "dl_rule_set.h" @@ -145,6 +141,12 @@ lbool dl_interface::query(expr * query) { query_pred = rules.get_output_predicate(); + TRACE("pdr", + tout << "rules:\n"; + m_ctx.display_rules(tout); + m_ctx.display_smt2(0, 0, tout); + ); + IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); m_pdr_rules.replace_rules(rules); m_pdr_rules.close(); @@ -152,12 +154,13 @@ lbool dl_interface::query(expr * query) { 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->set_axioms(bg_assertion); + m_context->set_axioms(bg_assertion); m_context->update_rules(m_pdr_rules); if (m_pdr_rules.get_rules().empty()) { diff --git a/src/muz/pdr/pdr_reachable_cache.cpp b/src/muz/pdr/pdr_reachable_cache.cpp index 85100c19f..2bedc1393 100644 --- a/src/muz/pdr/pdr_reachable_cache.cpp +++ b/src/muz/pdr/pdr_reachable_cache.cpp @@ -109,7 +109,12 @@ namespace pdr { UNREACHABLE(); break; } - if (found) m_stats.m_hits++; m_stats.m_miss++; + if (found) { + m_stats.m_hits++; + } + else { + m_stats.m_miss++; + } return found; } diff --git a/src/muz/pdr/pdr_sym_mux.cpp b/src/muz/pdr/pdr_sym_mux.cpp index a68b57e9c..47edb35ac 100644 --- a/src/muz/pdr/pdr_sym_mux.cpp +++ b/src/muz/pdr/pdr_sym_mux.cpp @@ -366,6 +366,7 @@ public: app * a = to_app(s); func_decl * sym = a->get_decl(); if(!m_parent.has_index(sym, m_from_idx)) { + (void) m_homogenous; SASSERT(!m_homogenous || !m_parent.is_muxed(sym)); return false; } diff --git a/src/muz/pdr/pdr_util.cpp b/src/muz/pdr/pdr_util.cpp index 9af3ea8b4..934be5c63 100644 --- a/src/muz/pdr/pdr_util.cpp +++ b/src/muz/pdr/pdr_util.cpp @@ -247,7 +247,7 @@ namespace pdr { } bool test_eq(expr* e) const { - expr* lhs, *rhs; + expr* lhs = 0, *rhs = 0; VERIFY(m.is_eq(e, lhs, rhs)); if (!a.is_int_real(lhs)) { return true; diff --git a/src/muz/rel/dl_base.cpp b/src/muz/rel/dl_base.cpp index b7f4d6cef..f79f6c8eb 100644 --- a/src/muz/rel/dl_base.cpp +++ b/src/muz/rel/dl_base.cpp @@ -391,7 +391,7 @@ namespace datalog { std::ostringstream buffer; buffer << "creating large table of size " << upper_bound; if (p) buffer << " for relation " << p->get_name(); - warning_msg(buffer.str().c_str()); + warning_msg("%s", buffer.str().c_str()); } for(table_element i = 0; i < upper_bound; i++) { diff --git a/src/muz/rel/dl_base.h b/src/muz/rel/dl_base.h index 1c7a81444..d3422f882 100644 --- a/src/muz/rel/dl_base.h +++ b/src/muz/rel/dl_base.h @@ -220,6 +220,8 @@ namespace datalog { */ class mutator_fn : public base_fn { public: + virtual ~mutator_fn() {} + virtual void operator()(base_object & t) = 0; virtual bool supports_attachment(base_object& other) { return false; } @@ -869,6 +871,7 @@ namespace datalog { 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 @@ -882,6 +885,7 @@ namespace datalog { 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 diff --git a/src/muz/rel/dl_compiler.cpp b/src/muz/rel/dl_compiler.cpp index 7fb2afd9e..0d4507971 100644 --- a/src/muz/rel/dl_compiler.cpp +++ b/src/muz/rel/dl_compiler.cpp @@ -466,7 +466,7 @@ namespace datalog { //used to save on filter_identical instructions where the check is already done //by the join operation - unsigned second_tail_arg_ofs; + unsigned second_tail_arg_ofs = 0; // whether to dealloc the previous result bool dealloc = true; diff --git a/src/muz/rel/dl_mk_partial_equiv.cpp b/src/muz/rel/dl_mk_partial_equiv.cpp deleted file mode 100644 index d79a46720..000000000 --- a/src/muz/rel/dl_mk_partial_equiv.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_partial_equiv.cpp - -Abstract: - - Rule transformer which identifies predicates that are partial equivalence relations. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-05-14 - -Revision History: - ---*/ - -#include "dl_mk_partial_equiv.h" -#include "dl_relation_manager.h" -#include "ast_pp.h" - -namespace datalog { - - bool mk_partial_equivalence_transformer::is_symmetry(rule const* r) { - func_decl* p = r->get_decl(); - return - p->get_arity() == 2 && - p->get_domain(0) == p->get_domain(1) && - r->get_tail_size() == 1 && - r->get_tail(0)->get_decl() == p && - r->get_head()->get_arg(0) == r->get_tail(0)->get_arg(1) && - r->get_head()->get_arg(1) == r->get_tail(0)->get_arg(0) && - is_var(r->get_head()->get_arg(0)) && - is_var(r->get_head()->get_arg(1)) && - r->get_head()->get_arg(0) != r->get_head()->get_arg(1); - } - - - bool mk_partial_equivalence_transformer::is_transitivity(rule const* r) { - func_decl* p = r->get_decl(); - if (p->get_arity() != 2 || - p->get_domain(0) != p->get_domain(1) || - r->get_tail_size() != 2 || - r->get_tail(0)->get_decl() != p || - r->get_tail(1)->get_decl() != p) { - return false; - } - app* h = r->get_head(); - app* a = r->get_tail(0); - app* b = r->get_tail(1); - expr* x1 = h->get_arg(0); - expr* x2 = h->get_arg(1); - expr* a1 = a->get_arg(0); - expr* a2 = a->get_arg(1); - expr* b1 = b->get_arg(0); - expr* b2 = b->get_arg(1); - - if (!(is_var(x1) && is_var(x2) && is_var(a1) && is_var(a2) && is_var(b1) && is_var(b2))) { - return false; - } - if (x1 == x2 || a1 == a2 || b1 == b2) { - return false; - } - if (a2 == b1) { - if (x1 == b2 && x2 == a1) { - return true; - } - if (x1 == a1 && x2 == b2) { - return true; - } - return false; - } - if (a1 == b2) { - if (x1 == b1 && x2 == a2) { - return true; - } - if (x1 == a2 && x2 == b1) { - return true; - } - return false; - } - - return false; -; - } - - - rule_set * mk_partial_equivalence_transformer::operator()(rule_set const & source) { - // TODO mc - - if (source.get_num_rules() == 0) { - return 0; - } - - if (m_context.get_engine() != DATALOG_ENGINE) { - return 0; - } - - relation_manager & rm = m_context.get_rel_context()->get_rmanager(); - rule_set::decl2rules::iterator it = source.begin_grouped_rules(); - rule_set::decl2rules::iterator end = source.end_grouped_rules(); - - rule_set* res = alloc(rule_set, m_context); - - for (; it != end; ++it) { - func_decl* p = it->m_key; - rule_vector const& rv = *(it->m_value); - bool has_symmetry = false; - bool has_transitivity = false; - unsigned i_symmetry, i_transitivity; - family_id kind = rm.get_requested_predicate_kind(p); - for (unsigned i = 0; i < rv.size(); ++i) { - - if (kind != null_family_id) { - res->add_rule(rv[i]); - } - else if (is_symmetry(rv[i])) { - i_symmetry = i; - has_symmetry = true; - } - else if (is_transitivity(rv[i])) { - i_transitivity = i; - has_transitivity = true; - } - else { - res->add_rule(rv[i]); - } - } - if (has_symmetry && !has_transitivity) { - res->add_rule(rv[i_symmetry]); - } - else if (!has_symmetry && has_transitivity) { - res->add_rule(rv[i_transitivity]); - } - else if (has_symmetry && has_transitivity) { - TRACE("dl", tout << "updating predicate " << mk_pp(p, m) << " to partial equivalence\n";); - SASSERT(kind == null_family_id); - rm.set_predicate_kind(p, rm.get_table_plugin(symbol("equivalence"))->get_kind()); - } - } - - if (res->get_num_rules() == source.get_num_rules()) { - dealloc(res); - return 0; - } - res->inherit_predicates(source); - - return res; - } - -}; - - diff --git a/src/muz/rel/dl_mk_partial_equiv.h b/src/muz/rel/dl_mk_partial_equiv.h deleted file mode 100644 index be871f53e..000000000 --- a/src/muz/rel/dl_mk_partial_equiv.h +++ /dev/null @@ -1,50 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - dl_mk_partial_equiv.h - -Abstract: - - Rule transformer which identifies predicates that are partial equivalence relations. - -Author: - - Nikolaj Bjorner (nbjorner) 2012-05-14 - -Revision History: - ---*/ - - -#ifndef DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ -#define DL_MK_PARTIAL_EQUIVALENCE_TRANSFORMER_H_ - -#include "dl_context.h" -#include "dl_rule_transformer.h" - -namespace datalog { - - class mk_partial_equivalence_transformer : public rule_transformer::plugin { - ast_manager & m; - context & m_context; - public: - mk_partial_equivalence_transformer(context & ctx, unsigned priority=30000) - : plugin(priority), - m(ctx.get_manager()), - m_context(ctx) {} - - rule_set * operator()(rule_set const & source); - - private: - - bool is_symmetry(rule const* r); - bool is_transitivity(rule const* r); - }; - -}; - -#endif /* DL_MK_PARTIAL_EQUIV_TRANSFORMER_H_ */ - - diff --git a/src/muz/rel/dl_product_relation.cpp b/src/muz/rel/dl_product_relation.cpp index 1bc5e3545..817ff194c 100644 --- a/src/muz/rel/dl_product_relation.cpp +++ b/src/muz/rel/dl_product_relation.cpp @@ -255,7 +255,7 @@ namespace datalog { table_plugin & tplugin = rmgr.get_appropriate_plugin(tsig); relation_plugin & inner_plugin = rmgr.get_table_relation_plugin(tplugin); - return sieve_relation_plugin::get_plugin(rmgr).mk_full(p, sig, inner_plugin); + return sieve_relation_plugin::get_plugin(rmgr).full(p, sig, inner_plugin); } void init(relation_signature const& r1_sig, unsigned num_rels1, relation_base const* const* r1, @@ -294,7 +294,7 @@ namespace datalog { rel2 = r1_plugin.mk_full(p, r2_sig, r1_kind); } else { - rel2 = sieve_relation_plugin::get_plugin(rmgr).mk_full(p, r2_sig, r1_plugin); + rel2 = sieve_relation_plugin::get_plugin(rmgr).full(p, r2_sig, r1_plugin); } m_offset1.push_back(i); m_kind1.push_back(T_INPUT); @@ -318,7 +318,7 @@ namespace datalog { rel1 = r2_plugin.mk_full(p, r1_sig, r2_kind); } else { - rel1 = sieve_relation_plugin::get_plugin(rmgr).mk_full(p, r1_sig, r2_plugin); + rel1 = sieve_relation_plugin::get_plugin(rmgr).full(p, r1_sig, r2_plugin); } m_offset1.push_back(m_full.size()); m_kind1.push_back(T_FULL); diff --git a/src/muz/rel/dl_relation_manager.cpp b/src/muz/rel/dl_relation_manager.cpp index 0b0dae12c..74206a1f6 100644 --- a/src/muz/rel/dl_relation_manager.cpp +++ b/src/muz/rel/dl_relation_manager.cpp @@ -277,7 +277,7 @@ namespace datalog { relation_plugin & relation_manager::get_relation_plugin(family_id kind) { SASSERT(kind>=0); SASSERT(kind(0)); } + virtual ~default_table_map_fn() {} + virtual void operator()(table_base & t) { SASSERT(t.get_signature()==m_aux_table->get_signature()); if(!m_aux_table->empty()) { @@ -1678,6 +1680,8 @@ namespace datalog { m_former_row.resize(get_result_signature().size()); } + virtual ~default_table_project_with_reduce_fn() {} + virtual void modify_fact(table_fact & f) const { unsigned ofs=1; unsigned r_i=1; diff --git a/src/muz/rel/dl_sieve_relation.cpp b/src/muz/rel/dl_sieve_relation.cpp index 9f9419089..7729ec2eb 100644 --- a/src/muz/rel/dl_sieve_relation.cpp +++ b/src/muz/rel/dl_sieve_relation.cpp @@ -253,7 +253,7 @@ namespace datalog { return mk_from_inner(s, inner_cols, inner); } - sieve_relation * sieve_relation_plugin::mk_full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin) { + 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); diff --git a/src/muz/rel/dl_sieve_relation.h b/src/muz/rel/dl_sieve_relation.h index da6c9bbff..4d89a66ae 100644 --- a/src/muz/rel/dl_sieve_relation.h +++ b/src/muz/rel/dl_sieve_relation.h @@ -104,8 +104,7 @@ namespace datalog { sieve_relation * mk_empty(const relation_signature & s, relation_plugin & inner_plugin); virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - sieve_relation * mk_full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin); - + 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); diff --git a/src/muz/rel/dl_sparse_table.cpp b/src/muz/rel/dl_sparse_table.cpp index 24fda3e96..f045bee4b 100644 --- a/src/muz/rel/dl_sparse_table.cpp +++ b/src/muz/rel/dl_sparse_table.cpp @@ -327,8 +327,7 @@ namespace datalog { key_value key; key.resize(key_len); - offset_vector * index_entry; - DEBUG_CODE( index_entry = 0; ); + offset_vector * index_entry = 0; bool key_modified = true; for (; ofs!=after_last; ofs+=t.m_fact_size) { diff --git a/src/muz/rel/dl_table.cpp b/src/muz/rel/dl_table.cpp index 0b8fc0388..9df9dde5e 100644 --- a/src/muz/rel/dl_table.cpp +++ b/src/muz/rel/dl_table.cpp @@ -292,482 +292,5 @@ namespace datalog { table_base::iterator bitvector_table::end() const { return mk_iterator(alloc(bv_iterator, *this, true)); } - - - - - // ----------------------------------- - // - // equivalence_table - // - // ----------------------------------- - - bool equivalence_table_plugin::can_handle_signature(const table_signature & sig) { - return sig.functional_columns() == 0 && sig.size() == 2 && sig[0] < UINT_MAX && sig[0] == sig[1]; - } - - bool equivalence_table_plugin::is_equivalence_table(table_base const& tbl) const { - if (tbl.get_kind() != get_kind()) return false; - equivalence_table const& t = static_cast(tbl); - return !t.is_sparse(); - } - - table_base * equivalence_table_plugin::mk_empty(const table_signature & s) { - TRACE("dl", for (unsigned i = 0; i < s.size(); ++i) tout << s[i] << " "; tout << "\n";); - SASSERT(can_handle_signature(s)); - return alloc(equivalence_table, *this, s); - } - - class equivalence_table_plugin::select_equal_and_project_fn : public table_transformer_fn { - unsigned m_val; - table_sort m_sort; - public: - select_equal_and_project_fn(const table_signature & sig, table_element val, unsigned col) - : m_val(static_cast(val)), - m_sort(sig[0]) { - SASSERT(val <= UINT_MAX); - SASSERT(col == 0 || col == 1); - SASSERT(sig.functional_columns() == 0); - SASSERT(sig.size() == 2); - SASSERT(sig[0] < UINT_MAX && sig[0] == sig[1]); - } - - virtual table_base* operator()(const table_base& tb) { - TRACE("dl", tout << "\n";); - table_plugin & plugin = tb.get_plugin(); - table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse")); - SASSERT(rp); - table_signature sig; - sig.push_back(m_sort); - table_base* result = rp->mk_empty(sig); - equivalence_table const& eq_table = static_cast(tb); - if (eq_table.is_valid(m_val)) { - table_fact fact; - fact.resize(1); - unsigned r = m_val; - do { - fact[0] = r; - result->add_fact(fact); - r = eq_table.m_uf.next(r); - } - while (r != m_val); - } - TRACE("dl", tb.display(tout << "src:\n"); result->display(tout << "result\n");); - return result; - } - }; - - table_transformer_fn * equivalence_table_plugin::mk_select_equal_and_project_fn( - const table_base & t, const table_element & value, unsigned col) { - return alloc(select_equal_and_project_fn, t.get_signature(), value, col); - } - - class equivalence_table_plugin::union_fn : public table_union_fn { - - equivalence_table_plugin& m_plugin; - - - void mk_union1(equivalence_table & tgt, const equivalence_table & src, table_base * delta) { - unsigned num_vars = src.m_uf.get_num_vars(); - table_fact fact; - fact.resize(2); - for (unsigned i = 0; i < num_vars; ++i) { - if (src.is_valid(i) && src.m_uf.find(i) == i) { - fact[0] = i; - equivalence_table::class_iterator it = src.class_begin(i); - equivalence_table::class_iterator end = src.class_end(i); - for (; it != end; ++it) { - fact[1] = *it; - if (!tgt.contains_fact(fact)) { - tgt.add_fact(fact); - if (delta) { - delta->add_fact(fact); - } - } - } - } - } - } - - void mk_union2(equivalence_table & tgt, const table_base & src, table_base * delta) { - table_fact fact; - table_base::iterator it = src.begin(), end = src.end(); - for (; it != end; ++it) { - it->get_fact(fact); - if (!tgt.contains_fact(fact)) { - tgt.add_fact(fact); - if (delta) { - delta->add_fact(fact); - TRACE("dl", - tout << "Add: "; - for (unsigned i = 0; i < fact.size(); ++i) tout << fact[i] << " "; - tout << "\n";); - } - } - } - } - - public: - union_fn(equivalence_table_plugin& p) : m_plugin(p) {} - - virtual void operator()(table_base & tgt0, const table_base & src, table_base * delta) { - TRACE("dl", tout << "union\n";); - equivalence_table & tgt = static_cast(tgt0); - if (m_plugin.is_equivalence_table(src)) { - mk_union1(tgt, static_cast(src), delta); - } - else { - mk_union2(tgt, src, delta); - } - TRACE("dl", src.display(tout << "src\n"); tgt.display(tout << "tgt\n"); - if (delta) delta->display(tout << "delta\n");); - } - }; - - table_union_fn * equivalence_table_plugin::mk_union_fn( - const table_base & tgt, const table_base & src, const table_base * delta) { - if (!is_equivalence_table(tgt) || - tgt.get_signature() != src.get_signature() || - (delta && delta->get_signature() != tgt.get_signature())) { - return 0; - } - return alloc(union_fn,*this); - } - - class equivalence_table_plugin::join_project_fn : public convenient_table_join_project_fn { - equivalence_table_plugin& m_plugin; - public: - join_project_fn( - equivalence_table_plugin& plugin, 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_plugin(plugin) { - m_removed_cols.push_back(UINT_MAX); - } - - virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { - SASSERT(m_cols1.size() == 1); - const table_signature & res_sign = get_result_signature(); - table_plugin * plugin = &tb1.get_plugin(); - if (!plugin->can_handle_signature(res_sign)) { - plugin = &tb2.get_plugin(); - if (!plugin->can_handle_signature(res_sign)) { - plugin = &tb1.get_manager().get_appropriate_plugin(res_sign); - } - } - SASSERT(plugin->can_handle_signature(res_sign)); - table_base * result = plugin->mk_empty(res_sign); - - if (m_plugin.is_equivalence_table(tb1)) { - mk_join(0, m_cols1[0], static_cast(tb1), - 2, m_cols2[0], tb2, result); - } - else if (m_plugin.is_equivalence_table(tb2)) { - mk_join(tb1.get_signature().size(), m_cols2[0], static_cast(tb2), - 0, m_cols1[0], tb1, result); - } - else { - UNREACHABLE(); - } - TRACE("dl", tb1.display(tout << "tb1\n"); tb2.display(tout << "tb2\n"); result->display(tout << "result\n");); - return result; - } - - private: - table_base * mk_join(unsigned offs1, unsigned col1, equivalence_table const & t1, - unsigned offs2, unsigned col2, table_base const& t2, table_base* res) { - table_base::iterator els2it = t2.begin(); - table_base::iterator els2end = t2.end(); - - table_fact acc, proj; - acc.resize(t1.get_signature().size() + t2.get_signature().size()); - - for(; els2it != els2end; ++els2it) { - const table_base::row_interface & row2 = *els2it; - table_element const& e2 = row2[col2]; - equivalence_table::class_iterator it = t1.class_begin(e2); - equivalence_table::class_iterator end = t1.class_end(e2); - if (it != end) { - for (unsigned i = 0; i < row2.size(); ++i) { - acc[i+offs2] = row2[i]; - } - } - for (; it != end; ++it) { - acc[offs1+col1] = e2; - acc[offs1+1-col1] = *it; - mk_project(acc, proj); - TRACE("dl", for (unsigned i = 0; i < proj.size(); ++i) tout << proj[i] << " "; tout << "\n";); - res->add_fact(proj); - } - } - return res; - } - - virtual void mk_project(table_fact const & f, table_fact & p) const { - unsigned sz = f.size(); - p.reset(); - for (unsigned i = 0, r = 0; i < sz; ++i) { - if (r < m_removed_cols.size() && m_removed_cols[r] == i) { - ++r; - } - else { - p.push_back(f[i]); - } - } - } - - - }; - - table_join_fn * equivalence_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 (col_cnt != 1) { - TRACE("dl", tout << "WARNING: join_project on multiple columns is not implemented\n";); - return 0; - } - if (is_equivalence_table(t1) || is_equivalence_table(t2)) { - return alloc(join_project_fn, *this, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, - removed_col_cnt, removed_cols); - } - return 0; - } - - class equivalence_table::eq_iterator : public iterator_core { - - equivalence_table const& m_eq; - unsigned m_last; - unsigned m_current; - unsigned m_next; - - class our_row : public caching_row_interface { - const eq_iterator& m_parent; - public: - our_row(const eq_iterator & p) : caching_row_interface(p.m_eq), m_parent(p) {} - - virtual void get_fact(table_fact& result) const { - if (result.size() < size()) { - result.resize(size(), 0); - } - result[0] = m_parent.m_current; - result[1] = m_parent.m_next; - } - - virtual table_element operator[](unsigned col) const { - if (col == 0) return m_parent.m_current; - if (col == 1) return m_parent.m_next; - UNREACHABLE(); - return 0; - } - - }; - our_row m_row_obj; - - public: - eq_iterator(const equivalence_table& eq, bool end): - m_eq(eq), - m_last(eq.m_uf.get_num_vars()), - m_current(end?m_last:0), - m_next(0), - m_row_obj(*this) - { - while (m_current < m_last && !m_eq.is_valid(m_current)) { - m_current++; - m_next = m_current; - } - } - - virtual bool is_finished() const { - return m_current == m_last; - } - - virtual row_interface & operator*() { - SASSERT(!is_finished()); - return m_row_obj; - } - - virtual void operator++() { - SASSERT(!is_finished()); - m_next = m_eq.m_uf.next(m_next); - if (m_next == m_current) { - do { - m_current++; - m_next = m_current; - } - while (m_current < m_last && !m_eq.is_valid(m_current)); - } - } - }; - - equivalence_table::equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig) - : table_base(plugin, sig), m_uf(m_ctx), m_sparse(0) { - SASSERT(plugin.can_handle_signature(sig)); - } - - equivalence_table::~equivalence_table() { - if (is_sparse()) { - m_sparse->deallocate(); - } - } - - - void equivalence_table::add_fact(const table_fact & f) { - if (is_sparse()) { - add_fact_sparse(f); - } - else { - TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";); - while (first(f) >= m_uf.get_num_vars()) m_uf.mk_var(); - while (second(f) >= m_uf.get_num_vars()) m_uf.mk_var(); - m_uf.merge(first(f), second(f)); - m_valid.reserve(m_uf.get_num_vars()); - m_valid.set(first(f)); - m_valid.set(second(f)); - } - } - - void equivalence_table::remove_fact(const table_element* fact) { - mk_sparse(); - m_sparse->remove_fact(fact); - } - - void equivalence_table::mk_sparse() { - if (m_sparse) return; - - TRACE("dl",tout << "\n";); - table_plugin & plugin = get_plugin(); - table_plugin* rp = plugin.get_manager().get_table_plugin(symbol("sparse")); - SASSERT(rp); - table_base* result = rp->mk_empty(get_signature()); - table_base::iterator it = begin(), e = end(); - table_fact fact; - for (; it != e; ++it) { - it->get_fact(fact); - result->add_fact(fact); - } - m_sparse = result; - } - - void equivalence_table::add_fact_sparse(table_fact const& f) { - table_base::iterator it = m_sparse->begin(), end = m_sparse->end(); - vector to_add; - to_add.push_back(f); - table_fact f1(f); - - f1[0] = f[1]; - f1[1] = f[0]; - to_add.push_back(f1); - - f1[0] = f[1]; - f1[1] = f[1]; - to_add.push_back(f1); - - f1[0] = f[0]; - f1[1] = f[0]; - to_add.push_back(f1); - - for (; it != end; ++it) { - if ((*it)[0] == f[0]) { - f1[0] = f[1]; - f1[1] = (*it)[1]; - to_add.push_back(f1); - std::swap(f1[0],f1[1]); - to_add.push_back(f1); - } - } - for (unsigned i = 0; i < to_add.size(); ++i) { - m_sparse->add_fact(to_add[i]); - } - } - - bool equivalence_table::contains_fact(const table_fact & f) const { - TRACE("dl_verbose", for (unsigned i = 0; i < f.size(); ++i) tout << f[i] << " "; tout << "\n";); - if (is_sparse()) { - return m_sparse->contains_fact(f); - } - return - is_valid(first(f)) && - is_valid(second(f)) && - m_uf.find(first(f)) == m_uf.find(second(f)); - } - - table_base* equivalence_table::clone() const { - if (is_sparse()) { - return m_sparse->clone(); - } - TRACE("dl",tout << "\n";); - table_plugin & plugin = get_plugin(); - table_base* result = plugin.mk_empty(get_signature()); - table_fact fact; - fact.resize(2); - for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) { - if (m_valid.get(i) && m_uf.find(i) == i) { - unsigned n = m_uf.next(i); - fact[0] = i; - while (n != i) { - fact[1] = n; - result->add_fact(fact); - n = m_uf.next(n); - } - } - } - return result; - } - - table_base::iterator equivalence_table::begin() const { - if (is_sparse()) return m_sparse->begin(); - return mk_iterator(alloc(eq_iterator, *this, false)); - } - - table_base::iterator equivalence_table::end() const { - if (is_sparse()) return m_sparse->end(); - return mk_iterator(alloc(eq_iterator, *this, true)); - } - - equivalence_table::class_iterator equivalence_table::class_begin(table_element const& _e) const { - SASSERT(!is_sparse()); - unsigned e = static_cast(_e); - return class_iterator(*this, e, !is_valid(e)); - } - - equivalence_table::class_iterator equivalence_table::class_end(table_element const& _e) const { - SASSERT(!is_sparse()); - unsigned e = static_cast(_e); - return class_iterator(*this, e, true); - } - - void equivalence_table::display(std::ostream& out) const { - if (is_sparse()) { - m_sparse->display(out); - return; - } - for (unsigned i = 0; i < m_uf.get_num_vars(); ++i) { - if (is_valid(i) && m_uf.find(i) == i) { - unsigned j = i, last = i; - do { - out << "<" << i << " " << j << ">\n"; - j = m_uf.next(j); - } - while (last != j); - } - } - } - - unsigned equivalence_table::get_size_estimate_rows() const { - if (is_sparse()) return m_sparse->get_size_estimate_rows(); - return static_cast(get_signature()[0]); - } - - unsigned equivalence_table::get_size_estimate_bytes() const { - if (is_sparse()) return m_sparse->get_size_estimate_bytes(); - return static_cast(get_signature()[0]); - } - - bool equivalence_table::knows_exact_size() const { - return (!is_sparse() || m_sparse->knows_exact_size()); - } - }; diff --git a/src/muz/rel/dl_table.h b/src/muz/rel/dl_table.h index b77e860d6..d25aa31bc 100644 --- a/src/muz/rel/dl_table.h +++ b/src/muz/rel/dl_table.h @@ -144,119 +144,6 @@ namespace datalog { virtual iterator end() const; }; - // ------------------------------------------- - // Equivalence table. - // Really: partial equivalence relation table. - // ------------------------------------------- - - class equivalence_table; - - class equivalence_table_plugin : public table_plugin { - class union_fn; - class select_equal_and_project_fn; - class join_project_fn; - - bool is_equivalence_table(table_base const& tbl) const; - - public: - typedef equivalence_table table; - - equivalence_table_plugin(relation_manager & manager) - : table_plugin(symbol("equivalence"), manager) {} - - virtual bool can_handle_signature(const table_signature & s); - - virtual table_base * mk_empty(const table_signature & s); - - protected: - virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta); - virtual table_transformer_fn * mk_select_equal_and_project_fn( - const table_base & t, - const table_element & value, unsigned col); - virtual 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); - - -#if 0 - virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - const table_element & value, unsigned col); - virtual 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); -#endif - }; - - class equivalence_table : public table_base { - friend class equivalence_table_plugin; - - class eq_iterator; - union_find_default_ctx m_ctx; - bit_vector m_valid; - union_find<> m_uf; - table_base* m_sparse; - - equivalence_table(equivalence_table_plugin & plugin, const table_signature & sig); - virtual ~equivalence_table(); - - unsigned first(table_fact const& f) const { return static_cast(f[0]); } - unsigned second(table_fact const& f) const { return static_cast(f[1]); } - - bool is_valid(unsigned entry) const { return entry < m_valid.size() && m_valid.get(entry); } - bool is_sparse() const { return m_sparse != 0; } - - // iterator over equivalence class of 'n'. - class class_iterator { - equivalence_table const& m_parent; - unsigned m_current; - unsigned m_last; - bool m_end; - public: - class_iterator(equivalence_table const& s, unsigned n, bool end): - m_parent(s), m_current(n), m_last(n), m_end(end) {} - - unsigned operator*() { return m_current; } - - class_iterator& operator++() { - m_current = m_parent.m_uf.next(m_current); - m_end = (m_current == m_last); - return *this; - } - - bool operator==(const class_iterator & it) const { - return - (m_end && it.m_end) || - (!m_end && !it.m_end && m_current == it.m_current); - } - bool operator!=(const class_iterator & it) const { return !operator==(it); } - - }; - class_iterator class_begin(table_element const& e) const; - class_iterator class_end(table_element const& e) const; - - void add_fact_sparse(table_fact const& f); - void mk_sparse(); - - - public: - virtual void add_fact(const table_fact & f); - virtual void remove_fact(const table_element* fact); - virtual bool contains_fact(const table_fact & f) const; - virtual table_base* clone() const; - virtual iterator begin() const; - virtual iterator end() const; - virtual unsigned get_size_estimate_rows() const; - virtual unsigned get_size_estimate_bytes() const; - virtual bool knows_exact_size() const; - virtual void display(std::ostream & out) const; - - }; }; diff --git a/src/muz/rel/dl_table_relation.cpp b/src/muz/rel/dl_table_relation.cpp index d42d071aa..00a25d169 100644 --- a/src/muz/rel/dl_table_relation.cpp +++ b/src/muz/rel/dl_table_relation.cpp @@ -54,7 +54,7 @@ namespace datalog { return alloc(table_relation, *this, s, t); } - relation_base * table_relation_plugin::mk_full(const relation_signature & s, func_decl* p, family_id kind) { + 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 0; diff --git a/src/muz/rel/dl_table_relation.h b/src/muz/rel/dl_table_relation.h index 8266995da..56ca509fa 100644 --- a/src/muz/rel/dl_table_relation.h +++ b/src/muz/rel/dl_table_relation.h @@ -49,7 +49,7 @@ namespace datalog { virtual bool can_handle_signature(const relation_signature & s); virtual relation_base * mk_empty(const relation_signature & s); - virtual relation_base * mk_full(const relation_signature & s, func_decl* p, family_id kind); + 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: diff --git a/src/muz/rel/rel_context.cpp b/src/muz/rel/rel_context.cpp index 6d167dfec..abaf88f32 100644 --- a/src/muz/rel/rel_context.cpp +++ b/src/muz/rel/rel_context.cpp @@ -43,7 +43,6 @@ Revision History: #include"dl_mk_similarity_compressor.h" #include"dl_mk_unbound_compressor.h" #include"dl_mk_subsumption_checker.h" -#include"dl_mk_partial_equiv.h" #include"dl_mk_coi_filter.h" #include"dl_mk_filter_rules.h" #include"dl_mk_rule_inliner.h" @@ -108,7 +107,6 @@ namespace datalog { 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(alloc(equivalence_table_plugin, rm)); rm.register_plugin(lazy_table_plugin::mk_sparse(rm)); // register plugins for builtin relations @@ -308,7 +306,6 @@ namespace datalog { transf.register_plugin(alloc(mk_similarity_compressor, m_context)); } transf.register_plugin(alloc(mk_rule_inliner, m_context)); - transf.register_plugin(alloc(mk_partial_equivalence_transformer, m_context)); transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context)); transf.register_plugin(alloc(mk_separate_negated_tails, m_context)); diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index c452e2611..0f155f65b 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -44,6 +44,7 @@ namespace datalog { if (m_context.has_facts(r->get_decl(i))) { return 0; } + if (r->is_neg_tail(i)) { if (!engine.get_fact(r->get_decl(i)).is_reachable()) { if (!new_tail) { @@ -53,11 +54,14 @@ namespace datalog { } new_tail = true; } - } else if (new_tail) { + } + else if (new_tail) { m_new_tail.push_back(r->get_tail(i)); m_new_tail_neg.push_back(true); } - } else { + } + + else { SASSERT(!new_tail); if (!engine.get_fact(r->get_decl(i)).is_reachable()) { contained = false; diff --git a/src/muz/transforms/dl_mk_filter_rules.cpp b/src/muz/transforms/dl_mk_filter_rules.cpp index d23da72fb..b112f4c99 100644 --- a/src/muz/transforms/dl_mk_filter_rules.cpp +++ b/src/muz/transforms/dl_mk_filter_rules.cpp @@ -111,7 +111,7 @@ namespace datalog { bool rule_modified = false; for (unsigned i = 0; i < sz; i++) { app * tail = r->get_tail(i); - if (is_candidate(tail)) { + 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); diff --git a/src/muz/transforms/dl_mk_loop_counter.cpp b/src/muz/transforms/dl_mk_loop_counter.cpp index 678bfc5a3..f97670ad0 100644 --- a/src/muz/transforms/dl_mk_loop_counter.cpp +++ b/src/muz/transforms/dl_mk_loop_counter.cpp @@ -56,7 +56,7 @@ namespace datalog { app_ref mk_loop_counter::del_arg(app* fn) { expr_ref_vector args(m); - func_decl* old_fn, *new_fn = fn->get_decl(); + func_decl* old_fn = 0, *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)); diff --git a/src/muz/transforms/dl_mk_magic_sets.cpp b/src/muz/transforms/dl_mk_magic_sets.cpp index 48bd69255..048decaf9 100644 --- a/src/muz/transforms/dl_mk_magic_sets.cpp +++ b/src/muz/transforms/dl_mk_magic_sets.cpp @@ -264,7 +264,7 @@ namespace datalog { } - func_decl * new_head_pred; + func_decl * new_head_pred = 0; 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()); diff --git a/src/muz/transforms/dl_mk_rule_inliner.h b/src/muz/transforms/dl_mk_rule_inliner.h index 98f65a734..66d17fdc8 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.h +++ b/src/muz/transforms/dl_mk_rule_inliner.h @@ -88,7 +88,7 @@ namespace datalog { svector m_can_remove, m_can_expand; obj_map m_positions; public: - visitor(context& c, substitution & s): st_visitor(s), m_context(c) {} + visitor(context& c, substitution & s): st_visitor(s), m_context(c) { (void) m_context; } virtual bool operator()(expr* e); void reset() { m_unifiers.reset(); } void reset(unsigned sz); diff --git a/src/muz/transforms/dl_mk_unbound_compressor.cpp b/src/muz/transforms/dl_mk_unbound_compressor.cpp index 48b41af56..41b181450 100644 --- a/src/muz/transforms/dl_mk_unbound_compressor.cpp +++ b/src/muz/transforms/dl_mk_unbound_compressor.cpp @@ -317,7 +317,6 @@ namespace datalog { unsigned tail_index = 0; while (tail_index < utail_len) { app * t = r->get_tail(tail_index); - func_decl * t_pred = t->get_decl(); add_in_progress_indices(arg_indices, t); @@ -345,6 +344,11 @@ namespace datalog { rule_set * mk_unbound_compressor::operator()(rule_set const & source) { // TODO mc + + if (!m_context.compress_unbound()) { + return 0; + } + m_modified = false; SASSERT(m_rules.empty()); diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index 4f9854d87..7582c8389 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -1283,7 +1283,7 @@ namespace nlsat { if (r == l_false) { // collect used literals from m_lemma_assumptions vector deps; - m_asm.linearize(m_lemma_assumptions.get(), 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) { @@ -1299,6 +1299,10 @@ namespace nlsat { 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 n = clauses.size(); unsigned j = 0; @@ -2712,6 +2716,10 @@ namespace nlsat { return m_imp->check(assumptions); } + void solver::get_core(vector& assumptions) { + return m_imp->get_core(assumptions); + } + void solver::reset() { m_imp->reset(); } diff --git a/src/nlsat/nlsat_solver.h b/src/nlsat/nlsat_solver.h index eec5ba19f..3668629cd 100644 --- a/src/nlsat/nlsat_solver.h +++ b/src/nlsat/nlsat_solver.h @@ -195,6 +195,14 @@ namespace nlsat { lbool value(literal l) const; + // ----------------------- + // + // Core + // + // ----------------------- + + void get_core(vector& deps); + // ----------------------- // // Misc diff --git a/src/nlsat/tactic/nlsat_tactic.cpp b/src/nlsat/tactic/nlsat_tactic.cpp index 94c57e16d..4ecdade38 100644 --- a/src/nlsat/tactic/nlsat_tactic.cpp +++ b/src/nlsat/tactic/nlsat_tactic.cpp @@ -25,6 +25,7 @@ Notes: #include"ast_smt2_pp.h" #include"z3_exception.h" #include"algebraic_numbers.h" +#include"ast_pp.h" class nlsat_tactic : public tactic { struct expr_display_var_proc : public nlsat::display_var_proc { @@ -78,9 +79,21 @@ class nlsat_tactic : public tactic { } return false; } + + bool eval_model(model& model, goal& g) { + unsigned sz = g.size(); + for (unsigned i = 0; i < sz; i++) { + expr_ref val(m); + if (model.eval(g.form(i), val) && !m.is_true(val)) { + TRACE("nlsat", tout << mk_pp(g.form(i), m) << " -> " << val << "\n";); + return false; + } + } + return true; + } // Return false if nlsat assigned noninteger value to an integer variable. - bool mk_model(expr_ref_vector & b2a, expr_ref_vector & x2t, model_converter_ref & mc) { + 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); @@ -110,6 +123,7 @@ class nlsat_tactic : public tactic { 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);); mc = model2model_converter(md.get()); return ok; } @@ -133,6 +147,7 @@ class nlsat_tactic : public tactic { 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(); @@ -140,7 +155,7 @@ class nlsat_tactic : public tactic { m_solver.set_display_var(m_display_var); lbool st = m_solver.check(); - + if (st == l_undef) { } else if (st == l_true) { @@ -151,16 +166,25 @@ class nlsat_tactic : public tactic { if (!contains_unsupported(b2a, x2t)) { // If mk_model is false it means that the model produced by nlsat // assigns noninteger values to integer variables - if (mk_model(b2a, x2t, mc)) { + if (mk_model(*g.get(), b2a, x2t, mc)) { // result goal is trivially SAT g->reset(); } } } else { - // TODO: extract unsat core - g->assert_expr(m.mk_false(), 0, 0); + expr_dependency* lcore = 0; + if (g->unsat_core_enabled()) { + vector assumptions; + m_solver.get_core(assumptions); + for (unsigned i = 0; i < assumptions.size(); ++i) { + expr_dependency* d = static_cast(assumptions[i]); + lcore = m.mk_join(lcore, d); + } + } + g->assert_expr(m.mk_false(), 0, lcore); } + g->inc_depth(); result.push_back(g.get()); TRACE("nlsat", g->display(tout);); diff --git a/src/opt/bcd2.cpp b/src/opt/bcd2.cpp deleted file mode 100644 index a373b652b..000000000 --- a/src/opt/bcd2.cpp +++ /dev/null @@ -1,405 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - bcd2.cpp - -Abstract: - - bcd2 based MaxSAT. - -Author: - - Nikolaj Bjorner (nbjorner) 2014-4-17 - -Notes: - ---*/ -#include "bcd2.h" -#include "pb_decl_plugin.h" -#include "uint_set.h" -#include "ast_pp.h" - - -namespace opt { - // ------------------------------------------------------ - // Morgado, Heras, Marques-Silva 2013 - // (initial version without model-based optimizations) - // - class bcd2 : public maxsmt_solver_base { - struct wcore { - expr* m_r; - unsigned_vector m_R; - rational m_lower; - rational m_mid; - rational m_upper; - }; - typedef obj_hashtable expr_set; - - pb_util pb; - expr_ref_vector m_soft_aux; - obj_map m_relax2index; // expr |-> index - obj_map m_soft2index; // expr |-> index - expr_ref_vector m_trail; - expr_ref_vector m_soft_constraints; - expr_set m_asm_set; - vector m_cores; - vector m_sigmas; - rational m_den; // least common multiplier of original denominators - bool m_enable_lazy; // enable adding soft constraints lazily (called 'mgbcd2') - unsigned_vector m_lazy_soft; // soft constraints to add lazily. - - void set2asms(expr_set const& set, expr_ref_vector & es) const { - es.reset(); - expr_set::iterator it = set.begin(), end = set.end(); - for (; it != end; ++it) { - es.push_back(m.mk_not(*it)); - } - } - void bcd2_init_soft(weights_t& weights, expr_ref_vector const& soft) { - - // normalize weights to be integral: - m_den = rational::one(); - for (unsigned i = 0; i < m_weights.size(); ++i) { - m_den = lcm(m_den, denominator(m_weights[i])); - } - if (!m_den.is_one()) { - for (unsigned i = 0; i < m_weights.size(); ++i) { - m_weights[i] = m_den*m_weights[i]; - SASSERT(m_weights[i].is_int()); - } - } - } - void init_bcd() { - m_trail.reset(); - m_asm_set.reset(); - m_cores.reset(); - m_sigmas.reset(); - m_lazy_soft.reset(); - for (unsigned i = 0; i < m_soft.size(); ++i) { - m_sigmas.push_back(m_weights[i]); - m_soft_aux.push_back(mk_fresh()); - if (m_enable_lazy) { - m_lazy_soft.push_back(i); - } - else { - enable_soft_constraint(i); - } - } - m_upper += rational(1); - } - - void process_sat() { - svector assignment; - update_assignment(assignment); - if (check_lazy_soft(assignment)) { - update_sigmas(); - } - } - - public: - bcd2(maxsat_context& c, - weights_t& ws, expr_ref_vector const& soft): - maxsmt_solver_base(c, ws, soft), - pb(m), - m_soft_aux(m), - m_trail(m), - m_soft_constraints(m), - m_enable_lazy(true) { - bcd2_init_soft(ws, soft); - } - - virtual ~bcd2() {} - - virtual lbool operator()() { - expr_ref fml(m), r(m); - lbool is_sat = l_undef; - expr_ref_vector asms(m); - init(); - init_bcd(); - if (m.canceled()) { - normalize_bounds(); - return l_undef; - } - process_sat(); - while (m_lower < m_upper) { - trace_bounds("bcd2"); - assert_soft(); - solver::scoped_push _scope2(s()); - TRACE("opt", display(tout);); - assert_cores(); - set2asms(m_asm_set, asms); - if (m.canceled()) { - normalize_bounds(); - return l_undef; - } - is_sat = s().check_sat(asms.size(), asms.c_ptr()); - switch(is_sat) { - case l_undef: - normalize_bounds(); - return l_undef; - case l_true: - process_sat(); - break; - case l_false: { - ptr_vector unsat_core; - uint_set subC, soft; - s().get_unsat_core(unsat_core); - core2indices(unsat_core, subC, soft); - SASSERT(unsat_core.size() == subC.num_elems() + soft.num_elems()); - if (soft.num_elems() == 0 && subC.num_elems() == 1) { - unsigned s = *subC.begin(); - wcore& c_s = m_cores[s]; - c_s.m_lower = refine(c_s.m_R, c_s.m_mid); - c_s.m_mid = div(c_s.m_lower + c_s.m_upper, rational(2)); - } - else { - wcore c_s; - rational delta = min_of_delta(subC); - rational lower = sum_of_lower(subC); - union_Rs(subC, c_s.m_R); - r = mk_fresh(); - relax(subC, soft, c_s.m_R, delta); - c_s.m_lower = refine(c_s.m_R, lower + delta - rational(1)); - c_s.m_upper = rational::one(); - c_s.m_upper += sum_of_sigmas(c_s.m_R); - c_s.m_mid = div(c_s.m_lower + c_s.m_upper, rational(2)); - c_s.m_r = r; - m_asm_set.insert(r); - subtract(m_cores, subC); - m_relax2index.insert(r, m_cores.size()); - m_cores.push_back(c_s); - } - break; - } - } - m_lower = compute_lower(); - } - normalize_bounds(); - return l_true; - } - - - private: - - void enable_soft_constraint(unsigned i) { - expr_ref fml(m); - expr* r = m_soft_aux[i].get(); - m_soft2index.insert(r, i); - fml = m.mk_or(r, m_soft[i]); - m_soft_constraints.push_back(fml); - m_asm_set.insert(r); - SASSERT(m_weights[i].is_int()); - } - - void assert_soft() { - for (unsigned i = 0; i < m_soft_constraints.size(); ++i) { - s().assert_expr(m_soft_constraints[i].get()); - } - m_soft_constraints.reset(); - } - - bool check_lazy_soft(svector const& assignment) { - bool all_satisfied = true; - for (unsigned i = 0; i < m_lazy_soft.size(); ++i) { - unsigned j = m_lazy_soft[i]; - if (!assignment[j]) { - enable_soft_constraint(j); - m_lazy_soft[i] = m_lazy_soft.back(); - m_lazy_soft.pop_back(); - --i; - all_satisfied = false; - } - } - return all_satisfied; - } - - void normalize_bounds() { - m_lower /= m_den; - m_upper /= m_den; - } - - expr* mk_fresh() { - expr* r = mk_fresh_bool("r"); - m_trail.push_back(r); - return r; - } - - void update_assignment(svector& new_assignment) { - expr_ref val(m); - rational new_upper(0); - model_ref model; - new_assignment.reset(); - s().get_model(model); - for (unsigned i = 0; i < m_soft.size(); ++i) { - new_assignment.push_back(model->eval(m_soft[i], val) && m.is_true(val)); - if (!new_assignment[i]) { - new_upper += m_weights[i]; - } - } - if (new_upper < m_upper) { - m_upper = new_upper; - m_model = model; - m_assignment.reset(); - m_assignment.append(new_assignment); - } - } - - void update_sigmas() { - for (unsigned i = 0; i < m_cores.size(); ++i) { - wcore& c_i = m_cores[i]; - unsigned_vector const& R = c_i.m_R; - c_i.m_upper.reset(); - for (unsigned j = 0; j < R.size(); ++j) { - unsigned r_j = R[j]; - if (!m_assignment[r_j]) { - c_i.m_upper += m_weights[r_j]; - m_sigmas[r_j] = m_weights[r_j]; - } - else { - m_sigmas[r_j].reset(); - } - } - c_i.m_mid = div(c_i.m_lower + c_i.m_upper, rational(2)); - } - } - - /** - * Minimum of two (positive) numbers. Zero is treated as +infinity. - */ - rational min_z(rational const& a, rational const& b) { - if (a.is_zero()) return b; - if (b.is_zero()) return a; - if (a < b) return a; - return b; - } - - rational min_of_delta(uint_set const& subC) { - rational delta(0); - for (uint_set::iterator it = subC.begin(); it != subC.end(); ++it) { - unsigned j = *it; - wcore const& core = m_cores[j]; - rational new_delta = rational(1) + core.m_upper - core.m_mid; - SASSERT(new_delta.is_pos()); - delta = min_z(delta, new_delta); - } - return delta; - } - - rational sum_of_lower(uint_set const& subC) { - rational lower(0); - for (uint_set::iterator it = subC.begin(); it != subC.end(); ++it) { - lower += m_cores[*it].m_lower; - } - return lower; - } - - rational sum_of_sigmas(unsigned_vector const& R) { - rational sum(0); - for (unsigned i = 0; i < R.size(); ++i) { - sum += m_sigmas[R[i]]; - } - return sum; - } - void union_Rs(uint_set const& subC, unsigned_vector& R) { - for (uint_set::iterator it = subC.begin(); it != subC.end(); ++it) { - R.append(m_cores[*it].m_R); - } - } - rational compute_lower() { - rational result(0); - for (unsigned i = 0; i < m_cores.size(); ++i) { - result += m_cores[i].m_lower; - } - return result; - } - void subtract(vector& cores, uint_set const& subC) { - unsigned j = 0; - for (unsigned i = 0; i < cores.size(); ++i) { - if (subC.contains(i)) { - m_asm_set.remove(cores[i].m_r); - } - else { - if (j != i) { - cores[j] = cores[i]; - } - ++j; - } - } - cores.resize(j); - for (unsigned i = 0; i < cores.size(); ++i) { - m_relax2index.insert(cores[i].m_r, i); - } - } - void core2indices(ptr_vector const& core, uint_set& subC, uint_set& soft) { - for (unsigned i = 0; i < core.size(); ++i) { - unsigned j; - expr* a; - VERIFY(m.is_not(core[i], a)); - if (m_relax2index.find(a, j)) { - subC.insert(j); - } - else { - VERIFY(m_soft2index.find(a, j)); - soft.insert(j); - } - } - } - rational refine(unsigned_vector const& idx, rational v) { - return v + rational(1); - } - void relax(uint_set& subC, uint_set& soft, unsigned_vector& R, rational& delta) { - for (uint_set::iterator it = soft.begin(); it != soft.end(); ++it) { - R.push_back(*it); - delta = min_z(delta, m_weights[*it]); - m_asm_set.remove(m_soft_aux[*it].get()); - } - } - void assert_cores() { - for (unsigned i = 0; i < m_cores.size(); ++i) { - assert_core(m_cores[i]); - } - } - void assert_core(wcore const& core) { - expr_ref fml(m); - vector ws; - ptr_vector rs; - rational w(0); - for (unsigned j = 0; j < core.m_R.size(); ++j) { - unsigned idx = core.m_R[j]; - ws.push_back(m_weights[idx]); - w += ws.back(); - rs.push_back(m_soft_aux[idx].get()); - } - w.neg(); - w += core.m_mid; - ws.push_back(w); - rs.push_back(core.m_r); - fml = pb.mk_le(ws.size(), ws.c_ptr(), rs.c_ptr(), core.m_mid); - s().assert_expr(fml); - } - void display(std::ostream& out) { - out << "[" << m_lower << ":" << m_upper << "]\n"; - s().display(out); - out << "\n"; - for (unsigned i = 0; i < m_cores.size(); ++i) { - wcore const& c = m_cores[i]; - out << mk_pp(c.m_r, m) << ": "; - for (unsigned j = 0; j < c.m_R.size(); ++j) { - out << c.m_R[j] << " (" << m_sigmas[c.m_R[j]] << ") "; - } - out << "[" << c.m_lower << ":" << c.m_mid << ":" << c.m_upper << "]\n"; - } - for (unsigned i = 0; i < m_soft.size(); ++i) { - out << mk_pp(m_soft[i], m) << " " << m_weights[i] << "\n"; - } - } - }; - - maxsmt_solver_base* mk_bcd2( - maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { - return alloc(bcd2, c, ws, soft); - } - -} diff --git a/src/opt/bcd2.h b/src/opt/bcd2.h deleted file mode 100644 index 50b7215e3..000000000 --- a/src/opt/bcd2.h +++ /dev/null @@ -1,28 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - bcd2.h - -Abstract: - - Bcd2 based MaxSAT. - -Author: - - Nikolaj Bjorner (nbjorner) 2014-4-17 - -Notes: - ---*/ - -#ifndef BCD2_H_ -#define BCD2_H_ - -#include "maxsmt.h" - -namespace opt { - maxsmt_solver_base* mk_bcd2(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); -} -#endif diff --git a/src/opt/fu_malik.cpp b/src/opt/fu_malik.cpp deleted file mode 100644 index 48ef50cbc..000000000 --- a/src/opt/fu_malik.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - fu_malik.cpp - -Abstract: - Fu & Malik built-in optimization method. - Adapted from sample code in C. - -Author: - - Anh-Dung Phan (t-anphan) 2013-10-15 - -Notes: - ---*/ - -#include "fu_malik.h" -#include "qfbv_tactic.h" -#include "tactic2solver.h" -#include "goal.h" -#include "probe.h" -#include "tactic.h" -#include "ast_pp.h" -#include "model_smt2_pp.h" -#include "opt_context.h" - -/** - \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. -*/ -namespace opt { - - class fu_malik : public maxsmt_solver_base { - filter_model_converter& m_fm; - expr_ref_vector m_aux_soft; - expr_ref_vector m_aux; - model_ref m_model; - - public: - fu_malik(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): - maxsmt_solver_base(c, ws, soft), - m_fm(c.fm()), - m_aux_soft(soft), - m_aux(m) - { - m_upper = rational(m_aux_soft.size() + 1); - m_lower.reset(); - m_assignment.resize(m_aux_soft.size(), false); - } - - /** - \brief One step of the Fu&Malik algorithm. - - 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 - */ - - typedef obj_hashtable expr_set; - - void set2vector(expr_set const& set, expr_ref_vector & es) const { - es.reset(); - expr_set::iterator it = set.begin(), end = set.end(); - for (; it != end; ++it) { - es.push_back(*it); - } - } - - void collect_statistics(statistics& st) const { - st.update("opt-fm-num-steps", m_aux_soft.size() + 2 - m_upper.get_unsigned()); - } - - void set_union(expr_set const& set1, expr_set const& set2, expr_set & set) const { - set.reset(); - expr_set::iterator it = set1.begin(), end = set1.end(); - for (; it != end; ++it) { - set.insert(*it); - } - it = set2.begin(); - end = set2.end(); - for (; it != end; ++it) { - set.insert(*it); - } - } - - lbool step() { - IF_VERBOSE(1, verbose_stream() << "(opt.max_sat step " << m_aux_soft.size() + 2 - m_upper.get_unsigned() << ")\n";); - expr_ref_vector assumptions(m), block_vars(m); - for (unsigned i = 0; i < m_aux_soft.size(); ++i) { - assumptions.push_back(m.mk_not(m_aux[i].get())); - } - lbool is_sat = s().check_sat(assumptions.size(), assumptions.c_ptr()); - if (is_sat != l_false) { - return is_sat; - } - - ptr_vector core; - s().get_unsat_core(core); - - SASSERT(!core.empty()); - - // Update soft-constraints and aux_vars - for (unsigned i = 0; i < m_aux_soft.size(); ++i) { - - bool found = false; - for (unsigned j = 0; !found && j < core.size(); ++j) { - found = assumptions[i].get() == core[j]; - } - if (!found) { - continue; - } - app_ref block_var(m), tmp(m); - block_var = m.mk_fresh_const("block_var", m.mk_bool_sort()); - m_aux[i] = m.mk_fresh_const("aux", m.mk_bool_sort()); - m_fm.insert(block_var->get_decl()); - m_fm.insert(to_app(m_aux[i].get())->get_decl()); - m_aux_soft[i] = m.mk_or(m_aux_soft[i].get(), block_var); - block_vars.push_back(block_var); - tmp = m.mk_or(m_aux_soft[i].get(), m_aux[i].get()); - s().assert_expr(tmp); - } - SASSERT (!block_vars.empty()); - assert_at_most_one(block_vars); - IF_VERBOSE(1, verbose_stream() << "(opt.max_sat # of non-blocked soft constraints: " << m_aux_soft.size() - block_vars.size() << ")\n";); - return l_false; - } - - void assert_at_most_one(expr_ref_vector const& block_vars) { - expr_ref has_one(m), has_zero(m), at_most_one(m); - mk_at_most_one(block_vars.size(), block_vars.c_ptr(), has_one, has_zero); - at_most_one = m.mk_or(has_one, has_zero); - s().assert_expr(at_most_one); - } - - void mk_at_most_one(unsigned n, expr* const * vars, expr_ref& has_one, expr_ref& has_zero) { - SASSERT(n != 0); - if (n == 1) { - has_one = vars[0]; - has_zero = m.mk_not(vars[0]); - } - else { - unsigned mid = n/2; - expr_ref has_one1(m), has_one2(m), has_zero1(m), has_zero2(m); - mk_at_most_one(mid, vars, has_one1, has_zero1); - mk_at_most_one(n-mid, vars+mid, has_one2, has_zero2); - has_one = m.mk_or(m.mk_and(has_one1, has_zero2), m.mk_and(has_one2, has_zero1)); - has_zero = m.mk_and(has_zero1, has_zero2); - } - } - - - // TBD: bug when cancel flag is set, fu_malik returns is_sat == l_true instead of l_undef - virtual lbool operator()() { - lbool is_sat = l_true; - if (m_aux_soft.empty()) { - return is_sat; - } - solver::scoped_push _sp(s()); - expr_ref tmp(m); - - TRACE("opt", - tout << "soft constraints:\n"; - for (unsigned i = 0; i < m_aux_soft.size(); ++i) { - tout << mk_pp(m_aux_soft[i].get(), m) << "\n"; - }); - - for (unsigned i = 0; i < m_aux_soft.size(); ++i) { - m_aux.push_back(m.mk_fresh_const("p", m.mk_bool_sort())); - m_fm.insert(to_app(m_aux.back())->get_decl()); - tmp = m.mk_or(m_aux_soft[i].get(), m_aux[i].get()); - s().assert_expr(tmp); - } - - do { - is_sat = step(); - --m_upper; - } - while (is_sat == l_false); - - if (is_sat == l_true) { - // Get a list satisfying m_aux_soft - s().get_model(m_model); - m_lower = m_upper; - m_assignment.reset(); - for (unsigned i = 0; i < m_soft.size(); ++i) { - expr_ref val(m); - if (!m_model->eval(m_soft[i], val)) return l_undef; - TRACE("opt", tout << val << "\n";); - m_assignment.push_back(m.is_true(val)); - } - TRACE("opt", tout << "maxsat cost: " << m_upper << "\n"; - model_smt2_pp(tout, m, *m_model, 0);); - } - // We are done and soft_constraints has - // been updated with the max-sat assignment. - return is_sat; - } - - virtual void get_model(model_ref& mdl) { - mdl = m_model.get(); - } - - virtual rational get_lower() const { - return rational(m_aux_soft.size())-m_upper; - } - - virtual rational get_upper() const { - return rational(m_aux_soft.size())-m_lower; - } - }; - - maxsmt_solver_base* mk_fu_malik(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft) { - return alloc(fu_malik, c, ws, soft); - } - -}; - diff --git a/src/opt/fu_malik.h b/src/opt/fu_malik.h deleted file mode 100644 index 52e960b5e..000000000 --- a/src/opt/fu_malik.h +++ /dev/null @@ -1,37 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - fu_malik.h - -Abstract: - - Fu&Malik built-in optimization method. - Adapted from sample code in C. - -Author: - - Anh-Dung Phan (t-anphan) 2013-10-15 - -Notes: - - Takes solver with hard constraints added. - Returns a maximal satisfying subset of soft_constraints - that are still consistent with the solver state. - ---*/ -#ifndef OPT_FU_MALIK_H_ -#define OPT_FU_MALIK_H_ - -#include "opt_solver.h" -#include "maxsmt.h" - -namespace opt { - - maxsmt_solver_base* mk_fu_malik(maxsat_context& c, weights_t & ws, expr_ref_vector const& soft); - - -}; - -#endif diff --git a/src/opt/hitting_sets.cpp b/src/opt/hitting_sets.cpp deleted file mode 100644 index 678d7924e..000000000 --- a/src/opt/hitting_sets.cpp +++ /dev/null @@ -1,1088 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - hitting_sets.h - -Abstract: - - Hitting set approximations. - -Author: - - Nikolaj Bjorner (nbjorner) 2014-06-06 - -Notes: - ---*/ -#include "vector.h" -#include "util.h" -#include "hitting_sets.h" -#include "simplex.h" -#include "sparse_matrix_def.h" -#include "simplex_def.h" - -typedef simplex::simplex Simplex; -typedef simplex::sparse_matrix sparse_matrix; - - -namespace opt { - - struct hitting_sets::imp { - class justification { - public: - enum kind_t { AXIOM, DECISION, CLAUSE }; - private: - kind_t m_kind; - unsigned m_value; - bool m_pos; - public: - explicit justification(kind_t k):m_kind(k), m_value(0), m_pos(false) {} - explicit justification(unsigned v, bool pos):m_kind(CLAUSE), m_value(v), m_pos(pos) {} - justification(justification const& other): - m_kind(other.m_kind), m_value(other.m_value), m_pos(other.m_pos) {} - justification& operator=(justification const& other) { - m_kind = other.m_kind; - m_value = other.m_value; - m_pos = other.m_pos; - return *this; - } - unsigned clause() const { return m_value; } - bool is_axiom() const { return m_kind == AXIOM; } - bool is_decision() const { return m_kind == DECISION; } - bool is_clause() const { return m_kind == CLAUSE; } - kind_t kind() const { return m_kind; } - bool pos() const { return m_pos; } - }; - - class set { - unsigned m_num_elems; - unsigned m_elems[0]; - set(): m_num_elems(0) {} - public: - - static set* mk(small_object_allocator& alloc, unsigned sz, unsigned const* elems) { - unsigned size = (sz+1)*sizeof(unsigned); - void * mem = alloc.allocate(size); - set* result = new (mem) set(); - result->m_num_elems = sz; - memcpy(result->m_elems, elems, sizeof(unsigned)*sz); - return result; - } - - inline unsigned operator[](unsigned idx) const { - SASSERT(idx < m_num_elems); - return m_elems[idx]; - } - - inline unsigned& operator[](unsigned idx) { - SASSERT(idx < m_num_elems); - return m_elems[idx]; - } - - unsigned size() const { return m_num_elems; } - - unsigned alloc_size() const { return (m_num_elems + 1)*sizeof(unsigned); } - - bool empty() const { return 0 == size(); } - }; - - reslimit& m_limit; - rational m_lower; - rational m_upper; - vector m_weights; - vector m_weights_inv; - rational m_max_weight; - rational m_denominator; - small_object_allocator m_alloc; - ptr_vector m_T; - ptr_vector m_F; - svector m_value; - svector m_model; - vector m_tuse_list; - vector m_fuse_list; - - // Custom CDCL solver. - svector m_justification; - vector m_twatch; - vector m_fwatch; - unsigned_vector m_level; - unsigned_vector m_trail; // trail of assigned literals - unsigned m_qhead; // queue head - justification m_conflict_j; // conflict justification - unsigned m_conflict_l; // conflict literal - bool m_inconsistent; - unsigned m_scope_lvl; - rational m_weight; // current weight of assignment. - unsigned_vector m_indices; - unsigned_vector m_scores; - vector m_scored_weights; - svector m_score_updated; - bool m_enable_simplex; - struct compare_scores { - imp* m_imp; - compare_scores():m_imp(0) {} - bool operator()(int v1, int v2) const { - return m_imp->m_scored_weights[v1] > m_imp->m_scored_weights[v2]; - } - }; - compare_scores m_compare_scores; - heap m_heap; - svector m_mark; - struct scope { - unsigned m_trail_lim; - }; - vector m_scopes; - unsigned_vector m_lemma; - unsigned m_conflict_lvl; - - // simplex - unsynch_mpz_manager m; - Simplex m_simplex; - unsigned m_weights_var; - - static unsigned const null_idx = UINT_MAX; - - imp(reslimit& lim): - m_limit(lim), - m_max_weight(0), - m_denominator(1), - m_alloc("hitting-sets"), - m_qhead(0), - m_conflict_j(justification(justification::AXIOM)), - m_inconsistent(false), - m_scope_lvl(0), - m_compare_scores(), - m_heap(0, m_compare_scores), - m_simplex(lim), - m_weights_var(0) { - m_enable_simplex = true; - m_compare_scores.m_imp = this; - } - ~imp() { - for (unsigned i = 0; i < m_T.size(); ++i) { - m_alloc.deallocate(m_T[i]->alloc_size(), m_T[i]); - } - for (unsigned i = 0; i < m_F.size(); ++i) { - m_alloc.deallocate(m_F[i]->alloc_size(), m_F[i]); - } - } - - void add_weight(rational const& w) { - SASSERT(w.is_pos()); - unsigned var = m_weights.size(); - m_simplex.ensure_var(var); - m_simplex.set_lower(var, mpq_inf(mpq(0),mpq(0))); - m_simplex.set_upper(var, mpq_inf(mpq(1),mpq(0))); - m_weights.push_back(w); - m_weights_inv.push_back(rational::one()); - m_value.push_back(l_undef); - m_justification.push_back(justification(justification::DECISION)); - m_tuse_list.push_back(unsigned_vector()); - m_fuse_list.push_back(unsigned_vector()); - m_twatch.push_back(unsigned_vector()); - m_fwatch.push_back(unsigned_vector()); - m_level.push_back(0); - m_indices.push_back(var); - m_model.push_back(l_undef); - m_mark.push_back(false); - m_scores.push_back(0); - m_scored_weights.push_back(rational(0)); - m_score_updated.push_back(true); - m_max_weight += w; - } - - justification add_exists_false(unsigned sz, unsigned const* S) { - return add_exists(sz, S, true); - } - - justification add_exists_true(unsigned sz, unsigned const* S) { - return add_exists(sz, S, false); - } - - justification add_exists(unsigned sz, unsigned const* S, bool sign) { - vector& use_list = sign?m_fuse_list:m_tuse_list; - lbool val = sign?l_false:l_true; - justification j(justification::AXIOM); - ptr_vector& Sets = sign?m_F:m_T; - vector& watch = sign?m_fwatch:m_twatch; - init_weights(); - if (sz == 0) { - set_conflict(0, justification(justification::AXIOM)); - } - else if (sz == 1) { - IF_VERBOSE(2, verbose_stream() << "unit literal : " << S[0] << " " << val << "\n";); - assign(S[0], val, justification(justification::AXIOM)); - } - else { - unsigned clause_id = Sets.size(); - for (unsigned i = 0; i < sz; ++i) { - use_list[S[i]].push_back(clause_id); - } - j = justification(clause_id, !sign); - watch[S[0]].push_back(clause_id); - watch[S[1]].push_back(clause_id); - Sets.push_back(set::mk(m_alloc, sz, S)); - if (!sign) { - pop(scope_lvl()); - inc_score(clause_id); - } - TRACE("opt", display(tout, j);); - IF_VERBOSE(2, if (!sign) display(verbose_stream(), j);); - if (!sign && m_enable_simplex) { - add_simplex_row(!sign, sz, S); - } - } - return j; - } - - lbool compute_lower() { - m_lower.reset(); - rational w1 = L1(); - rational w2 = L2(); - rational w3 = L3(); - if (w1 > m_lower) m_lower = w1; - if (w2 > m_lower) m_lower = w2; - if (w3 > m_lower) m_lower = w3; - return l_true; - } - - lbool compute_upper() { - m_upper = m_max_weight; - unsigned fsz = m_F.size(); - lbool r = search(); - pop(scope_lvl()); - - - IF_VERBOSE(1, verbose_stream() << "(hsmax.negated-size: " << fsz << ")\n";); -#if 0 - // garbage collect agressively on exit. - // all learned clases for negative branches are - // pruned. - for (unsigned i = fsz; i < m_F.size(); ++i) { - m_alloc.deallocate(m_F[i]->alloc_size(), m_F[i]); - } - m_F.resize(fsz); - for (unsigned i = 0; i < m_fuse_list.size(); ++i) { - unsigned_vector & uses = m_fuse_list[i]; - while (!uses.empty() && uses.back() >= fsz) uses.pop_back(); - unsigned_vector & watch = m_fwatch[i]; - unsigned j = 0, k = 0; - for (; j < watch.size(); ++j) { - if (watch[j] < fsz) { - watch[k] = watch[j]; - ++k; - } - } - watch.resize(k); - } -#endif - return r; - } - - rational get_lower() { - return m_lower/m_denominator; - } - - rational get_upper() { - return m_upper/m_denominator; - } - - void set_upper(rational const& r) { - m_max_weight = r*m_denominator; - } - - bool get_value(unsigned idx) { - return - idx < m_model.size() && - m_model[idx] == l_true; - } - - void collect_statistics(::statistics& st) const { - m_simplex.collect_statistics(st); - } - - void reset() { - m_lower.reset(); - m_upper = m_max_weight; - } - - void init_weights() { - if (m_weights_var != 0) { - return; - } - m_weights_var = m_weights.size(); - unsigned_vector vars; - scoped_mpz_vector coeffs(m); - - // normalize weights to integral. - rational d(1); - for (unsigned i = 0; i < m_weights.size(); ++i) { - d = lcm(d, denominator(m_weights[i])); - } - m_denominator = d; - if (!d.is_one()) { - for (unsigned i = 0; i < m_weights.size(); ++i) { - m_weights[i] *= d; - } - } - rational lc(1); - for (unsigned i = 0; i < m_weights.size(); ++i) { - lc = lcm(lc, m_weights[i]); - } - for (unsigned i = 0; i < m_weights.size(); ++i) { - m_weights_inv[i] = lc/m_weights[i]; - } - - m_heap.set_bounds(m_weights.size()); - for (unsigned i = 0; i < m_weights.size(); ++i) { - m_heap.insert(i); - } - update_heap(); - - // set up Simplex objective function. - for (unsigned i = 0; i < m_weights.size(); ++i) { - vars.push_back(i); - coeffs.push_back(m_weights[i].to_mpq().numerator()); - } - m_simplex.ensure_var(m_weights_var); - vars.push_back(m_weights_var); - coeffs.push_back(mpz(-1)); - m_simplex.add_row(m_weights_var, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); - - } - - void display(std::ostream& out) const { - out << "inconsistent: " << m_inconsistent << "\n"; - out << "weight: " << m_weight << "\n"; - for (unsigned i = 0; i < m_weights.size(); ++i) { - out << i << ": " << value(i) << " w: " << m_weights[i] << " s: " << m_scores[i] << "\n"; - } - for (unsigned i = 0; i < m_T.size(); ++i) { - display(out << "+" << i << ": ", *m_T[i]); - } - for (unsigned i = 0; i < m_F.size(); ++i) { - display(out << "-" << i << ": ", *m_F[i]); - } - out << "watch lists:\n"; - for (unsigned i = 0; i < m_fwatch.size(); ++i) { - out << i << ": "; - for (unsigned j = 0; j < m_twatch[i].size(); ++j) { - out << "+" << m_twatch[i][j] << " "; - } - for (unsigned j = 0; j < m_fwatch[i].size(); ++j) { - out << "-" << m_fwatch[i][j] << " "; - } - out << "\n"; - } - out << "trail\n"; - for (unsigned i = 0; i < m_trail.size(); ++i) { - unsigned idx = m_trail[i]; - out << (m_justification[idx].is_decision()?"d":"") << idx << " "; - } - out << "\n"; - } - - void display(std::ostream& out, set const& S) const { - for (unsigned i = 0; i < S.size(); ++i) { - out << S[i] << " "; - } - out << "\n"; - } - - void display(std::ostream& out, justification const& j) const { - switch(j.kind()) { - case justification::AXIOM: - out << "axiom\n"; - break; - case justification::DECISION: - out << "decision\n"; - break; - case justification::CLAUSE: { - out << "clause: "; - set const& S = j.pos()?(*m_T[j.clause()]):(*m_F[j.clause()]); - for (unsigned i = 0; i < S.size(); ++i) { - out << S[i] << " "; - } - out << "\n"; - } - } - } - - void display_lemma(std::ostream& out) { - out << "lemma: "; - for (unsigned i = 0; i < m_lemma.size(); ++i) { - out << m_lemma[i] << " "; - } - out << "\n"; - } - - struct scoped_push { - imp& s; - scoped_push(imp& s):s(s) { s.push(); } - ~scoped_push() { s.pop(1); } - }; - - struct value_lt { - vector const& weights; - value_lt(vector const& weights): - weights(weights) {} - bool operator()(int v1, int v2) const { - return weights[v1] > weights[v2]; - } - }; - - void inc_score(unsigned clause_id) { - set const& S = *m_T[clause_id]; - if (!has_selected(S)) { - for (unsigned j = 0; j < S.size(); ++j) { - ++m_scores[S[j]]; - m_score_updated[S[j]] = true; - } - } - } - - void dec_score(unsigned clause_id) { - set const& S = *m_T[clause_id]; - if (!has_selected(S)) { - for (unsigned j = 0; j < S.size(); ++j) { - SASSERT(m_scores[S[j]] > 0); - --m_scores[S[j]]; - m_score_updated[S[j]] = true; - } - } - } - - void update_score(unsigned idx, bool inc) { - unsigned_vector const& uses = m_tuse_list[idx]; - for (unsigned i = 0; i < uses.size(); ++i) { - if (inc) { - inc_score(uses[i]); - } - else { - dec_score(uses[i]); - } - } - } - - rational L1() { - rational w(m_weight); - scoped_push _sc(*this); - for (unsigned i = 0; !canceled() && i < m_T.size(); ++i) { - set const& S = *m_T[i]; - SASSERT(!S.empty()); - if (!has_selected(S)) { - w += m_weights[select_min(S)]; - for (unsigned j = 0; j < S.size(); ++j) { - assign(S[j], l_true, justification(justification::DECISION)); - } - } - } - return w; - } - - void update_heap() { - for (unsigned i = 0; i < m_scored_weights.size(); ++i) { - if (m_score_updated[i]) { - rational const& old_w = m_scored_weights[i]; - rational new_w = rational(m_scores[i])*m_weights_inv[i]; - if (new_w > old_w) { - m_scored_weights[i] = new_w; - //m_heap.decreased(i); - } - else if (new_w < old_w) { - m_scored_weights[i] = new_w; - //m_heap.increased(i); - } - m_score_updated[i] = false; - } - } - } - - rational L2() { - rational w(m_weight); - scoped_push _sc(*this); - int n = 0; - for (unsigned i = 0; i < m_T.size(); ++i) { - if (!has_selected(*m_T[i])) ++n; - } - - update_heap(); - value_lt lt(m_scored_weights); - std::sort(m_indices.begin(), m_indices.end(), lt); - for(unsigned i = 0; i < m_indices.size() && n > 0; ++i) { - // deg(c) = score(c) - // wt(c) = m_weights[c] - - unsigned idx = m_indices[i]; - if (m_scores[idx] == 0) { - break; - } - if (m_scores[idx] < static_cast(n) || m_weights[idx].is_one()) { - w += m_weights[idx]; - } - else { - w += div((rational(n)*m_weights[idx]), rational(m_scores[idx])); - } - n -= m_scores[idx]; - } - return w; - } - - rational L3() { - TRACE("simplex", m_simplex.display(tout);); - VERIFY(l_true == m_simplex.make_feasible()); - TRACE("simplex", m_simplex.display(tout);); - VERIFY(l_true == m_simplex.minimize(m_weights_var)); - mpq_inf const& val = m_simplex.get_value(m_weights_var); - unsynch_mpq_inf_manager mg; - unsynch_mpq_manager& mq = mg.get_mpq_manager(); - scoped_mpq c(mq); - mg.ceil(val, c); - rational w(c); - CTRACE("simplex", - w >= m_weight, tout << w << " " << m_weight << " !!!!\n"; - display(tout);); - SASSERT(w >= m_weight); - return w; - } - - void add_simplex_row(bool is_some_true, unsigned sz, unsigned const* S) { - unsigned_vector vars; - scoped_mpz_vector coeffs(m); - for (unsigned i = 0; i < sz; ++i) { - vars.push_back(S[i]); - coeffs.push_back(mpz(1)); - } - unsigned base_var = m_F.size() + m_T.size() + m_weights.size(); - m_simplex.ensure_var(base_var); - vars.push_back(base_var); - coeffs.push_back(mpz(-1)); - // S - base_var = 0 - if (is_some_true) { - // base_var >= 1 - m_simplex.set_lower(base_var, mpq_inf(mpq(1),mpq(0))); - } - else { - // base_var <= sz-1 - m_simplex.set_upper(base_var, mpq_inf(mpq(sz-1),mpq(0))); - } - m_simplex.add_row(base_var, coeffs.size(), vars.c_ptr(), coeffs.c_ptr()); - } - - unsigned select_min(set const& S) { - unsigned result = S[0]; - for (unsigned i = 1; i < S.size(); ++i) { - if (m_weights[result] > m_weights[S[i]]) { - result = S[i]; - } - } - return result; - } - - bool have_selected(lbool val, ptr_vector const& Sets, unsigned& i) { - for (i = 0; i < Sets.size(); ++i) { - if (!has_selected(val, *Sets[i])) return false; - } - return true; - } - - void set_undef_to_false() { - for (unsigned i = 0; i < m_model.size(); ++i) { - if (m_model[i] == l_undef) { - m_model[i] = l_false; - } - } - } - - bool values_satisfy_Fs(unsigned& i) { - unsigned j = 0; - for (i = 0; i < m_F.size(); ++i) { - set const& F = *m_F[i]; - for (j = 0; j < F.size(); ++j) { - if (m_model[F[j]] == l_false) { - break; - } - } - if (F.size() == j) { - break; - } - } - return i == m_F.size(); - } - - bool has_selected(set const& S) { - return has_selected(l_true, S); - } - - bool has_unselected(set const& S) { - return has_selected(l_false, S); - } - - bool has_unset(set const& S) { - return has_selected(l_undef, S); - } - - bool has_selected(lbool val, set const& S) { - for (unsigned i = 0; i < S.size(); ++i) { - if (val == value(S[i])) { - return true; - } - } - return false; - } - - // (greedy) CDCL learner for hitting sets. - - inline unsigned scope_lvl() const { return m_scope_lvl; } - inline bool inconsistent() const { return m_inconsistent; } - inline bool canceled() const { return !m_limit.inc(); } - inline unsigned lvl(unsigned idx) const { return m_level[idx]; } - inline lbool value(unsigned idx) const { return m_value[idx]; } - - inline bool is_marked(unsigned v) const { return m_mark[v] != 0; } - inline void mark(unsigned v) { SASSERT(!is_marked(v)); m_mark[v] = true; } - inline void reset_mark(unsigned v) { SASSERT(is_marked(v)); m_mark[v] = false; } - - void push() { - SASSERT(!inconsistent()); - ++m_scope_lvl; - m_scopes.push_back(scope()); - scope& s = m_scopes.back(); - s.m_trail_lim = m_trail.size(); - } - - void pop(unsigned n) { - if (n > 0) { - m_inconsistent = false; - m_scope_lvl = scope_lvl() - n; - unassign(m_scopes[scope_lvl()].m_trail_lim); - m_scopes.shrink(scope_lvl()); - } - } - - void assign(unsigned idx, lbool val, justification const& justification) { - if (val == l_true) { - m_weight += m_weights[idx]; - update_score(idx, false); - if (m_enable_simplex) { - m_simplex.set_lower(idx, mpq_inf(mpq(1),mpq(0))); - } - } - SASSERT(val != l_true || m_scores[idx] == 0); - m_value[idx] = val; - m_justification[idx] = justification; - m_trail.push_back(idx); - m_level[idx] = scope_lvl(); - TRACE("opt", tout << idx << " := " << val << " scope: " << scope_lvl() << " w: " << m_weight << "\n";); - } - - - svector m_replay_idx; - svector m_replay_val; - void unassign(unsigned sz) { - for (unsigned j = sz; j < m_trail.size(); ++j) { - unsigned idx = m_trail[j]; - lbool val = value(idx); - m_value[idx] = l_undef; - if (val == l_true) { - m_weight -= m_weights[idx]; - update_score(idx, true); - if (m_enable_simplex) { - m_simplex.set_lower(idx, mpq_inf(mpq(0),mpq(0))); - } - } - if (m_justification[idx].is_axiom()) { - m_replay_idx.push_back(idx); - m_replay_val.push_back(val); - } - } - TRACE("opt", tout << m_weight << "\n";); - m_trail.shrink(sz); - m_qhead = sz; - for (unsigned i = m_replay_idx.size(); i > 0; ) { - --i; - unsigned idx = m_replay_idx[i]; - lbool val = m_replay_val[i]; - assign(idx, val, justification(justification::AXIOM)); - } - m_replay_idx.reset(); - m_replay_val.reset(); - } - - - lbool search() { - TRACE("opt", display(tout);); - pop(scope_lvl()); - while (true) { - while (true) { - propagate(); - if (canceled()) return l_undef; - if (!inconsistent()) break; - if (!resolve_conflict()) return l_false; - SASSERT(!inconsistent()); - } - if (!decide()) { - SASSERT(validate_model()); - m_model.reset(); - m_model.append(m_value); - m_upper = m_weight; - // SASSERT(m_weight < m_max_weight); - return l_true; - } - } - } - - bool validate_model() { - for (unsigned i = 0; i < m_T.size(); ++i) { - set const& S = *m_T[i]; - bool found = false; - for (unsigned j = 0; !found && j < S.size(); ++j) { - found = value(S[j]) == l_true; - } - CTRACE("opt", !found, - display(tout << "not found: " << i << "\n", S); - display(tout);); - SASSERT(found); - } - for (unsigned i = 0; i < m_F.size(); ++i) { - set const& S = *m_F[i]; - bool found = false; - for (unsigned j = 0; !found && j < S.size(); ++j) { - found = value(S[j]) != l_true; - } - CTRACE("opt", !found, - display(tout << "not found: " << i << "\n", S); - display(tout);); - SASSERT(found); - } - - return true; - } - - bool invariant() { - DEBUG_CODE( - for (unsigned i = 0; i < m_fwatch.size(); ++i) { - for (unsigned j = 0; j < m_fwatch[i].size(); ++j) { - set const& S = *m_F[m_fwatch[i][j]]; - SASSERT(S[0] == i || S[1] == i); - } - } - for (unsigned i = 0; i < m_twatch.size(); ++i) { - for (unsigned j = 0; j < m_twatch[i].size(); ++j) { - set const& S = *m_T[m_twatch[i][j]]; - SASSERT(S[0] == i || S[1] == i); - } - }); - return true; - } - - bool resolve_conflict() { - while (true) { - if (!resolve_conflict_core()) return false; - if (!inconsistent()) return true; - } - } - - unsigned get_max_lvl(unsigned conflict_l, justification const& conflict_j) { - if (scope_lvl() == 0) return 0; - unsigned r = lvl(conflict_l); - if (conflict_j.is_clause()) { - unsigned clause = conflict_j.clause(); - ptr_vector const& S = conflict_j.pos()?m_T:m_F; - r = std::max(r, lvl((*S[clause])[0])); - r = std::max(r, lvl((*S[clause])[1])); - } - return r; - } - - bool resolve_conflict_core() { - SASSERT(inconsistent()); - TRACE("opt", display(tout);); - unsigned conflict_l = m_conflict_l; - justification conflict_j(m_conflict_j); - if (conflict_j.is_axiom()) { - return false; - } - m_conflict_lvl = get_max_lvl(conflict_l, conflict_j); - if (m_conflict_lvl == 0) { - return false; - } - unsigned idx = skip_above_conflict_level(); - unsigned num_marks = 0; - m_lemma.reset(); - m_lemma.push_back(0); - process_antecedent(conflict_l, num_marks); - do { - TRACE("opt", tout << "conflict literal: " << conflict_l << "\n"; - display(tout, conflict_j);); - if (conflict_j.is_clause()) { - unsigned cl = conflict_j.clause(); - unsigned i = 0; - SASSERT(value(conflict_l) != l_undef); - set const& T = conflict_j.pos()?(*m_T[cl]):(*m_F[cl]); - if (T[0] == conflict_l) { - i = 1; - } - else { - SASSERT(T[1] == conflict_l); - process_antecedent(T[0], num_marks); - i = 2; - } - unsigned sz = T.size(); - for (; i < sz; ++i) { - process_antecedent(T[i], num_marks); - } - } - else if (conflict_j.is_decision()) { - --num_marks; - SASSERT(num_marks == 0); - break; - } - else if (conflict_j.is_axiom()) { - IF_VERBOSE(0, verbose_stream() << "axiom " << conflict_l << " " << value(conflict_l) << " " << num_marks << "\n";); - --num_marks; - SASSERT(num_marks == 0); - break; - } - while (true) { - unsigned l = m_trail[idx]; - if (is_marked(l)) break; - SASSERT(idx > 0); - --idx; - } - conflict_l = m_trail[idx]; - conflict_j = m_justification[conflict_l]; - --idx; - --num_marks; - if (num_marks == 0 && value(conflict_l) == l_false) { - ++num_marks; - } - reset_mark(conflict_l); - } - while (num_marks > 0); - m_lemma[0] = conflict_l; - TRACE("opt", display_lemma(tout);); - SASSERT(value(conflict_l) == l_true); - unsigned new_scope_lvl = 0; - for (unsigned i = 1; i < m_lemma.size(); ++i) { - SASSERT(l_true == value(m_lemma[i])); - new_scope_lvl = std::max(new_scope_lvl, lvl(m_lemma[i])); - reset_mark(m_lemma[i]); - } - pop(scope_lvl() - new_scope_lvl); - SASSERT(l_undef == value(conflict_l)); - justification j = add_exists_false(m_lemma.size(), m_lemma.c_ptr()); - if (!j.is_axiom()) assign(conflict_l, l_false, j); - return true; - } - - - void process_antecedent(unsigned antecedent, unsigned& num_marks) { - unsigned alvl = lvl(antecedent); - SASSERT(alvl <= m_conflict_lvl); - if (!is_marked(antecedent) && alvl > 0 && !m_justification[antecedent].is_axiom()) { - mark(antecedent); - if (alvl == m_conflict_lvl || value(antecedent) == l_false) { - ++num_marks; - } - else { - m_lemma.push_back(antecedent); - } - } - } - - unsigned skip_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 set_conflict(unsigned idx, justification const& justification) { - if (!inconsistent()) { - TRACE("opt", tout << "conflict: " << idx << "\n";); - m_inconsistent = true; - m_conflict_j = justification; - m_conflict_l = idx; - } - } - - unsigned next_var() { - update_heap(); - - value_lt lt(m_scored_weights); - std::sort(m_indices.begin(), m_indices.end(), lt); - unsigned idx = m_indices[0]; - if (m_scores[idx] == 0) return UINT_MAX; - return idx; -#if 0 - int min_val = m_heap.min_value(); - if (min_val == -1) { - return UINT_MAX; - } - SASSERT(0 <= min_val && static_cast(min_val) < m_weights.size()); - if (m_scores[min_val] == 0) { - return UINT_MAX; - } - return static_cast(min_val); -#endif - } - - bool decide() { - unsigned idx = next_var(); - if (idx == UINT_MAX) { - return false; - } - else { - push(); - TRACE("opt", tout << "decide " << idx << "\n";); - assign(idx, l_true, justification(justification::DECISION)); - return true; - } - } - - void propagate() { - TRACE("opt", display(tout);); - SASSERT(invariant()); - while (m_qhead < m_trail.size() && !inconsistent() && !canceled()) { - unsigned idx = m_trail[m_qhead]; - ++m_qhead; - switch (value(idx)) { - case l_undef: - UNREACHABLE(); - break; - case l_true: - propagate(idx, l_false, m_fwatch, m_F); - break; - case l_false: - propagate(idx, l_true, m_twatch, m_T); - break; - } - } - prune_branch(); - } - - void propagate(unsigned idx, lbool good_val, vector& watch, ptr_vector& Fs) - { - TRACE("opt", tout << idx << " " << value(idx) << "\n";); - unsigned_vector& w = watch[idx]; - unsigned sz = w.size(); - lbool bad_val = ~good_val; - SASSERT(value(idx) == bad_val); - unsigned l = 0; - for (unsigned i = 0; i < sz && !canceled(); ++i, ++l) { - unsigned clause_id = w[i]; - set& F = *Fs[clause_id]; - SASSERT(F.size() >= 2); - bool k1 = (F[0] != idx); - bool k2 = !k1; - SASSERT(F[k1] == idx); - SASSERT(value(F[k1]) == bad_val); - if (value(F[k2]) == good_val) { - w[l] = w[i]; - continue; - } - bool found = false; - unsigned sz2 = F.size(); - for (unsigned j = 2; !found && j < sz2; ++j) { - unsigned idx2 = F[j]; - if (value(idx2) != bad_val) { - found = true; - std::swap(F[k1], F[j]); - --l; - watch[idx2].push_back(clause_id); - } - } - if (!found) { - if (value(F[k2]) == bad_val) { - set_conflict(F[k2], justification(clause_id, good_val == l_true)); - if (i == l) { - l = sz; - } - else { - for (; i < sz; ++i, ++l) { - w[l] = w[i]; - } - } - break; - } - else { - SASSERT(value(F[k2]) == l_undef); - assign(F[k2], good_val, justification(clause_id, good_val == l_true)); - w[l] = w[i]; - } - } - } - watch[idx].shrink(l); - SASSERT(invariant()); - TRACE("opt", tout << idx << " " << value(idx) << "\n";); - SASSERT(value(idx) == bad_val); - } - - bool infeasible_lookahead() { - if (m_enable_simplex && L3() >= m_max_weight) { - return true; - } - return - (L1() >= m_max_weight) || - (L2() >= m_max_weight); - } - - void prune_branch() { - if (inconsistent() || !infeasible_lookahead()) { - return; - } - - IF_VERBOSE(4, verbose_stream() << "(hs.prune-branch " << m_weight << ")\n";); - m_lemma.reset(); - unsigned i = 0; - rational w(0); - for (; i < m_trail.size() && w < m_max_weight; ++i) { - unsigned idx = m_trail[i]; - if (m_justification[idx].is_decision()) { - SASSERT(value(idx) == l_true); - m_lemma.push_back(idx); - w += m_weights[idx]; - } - } - // undo the lower bounds. - TRACE("opt", - tout << "prune branch: " << m_weight << " "; - display_lemma(tout); - display(tout); - ); - justification j = add_exists_false(m_lemma.size(), m_lemma.c_ptr()); - unsigned idx = m_lemma.empty()?0:m_lemma[0]; - set_conflict(idx, j); - } - - // TBD: derive strong inequalities and add them to Simplex. - // x_i1 + .. + x_ik >= k-1 for each subset k from set n: x_1 + .. + x_n >= k - }; - - - hitting_sets::hitting_sets(reslimit& lim) { m_imp = alloc(imp, lim); } - hitting_sets::~hitting_sets() { dealloc(m_imp); } - void hitting_sets::add_weight(rational const& w) { m_imp->add_weight(w); } - void hitting_sets::add_exists_true(unsigned sz, unsigned const* elems) { m_imp->add_exists_true(sz, elems); } - void hitting_sets::add_exists_false(unsigned sz, unsigned const* elems) { m_imp->add_exists_false(sz, elems); } - lbool hitting_sets::compute_lower() { return m_imp->compute_lower(); } - lbool hitting_sets::compute_upper() { return m_imp->compute_upper(); } - rational hitting_sets::get_lower() { return m_imp->get_lower(); } - rational hitting_sets::get_upper() { return m_imp->get_upper(); } - void hitting_sets::set_upper(rational const& r) { return m_imp->set_upper(r); } - bool hitting_sets::get_value(unsigned idx) { return m_imp->get_value(idx); } - void hitting_sets::collect_statistics(::statistics& st) const { m_imp->collect_statistics(st); } - void hitting_sets::reset() { m_imp->reset(); } - - -}; diff --git a/src/opt/hitting_sets.h b/src/opt/hitting_sets.h deleted file mode 100644 index c9708710f..000000000 --- a/src/opt/hitting_sets.h +++ /dev/null @@ -1,52 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - hitting_sets.h - -Abstract: - - Hitting set approximations. - -Author: - - Nikolaj Bjorner (nbjorner) 2014-06-06 - -Notes: - ---*/ -#ifndef HITTING_SETS_H_ -#define HITTING_SETS_H_ - -#include "rational.h" -#include "statistics.h" -#include "lbool.h" -#include "rlimit.h" - -namespace opt { - - class hitting_sets { - struct imp; - imp* m_imp; - public: - hitting_sets(reslimit& lim); - ~hitting_sets(); - void add_weight(rational const& w); - void add_exists_true(unsigned sz, unsigned const* elems); - void add_exists_false(unsigned sz, unsigned const* elems); - lbool compute_lower(); - lbool compute_upper(); - void set_upper(rational const& r); - rational get_lower(); - rational get_upper(); - bool get_value(unsigned idx); - void set_cancel(bool f); - void collect_statistics(::statistics& st) const; - void reset(); - }; - - -}; - -#endif diff --git a/src/opt/maxhs.cpp b/src/opt/maxhs.cpp deleted file mode 100644 index 2297bdc20..000000000 --- a/src/opt/maxhs.cpp +++ /dev/null @@ -1,556 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - maxhs.cpp - -Abstract: - - maxhs based MaxSAT. - -Author: - - Nikolaj Bjorner (nbjorner) 2014-4-17 - -Notes: - ---*/ -#include "optsmt.h" -#include "hitting_sets.h" -#include "stopwatch.h" -#include "ast_pp.h" -#include "model_smt2_pp.h" -#include "uint_set.h" -#include "maxhs.h" -#include "opt_context.h" - -namespace opt { - - class scoped_stopwatch { - double& m_time; - stopwatch m_watch; - public: - scoped_stopwatch(double& time): m_time(time) { - m_watch.start(); - } - ~scoped_stopwatch() { - m_watch.stop(); - m_time += m_watch.get_seconds(); - } - }; - - - // ---------------------------------- - // MaxSatHS+MSS - // variant of MaxSAT-HS (Algorithm 9) - // that also refines upper bound during progressive calls - // to the underlying optimization solver for the soft constraints. - // - - class maxhs : public maxsmt_solver_base { - struct stats { - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - unsigned m_num_iterations; - unsigned m_num_core_reductions_success; - unsigned m_num_core_reductions_failure; - unsigned m_num_model_expansions_success; - unsigned m_num_model_expansions_failure; - double m_core_reduction_time; - double m_model_expansion_time; - double m_aux_sat_time; - double m_disjoint_cores_time; - }; - - hitting_sets m_hs; - expr_ref_vector m_aux; // auxiliary (indicator) variables. - obj_map m_aux2index; // expr |-> index - unsigned_vector m_core_activity; // number of times soft constraint is used in a core. - svector m_seed; // clause selected in current model. - svector m_aux_active; // active soft clauses. - ptr_vector m_asms; // assumptions (over aux) - stats m_stats; - bool m_at_lower_bound; - - - public: - maxhs(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): - maxsmt_solver_base(c, ws, soft), - m_hs(m.limit()), - m_aux(m), - m_at_lower_bound(false) { - } - virtual ~maxhs() {} - - virtual void collect_statistics(statistics& st) const { - maxsmt_solver_base::collect_statistics(st); - m_hs.collect_statistics(st); - st.update("maxhs-num-iterations", m_stats.m_num_iterations); - st.update("maxhs-num-core-reductions-n", m_stats.m_num_core_reductions_failure); - st.update("maxhs-num-core-reductions-y", m_stats.m_num_core_reductions_success); - st.update("maxhs-num-model-expansions-n", m_stats.m_num_model_expansions_failure); - st.update("maxhs-num-model-expansions-y", m_stats.m_num_model_expansions_success); - st.update("maxhs-core-reduction-time", m_stats.m_core_reduction_time); - st.update("maxhs-model-expansion-time", m_stats.m_model_expansion_time); - st.update("maxhs-aux-sat-time", m_stats.m_aux_sat_time); - st.update("maxhs-disj-core-time", m_stats.m_disjoint_cores_time); - } - - lbool operator()() { - ptr_vector hs; - init(); - init_local(); - if (!disjoint_cores(hs)) { - return l_undef; - } - seed2assumptions(); - while (m_lower < m_upper) { - ++m_stats.m_num_iterations; - trace_bounds("maxhs"); - TRACE("opt", tout << "(maxhs [" << m_lower << ":" << m_upper << "])\n";); - if (m.canceled()) { - return l_undef; - } - - lbool core_found = generate_cores(hs); - switch(core_found) { - case l_undef: - return l_undef; - case l_true: { - lbool is_sat = next_seed(); - switch(is_sat) { - case l_true: - seed2hs(false, hs); - break; - case l_false: - TRACE("opt", tout << "no more seeds\n";); - IF_VERBOSE(1, verbose_stream() << "(opt.maxhs.no-more-seeds)\n";); - m_lower = m_upper; - return l_true; - case l_undef: - return l_undef; - } - break; - } - case l_false: - IF_VERBOSE(1, verbose_stream() << "(opt.maxhs.no-more-cores)\n";); - TRACE("opt", tout << "no more cores\n";); - m_lower = m_upper; - return l_true; - } - } - return l_true; - } - - private: - - unsigned num_soft() const { return m_soft.size(); } - - void init_local() { - unsigned sz = num_soft(); - app_ref fml(m), obj(m); - expr_ref_vector sum(m); - m_asms.reset(); - m_seed.reset(); - m_aux.reset(); - m_aux_active.reset(); - m_aux2index.reset(); - m_core_activity.reset(); - for (unsigned i = 0; i < sz; ++i) { - bool tt = is_true(m_model, m_soft[i]); - m_seed.push_back(tt); - m_aux. push_back(mk_fresh(m.mk_bool_sort())); - m_aux_active.push_back(false); - m_core_activity.push_back(0); - m_aux2index.insert(m_aux.back(), i); - if (tt) { - m_asms.push_back(m_aux.back()); - ensure_active(i); - } - } - - for (unsigned i = 0; i < m_weights.size(); ++i) { - m_hs.add_weight(m_weights[i]); - } - TRACE("opt", print_seed(tout);); - } - - - void hs2seed(ptr_vector const& hs) { - for (unsigned i = 0; i < num_soft(); ++i) { - m_seed[i] = true; - } - for (unsigned i = 0; i < hs.size(); ++i) { - m_seed[m_aux2index.find(hs[i])] = false; - } - TRACE("opt", - print_asms(tout << "hitting set: ", hs); - print_seed(tout);); - } - - void seed2hs(bool pos, ptr_vector& hs) { - hs.reset(); - for (unsigned i = 0; i < num_soft(); ++i) { - if (pos == m_seed[i]) { - hs.push_back(m_aux[i].get()); - } - } - TRACE("opt", - print_asms(tout << "hitting set: ", hs); - print_seed(tout);); - - } - - void seed2assumptions() { - seed2hs(true, m_asms); - } - - - // - // Find disjoint cores for soft constraints. - // - bool disjoint_cores(ptr_vector& hs) { - scoped_stopwatch _sw(m_stats.m_disjoint_cores_time); - m_asms.reset(); - svector active(num_soft(), true); - rational lower(0); - update_assumptions(active, lower, hs); - SASSERT(lower.is_zero()); - while (true) { - lbool is_sat = s().check_sat(m_asms.size(), m_asms.c_ptr()); - switch (is_sat) { - case l_true: - if (lower > m_lower) { - m_lower = lower; - } - return true; - case l_false: - if (!shrink()) return false; - block_up(); - update_assumptions(active, lower, hs); - break; - case l_undef: - return false; - } - } - } - - void update_assumptions(svector& active, rational& lower, ptr_vector& hs) { - rational arg_min(0); - expr* e = 0; - for (unsigned i = 0; i < m_asms.size(); ++i) { - unsigned index = m_aux2index.find(m_asms[i]); - active[index] = false; - if (arg_min.is_zero() || arg_min > m_weights[index]) { - arg_min = m_weights[index]; - e = m_asms[i]; - } - } - if (e) { - hs.push_back(e); - lower += arg_min; - } - m_asms.reset(); - for (unsigned i = 0; i < num_soft(); ++i) { - if (active[i]) { - m_asms.push_back(m_aux[i].get()); - ensure_active(i); - } - } - } - - // - // Auxiliary Algorithm 10 for producing cores. - // - lbool generate_cores(ptr_vector& hs) { - bool core = !m_at_lower_bound; - while (true) { - hs2seed(hs); - lbool is_sat = check_subset(); - switch(is_sat) { - case l_undef: - return l_undef; - case l_true: - if (!grow()) return l_undef; - block_down(); - return core?l_true:l_false; - case l_false: - core = true; - if (!shrink()) return l_undef; - block_up(); - find_non_optimal_hitting_set(hs); - break; - } - } - } - - struct lt_activity { - maxhs& hs; - lt_activity(maxhs& hs):hs(hs) {} - bool operator()(expr* a, expr* b) const { - unsigned w1 = hs.m_core_activity[hs.m_aux2index.find(a)]; - unsigned w2 = hs.m_core_activity[hs.m_aux2index.find(b)]; - return w1 < w2; - } - }; - - // - // produce the non-optimal hitting set by using the 10% heuristic. - // of most active cores constraints. - // m_asms contains the current core. - // - void find_non_optimal_hitting_set(ptr_vector& hs) { - std::sort(m_asms.begin(), m_asms.end(), lt_activity(*this)); - for (unsigned i = m_asms.size(); i > 9*m_asms.size()/10;) { - --i; - hs.push_back(m_asms[i]); - } - } - - // - // retrieve the next seed that satisfies state of hs. - // state of hs must be satisfiable before optimization is called. - // - lbool next_seed() { - scoped_stopwatch _sw(m_stats.m_aux_sat_time); - TRACE("opt", tout << "\n";); - - // min c_i*(not x_i) for x_i are soft clauses. - // max c_i*x_i for x_i are soft clauses - - m_at_lower_bound = false; - - lbool is_sat = m_hs.compute_upper(); - - if (is_sat == l_true) { - is_sat = m_hs.compute_lower(); - } - if (is_sat == l_true) { - m_at_lower_bound = m_hs.get_upper() == m_hs.get_lower(); - if (m_hs.get_lower() > m_lower) { - m_lower = m_hs.get_lower(); - } - for (unsigned i = 0; i < num_soft(); ++i) { - m_seed[i] = is_active(i) && !m_hs.get_value(i); - } - TRACE("opt", print_seed(tout);); - } - return is_sat; - } - - // - // check assignment returned by HS with the original - // hard constraints. - // - lbool check_subset() { - TRACE("opt", tout << "\n";); - m_asms.reset(); - for (unsigned i = 0; i < num_soft(); ++i) { - if (m_seed[i]) { - m_asms.push_back(m_aux[i].get()); - ensure_active(i); - } - } - return s().check_sat(m_asms.size(), m_asms.c_ptr()); - } - - // - // extend the current assignment to one that - // satisfies as many soft constraints as possible. - // update the upper bound based on this assignment - // - bool grow() { - scoped_stopwatch _sw(m_stats.m_model_expansion_time); - model_ref mdl; - s().get_model(mdl); - for (unsigned i = 0; i < num_soft(); ++i) { - ensure_active(i); - m_seed[i] = false; - } - for (unsigned i = 0; i < m_asms.size(); ++i) { - m_seed[m_aux2index.find(m_asms[i])] = true; - } - - for (unsigned i = 0; i < num_soft(); ++i) { - if (m_seed[i]) { - // already an assumption - } - else if (is_true(mdl, m_soft[i])) { - m_seed[i] = true; - m_asms.push_back(m_aux[i].get()); - } - else { - m_asms.push_back(m_aux[i].get()); - lbool is_sat = s().check_sat(m_asms.size(), m_asms.c_ptr()); - switch(is_sat) { - case l_undef: - return false; - case l_false: - ++m_stats.m_num_model_expansions_failure; - m_asms.pop_back(); - break; - case l_true: - ++m_stats.m_num_model_expansions_success; - s().get_model(mdl); - m_seed[i] = true; - break; - } - } - } - rational upper(0); - for (unsigned i = 0; i < num_soft(); ++i) { - if (!m_seed[i]) { - upper += m_weights[i]; - } - } - if (upper < m_upper) { - m_upper = upper; - m_hs.set_upper(upper); - m_model = mdl; - m_assignment.reset(); - m_assignment.append(m_seed); - TRACE("opt", - tout << "new upper: " << m_upper << "\n"; - model_smt2_pp(tout, m, *(mdl.get()), 0);); - } - DEBUG_CODE( - for (unsigned i = 0; i < num_soft(); ++i) { - SASSERT(is_true(mdl, m_soft[i]) == m_seed[i]); - }); - - return true; - } - - // - // remove soft constraints from the current core. - // - bool shrink() { - scoped_stopwatch _sw(m_stats.m_core_reduction_time); - m_asms.reset(); - s().get_unsat_core(m_asms); - TRACE("opt", print_asms(tout, m_asms);); - obj_map asm2index; - for (unsigned i = 0; i < m_asms.size(); ++i) { - asm2index.insert(m_asms[i], i); - } - obj_map::iterator it = asm2index.begin(), end = asm2index.end(); - for (; it != end; ++it) { - unsigned i = it->m_value; - if (i < m_asms.size()) { - expr* tmp = m_asms[i]; - expr* back = m_asms.back(); - m_asms[i] = back; - m_asms.pop_back(); - lbool is_sat = s().check_sat(m_asms.size(), m_asms.c_ptr()); - TRACE("opt", tout << "checking: " << mk_pp(tmp, m) << ": " << is_sat << "\n";); - switch(is_sat) { - case l_true: - ++m_stats.m_num_core_reductions_failure; - // put back literal into core - m_asms.push_back(back); - m_asms[i] = tmp; - break; - case l_false: - // update the core - m_asms.reset(); - ++m_stats.m_num_core_reductions_success; - s().get_unsat_core(m_asms); - TRACE("opt", print_asms(tout, m_asms);); - update_index(asm2index); - break; - case l_undef: - return false; - } - } - } - return true; - } - - void print_asms(std::ostream& out, ptr_vector const& asms) { - for (unsigned j = 0; j < asms.size(); ++j) { - out << mk_pp(asms[j], m) << " "; - } - out << "\n"; - } - - void print_seed(std::ostream& out) { - out << "seed: "; - for (unsigned i = 0; i < num_soft(); ++i) { - out << (m_seed[i]?"1":"0"); - } - out << "\n"; - } - - // - // must include some literal not from asms. - // (furthermore, update upper bound constraint in HS) - // - void block_down() { - uint_set indices; - unsigned_vector c_indices; - for (unsigned i = 0; i < m_asms.size(); ++i) { - indices.insert(m_aux2index.find(m_asms[i])); - } - for (unsigned i = 0; i < num_soft(); ++i) { - if (!indices.contains(i)) { - c_indices.push_back(i); - } - } - m_hs.add_exists_false(c_indices.size(), c_indices.c_ptr()); - } - - // should exclude some literal from core. - void block_up() { - unsigned_vector indices; - for (unsigned i = 0; i < m_asms.size(); ++i) { - unsigned index = m_aux2index.find(m_asms[i]); - m_core_activity[index]++; - indices.push_back(index); - } - m_hs.add_exists_true(indices.size(), indices.c_ptr()); - } - - void update_index(obj_map& asm2index) { - obj_map::iterator it = asm2index.begin(), end = asm2index.end(); - for (; it != end; ++it) { - it->m_value = UINT_MAX; - } - for (unsigned i = 0; i < m_asms.size(); ++i) { - asm2index.find(m_asms[i]) = i; - } - } - - app_ref mk_fresh(sort* s) { - app_ref r(m); - r = m.mk_fresh_const("r", s); - m_c.fm().insert(r->get_decl()); - return r; - } - - bool is_true(model_ref& mdl, expr* e) { - expr_ref val(m); - return mdl->eval(e, val) && m.is_true(val); - } - - bool is_active(unsigned i) const { - return m_aux_active[i]; - } - - void ensure_active(unsigned i) { - if (!is_active(i)) { - expr_ref fml(m); - fml = m.mk_implies(m_aux[i].get(), m_soft[i]); - s().assert_expr(fml); - m_aux_active[i] = true; - } - } - - }; - - maxsmt_solver_base* mk_maxhs( - maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { - return alloc(maxhs, c, ws, soft); - } - -} diff --git a/src/opt/maxhs.h b/src/opt/maxhs.h deleted file mode 100644 index 903354c0e..000000000 --- a/src/opt/maxhs.h +++ /dev/null @@ -1,29 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - maxhs.h - -Abstract: - - HS-max based MaxSAT. - -Author: - - Nikolaj Bjorner (nbjorner) 2014-4-17 - -Notes: - ---*/ - -#ifndef HS_MAX_H_ -#define HS_MAX_H_ - -#include "maxsmt.h" - -namespace opt { - maxsmt_solver_base* mk_maxhs(maxsat_context& c, - weights_t& ws, expr_ref_vector const& soft); -} -#endif diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index 7c1fad80e..7e9381e1d 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -93,7 +93,7 @@ private: mss m_mss; expr_ref_vector m_trail; strategy_t m_st; - rational m_max_upper; + rational m_max_upper; model_ref m_csmodel; unsigned m_correction_set_size; bool m_found_feasible_optimum; @@ -109,6 +109,7 @@ private: 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; @@ -119,7 +120,7 @@ public: maxsmt_solver_base(c, ws, soft), m_index(index), m_B(m), m_asms(m), m_defs(m), - m_mus(c.get_solver(), m), + m_mus(c.get_solver()), m_mss(c.get_solver(), m), m_trail(m), m_st(st), @@ -150,19 +151,16 @@ public: return is_uninterp_const(l) || (m.is_not(l, l) && is_uninterp_const(l)); - } - - + } void add_soft(expr* e, rational const& w) { - TRACE("opt", tout << mk_pp(e, m) << "\n";); + 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); - m_upper += w; return; } if (is_literal(e)) { @@ -174,7 +172,6 @@ public: s().assert_expr(fml); } new_assumption(asum, w); - m_upper += w; } void new_assumption(expr* e, rational const& w) { @@ -192,8 +189,9 @@ public: lbool mus_solver() { lbool is_sat = l_true; if (!init()) return l_undef; - init_local(); + is_sat = init_local(); trace(); + if (is_sat != l_true) return is_sat; while (m_lower < m_upper) { TRACE("opt", display_vec(tout, m_asms); @@ -207,6 +205,7 @@ public: } switch (is_sat) { case l_true: + SASSERT(is_true(m_asms)); found_optimum(); return l_true; case l_false: @@ -224,17 +223,19 @@ public: break; } } + found_optimum(); trace(); return l_true; } lbool primal_dual_solver() { if (!init()) return l_undef; - init_local(); + lbool is_sat = init_local(); trace(); exprs cs; + if (is_sat != l_true) return is_sat; while (m_lower < m_upper) { - lbool is_sat = check_sat_hill_climb(m_asms); + is_sat = check_sat_hill_climb(m_asms); if (m.canceled()) { return l_undef; } @@ -269,7 +270,6 @@ public: return l_true; } - lbool check_sat_hill_climb(expr_ref_vector& asms1) { expr_ref_vector asms(asms1); lbool is_sat = l_true; @@ -290,35 +290,31 @@ public: index = next_index(asms, index); } first = false; - IF_VERBOSE(3, verbose_stream() << "weight: " << get_weight(asms[0].get()) << " " << get_weight(asms[index-1].get()) << " num soft: " << index << "\n";); + // IF_VERBOSE(3, verbose_stream() << "weight: " << get_weight(asms[0].get()) << " " << get_weight(asms[index-1].get()) << " num soft: " << index << "\n";); 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) { - if (m_st == s_primal_dual && m_c.sat_enabled()) { - rational max_weight = m_upper; - vector weights; - for (unsigned i = 0; i < sz; ++i) { - weights.push_back(get_weight(asms[i])); + lbool r = s().check_sat(sz, asms); + if (r == l_true) { + model_ref mdl; + s().get_model(mdl); + if (mdl.get()) { + update_assignment(mdl.get()); } - return inc_sat_check_sat(s(), sz, asms, weights.c_ptr(), max_weight); - } - else { - return s().check_sat(sz, asms); } + return r; } void found_optimum() { IF_VERBOSE(1, verbose_stream() << "found optimum\n";); - s().get_model(m_model); - SASSERT(is_true(m_asms)); rational upper(0); for (unsigned i = 0; i < m_soft.size(); ++i) { m_assignment[i] = is_true(m_soft[i]); @@ -356,7 +352,7 @@ public: while (is_sat == l_false) { core.reset(); s().get_unsat_core(core); - //verify_core(core); + // verify_core(core); model_ref mdl; get_mus_model(mdl); is_sat = minimize_core(core); @@ -500,7 +496,7 @@ public: 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, m_B.size(), m_B.c_ptr())); + fml = mk_not(m, mk_and(m, core.size(), core.c_ptr())); s().assert_expr(fml); m_lower += w; if (m_st == s_primal_dual) { @@ -540,22 +536,18 @@ public: } lbool minimize_core(exprs& core) { - if (m_c.sat_enabled() || core.empty()) { + if (core.empty()) { + return l_true; + } + if (m_c.sat_enabled()) { return l_true; } m_mus.reset(); - for (unsigned i = 0; i < core.size(); ++i) { - m_mus.add_soft(core[i]); - } - unsigned_vector mus_idx; - lbool is_sat = m_mus.get_mus(mus_idx); + 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; } - m_new_core.reset(); - for (unsigned i = 0; i < mus_idx.size(); ++i) { - m_new_core.push_back(core[mus_idx[i]]); - } core.reset(); core.append(m_new_core); return l_true; @@ -624,8 +616,8 @@ public: // Soundness of this rule can be established using MaxRes // for (unsigned i = 1; i < core.size(); ++i) { - expr* b_i = m_B[i-1].get(); - expr* b_i1 = m_B[i].get(); + expr* b_i = core[i-1]; + expr* b_i1 = core[i]; if (i == 1) { d = to_app(b_i); } @@ -675,8 +667,8 @@ public: // d_i => d_{i-1} or b_{i-1} // for (unsigned i = 1; i < cs.size(); ++i) { - expr* b_i = m_B[i-1].get(); - expr* b_i1 = m_B[i].get(); + 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"); @@ -697,10 +689,11 @@ public: s().assert_expr(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(m_B.size(), m_B.c_ptr()); + fml = m.mk_or(cs.size(), cs.c_ptr()); s().assert_expr(fml); } @@ -737,8 +730,6 @@ public: for (unsigned i = 0; i < m_soft.size(); ++i) { m_assignment[i] = is_true(m_soft[i]); } - - DEBUG_CODE(verify_assignment();); @@ -758,7 +749,8 @@ public: nsoft.push_back(mk_not(m, m_soft[i])); } fml = u.mk_lt(nsoft.size(), m_weights.c_ptr(), nsoft.c_ptr(), m_upper); - s().assert_expr(fml); + TRACE("opt", tout << "block upper bound " << fml << "\n";);; + s().assert_expr(fml); } bool is_true(model* mdl, expr* e) { @@ -776,10 +768,9 @@ public: } bool is_true(expr_ref_vector const& es) { - for (unsigned i = 0; i < es.size(); ++i) { - if (!is_true(es[i])) return false; - } - return true; + unsigned i = 0; + for (; i < es.size() && is_true(es[i]); ++i) { } + return i == es.size(); } void remove_soft(exprs const& core, expr_ref_vector& asms) { @@ -799,7 +790,6 @@ public: virtual void updt_params(params_ref& p) { 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(); @@ -811,12 +801,18 @@ public: m_dump_benchmarks = _p.dump_benchmarks(); } - void init_local() { - m_upper.reset(); + lbool init_local() { m_lower.reset(); m_trail.reset(); - for (unsigned i = 0; i < m_soft.size(); ++i) { - add_soft(m_soft[i], m_weights[i]); + lbool is_sat = l_true; + obj_map new_soft; + is_sat = find_mutexes(new_soft); + if (is_sat != l_true) { + return is_sat; + } + obj_map::iterator it = new_soft.begin(), end = new_soft.end(); + for (; it != end; ++it) { + add_soft(it->m_key, it->m_value); } m_max_upper = m_upper; m_found_feasible_optimum = false; @@ -824,6 +820,7 @@ public: add_upper_bound_block(); m_csmodel = 0; m_correction_set_size = 0; + return l_true; } virtual void commit_assignment() { @@ -832,12 +829,8 @@ public: tout << m_defs; tout << m_asms; ); - for (unsigned i = 0; i < m_defs.size(); ++i) { - s().assert_expr(m_defs[i].get()); - } - for (unsigned i = 0; i < m_asms.size(); ++i) { - s().assert_expr(m_asms[i].get()); - } + s().assert_expr(m_defs); + s().assert_expr(m_asms); } // else: there is only a single assignment to these soft constraints. } @@ -848,9 +841,7 @@ public: for (unsigned i = 0; i < s().get_num_assertions(); ++i) { smt_solver->assert_expr(s().get_assertion(i)); } - for (unsigned i = 0; i < core.size(); ++i) { - smt_solver->assert_expr(core[i]); - } + smt_solver->assert_expr(core); lbool is_sat = smt_solver->check_sat(0, 0); if (is_sat == l_true) { IF_VERBOSE(0, verbose_stream() << "not a core\n";); @@ -876,7 +867,6 @@ public: IF_VERBOSE(0, verbose_stream() << "assignment is infeasible\n";); } } - }; opt::maxsmt_solver_base* opt::mk_maxres( diff --git a/src/opt/maxsls.cpp b/src/opt/maxsls.cpp deleted file mode 100644 index 4e59cfdfd..000000000 --- a/src/opt/maxsls.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - maxsls.cpp - -Abstract: - - Weighted SLS MAXSAT module - -Author: - - Nikolaj Bjorner (nbjorner) 2014-4-17 - -Notes: - ---*/ - -#include "maxsls.h" -#include "ast_pp.h" -#include "model_smt2_pp.h" -#include "opt_context.h" -#include "inc_sat_solver.h" - - -namespace opt { - - class sls : public maxsmt_solver_base { - public: - sls(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): - maxsmt_solver_base(c, ws, soft) { - } - virtual ~sls() {} - lbool operator()() { - IF_VERBOSE(1, verbose_stream() << "(opt.sls)\n";); - init(); - enable_sls(true); - lbool is_sat = check(); - if (is_sat == l_true) { - s().get_model(m_model); - m_upper.reset(); - for (unsigned i = 0; i < m_soft.size(); ++i) { - expr_ref tmp(m); - m_model->eval(m_soft[i], tmp, true); - m_assignment[i] = m.is_true(tmp); - if (!m_assignment[i]) { - m_upper += m_weights[i]; - } - } - } - return is_sat; - } - - lbool check() { - if (m_c.sat_enabled()) { - return inc_sat_check_sat( - s(), m_soft.size(), m_soft.c_ptr(), m_weights.c_ptr(), m_upper); - } - else { - return s().check_sat(0, 0); - } - } - - }; - - maxsmt_solver_base* mk_sls( - maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { - return alloc(sls, c, ws, soft); - } - - -}; diff --git a/src/opt/maxsls.h b/src/opt/maxsls.h deleted file mode 100644 index b23512df4..000000000 --- a/src/opt/maxsls.h +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - maxsls.h - -Abstract: - - Weighted SLS MAXSAT module - -Author: - - Nikolaj Bjorner (nbjorner) 2014-4-17 - -Notes: - - Partial, one-round SLS optimizer. Finds the first - local maximum given a resource bound and returns. - ---*/ -#ifndef OPT_SLS_MAX_SAT_H_ -#define OPT_SLS_MAX_SAT_H_ - -#include "maxsmt.h" - -namespace opt { - - - maxsmt_solver_base* mk_sls(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); - - -}; - -#endif diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index 5229fafab..f93291599 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -19,16 +19,13 @@ Notes: #include #include "maxsmt.h" -#include "fu_malik.h" #include "maxres.h" -#include "maxhs.h" -#include "bcd2.h" #include "wmax.h" -#include "maxsls.h" #include "ast_pp.h" #include "uint_set.h" #include "opt_context.h" #include "theory_wmaxsat.h" +#include "theory_pb.h" #include "ast_util.h" #include "pb_decl_plugin.h" @@ -41,7 +38,8 @@ namespace opt { m_c(c), m_soft(soft), m_weights(ws), - m_assertions(m) { + m_assertions(m), + m_trail(m) { c.get_base_model(m_model); SASSERT(m_model); updt_params(c.params()); @@ -57,15 +55,18 @@ namespace opt { void maxsmt_solver_base::commit_assignment() { expr_ref tmp(m); - rational k(0); + rational k(0), cost(0); for (unsigned i = 0; i < m_soft.size(); ++i) { if (get_assignment(i)) { k += m_weights[i]; } + else { + cost += m_weights[i]; + } } pb_util pb(m); tmp = pb.mk_ge(m_weights.size(), m_weights.c_ptr(), m_soft.c_ptr(), k); - TRACE("opt", tout << tmp << "\n";); + TRACE("opt", tout << "cost: " << cost << "\n" << tmp << "\n";); s().assert_expr(tmp); } @@ -128,6 +129,13 @@ namespace opt { 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; } @@ -135,7 +143,9 @@ namespace opt { m_wth = s.ensure_wmax_theory(); } maxsmt_solver_base::scoped_ensure_theory::~scoped_ensure_theory() { - //m_wth->reset_local(); + if (m_wth) { + m_wth->reset_local(); + } } smt::theory_wmaxsat& maxsmt_solver_base::scoped_ensure_theory::operator()() { return *m_wth; } @@ -148,6 +158,68 @@ namespace opt { verbose_stream() << "(opt." << solver << " [" << l << ":" << u << "])\n";); } + lbool maxsmt_solver_base::find_mutexes(obj_map& new_soft) { + m_lower.reset(); + for (unsigned i = 0; i < m_soft.size(); ++i) { + new_soft.insert(m_soft[i], m_weights[i]); + } + vector mutexes; + lbool is_sat = s().find_mutexes(m_soft, mutexes); + if (is_sat != l_true) { + return is_sat; + } + for (unsigned i = 0; i < mutexes.size(); ++i) { + process_mutex(mutexes[i], 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 (unsigned i = 0; i < mutex.size(); ++i) { + tout << mk_pp(mutex[i].get(), m) << " |-> " << new_soft.find(mutex[i].get()) << "\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 (unsigned i = 0; i < mutex.size(); ++i) { + rational w = new_soft.find(mutex[i].get()); + weights.push_back(w); + sum1 += w; + new_soft.remove(mutex[i].get()); + } + for (unsigned i = mutex.size(); i > 0; ) { + --i; + 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): @@ -159,32 +231,24 @@ namespace opt { m_msolver = 0; symbol const& maxsat_engine = m_c.maxsat_engine(); IF_VERBOSE(1, verbose_stream() << "(maxsmt)\n";); - TRACE("opt", tout << "maxsmt\n";); - if (m_soft_constraints.empty() || maxsat_engine == symbol("maxres")) { + TRACE("opt", tout << "maxsmt\n"; + s().display(tout); tout << "\n"; + ); + 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("bcd2")) { - m_msolver = mk_bcd2(m_c, 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("maxhs")) { - m_msolver = mk_maxhs(m_c, m_weights, m_soft_constraints); - } - else if (maxsat_engine == symbol("sls")) { - // NB: this is experimental one-round version of SLS - m_msolver = mk_sls(m_c, m_weights, m_soft_constraints); - } - else if (is_maxsat_problem(m_weights) && maxsat_engine == symbol("fu_malik")) { - m_msolver = mk_fu_malik(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 { - if (maxsat_engine != symbol::null && maxsat_engine != symbol("wmax")) { - warning_msg("solver %s is not recognized, using default 'wmax'", - maxsat_engine.str().c_str()); - } - m_msolver = mk_wmax(m_c, m_weights, m_soft_constraints); + 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) { @@ -277,15 +341,26 @@ namespace opt { TRACE("opt", tout << mk_pp(f, m) << " weight: " << w << "\n";); SASSERT(m.is_bool(f)); SASSERT(w.is_pos()); - m_soft_constraints.push_back(f); - m_weights.push_back(w); + 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; } void maxsmt::display_answer(std::ostream& out) const { for (unsigned i = 0; i < m_soft_constraints.size(); ++i) { - out << mk_pp(m_soft_constraints[i], m) - << (get_assignment(i)?" |-> true\n":" |-> false\n"); + expr* e = m_soft_constraints[i]; + bool is_not = m.is_not(e, e); + out << m_weights[i] << ": " << mk_pp(e, m) + << ((is_not != get_assignment(i))?" |-> true ":" |-> false ") + << "\n"; + } } @@ -317,4 +392,5 @@ namespace opt { } + }; diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h index 57fe12b59..358ff4995 100644 --- a/src/opt/maxsmt.h +++ b/src/opt/maxsmt.h @@ -62,6 +62,7 @@ namespace opt { const expr_ref_vector m_soft; vector m_weights; expr_ref_vector m_assertions; + expr_ref_vector m_trail; rational m_lower; rational m_upper; model_ref m_model; @@ -95,12 +96,17 @@ namespace opt { ~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); + + }; /** @@ -114,6 +120,7 @@ namespace opt { 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; @@ -132,7 +139,6 @@ namespace opt { expr* operator[](unsigned idx) const { return m_soft_constraints[idx]; } rational weight(unsigned idx) const { return m_weights[idx]; } void commit_assignment(); - rational get_value() const; rational get_lower() const; rational get_upper() const; void update_lower(rational const& r); diff --git a/src/opt/mus.cpp b/src/opt/mus.cpp deleted file mode 100644 index aa8446256..000000000 --- a/src/opt/mus.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - mus.cpp - -Abstract: - - MUS extraction. - -Author: - - Nikolaj Bjorner (nbjorner) 2014-20-7 - -Notes: - - ---*/ - -#include "solver.h" -#include "smt_literal.h" -#include "mus.h" -#include "ast_pp.h" -#include "ast_util.h" - -using namespace opt; - -// - -struct mus::imp { - solver& m_s; - ast_manager& m; - expr_ref_vector m_cls2expr; - obj_map m_expr2cls; - model_ref m_model; - expr_ref_vector m_soft; - vector m_weights; - rational m_weight; - - imp(solver& s, ast_manager& m): - m_s(s), m(m), m_cls2expr(m), m_soft(m) - {} - - void reset() { - m_cls2expr.reset(); - m_expr2cls.reset(); - } - - - unsigned add_soft(expr* cls) { - SASSERT(is_uninterp_const(cls) || - (m.is_not(cls) && is_uninterp_const(to_app(cls)->get_arg(0)))); - unsigned idx = m_cls2expr.size(); - m_expr2cls.insert(cls, idx); - m_cls2expr.push_back(cls); - TRACE("opt", tout << idx << ": " << mk_pp(cls, m) << "\n"; - display_vec(tout, m_cls2expr);); - return idx; - } - - lbool get_mus(unsigned_vector& mus) { - // SASSERT: mus does not have duplicates. - m_model.reset(); - unsigned_vector core; - for (unsigned i = 0; i < m_cls2expr.size(); ++i) { - core.push_back(i); - } - if (core.size() == 1) { - mus.push_back(core.back()); - return l_true; - } - mus.reset(); - expr_ref_vector assumptions(m); - ptr_vector core_exprs; - while (!core.empty()) { - IF_VERBOSE(2, verbose_stream() << "(opt.mus reducing core: " << core.size() << " new core: " << mus.size() << ")\n";); - unsigned cls_id = core.back(); - TRACE("opt", - display_vec(tout << "core: ", core); - display_vec(tout << "mus: ", mus); - ); - core.pop_back(); - expr* cls = m_cls2expr[cls_id].get(); - expr_ref not_cls(m); - not_cls = mk_not(m, cls); - unsigned sz = assumptions.size(); - assumptions.push_back(not_cls); - add_core(core, assumptions); - lbool is_sat = m_s.check_sat(assumptions.size(), assumptions.c_ptr()); - assumptions.resize(sz); - switch (is_sat) { - case l_undef: - return is_sat; - case l_true: - assumptions.push_back(cls); - mus.push_back(cls_id); - update_model(); - break; - default: - core_exprs.reset(); - m_s.get_unsat_core(core_exprs); - if (!core_exprs.contains(not_cls)) { - // core := core_exprs \ mus - core.reset(); - for (unsigned i = 0; i < core_exprs.size(); ++i) { - cls = core_exprs[i]; - cls_id = m_expr2cls.find(cls); - if (!mus.contains(cls_id)) { - core.push_back(cls_id); - } - } - TRACE("opt", display_vec(tout << "core exprs:", core_exprs); - display_vec(tout << "core:", core); - display_vec(tout << "mus:", mus); - ); - - } - break; - } - } -#if 0 - DEBUG_CODE( - assumptions.reset(); - for (unsigned i = 0; i < mus.size(); ++i) { - assumptions.push_back(m_cls2expr[mus[i]].get()); - } - lbool is_sat = m_s.check_sat(assumptions.size(), assumptions.c_ptr()); - SASSERT(is_sat == l_false); - ); -#endif - return l_true; - } - - void add_core(unsigned_vector const& core, expr_ref_vector& assumptions) { - for (unsigned i = 0; i < core.size(); ++i) { - assumptions.push_back(m_cls2expr[core[i]].get()); - } - } - - 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_s.get_model(mdl); - rational w; - for (unsigned i = 0; i < m_soft.size(); ++i) { - mdl->eval(m_soft[i].get(), tmp); - if (!m.is_true(tmp)) { - 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, ast_manager& m) { - m_imp = alloc(imp, s, m); -} - -mus::~mus() { - dealloc(m_imp); -} - -unsigned mus::add_soft(expr* cls) { - return m_imp->add_soft(cls); -} - -lbool mus::get_mus(unsigned_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); -} diff --git a/src/opt/mus.h b/src/opt/mus.h deleted file mode 100644 index 8ca7ab91d..000000000 --- a/src/opt/mus.h +++ /dev/null @@ -1,55 +0,0 @@ -/*++ -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_ - -namespace opt { - class mus { - struct imp; - imp * m_imp; - public: - mus(solver& s, ast_manager& m); - ~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); - - lbool get_mus(unsigned_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 diff --git a/src/opt/opt_cmds.cpp b/src/opt/opt_cmds.cpp index 8822f4e77..571c26e2d 100644 --- a/src/opt/opt_cmds.cpp +++ b/src/opt/opt_cmds.cpp @@ -32,7 +32,10 @@ Notes: #include "opt_params.hpp" #include "model_smt2_pp.h" -static opt::context& get_opt(cmd_context& cmd) { +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())); } @@ -43,12 +46,14 @@ static opt::context& get_opt(cmd_context& cmd) { class assert_soft_cmd : public parametric_cmd { unsigned m_idx; expr* m_formula; + opt::context* m_opt; public: - assert_soft_cmd(): + assert_soft_cmd(opt::context* opt): parametric_cmd("assert-soft"), m_idx(0), - m_formula(0) + m_formula(0), + m_opt(opt) {} virtual ~assert_soft_cmd() { @@ -92,12 +97,9 @@ public: virtual void execute(cmd_context & ctx) { symbol w("weight"); - rational weight = ps().get_rat(symbol("weight"), rational(0)); - if (weight.is_zero()) { - weight = rational::one(); - } - symbol id = ps().get_sym(symbol("id"), symbol::null); - get_opt(ctx).add_soft_constraint(m_formula, weight, id); + 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); reset(ctx); } @@ -108,11 +110,13 @@ public: class min_maximize_cmd : public cmd { bool m_is_max; + opt::context* m_opt; public: - min_maximize_cmd(bool is_max): + min_maximize_cmd(bool is_max, opt::context* opt): cmd(is_max?"maximize":"minimize"), - m_is_max(is_max) + m_is_max(is_max), + m_opt(opt) {} virtual void reset(cmd_context & ctx) { } @@ -126,7 +130,7 @@ public: if (!is_app(t)) { throw cmd_exception("malformed objective term: it cannot be a quantifier or bound variable"); } - get_opt(ctx).add_objective(to_app(t), m_is_max); + get_opt(ctx, m_opt).add_objective(to_app(t), m_is_max); } virtual void failure_cleanup(cmd_context & ctx) { @@ -139,10 +143,10 @@ public: -void install_opt_cmds(cmd_context & ctx) { - ctx.insert(alloc(assert_soft_cmd)); - ctx.insert(alloc(min_maximize_cmd, true)); - ctx.insert(alloc(min_maximize_cmd, false)); +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)); } diff --git a/src/opt/opt_cmds.h b/src/opt/opt_cmds.h index d8dd0dbfa..f0da778df 100644 --- a/src/opt/opt_cmds.h +++ b/src/opt/opt_cmds.h @@ -19,10 +19,11 @@ Notes: #define OPT_CMDS_H_ #include "ast.h" +#include "opt_context.h" class cmd_context; -void install_opt_cmds(cmd_context & ctx); +void install_opt_cmds(cmd_context & ctx, opt::context* opt = 0); #endif diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 0b71fff1c..56b152caa 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -35,13 +35,13 @@ Notes: #include "model_smt2_pp.h" #include "card2bv_tactic.h" #include "eq2bv_tactic.h" +#include "dt2bv_tactic.h" #include "inc_sat_solver.h" #include "bv_decl_plugin.h" #include "pb_decl_plugin.h" #include "ast_smt_pp.h" #include "filter_model_converter.h" #include "ast_pp_util.h" -#include "inc_sat_solver.h" #include "qsat.h" namespace opt { @@ -89,12 +89,6 @@ namespace opt { } unsigned context::scoped_state::add(expr* f, rational const& w, symbol const& id) { - if (w.is_neg()) { - throw default_exception("Negative weight supplied. Weight should be positive"); - } - if (w.is_zero()) { - throw default_exception("Zero weight supplied. Weight should be positive"); - } if (!m.is_bool(f)) { throw default_exception("Soft constraint should be Boolean"); } @@ -104,9 +98,11 @@ namespace opt { } SASSERT(m_indices.contains(id)); unsigned idx = m_indices[id]; - m_objectives[idx].m_terms.push_back(f); - m_objectives[idx].m_weights.push_back(w); - m_objectives_term_trail.push_back(idx); + 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; } @@ -173,6 +169,11 @@ namespace opt { r.append(m_labels); } + void context::get_unsat_core(ptr_vector & r) { + throw default_exception("Unsat cores are not supported with optimization"); + } + + void context::set_hard_constraints(ptr_vector& fmls) { if (m_scoped_state.set(fmls)) { clear_state(); @@ -184,6 +185,43 @@ namespace opt { 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); @@ -241,6 +279,7 @@ namespace opt { s.get_labels(m_labels); } if (is_sat != l_true) { + TRACE("opt", tout << m_hard_constraints << "\n";); return is_sat; } IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n";); @@ -321,6 +360,7 @@ namespace opt { } 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; } @@ -366,11 +406,16 @@ namespace opt { } lbool context::execute_box() { - if (m_box_index < m_objectives.size()) { + 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 = 0; + ++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; @@ -383,14 +428,14 @@ namespace opt { if (obj.m_type == O_MAXSMT) { solver::scoped_push _sp(get_solver()); r = execute(obj, false, false); - if (r == l_true) m_box_models.push_back(m_model.get()); + 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_objectives.size() > 0) { + if (r == l_true && m_box_models.size() > 0) { m_model = m_box_models[0]; } return r; @@ -410,7 +455,7 @@ namespace opt { expr_ref context::mk_gt(unsigned i, model_ref& mdl) { expr_ref result = mk_le(i, mdl); - result = m.mk_not(result); + result = mk_not(m, result); return result; } @@ -494,6 +539,9 @@ namespace opt { } std::string context::reason_unknown() const { + if (m.canceled()) { + return Z3_CANCELED_MSG; + } if (m_solver.get()) { return m_solver->reason_unknown(); } @@ -522,11 +570,11 @@ namespace opt { 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)) { - 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() { @@ -552,13 +600,15 @@ namespace opt { if (opt_params(m_params).priority() == symbol("pareto")) { return; } - m_params.set_bool("minimize_core_partial", true); // false); + 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); - unsigned sz = get_solver().get_num_assertions(); - for (unsigned i = 0; i < sz; ++i) { - m_sat_solver->assert_expr(get_solver().get_assertion(i)); - } + expr_ref_vector fmls(m); + get_solver().get_assertions(fmls); + m_sat_solver->assert_expr(fmls); m_solver = m_sat_solver.get(); } @@ -680,21 +730,19 @@ namespace opt { // NB: mk_elim_uncstr_tactic(m) is not sound with soft constraints mk_simplify_tactic(m)); opt_params optp(m_params); - tactic_ref tac2, tac3, tac4; + tactic_ref tac1, tac2, tac3, tac4; if (optp.elim_01()) { + tac1 = mk_dt2bv_tactic(m); tac2 = mk_elim01_tactic(m); tac3 = mk_lia2card_tactic(m); tac4 = mk_eq2bv_tactic(m); params_ref lia_p; lia_p.set_bool("compile_equality", optp.pb_compile_equality()); tac3->updt_params(lia_p); - set_simplify(and_then(tac0.get(), tac2.get(), tac3.get(), tac4.get(), mk_simplify_tactic(m))); + set_simplify(and_then(tac0.get(), tac1.get(), tac2.get(), tac3.get(), tac4.get(), mk_simplify_tactic(m))); } else { - tactic_ref tac1 = - and_then(tac0.get(), - mk_simplify_tactic(m)); - set_simplify(tac1.get()); + set_simplify(tac0.get()); } proof_converter_ref pc; expr_dependency_ref core(m); @@ -737,16 +785,26 @@ namespace opt { 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* arg = a->get_arg(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(1, 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)) { - offset += m_objectives[index].m_weights[i]; + IF_VERBOSE(1, 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(m_objectives[index].m_weights[i]); + weights.push_back(weight); } } id = m_objectives[index].m_id; @@ -769,7 +827,7 @@ namespace opt { weights[i].neg(); } else { - terms[i] = m.mk_not(terms[i].get()); + terms[i] = mk_not(m, terms[i].get()); } } TRACE("opt", @@ -798,7 +856,7 @@ namespace opt { for (unsigned i = 0; i < weights.size(); ++i) { if (weights[i].is_neg()) { weights[i].neg(); - terms[i] = m.mk_not(terms[i].get()); + terms[i] = mk_not(m, terms[i].get()); } offset += weights[i]; } @@ -845,9 +903,7 @@ namespace opt { 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); - if (sz > 0) { - m_objective_orig.insert(f, args[0]); - } + m_objective_orig.insert(f, sz > 0 ? args[0] : 0); return m.mk_app(f, sz, args); } @@ -866,10 +922,7 @@ namespace opt { } void context::from_fmls(expr_ref_vector const& fmls) { - TRACE("opt", - for (unsigned i = 0; i < fmls.size(); ++i) { - tout << mk_pp(fmls[i], m) << "\n"; - }); + TRACE("opt", tout << fmls << "\n";); m_hard_constraints.reset(); expr_ref orig_term(m); for (unsigned i = 0; i < fmls.size(); ++i) { @@ -1047,10 +1100,7 @@ namespace opt { break; } } - TRACE("opt", - for (unsigned i = 0; i < fmls.size(); ++i) { - tout << mk_pp(fmls[i].get(), m) << "\n"; - }); + TRACE("opt", tout << fmls << "\n";); } void context::internalize() { @@ -1327,14 +1377,21 @@ namespace opt { } std::string context::to_string() const { + return to_string(m_scoped_state.m_hard, m_scoped_state.m_objectives); + } + + std::string context::to_string_internal() const { + return to_string(m_hard_constraints, m_objectives); + } + + std::string context::to_string(expr_ref_vector const& hard, vector const& objectives) const { smt2_pp_environment_dbg env(m); ast_pp_util visitor(m); std::ostringstream out; -#define PP(_e_) ast_smt2_pp(out, _e_, env); - visitor.collect(m_scoped_state.m_hard); + visitor.collect(hard); - for (unsigned i = 0; i < m_scoped_state.m_objectives.size(); ++i) { - objective const& obj = m_scoped_state.m_objectives[i]; + for (unsigned i = 0; i < objectives.size(); ++i) { + objective const& obj = objectives[i]; switch(obj.m_type) { case O_MAXIMIZE: case O_MINIMIZE: @@ -1350,33 +1407,34 @@ namespace opt { } visitor.display_decls(out); - visitor.display_asserts(out, m_scoped_state.m_hard, m_pp_neat); - for (unsigned i = 0; i < m_scoped_state.m_objectives.size(); ++i) { - objective const& obj = m_scoped_state.m_objectives[i]; + visitor.display_asserts(out, hard, m_pp_neat); + for (unsigned i = 0; i < objectives.size(); ++i) { + objective const& obj = objectives[i]; switch(obj.m_type) { case O_MAXIMIZE: out << "(maximize "; - PP(obj.m_term); + ast_smt2_pp(out, obj.m_term, env); out << ")\n"; break; case O_MINIMIZE: out << "(minimize "; - PP(obj.m_term); + 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 "; - PP(obj.m_terms[j]); + ast_smt2_pp(out, obj.m_terms[j], env); rational w = obj.m_weights[j]; - if (w.is_int()) { - out << " :weight " << w; - } - else { - out << " :dweight " << w; - } + + w.display_decimal(out << " :weight ", 3, true); if (obj.m_id != symbol::null) { - out << " :id " << obj.m_id; + 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"; } @@ -1395,6 +1453,32 @@ namespace opt { return out.str(); } + void context::validate_maxsat(symbol const& id) { + maxsmt& ms = *m_maxsmts.find(id); + for (unsigned i = 0; i < m_objectives.size(); ++i) { + objective const& obj = m_objectives[i]; + if (obj.m_id == id) { + SASSERT(obj.m_type == O_MAXSMT); + rational value(0); + expr_ref val(m); + for (unsigned i = 0; i < obj.m_terms.size(); ++i) { + bool evaluated = m_model->eval(obj.m_terms[i], val); + SASSERT(evaluated); + CTRACE("opt", evaluated && !m.is_true(val) && !m.is_false(val), tout << mk_pp(obj.m_terms[i], m) << " " << val << "\n";); + CTRACE("opt", !evaluated, tout << mk_pp(obj.m_terms[i], m) << "\n";); + if (evaluated && !m.is_true(val)) { + 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";); + SASSERT(value == value0); + } + } + } + void context::validate_lex() { rational r1; expr_ref val(m); diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index 07fe53268..461506258 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -48,7 +48,7 @@ namespace opt { virtual filter_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() = 0; + 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. @@ -175,6 +175,8 @@ namespace opt { unsigned add_objective(app* t, bool is_max); void add_hard_constraint(expr* f); + void get_hard_constraints(expr_ref_vector& hard); + expr_ref get_objective(unsigned i); virtual void push(); virtual void pop(unsigned n); @@ -188,7 +190,7 @@ namespace opt { virtual void collect_statistics(statistics& stats) const; virtual proof* get_proof() { return 0; } virtual void get_labels(svector & r); - virtual void get_unsat_core(ptr_vector & r) {} + virtual void get_unsat_core(ptr_vector & r); virtual std::string reason_unknown() const; virtual void set_reason_unknown(char const* msg) { m_unknown = msg; } @@ -208,7 +210,7 @@ namespace opt { std::string to_string() const; - virtual unsigned num_objectives() { return m_objectives.size(); } + virtual unsigned num_objectives() { return m_scoped_state.m_objectives.size(); } virtual expr_ref mk_gt(unsigned i, model_ref& model); virtual expr_ref mk_ge(unsigned i, model_ref& model); virtual expr_ref mk_le(unsigned i, model_ref& model); @@ -217,7 +219,7 @@ namespace opt { virtual filter_model_converter& fm() { return m_fm; } virtual bool sat_enabled() const { return 0 != m_sat_solver.get(); } virtual solver& get_solver(); - virtual ast_manager& get_manager() { return this->m; } + virtual ast_manager& get_manager() const { return this->m; } virtual params_ref& params() { return m_params; } virtual void enable_sls(bool force); virtual symbol const& maxsat_engine() const { return m_maxsat_engine; } @@ -284,8 +286,12 @@ namespace opt { 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(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 display_benchmark(); diff --git a/src/opt/opt_params.pyg b/src/opt/opt_params.pyg index 37b9ff372..a7c9e0011 100644 --- a/src/opt/opt_params.pyg +++ b/src/opt/opt_params.pyg @@ -1,9 +1,8 @@ def_module_params('opt', description='optimization parameters', export=True, - params=(('timeout', UINT, UINT_MAX, 'set timeout'), - ('optsmt_engine', SYMBOL, 'basic', "select optimization engine: 'basic', 'farkas', 'symba'"), - ('maxsat_engine', SYMBOL, 'maxres', "select engine for maxsat: 'fu_malik', 'core_maxsat', 'wmax', 'pbmax', 'maxres', 'pd-maxres', 'bcd2', 'wpm2', 'sls', 'maxhs'"), + 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', or 'box'"), ('dump_benchmarks', BOOL, False, 'dump benchmarks for profiling'), ('print_model', BOOL, False, 'display model for satisfiable constraints'), diff --git a/src/opt/opt_sls_solver.h b/src/opt/opt_sls_solver.h index 5071563f7..5b7f630b4 100644 --- a/src/opt/opt_sls_solver.h +++ b/src/opt/opt_sls_solver.h @@ -90,19 +90,6 @@ namespace opt { virtual void get_labels(svector & r) { m_solver->get_labels(r); } - virtual void set_cancel(bool f) { - m_solver->set_cancel(f); - m_pb2bv.set_cancel(f); - #pragma omp critical (sls_solver) - { - if (m_bvsls) { - m_bvsls->set_cancel(f); - } - if (m_pbsls) { - m_pbsls->set_cancel(f); - } - } - } virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } @@ -184,9 +171,10 @@ namespace opt { 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); + 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); @@ -198,17 +186,15 @@ namespace opt { 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) { - #pragma omp critical (sls_solver) - { - if (m_pbsls) { - m_pbsls->reset(); - } - else { - m_pbsls = alloc(smt::pb_sls, m); - } + 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); @@ -224,12 +210,10 @@ namespace opt { } void bvsls_opt(model_ref& mdl) { - #pragma omp critical (sls_solver) - { - m_bvsls = alloc(bvsls_opt_engine, m, m_params); - } + 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 { diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp index fccc7fa1c..351141a3f 100644 --- a/src/opt/opt_solver.cpp +++ b/src/opt/opt_solver.cpp @@ -57,7 +57,7 @@ namespace opt { opt_solver::~opt_solver() { } - void opt_solver::updt_params(params_ref & _p) { + void opt_solver::updt_params(params_ref const & _p) { opt_params p(_p); m_dump_benchmarks = p.dump_benchmarks(); m_params.updt_params(_p); @@ -78,6 +78,9 @@ namespace opt { } void opt_solver::assert_expr(expr * t) { + if (has_quantifiers(t)) { + m_params.m_relevancy_lvl = 2; + } m_context.assert_expr(t); } @@ -191,6 +194,15 @@ namespace opt { } } + 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); + } + + /** \brief maximize the value of objective i in the current state. @@ -315,11 +327,7 @@ namespace opt { SASSERT(idx < get_num_assertions()); return m_context.get_formulas()[idx]; } - - void opt_solver::display(std::ostream & out) const { - m_context.display(out); - } - + smt::theory_var opt_solver::add_objective(app* term) { smt::theory_var v = get_optimizer().add_objective(term); m_objective_vars.push_back(v); @@ -344,10 +352,9 @@ namespace opt { } 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); - } + 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]; @@ -371,13 +378,13 @@ namespace opt { if (typeid(smt::theory_idl) == typeid(opt)) { smt::theory_idl& th = dynamic_cast(opt); - return th.mk_ge(m_fm, v, val.get_rational()); + return th.mk_ge(m_fm, v, val); } if (typeid(smt::theory_rdl) == typeid(opt) && val.get_infinitesimal().is_zero()) { smt::theory_rdl& th = dynamic_cast(opt); - return th.mk_ge(m_fm, v, val.get_rational()); + return th.mk_ge(m_fm, v, val); } // difference logic? diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h index 3c58a4fec..27168e2ca 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -88,7 +88,7 @@ namespace opt { virtual ~opt_solver(); virtual solver* translate(ast_manager& m, params_ref const& p); - virtual void updt_params(params_ref & p); + virtual void updt_params(params_ref const& p); virtual void collect_param_descrs(param_descrs & r); virtual void collect_statistics(statistics & st) const; virtual void assert_expr(expr * t); @@ -104,8 +104,9 @@ namespace opt { virtual void set_progress_callback(progress_callback * callback); virtual unsigned get_num_assertions() const; virtual expr * get_assertion(unsigned idx) const; - virtual void display(std::ostream & out) const; - virtual ast_manager& get_manager() { return m; } + virtual ast_manager& get_manager() const { return m; } + virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes); + virtual lbool preferred_sat(expr_ref_vector const& asms, vector& cores); void set_logic(symbol const& logic); smt::theory_var add_objective(app* term); diff --git a/src/opt/pb_sls.cpp b/src/opt/pb_sls.cpp index 95b489394..32c144652 100644 --- a/src/opt/pb_sls.cpp +++ b/src/opt/pb_sls.cpp @@ -20,9 +20,49 @@ Notes: #include "smt_literal.h" #include "ast_pp.h" #include "th_rewriter.h" -#include "sat_sls.h" +#include "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 { @@ -73,8 +113,8 @@ namespace smt { expr_ref_vector m_trail; obj_map m_decl2var; // map declarations to Boolean variables. ptr_vector m_var2decl; // reverse map - sat::index_set m_hard_false; // list of hard clauses that are false. - sat::index_set m_soft_false; // list of soft clauses that are false. + 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; diff --git a/src/opt/sortmax.cpp b/src/opt/sortmax.cpp new file mode 100644 index 000000000..e3cf59de4 --- /dev/null +++ b/src/opt/sortmax.cpp @@ -0,0 +1,160 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + sortmax.cpp + +Abstract: + + Theory based MaxSAT. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-11-18 + +Notes: + +--*/ +#include "maxsmt.h" +#include "uint_set.h" +#include "ast_pp.h" +#include "model_smt2_pp.h" +#include "smt_theory.h" +#include "smt_context.h" +#include "opt_context.h" +#include "sorting_network.h" +#include "filter_model_converter.h" + +namespace opt { + + class sortmax : public maxsmt_solver_base { + public: + typedef expr* literal; + typedef ptr_vector literal_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) {} + + virtual ~sortmax() {} + + lbool operator()() { + 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(filter_model_converter, m); + 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) { + expr_ref tmp(m); + if (m_model->eval(it->m_key, tmp) && m.is_true(tmp)) { + 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, 0); + 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 (unsigned i = 0; i < m_soft.size(); ++i) { + m_assignment[i] = is_true(m_soft[i]); + } + } + + bool is_true(expr* e) { + expr_ref tmp(m); + return m_model->eval(e, tmp) && m.is_true(tmp); + } + + // definitions used for sorting network + literal mk_false() { return m.mk_false(); } + literal mk_true() { return m.mk_true(); } + literal mk_max(literal a, literal b) { return trail(m.mk_or(a, b)); } + literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } + literal mk_not(literal a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } + + std::ostream& pp(std::ostream& out, literal lit) { return out << mk_pp(lit, m); } + + literal trail(literal l) { + m_trail.push_back(l); + return l; + } + literal fresh() { + expr_ref fr(m.mk_fresh_const("sn", m.mk_bool_sort()), m); + func_decl* f = to_app(fr)->get_decl(); + m_fresh.push_back(f); + m_filter->insert(f); + return trail(fr); + } + + void mk_clause(unsigned n, literal 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); + } + +} diff --git a/src/opt/wmax.cpp b/src/opt/wmax.cpp index ef4989cee..9708bdc8f 100644 --- a/src/opt/wmax.cpp +++ b/src/opt/wmax.cpp @@ -32,48 +32,289 @@ namespace opt { 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) {} + maxsmt_solver_base(c, ws, soft), + m_trail(m), + m_defs(m) {} + virtual ~wmax() {} lbool operator()() { TRACE("opt", tout << "weighted maxsat\n";); scoped_ensure_theory wth(*this); - lbool is_sat = l_true; - bool was_sat = false; - for (unsigned i = 0; i < m_soft.size(); ++i) { - wth().assert_weighted(m_soft[i], m_weights[i]); + obj_map soft; + reset(); + lbool is_sat = find_mutexes(soft); + if (is_sat != l_true) { + return is_sat; } - while (l_true == is_sat) { - is_sat = s().check_sat(0,0); + m_upper = m_lower; + bool was_sat = false; + expr_ref_vector asms(m); + vector cores; + + obj_map::iterator it = soft.begin(), end = soft.end(); + for (; it != end; ++it) { + expr* c = 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); tout << "\n"; + tout << "lower: " << m_lower << " upper: " << m_upper << "\n";); + while (!m.canceled() && m_lower < m_upper) { + //mk_assumptions(asms); + //is_sat = s().preferred_sat(asms, cores); + is_sat = s().check_sat(0, 0); if (m.canceled()) { is_sat = l_undef; } + if (is_sat == l_false) { + TRACE("opt", tout << "Unsat\n";); + break; + } if (is_sat == l_true) { if (wth().is_optimal()) { - m_upper = wth().get_min_cost(); + 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); was_sat = true; } + else { + //DEBUG_CODE(verify_cores(cores);); + } + update_cores(wth(), cores); + wth().init_min_cost(m_upper - m_lower); trace_bounds("wmax"); + SASSERT(m_lower <= m_upper); } - if (was_sat) { - wth().get_assignment(m_assignment); - } - if (is_sat == l_false && was_sat) { + + update_assignment(); + + if (!m.canceled() && is_sat == l_undef && m_lower == m_upper) { is_sat = l_true; } - m_upper = wth().get_min_cost(); - if (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) { + expr_ref tmp(m); + return m_model->eval(e, tmp) && m.is_true(tmp); + } + + void update_assignment() { + m_assignment.reset(); + for (unsigned i = 0; i < m_soft.size(); ++i) { + m_assignment.push_back(is_true(m_soft[i])); + } + } + + 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, 0)); + 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; + 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) { + expr_ref val(m); + if (m_model && m_model->eval(value, val, true)) { + m_model->register_decl(to_app(def)->get_decl(), val); + } + } + }; maxsmt_solver_base* mk_wmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft) { diff --git a/src/opt/wmax.h b/src/opt/wmax.h index 094bb8644..3d9d206ad 100644 --- a/src/opt/wmax.h +++ b/src/opt/wmax.h @@ -25,5 +25,7 @@ Notes: 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 diff --git a/src/parsers/smt/smtparser.cpp b/src/parsers/smt/smtparser.cpp index f50e8b339..c9b20850c 100644 --- a/src/parsers/smt/smtparser.cpp +++ b/src/parsers/smt/smtparser.cpp @@ -1573,7 +1573,8 @@ private: return false; } } - expr * p = m_manager.mk_pattern(ts.size(), (app*const*)(ts.c_ptr())); + expr_ref p(m_manager); + p = m_manager.mk_pattern(ts.size(), (app*const*)(ts.c_ptr())); if (!p || (!ignore_user_patterns() && !m_pattern_validator(num_bindings, p, children[0]->line(), children[0]->pos()))) { set_error("invalid pattern", children[0]); return false; @@ -1581,8 +1582,11 @@ private: patterns.push_back(p); } else if (children[0]->string() == symbol("ex_act") && ts.size() == 1) { - app * sk_hack = m_manager.mk_app(m_sk_hack, 1, ts.c_ptr()); - expr * p = m_manager.mk_pattern(1, &sk_hack); + app_ref sk_hack(m_manager); + sk_hack = m_manager.mk_app(m_sk_hack, 1, ts.c_ptr()); + app * sk_hackp = sk_hack.get(); + expr_ref p(m_manager); + p = m_manager.mk_pattern(1, &sk_hackp); if (!p || (!ignore_user_patterns() && !m_pattern_validator(num_bindings, p, children[0]->line(), children[0]->pos()))) { set_error("invalid pattern", children[0]); return false; diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index cdef41b72..fc28fa6e7 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -112,7 +112,6 @@ namespace smt2 { 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(); } @@ -120,7 +119,7 @@ namespace smt2 { 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; } @@ -132,7 +131,7 @@ namespace smt2 { m_decl(d), m_spos(spos) { } }; - + typedef psort_frame sort_frame; enum expr_frame_kind { EF_APP, EF_LET, EF_LET_DECL, EF_QUANT, EF_ATTR_EXPR, EF_PATTERN }; @@ -141,17 +140,17 @@ namespace smt2 { 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), + expr_frame(EF_APP), + m_f(f), + m_expr_spos(expr_spos), + m_param_spos(param_spos), m_as_sort(as_sort) {} }; @@ -166,8 +165,8 @@ namespace smt2 { unsigned m_sort_spos; unsigned m_expr_spos; quant_frame(bool forall, unsigned pat_spos, unsigned nopat_spos, unsigned sym_spos, unsigned sort_spos, unsigned expr_spos): - expr_frame(EF_QUANT), m_forall(forall), m_weight(1), - m_pat_spos(pat_spos), m_nopat_spos(nopat_spos), + expr_frame(EF_QUANT), m_forall(forall), 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) {} }; @@ -178,7 +177,7 @@ namespace smt2 { 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) {} }; @@ -189,9 +188,9 @@ namespace smt2 { 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), + expr_frame(EF_ATTR_EXPR), + m_prev(prev), + m_sym_spos(sym_spos), m_expr_spos(expr_spos) {} }; @@ -231,12 +230,12 @@ namespace smt2 { m_expr_stack = alloc(expr_ref_vector, m()); return *(m_expr_stack.get()); } - + template static unsigned size(scoped_ptr & v) { return v.get() == 0 ? 0 : v->size(); } - + template static void shrink(scoped_ptr & v, unsigned old_sz) { if (v.get() == 0) { @@ -258,7 +257,7 @@ namespace smt2 { m_nopattern_stack = alloc(expr_ref_vector, m()); return *(m_nopattern_stack.get()); } - + svector & symbol_stack() { return m_symbol_stack; } @@ -337,7 +336,7 @@ namespace smt2 { bool sync_after_error() { while (true) { try { - while (curr_is_rparen()) + while (curr_is_rparen()) next(); if (m_num_open_paren < 0) m_num_open_paren = 0; @@ -346,7 +345,7 @@ namespace smt2 { 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: " + 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; @@ -374,7 +373,7 @@ namespace smt2 { } throw parser_exception(msg); } - + symbol const & curr_id() const { return m_scanner.get_id(); } rational curr_numeral() const { return m_scanner.get_number(); } @@ -411,15 +410,20 @@ namespace smt2 { 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); } + 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; + m_ctx.diagnostic_stream() << std::endl; } else { - m_ctx.regular_stream() << "(error \"line " << line << " column " << pos << ": " << escaped(msg, true) << "\")" << std::endl; + 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()) { exit(1); @@ -440,7 +444,7 @@ namespace smt2 { 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 += ": "; @@ -453,10 +457,10 @@ namespace smt2 { unsigned num_parens = 0; do { switch (curr()) { - case scanner::LEFT_PAREN: - num_parens++; + case scanner::LEFT_PAREN: + num_parens++; break; - case scanner::RIGHT_PAREN: + case scanner::RIGHT_PAREN: if (num_parens == 0) throw parser_exception("invalid s-expression, unexpected ')'"); num_parens--; @@ -574,7 +578,7 @@ namespace smt2 { else { if (ignore_unknow_sort) return 0; - unknown_sort(id); + unknown_sort(id); UNREACHABLE(); return 0; } @@ -588,7 +592,7 @@ namespace smt2 { check_identifier("invalid indexed sort, symbol expected"); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); - if (d == 0) + if (d == 0) unknown_sort(id); next(); sbuffer args; @@ -613,13 +617,13 @@ namespace smt2 { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); - if (d == 0) + if (d == 0) 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()); @@ -656,7 +660,7 @@ namespace smt2 { else { check_lparen_next("invalid sort, symbol, '_' or '(' expected"); if (!curr_is_identifier()) - throw parser_exception("invalid sort, symbol or '_' expected"); + throw parser_exception("invalid sort, symbol or '_' expected"); if (curr_id_is_underscore()) { psort_stack().push_back(pm().mk_psort_cnst(parse_indexed_sort())); } @@ -674,13 +678,13 @@ namespace smt2 { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); - if (d == 0) + if (d == 0) 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()); @@ -719,8 +723,8 @@ namespace smt2 { } 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_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()); } @@ -735,7 +739,7 @@ namespace smt2 { } unsigned parse_sorts(char const* context) { - unsigned sz = 0; + unsigned sz = 0; check_lparen_next(context); while (!curr_is_rparen()) { parse_sort(context); @@ -958,7 +962,7 @@ namespace smt2 { } // parse expression state - enum pe_state { + enum pe_state { PES_EXPR, // expecting PES_DECL, // expecting ( ) PES_PATTERN, @@ -1024,7 +1028,7 @@ namespace smt2 { else { // just consume pattern next(); - consume_sexpr(); + consume_sexpr(); } } else if (id == m_nopattern) { @@ -1043,9 +1047,9 @@ namespace smt2 { else { std::ostringstream str; str << "unknown attribute " << id; - warning_msg(str.str().c_str()); + warning_msg("%s", str.str().c_str()); next(); - // just consume the + // just consume the consume_sexpr(); } if (curr_is_rparen()) @@ -1060,13 +1064,13 @@ namespace smt2 { switch (fr->m_kind) { case EF_LET: return static_cast(fr)->m_in_decls ? PES_DECL : PES_EXPR; - case EF_ATTR_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()); @@ -1123,7 +1127,7 @@ namespace smt2 { expr_stack().push_back(0); // empty pattern return; } - + if (curr_is_lparen()) { // multi-pattern void * mem = m_stack.allocate(sizeof(pattern_frame)); @@ -1213,9 +1217,9 @@ namespace smt2 { SASSERT(curr_id_is_forall() || curr_id_is_exists()); SASSERT(!is_forall || curr_id_is_forall()); SASSERT(is_forall || curr_id_is_exists()); - next(); + next(); void * mem = m_stack.allocate(sizeof(quant_frame)); - new (mem) quant_frame(is_forall, pattern_stack().size(), nopattern_stack().size(), symbol_stack().size(), + new (mem) quant_frame(is_forall, 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(); @@ -1257,11 +1261,11 @@ namespace smt2 { next(); return r; } - check_lparen_next("invalid (indexed) identifier, '(_' or symbol expected"); + check_lparen_next("invalid (indexed) identifier, '(_' or symbol expected"); return parse_indexed_identifier_core(); } - // parse: + // parse: // 'as' ')' // '_' + ')' // 'as' (|)+ ')' ')' @@ -1283,7 +1287,7 @@ namespace smt2 { } } - // parse: + // parse: // // '(' 'as' ')' // '(' '_' + ')' @@ -1309,8 +1313,8 @@ namespace smt2 { throw parser_exception(msg.c_str()); } - rational m_last_bv_numeral; // for bv, bvbin, bvhex - + 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";); @@ -1349,7 +1353,7 @@ namespace smt2 { 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'); @@ -1357,7 +1361,7 @@ namespace smt2 { if (*s != 'e') return false; ++s; if (*s != 'x') return false; - ++s; + ++s; rational & n = m_last_bv_numeral; unsigned i = 0; n = rational(0); @@ -1368,7 +1372,7 @@ namespace smt2 { } else if ('a' <= *s && *s <= 'f') { n *= rational(16); - n += rational(10 + (*s - 'a')); + n += rational(10 + (*s - 'a')); } else if ('A' <= *s && *s <= 'F') { n *= rational(16); @@ -1385,11 +1389,11 @@ namespace smt2 { } } - // Return true if + // 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 + // 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; @@ -1433,7 +1437,7 @@ namespace smt2 { } 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) { @@ -1571,7 +1575,7 @@ namespace smt2 { 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()); - m_ctx.mk_app(fr->m_f, + m_ctx.mk_app(fr->m_f, num_args, expr_stack().c_ptr() + fr->m_expr_spos, num_indices, @@ -1655,7 +1659,7 @@ namespace smt2 { fr->m_qid = symbol(m_scanner.get_line()); if (!m().is_bool(expr_stack().back())) throw parser_exception("quantifier body must be a Boolean expression"); - quantifier * new_q = m().mk_quantifier(fr->m_forall, + quantifier * new_q = m().mk_quantifier(fr->m_forall, num_decls, sort_stack().c_ptr() + fr->m_sort_spos, symbol_stack().c_ptr() + fr->m_sym_spos, @@ -1716,7 +1720,7 @@ namespace smt2 { case EF_APP: pop_app_frame(static_cast(fr)); break; - case EF_LET: + case EF_LET: pop_let_frame(static_cast(fr)); break; case EF_LET_DECL: @@ -1742,7 +1746,7 @@ namespace smt2 { void parse_expr() { m_num_expr_frames = 0; do { - TRACE("parse_expr", tout << "curr(): " << curr() << ", m_num_expr_frames: " << m_num_expr_frames + 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) @@ -1826,7 +1830,7 @@ namespace smt2 { SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_declare_sort); next(); - + check_identifier("invalid sort declaration, symbol expected"); symbol id = curr_id(); if (m_ctx.find_psort_decl(id) != 0) @@ -1840,7 +1844,7 @@ namespace smt2 { check_int("invalid sort declaration, arity () or ')' expected"); rational n = curr_numeral(); if (!n.is_unsigned()) - throw parser_exception("invalid sort declaration, arity is too big to fit in an unsigned machine integer"); + throw parser_exception("invalid sort declaration, arity is too big to fit in an unsigned machine integer"); psort_decl * decl = pm().mk_psort_user_decl(n.get_unsigned(), id, 0); m_ctx.insert(decl); next(); @@ -1900,12 +1904,12 @@ namespace smt2 { } void parse_define_fun_rec() { - // ( define-fun-rec hfun_defi ) + // ( 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()); @@ -1918,7 +1922,7 @@ namespace smt2 { } void parse_define_funs_rec() { - // ( define-funs-rec ( hfun_decin+1 ) ( htermin+1 ) ) + // ( define-funs-rec ( hfun_decin+1 ) ( htermin+1 ) ) SASSERT(curr_is_identifier()); SASSERT(curr_id() == m_define_funs_rec); SASSERT(m_num_bindings == 0); @@ -1948,14 +1952,14 @@ namespace smt2 { 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(); } next(); } @@ -1978,7 +1982,7 @@ namespace smt2 { sort_stack().shrink(sort_spos); expr_stack().shrink(expr_spos); m_env.end_scope(); - m_num_bindings = 0; + m_num_bindings = 0; } void parse_rec_fun_bodies(func_decl_ref_vector const& decls, vector const& bindings, vector >const & ids) { @@ -1991,10 +1995,10 @@ namespace smt2 { } if (i != decls.size()) { - throw parser_exception("the number of declarations does not match number of supplied definitions"); + throw parser_exception("the number of declarations does not match number of supplied definitions"); } check_rparen("invalid recursive function definition, ')' expected"); - next(); + next(); } void parse_rec_fun_body(func_decl* f, expr_ref_vector const& bindings, svector const& ids) { @@ -2008,19 +2012,19 @@ namespace smt2 { for (unsigned i = 0; i < num_vars; ++i) { m_env.insert(ids[i], local(bindings[i], num_vars)); } - parse_expr(); + parse_expr(); body = expr_stack().back(); expr_stack().pop_back(); symbol_stack().shrink(sym_spos); m_env.end_scope(); - m_num_bindings = 0; + 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(f->get_range(), m()) << " but function body has sort " << mk_pp(m().get_sort(body), m()); throw parser_exception(buffer.str().c_str()); - } + } m_ctx.insert_rec_fun(f, bindings, ids, body); } @@ -2211,9 +2215,9 @@ namespace smt2 { SASSERT(curr_id() == m_check_sat_assuming); next(); unsigned spos = expr_stack().size(); - check_rparen_next("invalid check-sat-assuming command, '(', expected"); + check_lparen_next("invalid check-sat-assuming command, '(', expected"); parse_assumptions(); - check_rparen_next("invalid check-sat-assuming command, ')', expected"); + 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); @@ -2229,7 +2233,7 @@ namespace smt2 { 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(); @@ -2269,7 +2273,7 @@ namespace smt2 { SASSERT(curr_id() == m_reset); next(); check_rparen("invalid reset command, ')' expected"); - m_ctx.reset(); + m_ctx.reset(); reset(); m_ctx.print_success(); next(); @@ -2351,7 +2355,7 @@ namespace smt2 { } next(); } - + void parse_next_cmd_arg() { SASSERT(m_curr_cmd != 0); cmd_arg_kind k = m_curr_cmd->next_arg_kind(m_ctx); @@ -2360,7 +2364,7 @@ namespace smt2 { check_int("invalid command argument, unsigned integer expected"); rational n = curr_numeral(); if (!n.is_unsigned()) - throw parser_exception("invalid command argument, numeral is too big to fit in an unsigned machine integer"); + throw parser_exception("invalid command argument, numeral is too big to fit in an unsigned machine integer"); m_curr_cmd->set_next_arg(m_ctx, n.get_unsigned()); next(); break; @@ -2473,7 +2477,7 @@ namespace smt2 { m_curr_cmd = m_ctx.find_cmd(s); if (m_curr_cmd == 0) { parse_unknown_cmd(); - return; + return; } next(); unsigned arity = m_curr_cmd->get_arity(); @@ -2503,14 +2507,14 @@ namespace smt2 { return; } else { - if (arity != VAR_ARITY && i == arity) + 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(); @@ -2559,7 +2563,7 @@ namespace smt2 { return; } if (s == m_declare_datatypes) { - parse_declare_datatypes(); + parse_declare_datatypes(); return; } if (s == m_get_value) { @@ -2586,8 +2590,8 @@ namespace smt2 { } public: - parser(cmd_context & ctx, std::istream & is, bool interactive, params_ref const & p): - m_ctx(ctx), + parser(cmd_context & ctx, std::istream & is, bool interactive, params_ref const & p, char const * filename=0): + m_ctx(ctx), m_params(p), m_scanner(ctx, is, interactive), m_curr(scanner::NULL_TOKEN), @@ -2625,14 +2629,15 @@ namespace smt2 { m_check_sat_assuming("check-sat-assuming"), m_define_fun_rec("define-fun-rec"), m_define_funs_rec("define-funs-rec"), - m_underscore("_"), - m_num_open_paren(0) { + 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(); } @@ -2643,7 +2648,7 @@ namespace smt2 { 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; @@ -2658,7 +2663,7 @@ namespace smt2 { m_env .reset(); m_sort_id2param_idx .reset(); m_dt_name2idx .reset(); - + m_bv_util = 0; m_arith_util = 0; m_seq_util = 0; @@ -2708,9 +2713,9 @@ namespace smt2 { return !found_errors; } catch (parser_exception & ex) { - if (ex.has_pos()) + if (ex.has_pos()) error(ex.line(), ex.pos(), ex.msg()); - else + else error(ex.msg()); } catch (ast_exception & ex) { @@ -2733,8 +2738,8 @@ namespace smt2 { }; }; -bool parse_smt2_commands(cmd_context & ctx, std::istream & is, bool interactive, params_ref const & ps) { - smt2::parser p(ctx, is, interactive, ps); +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(); } diff --git a/src/parsers/smt2/smt2parser.h b/src/parsers/smt2/smt2parser.h index 5b4384917..77fd41d5d 100644 --- a/src/parsers/smt2/smt2parser.h +++ b/src/parsers/smt2/smt2parser.h @@ -21,6 +21,6 @@ Revision History: #include"cmd_context.h" -bool parse_smt2_commands(cmd_context & ctx, std::istream & is, bool interactive = false, params_ref const & p = params_ref()); +bool parse_smt2_commands(cmd_context & ctx, std::istream & is, bool interactive = false, params_ref const & p = params_ref(), char const * filename = 0); #endif diff --git a/src/parsers/smt2/smt2scanner.cpp b/src/parsers/smt2/smt2scanner.cpp index 156cc2e5d..fb2f9f34a 100644 --- a/src/parsers/smt2/smt2scanner.cpp +++ b/src/parsers/smt2/smt2scanner.cpp @@ -23,10 +23,12 @@ namespace smt2 { void scanner::next() { if (m_cache_input) - m_cache.push_back(m_curr); - SASSERT(m_curr != EOF); + 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]; @@ -37,7 +39,7 @@ namespace smt2 { m_bend = static_cast(m_stream.gcount()); m_bpos = 0; if (m_bpos == m_bend) { - m_curr = EOF; + m_at_eof = true; } else { m_curr = m_buffer[m_bpos]; @@ -52,7 +54,7 @@ namespace smt2 { next(); while (true) { char c = curr(); - if (c == EOF) + if (m_at_eof) return; if (c == '\n') { new_line(); @@ -70,7 +72,7 @@ namespace smt2 { next(); while (true) { char c = curr(); - if (c == EOF) { + if (m_at_eof) { throw scanner_exception("unexpected end of quoted symbol", m_line, m_spos); } else if (c == '\n') { @@ -90,7 +92,7 @@ namespace smt2 { } scanner::token scanner::read_symbol_core() { - while (true) { + while (!m_at_eof) { char c = curr(); signed char n = m_normalized[static_cast(c)]; if (n == 'a' || n == '0' || n == '-') { @@ -104,6 +106,7 @@ namespace smt2 { return SYMBOL_TOKEN; } } + return EOF_TOKEN; } scanner::token scanner::read_symbol() { @@ -167,7 +170,7 @@ namespace smt2 { m_string.reset(); while (true) { char c = curr(); - if (c == EOF) + if (m_at_eof) throw scanner_exception("unexpected end of string", m_line, m_spos); if (c == '\n') { new_line(); @@ -237,10 +240,11 @@ namespace smt2 { } } - scanner::scanner(cmd_context & ctx, std::istream& stream, bool interactive): + 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), @@ -289,9 +293,13 @@ namespace smt2 { } scanner::token scanner::scan() { - while (true) { + while (true) { signed char c = curr(); m_pos = m_spos; + + if (m_at_eof) + return EOF_TOKEN; + switch (m_normalized[(unsigned char) c]) { case ' ': next(); @@ -327,8 +335,6 @@ namespace smt2 { return read_symbol(); else return read_signed_number(); - case -1: - return EOF_TOKEN; default: { scanner_exception ex("unexpected character", m_line, m_spos); next(); diff --git a/src/parsers/smt2/smt2scanner.h b/src/parsers/smt2/smt2scanner.h index 8313c24df..3ad47dfb1 100644 --- a/src/parsers/smt2/smt2scanner.h +++ b/src/parsers/smt2/smt2scanner.h @@ -34,6 +34,7 @@ namespace smt2 { 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 diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index 721e38942..4bf3c1580 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -362,7 +362,7 @@ namespace qe { } app* ite; if (find_ite(fml, ite)) { - expr* cond, *th, *el; + expr* cond = 0, *th = 0, *el = 0; VERIFY(m.is_ite(ite, cond, th, el)); expr_ref tmp1(fml, m), tmp2(fml, m); m_replace->apply_substitution(ite, th, tmp1); diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 38d46fefe..d98f36d7d 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -15,7 +15,7 @@ Author: Revision History: - + Moved projection functionality to model_based_opt module. 2016-06-26 --*/ @@ -32,64 +32,19 @@ Revision History: #include "model_evaluator.h" namespace qe { - - 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; - } - - 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); - } struct arith_project_plugin::imp { ast_manager& m; arith_util a; - th_rewriter m_rw; - expr_ref_vector m_ineq_terms; - vector m_ineq_coeffs; - svector m_ineq_types; - expr_ref_vector m_div_terms; - vector m_div_divisors, m_div_coeffs; - expr_ref_vector m_new_lits; - expr_ref_vector m_trail; - rational m_delta, m_u; - scoped_ptr m_var; - unsigned m_num_pos, m_num_neg; - bool m_pos_is_unit, m_neg_is_unit; - - sort* var_sort() const { return m.get_sort(m_var->x()); } - - bool is_int() const { return a.is_int(m_var->x()); } - - void display(std::ostream& out) const { - for (unsigned i = 0; i < num_ineqs(); ++i) { - display_ineq(out, i); - } - for (unsigned i = 0; i < num_divs(); ++i) { - display_div(out, i); - } - } 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 { - TRACE("qe", tout << "Adding variable " << mk_pp(x, m) << "\n";); ts.insert(x, v); } } @@ -99,68 +54,100 @@ namespace qe { // 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& model, expr* lit, expr_ref_vector& fmls, obj_map& tids) { + 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); VERIFY(model.eval(lit, val) && m.is_true(val));); + 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)) && a.is_real(e1)) { - linearize(mbo, model, mul, e1, c, fmls, ts, tids); - linearize(mbo, model, -mul, e2, c, fmls, ts, tids); + 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)) && a.is_real(e1)) { - linearize(mbo, model, mul, e1, c, fmls, ts, tids); - linearize(mbo, model, -mul, e2, c, fmls, ts, tids); + 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 && a.is_real(e1)) { - linearize(mbo, model, mul, e1, c, fmls, ts, tids); - linearize(mbo, model, -mul, e2, c, fmls, ts, tids); + 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 && a.is_real(e1)) { - expr_ref val1(m), val2(m); + else if (m.is_eq(lit, e1, e2) && is_not && is_arith(e1)) { + rational r1, r2; - VERIFY(model.eval(e1, val1) && a.is_numeral(val1, r1)); - VERIFY(model.eval(e2, val2) && a.is_numeral(val2, r2)); + expr_ref val1 = eval(e1); + expr_ref val2 = eval(e2); + VERIFY(a.is_numeral(val1, r1)); + VERIFY(a.is_numeral(val2, r2)); SASSERT(r1 != r2); - if (r2 < r1) { + if (r1 < r2) { std::swap(e1, e2); } ty = opt::t_lt; - linearize(mbo, model, mul, e1, c, fmls, ts, tids); - linearize(mbo, model, -mul, e2, c, fmls, ts, tids); + 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 && a.is_real(to_app(lit)->get_arg(0))) { - TRACE("qe", tout << "TBD: handle distinc\n";); - return false; + 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 (unsigned i = 0; i < alit->get_num_args(); ++i) { + val = eval(alit->get_arg(i)); + VERIFY(a.is_numeral(val, r)); + nums.push_back(std::make_pair(alit->get_arg(i), 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 && a.is_real(to_app(lit)->get_arg(0))) { - TRACE("qe", tout << "TBD: handle negation of distinc\n";); - return false; + 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 = 0; + rational r; + expr_ref val = eval(arg1); + VERIFY(a.is_numeral(val, r)); + 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; } -#if 0 - TBD for integers - if (ty == opt::t_lt && false) { - c += rational(1); - ty = opt::t_le; - } -#endif vars coeffs; - extract_coefficients(mbo, model, ts, tids, coeffs); + extract_coefficients(mbo, eval, ts, tids, coeffs); mbo.add_constraint(coeffs, c, ty); return true; } @@ -168,239 +155,97 @@ namespace qe { // // convert linear arithmetic term into an inequality for mbo. // - void linearize(opt::model_based_opt& mbo, model& model, rational const& mul, expr* t, rational& c, + 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(model, t1, mul1)) { - linearize(mbo, model, mul* mul1, t2, c, fmls, ts, tids); + 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(model, t2, mul1)) { - linearize(mbo, model, mul* mul1, t1, 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 (unsigned i = 0; i < ap->get_num_args(); ++i) { - linearize(mbo, model, mul, ap->get_arg(i), c, fmls, ts, tids); + linearize(mbo, eval, mul, ap->get_arg(i), c, fmls, ts, tids); } } else if (a.is_sub(t, t1, t2)) { - linearize(mbo, model, mul, t1, c, fmls, ts, tids); - linearize(mbo, model, -mul, t2, c, fmls, ts, tids); + 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, model, -mul, t1, c, fmls, ts, tids); + 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)) { - VERIFY(model.eval(t1, val)); + 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, model, mul, t2, c, fmls, ts, tids); + 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, model, mul, t3, c, fmls, ts, tids); + linearize(mbo, eval, mul, t3, c, fmls, ts, tids); } } + else if (a.is_mod(t, t1, t2) && is_numeral(t2, mul1)) { + 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); } } - // - // extract linear terms from t into c and ts. - // - void is_linear(model& model, rational const& mul, expr* t, rational& c, expr_ref_vector& ts) { - expr* t1, *t2, *t3; - rational mul1; - expr_ref val(m); - if (t == m_var->x()) { - c += mul; - } - else if (a.is_mul(t, t1, t2) && is_numeral(model, t1, mul1)) { - is_linear(model, mul* mul1, t2, c, ts); - } - else if (a.is_mul(t, t1, t2) && is_numeral(model, t2, mul1)) { - is_linear(model, mul* mul1, t1, c, ts); - } - else if (a.is_add(t)) { - app* ap = to_app(t); - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - is_linear(model, mul, ap->get_arg(i), c, ts); - } - } - else if (a.is_sub(t, t1, t2)) { - is_linear(model, mul, t1, c, ts); - is_linear(model, -mul, t2, c, ts); - } - else if (a.is_uminus(t, t1)) { - is_linear(model, -mul, t1, c, ts); - } - else if (a.is_numeral(t, mul1)) { - ts.push_back(mk_num(mul*mul1)); - } - else if (extract_mod(model, t, val)) { - ts.push_back(mk_mul(mul, val)); - } - else if (m.is_ite(t, t1, t2, t3)) { - VERIFY(model.eval(t1, val)); - SASSERT(m.is_true(val) || m.is_false(val)); - TRACE("qe", tout << mk_pp(t1, m) << " := " << val << "\n";); - if (m.is_true(val)) { - is_linear(model, mul, t2, c, ts); - } - else { - is_linear(model, mul, t3, c, ts); - } - } - else if ((*m_var)(t)) { - TRACE("qe", tout << "can't project:" << mk_pp(t, m) << "\n";); - throw cant_project(); - } - else { - ts.push_back(mk_mul(mul, t)); - } - } - - // - // extract linear inequalities from literal lit. - // - bool is_linear(model& model, expr* lit, bool& found_eq) { - rational c(0), mul(1); - expr_ref t(m); - opt::ineq_type ty = opt::t_le; - expr* e1, *e2; - expr_ref_vector ts(m); - 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)) { - is_linear(model, mul, e1, c, ts); - is_linear(model, -mul, e2, c, ts); - ty = is_not? opt::t_lt : opt::t_le; - } - else if (a.is_lt(lit, e1, e2) || a.is_gt(lit, e2, e1)) { - is_linear(model, mul, e1, c, ts); - is_linear(model, -mul, e2, c, ts); - ty = is_not? opt::t_le: opt::t_lt; - } - else if (m.is_eq(lit, e1, e2) && !is_not && is_arith(e1)) { - is_linear(model, mul, e1, c, ts); - is_linear(model, -mul, e2, c, ts); - ty = opt::t_eq; - } - 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 (unsigned i = 0; i < alit->get_num_args(); ++i) { - VERIFY(model.eval(alit->get_arg(i), val) && a.is_numeral(val, r)); - nums.push_back(std::make_pair(alit->get_arg(i), 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); - c.reset(); - ts.reset(); - is_linear(model, mul, nums[i+1].first, c, ts); - is_linear(model, -mul, nums[i].first, c, ts); - t = add(ts); - accumulate_linear(model, c, t, opt::t_lt); - } - t = mk_num(0); - c.reset(); - return true; - } - else if (m.is_distinct(lit) && is_not && is_arith(to_app(lit)->get_arg(0))) { - expr_ref eq = project_plugin::pick_equality(m, model, to_app(lit)->get_arg(0)); - return is_linear(model, eq, found_eq); - } - else if (m.is_eq(lit, e1, e2) && is_not && is_arith(e1)) { - expr_ref val1(m), val2(m); - rational r1, r2; - VERIFY(model.eval(e1, val1) && a.is_numeral(val1, r1)); - VERIFY(model.eval(e2, val2) && a.is_numeral(val2, r2)); - SASSERT(r1 != r2); - if (r1 < r2) { - std::swap(e1, e2); - } - ty = opt::t_lt; - is_linear(model, mul, e1, c, ts); - is_linear(model, -mul, e2, c, ts); - } - else { - TRACE("qe", tout << "can't project:" << mk_pp(lit, m) << "\n";); - throw cant_project(); - } - if (ty == opt::t_lt && is_int()) { - ts.push_back(mk_num(1)); - ty = opt::t_le; - } - t = add(ts); - if (ty == opt::t_eq && c.is_neg()) { - t = mk_uminus(t); - c.neg(); - } - if (ty == opt::t_eq && c > rational(1)) { - m_ineq_coeffs.push_back(-c); - m_ineq_terms.push_back(mk_uminus(t)); - m_ineq_types.push_back(opt::t_le); - m_num_neg++; - ty = opt::t_le; - } - accumulate_linear(model, c, t, ty); - found_eq = !c.is_zero() && ty == opt::t_eq; - return true; - } - - bool is_numeral(model& model, expr* t, rational& r) { - expr* t1, *t2, *t3; + bool is_numeral(expr* t, rational& r) { + expr* t1, *t2; rational r1, r2; - expr_ref val(m); - if (a.is_numeral(t, r)) return true; - - if (a.is_uminus(t, t1) && is_numeral(model, t1, r)) { - r.neg(); - return true; - } - else if (a.is_mul(t, t1, t2) && is_numeral(model, t1, r1) && is_numeral(model, t2, r2)) { - r = r1*r2; - return true; + if (a.is_numeral(t, r)) { + // no-op } - else if (a.is_add(t)) { + 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 (unsigned i = 0; i < ap->get_num_args(); ++i) { - if (!is_numeral(model, ap->get_arg(i), r1)) return false; + if (!is_numeral(ap->get_arg(i), r1)) return false; r *= r1; } - return true; } - else if (m.is_ite(t, t1, t2, t3)) { - VERIFY (model.eval(t1, val)); - if (m.is_true(val)) { - return is_numeral(model, t1, r); - } - else { - return is_numeral(model, t2, r); + else if (a.is_add(t)) { + app* ap = to_app(t); + r = rational(0); + for (unsigned i = 0; i < ap->get_num_args(); ++i) { + if (!is_numeral(ap->get_arg(i), r1)) return false; + r += r1; } } - else if (a.is_sub(t, t1, t2) && is_numeral(model, t1, r1) && is_numeral(model, t2, r2)) { + else if (a.is_sub(t, t1, t2) && is_numeral(t1, r1) && is_numeral(t2, r2)) { r = r1 - r2; - return true; } - - return false; + else { + return false; + } + return true; } struct compare_second { @@ -410,614 +255,60 @@ namespace qe { } }; - void accumulate_linear(model& model, rational const& c, expr_ref& t, opt::ineq_type ty) { - if (c.is_zero()) { - switch (ty) { - case opt::t_eq: - t = a.mk_eq(t, mk_num(0)); - break; - case opt::t_lt: - t = a.mk_lt(t, mk_num(0)); - break; - case opt::t_le: - t = a.mk_le(t, mk_num(0)); - break; - } - add_lit(model, m_new_lits, t); - } - else { - m_ineq_coeffs.push_back(c); - m_ineq_terms.push_back(t); - m_ineq_types.push_back(ty); - if (ty == opt::t_eq) { - // skip - } - else if (c.is_pos()) { - ++m_num_pos; - m_pos_is_unit &= c.is_one(); - } - else { - ++m_num_neg; - m_neg_is_unit &= c.is_minus_one(); - } - } - } - bool is_arith(expr* e) { return a.is_int(e) || a.is_real(e); } - - expr_ref add(expr_ref_vector const& ts) { - switch (ts.size()) { - case 0: - return mk_num(0); - case 1: - return expr_ref(ts[0], m); - default: - return expr_ref(a.mk_add(ts.size(), ts.c_ptr()), m); - } - } - - - // e is of the form (ax + t) mod k - bool is_mod(model& model, expr* e, rational& k, expr_ref& t, rational& c) { - expr* t1, *t2; - expr_ref_vector ts(m); - if (a.is_mod(e, t1, t2) && - a.is_numeral(t2, k) && - (*m_var)(t1)) { - c.reset(); - rational mul(1); - is_linear(model, mul, t1, c, ts); - t = add(ts); - return true; - } - return false; - } - - bool extract_mod(model& model, expr* e, expr_ref& val) { - rational c, k; - expr_ref t(m); - if (is_mod(model, e, k, t, c)) { - VERIFY (model.eval(e, val)); - SASSERT (a.is_numeral(val)); - TRACE("qe", tout << "extract: " << mk_pp(e, m) << " evals: " << val << " c: " << c << " t: " << t << "\n";); - if (!c.is_zero()) { - t = mk_sub(t, val); - m_div_terms.push_back(t); - m_div_divisors.push_back(k); - m_div_coeffs.push_back(c); - } - else { - t = m.mk_eq(a.mk_mod(t, mk_num(k)), val); - add_lit(model, m_new_lits, t); - } - return true; - } - return false; - } - - bool lit_is_true(model& model, expr* e) { - expr_ref val(m); - VERIFY(model.eval(e, val)); - CTRACE("qe", !m.is_true(val), tout << "eval: " << mk_pp(e, m) << " " << val << "\n";); - return m.is_true(val); - } - - expr_ref mk_num(unsigned n) { - rational r(n); - return mk_num(r); - } - - expr_ref mk_num(rational const& r) const { - return expr_ref(a.mk_numeral(r, var_sort()), m); - } - - expr_ref mk_divides(rational const& k, expr* t) { - return expr_ref(m.mk_eq(a.mk_mod(t, mk_num(abs(k))), mk_num(0)), m); - } - - void reset() { - reset_ineqs(); - reset_divs(); - m_delta = rational(1); - m_u = rational(0); - m_new_lits.reset(); - } - - void reset_divs() { - m_div_terms.reset(); - m_div_coeffs.reset(); - m_div_divisors.reset(); - } - - void reset_ineqs() { - m_ineq_terms.reset(); - m_ineq_coeffs.reset(); - m_ineq_types.reset(); - } - - expr* ineq_term(unsigned i) const { return m_ineq_terms[i]; } - rational const& ineq_coeff(unsigned i) const { return m_ineq_coeffs[i]; } - opt::ineq_type ineq_ty(unsigned i) const { return m_ineq_types[i]; } - app_ref mk_ineq_pred(unsigned i) { - app_ref result(m); - result = to_app(mk_add(mk_mul(ineq_coeff(i), m_var->x()), ineq_term(i))); - switch (ineq_ty(i)) { - case opt::t_lt: - result = a.mk_lt(result, mk_num(0)); - break; - case opt::t_le: - result = a.mk_le(result, mk_num(0)); - break; - case opt::t_eq: - result = m.mk_eq(result, mk_num(0)); - break; - } - return result; - } - void display_ineq(std::ostream& out, unsigned i) const { - out << mk_pp(ineq_term(i), m) << " " << ineq_coeff(i) << "*" << mk_pp(m_var->x(), m); - switch (ineq_ty(i)) { - case opt::t_eq: out << " = 0\n"; break; - case opt::t_le: out << " <= 0\n"; break; - case opt::t_lt: out << " < 0\n"; break; - } - } - unsigned num_ineqs() const { return m_ineq_terms.size(); } - expr* div_term(unsigned i) const { return m_div_terms[i]; } - rational const& div_coeff(unsigned i) const { return m_div_coeffs[i]; } - rational const& div_divisor(unsigned i) const { return m_div_divisors[i]; } - void display_div(std::ostream& out, unsigned i) const { - out << div_divisor(i) << " | ( " << mk_pp(div_term(i), m) << " " << div_coeff(i) << "*" - << mk_pp(m_var->x(), m) << ")\n"; - } - unsigned num_divs() const { return m_div_terms.size(); } - - void project(model& model, expr_ref_vector& lits) { - TRACE("qe", - tout << "project: " << mk_pp(m_var->x(), m) << "\n"; - tout << lits; - model_v2_pp(tout, model); ); - - m_num_pos = 0; m_num_neg = 0; - m_pos_is_unit = true; m_neg_is_unit = true; - unsigned eq_index = 0; - reset(); - bool found_eq = false; - for (unsigned i = 0; i < lits.size(); ++i) { - bool found_eq0 = false; - expr* e = lits[i].get(); - if (!(*m_var)(e)) { - m_new_lits.push_back(e); - } - else if (!is_linear(model, e, found_eq0)) { - TRACE("qe", tout << "can't project:" << mk_pp(e, m) << "\n";); - throw cant_project(); - } - if (found_eq0 && !found_eq) { - found_eq = true; - eq_index = num_ineqs()-1; - } - } - TRACE("qe", display(tout << mk_pp(m_var->x(), m) << ":\n"); - tout << "found eq: " << found_eq << " @ " << eq_index << "\n"; - tout << "num pos: " << m_num_pos << " num neg: " << m_num_neg << " num divs " << num_divs() << "\n"; - ); - lits.reset(); - lits.append(m_new_lits); - if (found_eq) { - apply_equality(model, eq_index, lits); - return; - } - if (num_divs() == 0 && (m_num_pos == 0 || m_num_neg == 0)) { - return; - } - if (num_divs() > 0) { - apply_divides(model, lits); - TRACE("qe", display(tout << "after division " << mk_pp(m_var->x(), m) << "\n");); - } - if (m_num_pos == 0 || m_num_neg == 0) { - return; - } - if ((m_num_pos <= 2 || m_num_neg <= 2) && - (m_num_pos == 1 || m_num_neg == 1 || (m_num_pos <= 3 && m_num_neg <= 3)) && - (!is_int() || m_pos_is_unit || m_neg_is_unit)) { - - unsigned index1 = num_ineqs(); - unsigned index2 = num_ineqs(); - bool is_pos = m_num_pos <= m_num_neg; - for (unsigned i = 0; i < num_ineqs(); ++i) { - if (ineq_coeff(i).is_pos() == is_pos) { - if (index1 == num_ineqs()) { - index1 = i; - } - else { - SASSERT(index2 == num_ineqs()); - index2 = i; - } - } - } - for (unsigned i = 0; i < num_ineqs(); ++i) { - if (ineq_coeff(i).is_pos() != is_pos) { - SASSERT(index1 != num_ineqs()); - mk_lt(model, lits, i, index1); - if (index2 != num_ineqs()) { - mk_lt(model, lits, i, index2); - } - } - } - } - else { - expr_ref t(m); - bool use_pos = m_num_pos < m_num_neg; - unsigned max_t = find_max(model, use_pos); - - for (unsigned i = 0; i < num_ineqs(); ++i) { - if (i != max_t) { - if (ineq_coeff(i).is_pos() == use_pos) { - t = mk_le(i, max_t); - add_lit(model, lits, t); - } - else { - mk_lt(model, lits, i, max_t); - } - } - } - } - TRACE("qe", tout << lits;); - } - - unsigned find_max(model& mdl, bool do_pos) { - unsigned result; - bool new_max = true; - rational max_r, r; - expr_ref val(m); - model_evaluator eval(mdl); - eval.set_model_completion(true); - - bool is_int = a.is_int(m_var->x()); - for (unsigned i = 0; i < num_ineqs(); ++i) { - rational const& ac = m_ineq_coeffs[i]; - SASSERT(!is_int || opt::t_le == ineq_ty(i)); - - // - // ac*x + t < 0 - // ac > 0: x + max { t/ac | ac > 0 } < 0 <=> x < - max { t/ac | ac > 0 } - // ac < 0: x + t/ac > 0 <=> x > max { - t/ac | ac < 0 } = max { t/|ac| | ac < 0 } - // - if (ac.is_pos() == do_pos) { - eval(ineq_term(i), val); - VERIFY(a.is_numeral(val, r)); - r /= abs(ac); - new_max = - new_max || - (r > max_r) || - (r == max_r && opt::t_lt == ineq_ty(i)) || - (r == max_r && is_int && ac.is_one()); - TRACE("qe", tout << "max: " << mk_pp(ineq_term(i), m) << "/" << abs(ac) << " := " << r << " " - << (new_max?"":"not ") << "new max\n";); - if (new_max) { - result = i; - max_r = r; - } - new_max = false; - } - } - SASSERT(!new_max); - 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 - void mk_lt(model& model, expr_ref_vector& lits, unsigned i, unsigned j) { - rational const& ac = ineq_coeff(i); - rational const& bc = ineq_coeff(j); - SASSERT(ac.is_pos() != bc.is_pos()); - SASSERT(ac.is_neg() != bc.is_neg()); - - TRACE("qe", display_ineq(tout, i); display_ineq(tout, j);); - - if (is_int() && !abs(ac).is_one() && !abs(bc).is_one()) { - return mk_int_lt(model, lits, i, j); - } - expr* t = ineq_term(i); - expr* s = ineq_term(j); - expr_ref bt = mk_mul(abs(bc), t); - expr_ref as = mk_mul(abs(ac), s); - expr_ref ts = mk_add(bt, as); - expr_ref z = mk_num(0); - expr_ref fml(m); - if (opt::t_lt == ineq_ty(i) || opt::t_lt == ineq_ty(j)) { - fml = a.mk_lt(ts, z); - } - else { - fml = a.mk_le(ts, z); - } - add_lit(model, lits, fml); - } - - void mk_int_lt(model& model, expr_ref_vector& lits, unsigned i, unsigned j) { - TRACE("qe", display_ineq(tout, i); display_ineq(tout, j);); - - expr* t = ineq_term(i); - expr* s = ineq_term(j); - rational ac = ineq_coeff(i); - rational bc = ineq_coeff(j); - expr_ref tmp(m); - SASSERT(opt::t_le == ineq_ty(i) && opt::t_le == ineq_ty(j)); - SASSERT(ac.is_pos() == bc.is_neg()); - rational abs_a = abs(ac); - rational abs_b = abs(bc); - expr_ref as(mk_mul(abs_a, s), m); - expr_ref bt(mk_mul(abs_b, t), m); - - rational slack = (abs_a - rational(1))*(abs_b-rational(1)); - rational sval, tval; - VERIFY (model.eval(ineq_term(i), tmp) && a.is_numeral(tmp, tval)); - VERIFY (model.eval(ineq_term(j), tmp) && a.is_numeral(tmp, sval)); - bool use_case1 = ac*sval + bc*tval + slack <= rational(0); - if (use_case1) { - expr_ref_vector ts(m); - ts.push_back(as); - ts.push_back(bt); - ts.push_back(mk_num(-slack)); - tmp = a.mk_le(add(ts), mk_num(0)); - add_lit(model, lits, tmp); - return; - } - - if (abs_a < abs_b) { - std::swap(abs_a, abs_b); - std::swap(ac, bc); - std::swap(s, t); - std::swap(as, bt); - std::swap(sval, tval); - } - 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 - // - - rational z = mod(sval, abs_b); - if (!z.is_zero()) z = abs_b - z; - expr_ref s_plus_z(mk_add(z, s), m); - - tmp = mk_divides(abs_b, s_plus_z); - add_lit(model, lits, tmp); - tmp = a.mk_le(mk_add(mk_mul(ac*n_sign(bc), s_plus_z), - mk_mul(abs_b, t)), mk_num(0)); - add_lit(model, lits, tmp); - } - rational n_sign(rational const& b) { return rational(b.is_pos()?-1:1); } - // 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 = ineq_coeff(i); - rational const& bc = ineq_coeff(j); - SASSERT(ac.is_pos() == bc.is_pos()); - SASSERT(ac.is_neg() == bc.is_neg()); - expr* t = ineq_term(i); - expr* s = ineq_term(j); - expr_ref bt = mk_mul(abs(bc), t); - expr_ref as = mk_mul(abs(ac), s); - if (opt::t_lt == ineq_ty(i) && opt::t_le == ineq_ty(j)) { - return expr_ref(a.mk_lt(bt, as), m); - } - else { - return expr_ref(a.mk_le(bt, as), m); - } - } - - expr_ref mk_add(expr* t1, expr* t2) { - rational r; - if (a.is_numeral(t1, r) && r.is_zero()) return expr_ref(t2, m); - if (a.is_numeral(t2, r) && r.is_zero()) return expr_ref(t1, m); - return expr_ref(a.mk_add(t1, t2), m); - } - expr_ref mk_add(rational const& r, expr* e) { - if (r.is_zero()) return expr_ref(e, m); - return mk_add(mk_num(r), e); - } - - expr_ref mk_mul(rational const& r, expr* t) { - if (r.is_one()) return expr_ref(t, m); - return expr_ref(a.mk_mul(mk_num(r), t), m); - } - - expr_ref mk_sub(expr* t1, expr* t2) { - rational r1, r2; - if (a.is_numeral(t2, r2) && r2.is_zero()) return expr_ref(t1, m); - if (a.is_numeral(t1, r1) && a.is_numeral(t2, r2)) return mk_num(r1 - r2); - return expr_ref(a.mk_sub(t1, t2), m); - } - - expr_ref mk_uminus(expr* t) { - rational r; - if (a.is_numeral(t, r)) { - return mk_num(-r); - } - return expr_ref(a.mk_uminus(t), m); - } - - void add_lit(model& model, expr_ref_vector& lits, expr* e) { - expr_ref orig(e, m), result(m); - m_rw(orig, result); - TRACE("qe", tout << mk_pp(orig, m) << " -> " << result << "\n";); - SASSERT(lit_is_true(model, orig)); - SASSERT(lit_is_true(model, result)); - if (!m.is_true(result)) { - lits.push_back(result); - } - } - - - // 3x + t = 0 & 7 | (c*x + s) & ax <= u - // 3 | -t & 21 | (-ct + 3s) & a-t <= 3u - - void apply_equality(model& model, unsigned eq_index, expr_ref_vector& lits) { - rational c = ineq_coeff(eq_index); - expr* t = ineq_term(eq_index); - SASSERT(c.is_pos()); - if (is_int()) { - add_lit(model, lits, mk_divides(c, ineq_term(eq_index))); - } - - for (unsigned i = 0; i < num_divs(); ++i) { - add_lit(model, lits, mk_divides(c*div_divisor(i), - mk_sub(mk_mul(c, div_term(i)), mk_mul(div_coeff(i), t)))); - } - for (unsigned i = 0; i < num_ineqs(); ++i) { - if (eq_index != i) { - expr_ref lhs(m), val(m); - lhs = mk_sub(mk_mul(c, ineq_term(i)), mk_mul(ineq_coeff(i), t)); - switch (ineq_ty(i)) { - case opt::t_lt: lhs = a.mk_lt(lhs, mk_num(0)); break; - case opt::t_le: lhs = a.mk_le(lhs, mk_num(0)); break; - case opt::t_eq: lhs = m.mk_eq(lhs, mk_num(0)); break; - } - TRACE("qe", tout << lhs << "\n";); - SASSERT (model.eval(lhs, val) && m.is_true(val)); - add_lit(model, lits, lhs); - } - } - } - - // - // compute D and u. - // - // D = lcm(d1, d2) - // u = eval(x) mod D - // - // d1 | (a1x + t1) & d2 | (a2x + t2) - // = - // D | (D/d1)(a1x + t1) & D | (D/d2)(a2x + t2) - // = - // D | D1(a1*u + t1) & D | D2(a2*u + t2) & x = D*x' + u & 0 <= u < D - // = - // D | D1(a1*u + t1) & D | D2(a2*u + t2) & x = D*x' + u & 0 <= u < D - // - // x := D*x' + u - // - void apply_divides(model& model, expr_ref_vector& lits) { - SASSERT(m_delta.is_one()); - unsigned n = num_divs(); - if (n == 0) { - return; - } - for (unsigned i = 0; i < n; ++i) { - m_delta = lcm(m_delta, div_divisor(i)); - } - expr_ref val(m); - rational r; - VERIFY (model.eval(m_var->x(), val) && a.is_numeral(val, r)); - m_u = mod(r, m_delta); - SASSERT(m_u < m_delta && rational(0) <= m_u); - for (unsigned i = 0; i < n; ++i) { - add_lit(model, lits, mk_divides(div_divisor(i), - mk_add(mk_num(div_coeff(i) * m_u), div_term(i)))); - } - reset_divs(); - // - // 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 - for (unsigned i = 0; i < num_ineqs(); ++i) { - if (!m_u.is_zero()) { - m_ineq_terms[i] = a.mk_add(ineq_term(i), mk_num(m_ineq_coeffs[i]*m_u)); - } - m_ineq_coeffs[i] *= m_delta; - } - r = (r - m_u) / m_delta; - SASSERT(r.is_int()); - val = a.mk_numeral(r, true); - model.register_decl(m_var->x()->get_decl(), val); - TRACE("qe", model_v2_pp(tout, model);); - } - imp(ast_manager& m): - m(m), a(m), m_rw(m), m_ineq_terms(m), m_div_terms(m), m_new_lits(m), m_trail(m) { - params_ref params; - params.set_bool("gcd_rouding", true); - m_rw.updt_params(params); - } + m(m), a(m) {} - ~imp() { - } + ~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) { - SASSERT(a.is_real(v) || a.is_int(v)); - m_var = alloc(contains_app, m, v); - try { - project(model, lits); - } - catch (cant_project) { - TRACE("qe", tout << "can't project:" << mk_pp(v, m) << "\n";); - return false; - } - return true; + app_ref_vector vs(m); + vs.push_back(v); + (*this)(model, vs, lits); + return vs.empty(); } typedef opt::model_based_opt::var var; typedef opt::model_based_opt::row row; typedef vector vars; - void operator()(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { - bool has_real = false; - for (unsigned i = 0; !has_real && i < vars.size(); ++i) { - has_real = a.is_real(vars[i].get()); + bool has_arith = false; + for (unsigned i = 0; !has_arith && i < vars.size(); ++i) { + expr* v = vars[i].get(); + has_arith |= is_arith(v); } - if (!has_real) { + if (!has_arith) { return; } + model_evaluator eval(model); + // eval.set_model_completion(true); opt::model_based_opt mbo; obj_map tids; - m_trail.reset(); unsigned j = 0; for (unsigned i = 0; i < fmls.size(); ++i) { - if (!linearize(mbo, model, fmls[i].get(), fmls, tids)) { + expr* fml = fmls[i].get(); + if (!linearize(mbo, eval, fml, fmls, tids)) { if (i != j) { fmls[j] = fmls[i].get(); } ++j; } + else { + TRACE("qe", tout << mk_pp(fml, m) << "\n";); + } } fmls.resize(j); @@ -1033,8 +324,12 @@ namespace qe { for (unsigned i = 0; i < vars.size(); ++i) { app* v = vars[i].get(); var_mark.mark(v); - if (a.is_real(v) && !tids.contains(v)) { - tids.insert(v, tids.size()); + if (is_arith(v) && !tids.contains(v)) { + rational r; + expr_ref val = eval(v); + 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))); } } for (unsigned i = 0; i < fmls.size(); ++i) { @@ -1053,7 +348,7 @@ namespace qe { unsigned_vector real_vars; for (unsigned i = 0; i < vars.size(); ++i) { app* v = vars[i].get(); - if (a.is_real(v) && !fmls_mark.is_marked(v)) { + if (is_arith(v) && !fmls_mark.is_marked(v)) { real_vars.push_back(tids.find(v)); } else { @@ -1064,26 +359,51 @@ namespace qe { } } vars.resize(j); + TRACE("qe", tout << "remaining vars: " << vars << "\n"; + for (unsigned i = 0; i < real_vars.size(); ++i) { + unsigned v = real_vars[i]; + tout << "v" << v << " " << mk_pp(index2expr[v], m) << "\n"; + } + mbo.display(tout);); mbo.project(real_vars.size(), real_vars.c_ptr()); + TRACE("qe", mbo.display(tout);); vector rows; mbo.get_live_rows(rows); for (unsigned i = 0; i < rows.size(); ++i) { expr_ref_vector ts(m); - expr_ref t(m), s(m); + expr_ref t(m), s(m), val(m); row const& r = rows[i]; + if (r.m_vars.size() == 0) { + 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 (j = 0; j < r.m_vars.size(); ++j) { var const& v = r.m_vars[j]; t = index2expr[v.m_id]; if (!v.m_coeff.is_one()) { - t = a.mk_mul(t, a.mk_numeral(v.m_coeff, v.m_coeff.is_int())); + t = a.mk_mul(a.mk_numeral(v.m_coeff, a.is_int(t)), t); } ts.push_back(t); } - if (ts.empty()) { - continue; - } - s = a.mk_numeral(-r.m_coeff, r.m_coeff.is_int()); + s = a.mk_numeral(-r.m_coeff, a.is_int(t)); if (ts.size() == 1) { t = ts[0].get(); } @@ -1094,41 +414,48 @@ namespace qe { 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";); + } } - opt::inf_eps maximize(expr_ref_vector const& fmls0, model& mdl, app* t, expr_ref& bound) { - m_trail.reset(); + 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, mdl, mul, t, c, fmls, ts, tids); - extract_coefficients(mbo, mdl, ts, tids, coeffs); + 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 (unsigned i = 0; i < fmls.size(); ++i) { - linearize(mbo, mdl, fmls[i].get(), fmls, tids); + linearize(mbo, eval, fmls[i].get(), fmls, tids); } // find optimal value value = mbo.maximize(); - expr_ref val(a.mk_numeral(value.get_rational(), false), m); - if (!value.is_finite()) { - bound = m.mk_false(); - return value; - } // update model to use new values that satisfy optimality ptr_vector vars; @@ -1145,36 +472,58 @@ namespace qe { 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. - if (value.get_infinitesimal().is_neg()) { - bound = a.mk_le(val, 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 { - bound = a.mk_lt(val, t); - } + ge = a.mk_ge(t, val); + gt = a.mk_gt(t, val); + } + SASSERT(validate_model(eval, fmls0)); return value; } - void extract_coefficients(opt::model_based_opt& mbo, model& model, obj_map const& ts, obj_map& tids, vars& coeffs) { + 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); obj_map::iterator it = ts.begin(), end = ts.end(); for (; it != end; ++it) { unsigned id; - if (!tids.find(it->m_key, id)) { + expr* v = it->m_key; + if (!tids.find(v, id)) { rational r; - expr_ref val(m); - if (model.eval(it->m_key, val) && a.is_numeral(val, r)) { - id = mbo.add_var(r); - } - else { - TRACE("qe", tout << "extraction of coefficients cancelled\n";); - return; - } - tids.insert(it->m_key, id); - m_trail.push_back(it->m_key); + 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", it->m_value.is_zero(), tout << mk_pp(v, m) << " has coefficeint 0\n";); + if (!it->m_value.is_zero()) { + coeffs.push_back(var(id, it->m_value)); } - coeffs.push_back(var(id, it->m_value)); } } @@ -1204,8 +553,8 @@ namespace qe { 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& bound) { - return m_imp->maximize(fmls, mdl, t, bound); + 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); } bool arith_project(model& model, app* var, expr_ref_vector& lits) { diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h index f71156b1e..616d1a8d0 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -32,13 +32,11 @@ namespace qe { virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits); - opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound); + opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt); }; bool arith_project(model& model, app* var, expr_ref_vector& lits); - // match e := t mod k = 0. - bool is_divides(arith_util& a, expr* e, rational& k, expr_ref& t); }; diff --git a/src/qe/qe_arith_plugin.cpp b/src/qe/qe_arith_plugin.cpp index 38861df65..4281ec909 100644 --- a/src/qe/qe_arith_plugin.cpp +++ b/src/qe/qe_arith_plugin.cpp @@ -36,6 +36,27 @@ Revision History: 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; @@ -2445,7 +2466,7 @@ public: } virtual void assign(contains_app& x, expr* fml, rational const& vl) { - nlarith::branch_conditions *brs; + nlarith::branch_conditions *brs = 0; VERIFY (m_cache.find(x.x(), fml, brs)); SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < brs->size()); diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index f8f44b6d9..a010c4ae4 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -110,7 +110,7 @@ namespace qe { imp(ast_manager& m): m(m), a(m) {} ~imp() {} - virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return false; } diff --git a/src/qe/qe_datatype_plugin.cpp b/src/qe/qe_datatype_plugin.cpp index 088d2252d..78562cf00 100644 --- a/src/qe/qe_datatype_plugin.cpp +++ b/src/qe/qe_datatype_plugin.cpp @@ -792,9 +792,8 @@ namespace qe { TRACE("qe", tout << mk_pp(x.x(), m) << " has a recognizer\n";); } else { - unsigned sz = m_datatype_util.get_datatype_num_constructors(s); SASSERT(vl.is_unsigned()); - SASSERT(vl.get_unsigned() < sz); + SASSERT(vl.get_unsigned() < m_datatype_util.get_datatype_num_constructors(s)); c = (*m_datatype_util.get_datatype_constructors(s))[vl.get_unsigned()]; } subst_constructor(x, c, fml, def); diff --git a/src/qe/qe_datatypes.cpp b/src/qe/qe_datatypes.cpp index aa67d28a3..8536e337f 100644 --- a/src/qe/qe_datatypes.cpp +++ b/src/qe/qe_datatypes.cpp @@ -37,7 +37,7 @@ namespace qe { imp(ast_manager& m): m(m), dt(m), m_val(m) {} - virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return lift_foreign(vars, lits); } diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index f28a93753..2b73381a9 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -1667,7 +1667,7 @@ namespace fm { sbuffer xs; buffer as; rational c; - bool strict; + bool strict = false; unsigned num; expr * const * args; if (m.is_or(f)) { diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 2bacb3a4f..f4c0c9339 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -29,6 +29,7 @@ Revision History: #include "model_v2_pp.h" #include "expr_functors.h" #include "for_each_expr.h" +#include "model_evaluator.h" using namespace qe; @@ -125,6 +126,7 @@ class mbp::impl { th_rewriter m_rw; ptr_vector m_plugins; expr_mark m_visited; + expr_mark m_bool_visited; void add_plugin(project_plugin* p) { family_id fid = p->get_family_id(); @@ -212,10 +214,12 @@ class mbp::impl { } } - - void extract_bools(model& model, expr_ref_vector& fmls, expr* fml) { + 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()); } @@ -226,16 +230,16 @@ class mbp::impl { continue; } m_visited.mark(e); - if (m.is_bool(e)) { - expr_ref val(m); - VERIFY(model.eval(e, val)); + 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";); - if (m.is_true(val)) { - fmls.push_back(e); - } - else { - fmls.push_back(mk_not(m, e)); + 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()); @@ -244,6 +248,14 @@ class mbp::impl { 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& model, app_ref_vector& vars, expr_ref_vector& fmls) { @@ -270,6 +282,8 @@ class mbp::impl { sub(fmls[i].get(), val); m_rw(val); if (!m.is_true(val)) { + TRACE("qe", tout << mk_pp(fmls[i].get(), m) << " -> " << val << "\n";); + fmls[i] = val; if (j != i) { fmls[j] = fmls[i].get(); } @@ -285,13 +299,15 @@ class mbp::impl { public: - opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound) { + 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, bound); + 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)); @@ -301,7 +317,7 @@ public: } else if (m.is_or(fml)) { for (unsigned j = 0; j < to_app(fml)->get_num_args(); ++j) { - VERIFY (model.eval(to_app(fml)->get_arg(j), val)); + val = eval(to_app(fml)->get_arg(j)); if (m.is_true(val)) { fmls[i] = to_app(fml)->get_arg(j); --i; @@ -314,7 +330,7 @@ public: project_plugin::erase(fmls, i); } else if (m.is_iff(fml, f1, f2) || (m.is_not(fml, nfml) && m.is_xor(nfml, f1, f2))) { - VERIFY (model.eval(f1, val)); + val = eval(f1); if (m.is_false(val)) { f1 = mk_not(m, f1); f2 = mk_not(m, f2); @@ -324,7 +340,7 @@ public: --i; } else if (m.is_implies(fml, f1, f2)) { - VERIFY (model.eval(f2, val)); + val = eval(f2); if (m.is_true(val)) { fmls[i] = f2; } @@ -334,7 +350,7 @@ public: --i; } else if (m.is_ite(fml, f1, f2, f3)) { - VERIFY (model.eval(f1, val)); + val = eval(f1); if (m.is_true(val)) { project_plugin::push_back(fmls, f1); project_plugin::push_back(fmls, f2); @@ -351,7 +367,7 @@ public: } else if (m.is_not(fml, nfml) && m.is_and(nfml)) { for (unsigned j = 0; j < to_app(nfml)->get_num_args(); ++j) { - VERIFY (model.eval(to_app(nfml)->get_arg(j), val)); + 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; @@ -366,7 +382,7 @@ public: project_plugin::erase(fmls, i); } else if ((m.is_not(fml, nfml) && m.is_iff(nfml, f1, f2)) || m.is_xor(fml, f1, f2)) { - VERIFY (model.eval(f1, val)); + val = eval(f1); if (m.is_true(val)) { f2 = mk_not(m, f2); } @@ -383,7 +399,7 @@ public: project_plugin::erase(fmls, i); } else if (m.is_not(fml, nfml) && m.is_ite(nfml, f1, f2, f3)) { - VERIFY (model.eval(f1, val)); + val = eval(f1); if (m.is_true(val)) { project_plugin::push_back(fmls, f1); project_plugin::push_back(fmls, mk_not(m, f2)); @@ -395,14 +411,19 @@ public: project_plugin::erase(fmls, i); } else if (m.is_not(fml, nfml)) { - extract_bools(model, fmls, nfml); + if (extract_bools(eval, fmls, nfml)) { + project_plugin::erase(fmls, i); + } } else { - extract_bools(model, fmls, fml); + if (extract_bools(eval, fmls, fml)) { + project_plugin::erase(fmls, i); + } // TBD other Boolean operations. } } - m_visited.reset(); + TRACE("qe", tout << fmls << "\n";); + m_bool_visited.reset(); } impl(ast_manager& m):m(m), m_rw(m) { @@ -428,7 +449,16 @@ public: } } + bool validate_model(model& model, expr_ref_vector const& fmls) { + expr_ref val(m); + for (unsigned i = 0; i < fmls.size(); ++i) { + VERIFY(model.eval(fmls[i], val) && m.is_true(val)); + } + 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); @@ -445,7 +475,7 @@ public: (*p)(model, vars, fmls); } } - while (!vars.empty() && !fmls.empty()) { + while (!vars.empty() && !fmls.empty()) { var = vars.back(); vars.pop_back(); project_plugin* p = get_plugin(var); @@ -483,6 +513,7 @@ public: vars.reset(); } fmls.append(unused_fmls); + SASSERT(validate_model(model, fmls)); TRACE("qe", tout << vars << " " << fmls << "\n";); } @@ -508,6 +539,6 @@ void mbp::extract_literals(model& model, expr_ref_vector& lits) { m_impl->extract_literals(model, lits); } -opt::inf_eps mbp::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound) { - return m_impl->maximize(fmls, mdl, t, bound); +opt::inf_eps mbp::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { + return m_impl->maximize(fmls, mdl, t, ge, gt); } diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index 6c28555b0..b195d3a35 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -79,7 +79,7 @@ namespace qe { \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& bound); + opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt); }; } diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index a9ca9e99d..841bdde9b 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -34,7 +34,9 @@ Notes: #include "expr_replacer.h" #include "th_rewriter.h" #include "model_evaluator.h" - +#include "smt_solver.h" +#include "solver.h" +#include "mus.h" namespace qe { @@ -156,6 +158,8 @@ namespace qe { 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) { @@ -167,7 +171,7 @@ namespace qe { if (m.is_false(val)) { m_asms.push_back(m.mk_not(p)); } - else { + else { SASSERT(m.is_true(val)); m_asms.push_back(p); } @@ -505,36 +509,57 @@ namespace qe { } } + bool pred_abs::validate_defs(model& model) const { + bool valid = true; + obj_map::iterator it = m_pred2lit.begin(), end = m_pred2lit.end(); + for (; it != end; ++it) { + expr_ref val_a(m), val_b(m); + expr* a = it->m_key; + expr* b = it->m_value; + VERIFY(model.eval(a, val_a)); + VERIFY(model.eval(b, val_b)); + if (val_a != 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 { - smt_params m_smtp; - smt::kernel m_kernel; + ast_manager& m; + params_ref m_params; + ref m_solver; public: kernel(ast_manager& m): - m_kernel(m, m_smtp) + m(m), + m_solver(mk_smt_solver(m, m_params, symbol::null)) { - m_smtp.m_model = true; - m_smtp.m_relevancy_lvl = 0; - m_smtp.m_case_split_strategy = CS_ACTIVITY_WITH_CACHE; + 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); } - smt::kernel& k() { return m_kernel; } - smt::kernel const& k() const { return m_kernel; } + solver& s() { return *m_solver; } + solver const& s() const { return *m_solver; } + + void reset() { + m_solver = mk_smt_solver(m, m_params, symbol::null); + } void assert_expr(expr* e) { - m_kernel.assert_expr(e); + m_solver->assert_expr(e); } void get_core(expr_ref_vector& core) { - unsigned sz = m_kernel.get_unsat_core_size(); core.reset(); - for (unsigned i = 0; i < sz; ++i) { - core.push_back(m_kernel.get_unsat_core_expr(i)); - } - TRACE("qe", tout << "core: " << core << "\n"; - m_kernel.display(tout); - tout << "\n"; - ); + m_solver->get_unsat_core(core); + TRACE("qe", m_solver->display(tout << "core: " << core << "\n") << "\n";); } }; @@ -572,6 +597,9 @@ namespace qe { 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; /** @@ -585,23 +613,35 @@ namespace qe { 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";); - smt::kernel& k = get_kernel(m_level).k(); - lbool res = k.check(asms); + solver& s = get_kernel(m_level).s(); + lbool res = s.check_sat(asms); switch (res) { case l_true: - k.get_model(m_model); + s.get_model(m_model); + SASSERT(validate_defs("check_sat")); + SASSERT(validate_assumptions(*m_model.get(), asms)); SASSERT(validate_model(asms)); - TRACE("qe", k.display(tout); display(tout << "\n", *m_model.get()); display(tout, asms); ); + TRACE("qe", s.display(tout); display(tout << "\n", *m_model.get()); display(tout, asms); ); push(); + if (m_level == 1 && m_mode == qsat_maximize) { + maximize_model(); + } break; case l_false: switch (m_level) { - case 0: return l_false; + case 0: + return l_false; case 1: - if (m_mode == qsat_sat) return l_true; + if (m_mode == qsat_sat) { + return l_true; + } if (m_model.get()) { - project_qe(asms); + SASSERT(validate_assumptions(*m_model.get(), asms)); + if (!project_qe(asms)) return l_undef; } else { pop(1); @@ -609,7 +649,7 @@ namespace qe { break; default: if (m_model.get()) { - project(asms); + if (!project(asms)) return l_undef; } else { pop(1); @@ -655,8 +695,8 @@ namespace qe { void reset() { m_st.reset(); - m_fa.k().collect_statistics(m_st); - m_ex.k().collect_statistics(m_st); + 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(); @@ -664,8 +704,8 @@ namespace qe { m_pred_abs.reset(); m_vars.reset(); m_model = 0; - m_fa.k().reset(); - m_ex.k().reset(); + m_fa.reset(); + m_ex.reset(); m_free_vars.reset(); } @@ -728,9 +768,59 @@ namespace qe { } } - void get_core(expr_ref_vector& core, unsigned level) { + 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() { @@ -768,37 +858,40 @@ namespace qe { m_pred_abs.set_expr_level(b, lvl); } - void project_qe(expr_ref_vector& core) { + bool project_qe(expr_ref_vector& core) { SASSERT(m_level == 1); expr_ref fml(m); model& mdl = *m_model.get(); - get_core(core, m_level); - SASSERT(validate_core(core)); + 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, mdl); - pop(1); + 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); } + pop(1); + return true; } - void project(expr_ref_vector& core) { - get_core(core, m_level); + bool project(expr_ref_vector& core) { + if (!get_core(core, m_level)) return false; TRACE("qe", display(tout); display(tout << "core\n", core);); - SASSERT(validate_core(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); @@ -816,6 +909,7 @@ namespace qe { 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); @@ -833,6 +927,8 @@ namespace qe { 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) { @@ -962,13 +1058,28 @@ namespace qe { } } - bool validate_core(expr_ref_vector const& core) { + bool validate_assumptions(model& mdl, expr_ref_vector const& core) { + for (unsigned i = 0; i < core.size(); ++i) { + expr_ref val(m); + VERIFY(mdl.eval(core[i], val)); + if (!m.is_true(val)) { + TRACE("qe", tout << "component of core is not true: " << mk_pp(core[i], 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";); - smt::kernel& k = get_kernel(m_level).k(); + solver& s = get_kernel(m_level).s(); expr_ref_vector fmls(m); fmls.append(core.size(), core.c_ptr()); - fmls.append(k.size(), k.get_formulas()); + s.get_assertions(fmls); return check_fmls(fmls) || m.canceled(); +#endif } bool check_fmls(expr_ref_vector const& fmls) { @@ -984,11 +1095,16 @@ namespace qe { } bool validate_model(expr_ref_vector const& asms) { + return true; +#if 0 TRACE("qe", tout << "Validate model\n";); - smt::kernel& k = get_kernel(m_level).k(); + 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, k.size(), k.get_formulas()); + validate_model(*m_model, fmls.size(), fmls.c_ptr()); +#endif } bool validate_model(model& mdl, unsigned sz, expr* const* fmls) { @@ -1014,6 +1130,8 @@ namespace qe { // (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; @@ -1057,6 +1175,7 @@ namespace qe { TRACE("qe", tout << "implication check failed, could be due to turning != into >\n";); } return true; +#endif } @@ -1076,7 +1195,8 @@ namespace qe { m_free_vars(m), m_objective(0), m_value(0), - m_was_sat(false) + m_was_sat(false), + m_gt(m) { reset(); } @@ -1126,7 +1246,6 @@ namespace qe { fml = push_not(fml); } hoist(fml); -// hoist_ite(fml); redundant provided theories understand to deal with ite terms. m_pred_abs.abstract_atoms(fml, defs); fml = m_pred_abs.mk_abstract(fml); m_ex.assert_expr(mk_and(defs)); @@ -1161,9 +1280,9 @@ namespace qe { break; case l_undef: result.push_back(in.get()); - std::string s = m_ex.k().last_failure_as_string(); - if (s == "ok") { - s = m_fa.k().last_failure_as_string(); + std::string s = m_ex.s().reason_unknown(); + if (s == "ok" || s == "unknown") { + s = m_fa.s().reason_unknown(); } throw tactic_exception(s.c_str()); } @@ -1171,8 +1290,8 @@ namespace qe { void collect_statistics(statistics & st) const { st.copy(m_st); - m_fa.k().collect_statistics(st); - m_ex.k().collect_statistics(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); @@ -1180,8 +1299,8 @@ namespace qe { void reset_statistics() { m_stats.reset(); - m_fa.k().reset_statistics(); - m_ex.k().reset_statistics(); + m_fa.reset(); + m_ex.reset(); } void cleanup() { @@ -1205,6 +1324,7 @@ namespace qe { 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)); @@ -1218,14 +1338,15 @@ namespace qe { 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.k().last_failure_as_string(); + std::string s = m_ex.s().reason_unknown(); if (s == "ok") { - s = m_fa.k().last_failure_as_string(); + s = m_fa.s().reason_unknown(); } throw tactic_exception(s.c_str()); @@ -1233,18 +1354,50 @@ namespace qe { return l_true; } - void maximize(expr_ref_vector const& core, model& mdl) { + 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_mbp.maximize(core, mdl, m_objective, bound); - IF_VERBOSE(0, verbose_stream() << "(maximize " << *m_value << " bound: " << bound << ")\n";); - m_ex.assert_expr(bound); + *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")); + } }; @@ -1276,8 +1429,7 @@ namespace qe { 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) { diff --git a/src/qe/qsat.h b/src/qe/qsat.h index 66676da05..b6d21db1e 100644 --- a/src/qe/qsat.h +++ b/src/qe/qsat.h @@ -112,6 +112,8 @@ namespace qe { 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 { diff --git a/src/sat/sat_bceq.cpp b/src/sat/sat_bceq.cpp deleted file mode 100644 index 9ff286aaa..000000000 --- a/src/sat/sat_bceq.cpp +++ /dev/null @@ -1,530 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - sat_bceq.cpp - -Abstract: - - Find equivalent literals based on blocked clause decomposition. - -Author: - - Nikolaj Bjorner (nbjorner) 2014-09-27. - - -Revision History: - ---*/ -#include"sat_bceq.h" -#include"sat_solver.h" -#include"trace.h" -#include"bit_vector.h" -#include"map.h" -#include"sat_elim_eqs.h" - -namespace sat { - - void bceq::use_list::init(unsigned num_vars) { - m_clauses.reset(); - m_clauses.resize(2*num_vars); - } - - void bceq::use_list::insert(clause& c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - m_clauses[c[i].index()].push_back(&c); - } - } - - void bceq::use_list::erase(clause& c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - m_clauses[c[i].index()].erase(&c); - } - } - - ptr_vector& bceq::use_list::get(literal lit) { - return m_clauses[lit.index()]; - } - - bceq::bceq(solver & s): - m_solver(s) { - } - - void bceq::register_clause(clause* cls) { - m_clauses.setx(cls->id(), cls, 0); - } - - void bceq::unregister_clause(clause* cls) { - m_clauses.setx(cls->id(), 0, 0); - } - - void bceq::init() { - m_clauses.reset(); - m_bin_clauses.reset(); - m_L.reset(); - m_R.reset(); - m_L_blits.reset(); - m_R_blits.reset(); - m_bce_use_list.reset(); - clause * const* it = m_solver.begin_clauses(); - clause * const* end = m_solver.end_clauses(); - for (; it != end; ++it) { - clause* cls = *it; - if (!cls->was_removed()) { - m_use_list->insert(*cls); - register_clause(cls); - } - } - bin_clauses bc; - m_solver.collect_bin_clauses(bc, false); // exclude roots. - literal lits[2]; - for (unsigned i = 0; i < bc.size(); ++i) { - lits[0] = bc[i].first; - lits[1] = bc[i].second; - clause* cls = m_solver.m_cls_allocator.mk_clause(2, lits, false); - m_use_list->insert(*cls); - m_bin_clauses.push_back(cls); - register_clause(cls); - } - TRACE("sat", - for (unsigned i = 0; i < m_clauses.size(); ++i) { - clause const* cls = m_clauses[i]; - if (cls) tout << *cls << "\n"; - }); - } - - void bceq::pure_decompose() { - // while F != empty - // pick a clause and variable x in clause. - // get use list U1 of x and U2 of ~x - // assume |U1| >= |U2| - // add U1 to clause set. - for (unsigned i = 0; i < m_clauses.size(); ++i) { - clause* cls = m_clauses[i]; - if (cls) { - SASSERT(i == cls->id()); - pure_decompose((*cls)[0]); - SASSERT(!m_clauses[i]); - } - } - m_L.reverse(); - m_L_blits.reverse(); - } - - void bceq::pure_decompose(literal lit) { - clause_use_list& pos = m_use_list->get(lit); - clause_use_list& neg = m_use_list->get(~lit); - unsigned sz1 = m_L.size(); - unsigned sz2 = m_R.size(); - pure_decompose(pos, m_L); - pure_decompose(neg, m_R); - unsigned delta1 = m_L.size() - sz1; - unsigned delta2 = m_R.size() - sz2; - if (delta1 < delta2) { - m_L_blits.resize(sz1+delta2, ~lit); - m_R_blits.resize(sz2+delta1, lit); - for (unsigned i = 0; i < delta1; ++i) { - std::swap(m_L[sz1 + i], m_R[sz2 + i]); - } - for (unsigned i = delta1; i < delta2; ++i) { - m_L.push_back(m_R[sz2 + i]); - } - m_R.resize(sz2 + delta1); - std::swap(delta1, delta2); - } - else { - m_L_blits.resize(sz1+delta1, lit); - m_R_blits.resize(sz2+delta2, ~lit); - } - TRACE("bceq", tout << lit << " " << "pos: " << delta1 << " " << "neg: " << delta2 << "\n";); - } - - void bceq::pure_decompose(clause_use_list& uses, svector& clauses) { - unsigned sz = uses.size(); - for (unsigned i = 0; i < sz; ++i) { - clause& cls = *uses[i]; - if (!cls.was_removed() && m_clauses[cls.id()]) { - clauses.push_back(&cls); - m_clauses[cls.id()] = 0; - } - } - } - - void bceq::post_decompose() { - m_marked.reset(); - m_marked.resize(2*m_solver.num_vars(), false); - use_list ul; - use_list* save = m_use_list; - m_use_list = &ul; - ul.init(m_solver.num_vars()); - for (unsigned i = 0; i < m_L.size(); ++i) { - ul.insert(*m_L[i]); - } - - // cheap pass: add clauses from R in order - // such that they are blocked with respect to - // predecessors. - m_removed.reset(); - for (unsigned i = 0; i < m_R.size(); ++i) { - literal lit = find_blocked(*m_R[i]); - if (lit != null_literal) { - m_L.push_back(m_R[i]); - m_L_blits.push_back(lit); - ul.insert(*m_R[i]); - m_R[i] = m_R.back(); - m_R_blits[i] = m_R_blits.back(); - m_R.pop_back(); - m_R_blits.pop_back(); - --i; - } - } - // expensive pass: add clauses from R as long - // as BCE produces the empty set of clauses. - m_bce_use_list.init(m_solver.num_vars()); - for (unsigned i = 0; i < m_L.size(); ++i) { - m_bce_use_list.insert(*m_L[i]); - } - for (unsigned i = 0; i < m_R.size(); ++i) { - if (bce(*m_R[i])) { - m_R[i] = m_R.back(); - m_R_blits[i] = m_R_blits.back(); - m_R.pop_back(); - m_R_blits.pop_back(); - --i; - } - } - m_use_list = save; - } - - - // Note: replay blocked clause elimination: - // Suppose C u { c1 } is blocked. - // annotate each clause by blocking literal. - // for new clause c2, check if C u { c2 } is blocked. - // For each c in C record which literal it is blocked. - // (Order the clauses in C by block ordering) - // l | c is blocked, - // -> c2 contains ~l => check if c c2 is blocked - // - bool bceq::bce(clause& cls0) { - IF_VERBOSE(1, verbose_stream() << "bce " << m_L.size() << " " << m_R.size() << " " << cls0 << "\n";); - unsigned_vector& live_clauses = m_live_clauses; - live_clauses.reset(); - m_use_list = &m_bce_use_list; - m_bce_use_list.insert(cls0); - svector& clauses = m_L; - literal_vector& blits = m_L_blits; - clauses.push_back(&cls0); - blits.push_back(null_literal); - bool removed = false; - m_removed.reset(); - for (unsigned i = 0; i < clauses.size(); ++i) { - clause& cls1 = *clauses[i]; - literal lit = find_blocked(cls1); - if (lit == null_literal) { - live_clauses.push_back(i); - } - else { - m_removed.setx(cls1.id(), true, false); - removed = true; - } - } - while (removed) { - removed = false; - //std::cout << live_clauses.size() << " "; - for (unsigned i = 0; i < live_clauses.size(); ++i) { - clause& cls1 = *clauses[live_clauses[i]]; - literal lit = find_blocked(cls1); - if (lit != null_literal) { - m_removed.setx(cls1.id(), true, false); - removed = true; - live_clauses[i] = live_clauses.back(); - live_clauses.pop_back(); - --i; - } - } - } - //std::cout << "\n"; - m_bce_use_list.erase(cls0); - clauses.pop_back(); - blits.pop_back(); - return live_clauses.empty(); - } - - literal bceq::find_blocked(clause const& cls) { - TRACE("bceq", tout << cls << "\n";); - - unsigned sz = cls.size(); - for (unsigned i = 0; i < sz; ++i) { - m_marked[(~cls[i]).index()] = true; - } - literal result = null_literal; - for (unsigned i = 0; i < sz; ++i) { - literal lit = cls[i]; - if (is_blocked(lit)) { - TRACE("bceq", tout << "is blocked " << lit << " : " << cls << "\n";); - result = lit; - break; - } - } - for (unsigned i = 0; i < sz; ++i) { - m_marked[(~cls[i]).index()] = false; - } - return result; - } - - bool bceq::is_blocked(literal lit) const { - clause_use_list& uses = m_use_list->get(~lit); - unsigned sz = uses.size(); - for (unsigned i = 0; i < sz; ++i) { - clause const& cls = *uses[i]; - unsigned sz = cls.size(); - bool is_axiom = m_removed.get(cls.id(), false); - for (unsigned i = 0; !is_axiom && i < sz; ++i) { - is_axiom = m_marked[cls[i].index()] && cls[i] != ~lit; - } - - TRACE("bceq", tout << "resolvent " << lit << " : " << cls << " " << (is_axiom?"axiom":"non-axiom") << "\n";); - if (!is_axiom) { - return false; - } - } - return true; - } - - - void bceq::init_rbits() { - m_rbits.reset(); - for (unsigned i = 0; i < m_solver.num_vars(); ++i) { - uint64 lo = m_rand() + (m_rand() << 16); - uint64 hi = m_rand() + (m_rand() << 16); - m_rbits.push_back(lo + (hi << 32ULL)); - } - } - - void bceq::init_reconstruction_stack() { - m_rstack.reset(); - m_bstack.reset(); - // decomposition already creates a blocked stack in the proper order. - m_rstack.append(m_L); - m_bstack.append(m_L_blits); - } - - uint64 bceq::eval_clause(clause const& cls) const { - uint64 b = 0; - unsigned sz = cls.size(); - for (unsigned i = 0; i < sz; ++i) { - literal lit = cls[i]; - uint64 val = m_rbits[lit.var()]; - if (lit.sign()) { - val = ~val; - } - b |= val; - } - return b; - } - - void bceq::sat_sweep() { - init_rbits(); - init_reconstruction_stack(); - for (unsigned i = 0; i < m_rstack.size(); ++i) { - clause const& cls = *m_rstack[i]; - literal block_lit = m_bstack[i]; - uint64 b = eval_clause(cls); - // v = 0, b = 0 -> v := 1 - // v = 0, b = 1 -> v := 0 - // v = 1, b = 0 -> v := 0 - // v = 1, b = 1 -> v := 1 - m_rbits[block_lit.var()] ^= ~b; - - } - DEBUG_CODE(verify_sweep();); - } - - void bceq::verify_sweep() { - DEBUG_CODE( - for (unsigned i = 0; i < m_L.size(); ++i) { - uint64 b = eval_clause(*m_L[i]); - SASSERT((~b) == 0); - }); - } - - struct u64_hash { unsigned operator()(uint64 u) const { return (unsigned)u; } }; - - struct u64_eq { bool operator()(uint64 u1, uint64 u2) const { return u1 == u2; } }; - - void bceq::extract_partition() { - unsigned num_vars = m_solver.num_vars(); - map table; - union_find<> union_find(m_union_find_ctx); - for (unsigned i = 0; i < num_vars; ++i) { - m_s->mk_var(true, true); - union_find.mk_var(); - } - for (unsigned i = 0; i < m_L.size(); ++i) { - m_s->mk_clause(m_L[i]->size(), m_L[i]->begin()); - } - for (unsigned i = 0; i < num_vars; ++i) { - uint64 val = m_rbits[i]; - unsigned index; - if (table.find(val, index)) { - union_find.merge(i, index); - } - else if (table.find(~val, index)) { - union_find.merge(i, index); - } - else { - table.insert(val, i); - } - } - TRACE("sat", union_find.display(tout);); - - // - // Preliminary version: - // A more appropriate is to walk each pair, - // and refine partition based on SAT results. - // - for (unsigned i = 0; i < num_vars; ++i) { - if (!union_find.is_root(i)) continue; - unsigned v = union_find.next(i); - unsigned last_v = UINT_MAX; - if (!m_solver.was_eliminated(i)) { - last_v = i; - } - while (v != i) { - if (!m_solver.was_eliminated(v)) { - if (last_v != UINT_MAX) { - if (check_equality(v, last_v)) { - // last_v was eliminated. - - } - else { - // TBD: refine partition. - } - } - last_v = v; - } - v = union_find.next(v); - } - } - } - - bool bceq::check_equality(unsigned v1, unsigned v2) { - TRACE("sat", tout << "check: " << v1 << " = " << v2 << "\n";); - uint64 val1 = m_rbits[v1]; - uint64 val2 = m_rbits[v2]; - literal l1 = literal(v1, false); - literal l2 = literal(v2, false); - if (val1 != val2) { - SASSERT(val1 == ~val2); - l2.neg(); - } - if (is_already_equiv(l1, l2)) { - TRACE("sat", tout << "Already equivalent: " << l1 << " " << l2 << "\n";); - return false; - } - - literal lits[2]; - lits[0] = l1; - lits[1] = ~l2; - lbool is_sat = m_s->check(2, lits); - if (is_sat == l_false) { - lits[0] = ~l1; - lits[1] = l2; - is_sat = m_s->check(2, lits); - } - if (is_sat == l_false) { - TRACE("sat", tout << "Found equivalent: " << l1 << " " << l2 << "\n";); - assert_equality(l1, l2); - } - else { - TRACE("sat", tout << "Not equivalent: " << l1 << " " << l2 << "\n";); - // TBD: if is_sat == l_true, then refine partition. - } - return is_sat == l_false; - } - - bool bceq::is_already_equiv(literal l1, literal l2) { - watch_list const& w1 = m_solver.get_wlist(l1); - bool found = false; - for (unsigned i = 0; !found && i < w1.size(); ++i) { - watched const& w = w1[i]; - found = w.is_binary_clause() && w.get_literal() == ~l2; - } - if (!found) return false; - found = false; - watch_list const& w2 = m_solver.get_wlist(~l1); - for (unsigned i = 0; !found && i < w2.size(); ++i) { - watched const& w = w2[i]; - found = w.is_binary_clause() && w.get_literal() == l2; - } - return found; - } - - void bceq::assert_equality(literal l1, literal l2) { - if (l2.sign()) { - l1.neg(); - l2.neg(); - } - literal_vector roots; - bool_var_vector vars; - for (unsigned i = 0; i < m_solver.num_vars(); ++i) { - roots.push_back(literal(i, false)); - } - roots[l2.var()] = l1; - vars.push_back(l2.var()); - elim_eqs elim(m_solver); - IF_VERBOSE(1, - for (unsigned i = 0; i < vars.size(); ++i) { - verbose_stream() << "var: " << vars[i] << " root: " << roots[vars[i]] << "\n"; - }); - elim(roots, vars); - } - - void bceq::cleanup() { - m_solver.del_clauses(m_bin_clauses.begin(), m_bin_clauses.end()); - m_bin_clauses.reset(); - } - - - void bceq::operator()() { - if (!m_solver.m_config.m_bcd) return; - flet _disable_bcd(m_solver.m_config.m_bcd, false); - flet _disable_min(m_solver.m_config.m_minimize_core, false); - flet _disable_opt(m_solver.m_config.m_optimize_model, false); - flet _bound_maxc(m_solver.m_config.m_max_conflicts, 1500); - - use_list ul; - solver s(m_solver.m_params, m_solver.rlimit(), 0); - s.m_config.m_bcd = false; - s.m_config.m_minimize_core = false; - s.m_config.m_optimize_model = false; - s.m_config.m_max_conflicts = 1500; - m_use_list = &ul; - m_s = &s; - ul.init(m_solver.num_vars()); - init(); - pure_decompose(); - post_decompose(); - IF_VERBOSE(1, verbose_stream() << "Decomposed set " << m_L.size() << " rest: " << m_R.size() << "\n";); - - TRACE("sat", - tout << "Decomposed set " << m_L.size() << "\n"; - for (unsigned i = 0; i < m_L.size(); ++i) { - clause const* cls = m_L[i]; - if (cls) tout << *cls << "\n"; - } - tout << "remainder " << m_R.size() << "\n"; - for (unsigned i = 0; i < m_R.size(); ++i) { - clause const* cls = m_R[i]; - if (cls) tout << *cls << "\n"; - } - ); - sat_sweep(); - extract_partition(); - cleanup(); - } -}; diff --git a/src/sat/sat_bceq.h b/src/sat/sat_bceq.h deleted file mode 100644 index c9d01e78b..000000000 --- a/src/sat/sat_bceq.h +++ /dev/null @@ -1,89 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - sat_bceq.h - -Abstract: - - Find equivalent literals based on blocked clause decomposition. - -Author: - - Nikolaj Bjorner (nbjorner) 2014-09-27. - -Revision History: - ---*/ -#ifndef SAT_BCEQ_H_ -#define SAT_BCEQ_H_ - -#include"sat_types.h" -#include"union_find.h" - - -namespace sat { - class solver; - - class bceq { - typedef ptr_vector clause_use_list; - class use_list { - vector > m_clauses; - public: - use_list() {} - void init(unsigned num_vars); - void reset() { m_clauses.reset(); } - void erase(clause& c); - void insert(clause& c); - ptr_vector& get(literal lit); - }; - typedef std::pair bin_clause; - typedef svector bin_clauses; - solver & m_solver; - use_list* m_use_list; - use_list m_bce_use_list; - solver* m_s; - random_gen m_rand; - svector m_clauses; - svector m_L; - svector m_R; - literal_vector m_L_blits; - literal_vector m_R_blits; - svector m_bin_clauses; - svector m_rbits; - svector m_rstack; // stack of blocked clauses - literal_vector m_bstack; // stack of blocking literals - svector m_marked; - svector m_removed; // set of clauses removed (not considered in clause set during BCE) - union_find_default_ctx m_union_find_ctx; - unsigned_vector m_live_clauses; - - void init(); - void register_clause(clause* cls); - void unregister_clause(clause* cls); - void pure_decompose(); - void pure_decompose(literal lit); - void pure_decompose(ptr_vector& uses, svector& clauses); - void post_decompose(); - literal find_blocked(clause const& cls); - bool bce(clause& cls); - bool is_blocked(literal lit) const; - void init_rbits(); - void init_reconstruction_stack(); - void sat_sweep(); - void cleanup(); - uint64 eval_clause(clause const& cls) const; - void verify_sweep(); - void extract_partition(); - bool check_equality(unsigned v1, unsigned v2); - bool is_already_equiv(literal l1, literal l2); - void assert_equality(literal l1, literal l2); - public: - bceq(solver & s); - void operator()(); - }; - -}; - -#endif diff --git a/src/sat/sat_clause.cpp b/src/sat/sat_clause.cpp index eabc4fdb1..1efbd6758 100644 --- a/src/sat/sat_clause.cpp +++ b/src/sat/sat_clause.cpp @@ -41,11 +41,11 @@ namespace sat { var_approx_set clause::approx(unsigned num, literal const * lits) { var_approx_set r; - for (unsigned i = 0; i < num; i++) + 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); } @@ -123,70 +123,95 @@ namespace sat { clause_allocator::clause_allocator(): m_allocator("clause-allocator") { -#ifdef _AMD64_ +#if defined(_AMD64_) m_num_segments = 0; #endif } clause * clause_allocator::get_clause(clause_offset cls_off) const { -#ifdef _AMD64_ - return reinterpret_cast(m_segments[cls_off & c_aligment_mask] + (static_cast(cls_off) & ~c_aligment_mask)); +#if defined(_AMD64_) +#if defined (Z3DEBUG) + clause const* result; + if (((cls_off & c_alignment_mask) == c_last_segment)) { + unsigned id = cls_off >> c_cls_alignment; + bool check = m_last_seg_id2cls.find(id, result); + SASSERT(check); + return const_cast(result); + } +#endif + return reinterpret_cast(m_segments[cls_off & c_alignment_mask] + (static_cast(cls_off) & ~c_alignment_mask)); #else return reinterpret_cast(cls_off); #endif } -#ifdef _AMD64_ - unsigned clause_allocator::get_segment(size_t ptr) { - SASSERT((ptr & c_aligment_mask) == 0); - ptr &= ~0xFFFFFFFFull; // Keep only high part +#if defined(_AMD64_) + unsigned clause_allocator::get_segment(clause const* cls) { + size_t ptr = reinterpret_cast(cls); + + SASSERT((ptr & c_alignment_mask) == 0); + ptr &= 0xFFFFFFFF00000000ull; // Keep only high part unsigned i = 0; for (i = 0; i < m_num_segments; ++i) if (m_segments[i] == ptr) return i; i = m_num_segments; - m_num_segments++; - SASSERT(m_num_segments <= c_max_segments); - if (i >= c_max_segments) + SASSERT(i <= c_last_segment); +#if defined(Z3DEBUG) + if (i == c_last_segment) { + if (!m_last_seg_id2cls.contains(cls->id())) + m_last_seg_id2cls.insert(cls->id(), cls); + } + else { + ++m_num_segments; + m_segments[i] = ptr; + } +#else + if (i == c_last_segment) { throw default_exception("segment out of range"); + } m_segments[i] = ptr; + ++m_num_segments; +#endif + return i; } #endif - clause_offset clause_allocator::get_offset(clause const * ptr) const { -#ifdef _AMD64_ - return static_cast(reinterpret_cast(ptr)) + const_cast(this)->get_segment(reinterpret_cast(ptr)); + clause_offset clause_allocator::get_offset(clause const * cls) const { +#if defined(_AMD64_) + unsigned segment = const_cast(this)->get_segment(cls); +#if defined(Z3DEBUG) + SASSERT(segment <= c_last_segment); + if (segment == c_last_segment) { + SASSERT(m_last_seg_id2cls.contains(cls->id())); + return (cls->id() << c_cls_alignment) | c_last_segment; + } +#endif + return static_cast(reinterpret_cast(cls)) + segment; #else - return reinterpret_cast(ptr); + return reinterpret_cast(cls); #endif } - + clause * clause_allocator::mk_clause(unsigned num_lits, literal const * lits, bool learned) { size_t size = clause::get_obj_size(num_lits); -#ifdef _AMD64_ - size_t slot = size >> c_cls_alignment; - if ((size & c_aligment_mask) != 0) - slot++; - size = slot << c_cls_alignment; -#endif void * mem = m_allocator.allocate(size); clause * cls = new (mem) clause(m_id_gen.mk(), num_lits, lits, learned); - TRACE("sat", tout << "alloc: " << cls->id() << " " << cls << " " << *cls << " " << (learned?"l":"a") << "\n";); + TRACE("sat", tout << "alloc: " << cls->id() << " " << *cls << " " << (learned?"l":"a") << "\n";); SASSERT(!learned || cls->is_learned()); return cls; } void clause_allocator::del_clause(clause * cls) { - TRACE("sat", tout << "delete: " << cls->id() << " " << cls << " " << *cls << "\n";); + TRACE("sat", tout << "delete: " << cls->id() << " " << *cls << "\n";); m_id_gen.recycle(cls->id()); - size_t size = clause::get_obj_size(cls->m_capacity); -#ifdef _AMD64_ - size_t slot = size >> c_cls_alignment; - if ((size & c_aligment_mask) != 0) - slot++; - size = slot << c_cls_alignment; +#if defined(_AMD64_) +#if defined(Z3DEBUG) + m_last_seg_id2cls.remove(cls->id()); #endif +#endif + size_t size = clause::get_obj_size(cls->m_capacity); cls->~clause(); m_allocator.deallocate(size, cls); } @@ -212,16 +237,16 @@ namespace sat { } return out; } - - bool clause_wrapper::contains(literal l) const { + + 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 { + + bool clause_wrapper::contains(bool_var v) const { unsigned sz = size(); for (unsigned i = 0; i < sz; i++) if (operator[](i).var() == v) diff --git a/src/sat/sat_clause.h b/src/sat/sat_clause.h index 6a183c3df..27a0ed739 100644 --- a/src/sat/sat_clause.h +++ b/src/sat/sat_clause.h @@ -22,6 +22,7 @@ Revision History: #include"sat_types.h" #include"small_object_allocator.h" #include"id_gen.h" +#include"map.h" #ifdef _MSC_VER #pragma warning(disable : 4200) @@ -46,7 +47,7 @@ namespace sat { unsigned m_frozen:1; unsigned m_reinit_stack:1; unsigned m_inact_rounds:8; - unsigned m_glue:8; + unsigned m_glue:8; unsigned m_psm:8; // transient field used during gc literal m_lits[0]; @@ -101,10 +102,14 @@ namespace sat { 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)) {} + 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 { @@ -124,13 +129,16 @@ namespace sat { class clause_allocator { small_object_allocator m_allocator; id_gen m_id_gen; -#ifdef _AMD64_ - unsigned get_segment(size_t ptr); - static const unsigned c_cls_alignment = 3; - static const unsigned c_max_segments = 1 << c_cls_alignment; - static const size_t c_aligment_mask = (1ull << c_cls_alignment) - 1ull; +#if defined(_AMD64_) + unsigned get_segment(clause const* cls); + static const unsigned c_cls_alignment = 3; + static const unsigned c_last_segment = (1ull << c_cls_alignment) - 1ull; + static const size_t c_alignment_mask = (1ull << c_cls_alignment) - 1ull; unsigned m_num_segments; - size_t m_segments[c_max_segments]; + size_t m_segments[c_last_segment]; +#if defined(Z3DEBUG) + u_map m_last_seg_id2cls; +#endif #endif public: clause_allocator(); @@ -143,7 +151,7 @@ namespace sat { /** \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). + clause_ref wraps a clause object or a pair of literals (i.e., a binary clause). */ class clause_wrapper { union { @@ -157,7 +165,7 @@ namespace sat { 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 { + literal operator[](unsigned idx) const { SASSERT(idx < size()); if (is_binary()) return idx == 0 ? to_literal(m_l1_idx) : to_literal(m_l2_idx); diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index aff023b22..9206ea8bc 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -23,6 +23,7 @@ Revision History: namespace sat { config::config(params_ref const & p): + m_restart_max(0), m_always_true("always_true"), m_always_false("always_false"), m_caching("caching"), @@ -34,6 +35,7 @@ namespace sat { m_glue("glue"), m_glue_psm("glue_psm"), m_psm_glue("psm_glue") { + m_num_parallel = 1; updt_params(p); } @@ -66,7 +68,8 @@ namespace sat { m_restart_initial = p.restart_initial(); m_restart_factor = p.restart_factor(); - + m_restart_max = p.restart_max(); + m_random_freq = p.random_freq(); m_random_seed = p.random_seed(); if (m_random_seed == 0) @@ -75,6 +78,7 @@ namespace sat { m_burst_search = p.burst_search(); m_max_conflicts = p.max_conflicts(); + m_num_parallel = p.parallel_threads(); // These parameters are not exposed m_simplify_mult1 = _p.get_uint("simplify_mult1", 300); @@ -107,10 +111,8 @@ namespace sat { m_gc_increment = p.gc_increment(); } m_minimize_lemmas = p.minimize_lemmas(); - m_minimize_core = p.minimize_core(); - m_minimize_core_partial = p.minimize_core_partial(); - m_optimize_model = p.optimize_model(); - m_bcd = p.bcd(); + m_core_minimize = p.core_minimize(); + m_core_minimize_partial = p.core_minimize_partial(); m_dyn_sub_res = p.dyn_sub_res(); } diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index 2eff402ad..405cbd092 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -52,10 +52,12 @@ namespace sat { restart_strategy m_restart; unsigned m_restart_initial; double m_restart_factor; // for geometric case + unsigned m_restart_max; double m_random_freq; unsigned m_random_seed; unsigned m_burst_search; unsigned m_max_conflicts; + unsigned m_num_parallel; unsigned m_simplify_mult1; double m_simplify_mult2; @@ -69,10 +71,8 @@ namespace sat { bool m_minimize_lemmas; bool m_dyn_sub_res; - bool m_minimize_core; - bool m_minimize_core_partial; - bool m_optimize_model; - bool m_bcd; + bool m_core_minimize; + bool m_core_minimize_partial; symbol m_always_true; diff --git a/src/sat/sat_integrity_checker.cpp b/src/sat/sat_integrity_checker.cpp index c60fb86cb..f7cd371f8 100644 --- a/src/sat/sat_integrity_checker.cpp +++ b/src/sat/sat_integrity_checker.cpp @@ -70,9 +70,9 @@ namespace sat { tout << "watch_list:\n"; sat::display(tout, s.m_cls_allocator, s.get_wlist(~c[0])); tout << "\n";); - SASSERT(contains_watched(s.get_wlist(~c[0]), c[1], c[2])); - SASSERT(contains_watched(s.get_wlist(~c[1]), c[0], c[2])); - SASSERT(contains_watched(s.get_wlist(~c[2]), c[0], c[1])); + 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) { @@ -96,8 +96,8 @@ namespace sat { } // the first two literals must be watched. - SASSERT(contains_watched(s.get_wlist(~c[0]), c, s.get_offset(c))); - SASSERT(contains_watched(s.get_wlist(~c[1]), c, s.get_offset(c))); + 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; } diff --git a/src/sat/sat_model_converter.cpp b/src/sat/sat_model_converter.cpp index 3a39af31b..8901c276f 100644 --- a/src/sat/sat_model_converter.cpp +++ b/src/sat/sat_model_converter.cpp @@ -42,7 +42,7 @@ namespace sat { // if it->get_kind() == BLOCK_LIT, then it might be the case that m[it->var()] != l_undef, // and the following procedure flips its value. bool sat = false; - bool var_sign; + bool var_sign = false; literal_vector::const_iterator it2 = it->m_clauses.begin(); literal_vector::const_iterator end2 = it->m_clauses.end(); for (; it2 != end2; ++it2) { diff --git a/src/sat/sat_mus.cpp b/src/sat/sat_mus.cpp index 9c7c8c7bb..06851d10d 100644 --- a/src/sat/sat_mus.cpp +++ b/src/sat/sat_mus.cpp @@ -20,11 +20,10 @@ Notes: #include "sat_solver.h" #include "sat_mus.h" -#include "sat_sls.h" namespace sat { - mus::mus(solver& s):s(s), m_is_active(false), m_best_value(0), m_restart(0), m_max_restarts(0) {} + mus::mus(solver& s):s(s), m_is_active(false), m_max_num_restarts(UINT_MAX) {} mus::~mus() {} @@ -32,9 +31,6 @@ namespace sat { m_core.reset(); m_mus.reset(); m_model.reset(); - m_best_value = 0; - m_max_restarts = (s.m_stats.m_restart - m_restart) + 10; - m_restart = s.m_stats.m_restart; } void mus::set_core() { @@ -45,41 +41,33 @@ namespace sat { } void mus::update_model() { - double new_value = s.m_wsls.evaluate_model(s.m_model); if (m_model.empty()) { m_model.append(s.m_model); - m_best_value = new_value; - } - else if (m_best_value > new_value) { - m_model.reset(); - m_model.append(s.m_model); - m_best_value = new_value; } } lbool mus::operator()() { - flet _disable_min(s.m_config.m_minimize_core, false); - flet _disable_opt(s.m_config.m_optimize_model, false); + 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 " << s.get_core() << ")\n";); + IF_VERBOSE(3, verbose_stream() << "(sat.mus size: " << s.get_core().size() << " core: [" << s.get_core() << "])\n";); reset(); lbool r = mus1(); - m_restart = s.m_stats.m_restart; return r; } lbool mus::mus1() { - bool minimize_partial = s.m_config.m_minimize_core_partial; + 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 (core.size() > 64) { + if (!minimize_partial && core.size() > 64) { return mus2(); } - unsigned delta_time = 0; - unsigned core_miss = 0; while (!core.empty()) { - IF_VERBOSE(3, verbose_stream() << "(opt.mus reducing core: " << core.size() << " mus: " << mus.size() << ")\n";); + 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";); @@ -88,51 +76,47 @@ namespace sat { set_core(); return l_undef; } - if (minimize_partial && 3*delta_time > core.size() && core.size() < mus.size()) { - break; - } unsigned num_literals = core.size() + mus.size(); if (num_literals <= 2) { // IF_VERBOSE(0, verbose_stream() << "num literals: " << core << " " << mus << "\n";); break; } - if (s.m_config.m_minimize_core_partial && s.m_stats.m_restart - m_restart > m_max_restarts) { - IF_VERBOSE(1, verbose_stream() << "(sat restart budget exceeded)\n";); - set_core(); - return l_true; - } 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: - core.push_back(lit); - set_core(); - return 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(); - if (!core.empty()) { - // mr(); // TBD: measure - } break; } case l_false: literal_vector const& new_core = s.get_core(); if (new_core.contains(~lit)) { - IF_VERBOSE(3, verbose_stream() << "miss core " << lit << "\n";); - ++core_miss; + IF_VERBOSE(3, verbose_stream() << "(sat.mus unit reduction, literal is in both cores " << lit << ")\n";); } else { - core_miss = 0; TRACE("sat", tout << "core: " << new_core << " mus: " << mus << "\n";); core.reset(); for (unsigned i = 0; i < new_core.size(); ++i) { @@ -144,14 +128,6 @@ namespace sat { } break; } - - unsigned new_num_literals = core.size() + mus.size(); - if (new_num_literals == num_literals) { - delta_time++; - } - else { - delta_time = 0; - } } set_core(); IF_VERBOSE(3, verbose_stream() << "(sat.mus.new " << s.m_core << ")\n";); @@ -172,13 +148,9 @@ namespace sat { lbool mus::qx(literal_set& assignment, literal_set& support, bool has_support) { lbool is_sat = l_true; - if (s.m_config.m_minimize_core_partial && s.m_stats.m_restart - m_restart > m_max_restarts) { - IF_VERBOSE(1, verbose_stream() << "(sat restart budget exceeded)\n";); - return l_true; - } if (has_support) { scoped_append _sa(m_mus, support.to_vector()); - is_sat = s.check(m_mus.size(), m_mus.c_ptr()); + is_sat = s.check(m_mus.size(), m_mus.c_ptr()); switch (is_sat) { case l_false: { literal_set core(s.get_core()); @@ -186,7 +158,7 @@ namespace sat { assignment.reset(); return l_true; } - case l_undef: + case l_undef: return l_undef; case l_true: update_model(); @@ -262,27 +234,5 @@ namespace sat { IF_VERBOSE(3, verbose_stream() << "core verification: " << is_sat << " " << core << "\n";); } - void mus::mr() { - sls sls(s); - literal_vector tabu; - tabu.append(m_mus); - tabu.append(m_core); - bool reuse_model = false; - for (unsigned i = m_mus.size(); i < tabu.size(); ++i) { - tabu[i] = ~tabu[i]; - lbool is_sat = sls(tabu.size(), tabu.c_ptr(), reuse_model); - tabu[i] = ~tabu[i]; - if (is_sat == l_true) { - m_mus.push_back(tabu[i]); - m_core.erase(tabu[i]); - IF_VERBOSE(3, verbose_stream() << "in core " << tabu[i] << "\n";); - reuse_model = true; - } - else { - IF_VERBOSE(3, verbose_stream() << "NOT in core " << tabu[i] << "\n";); - reuse_model = false; - } - } - } } diff --git a/src/sat/sat_mus.h b/src/sat/sat_mus.h index 617bbc757..946f66ed6 100644 --- a/src/sat/sat_mus.h +++ b/src/sat/sat_mus.h @@ -26,9 +26,7 @@ namespace sat { literal_vector m_mus; bool m_is_active; model m_model; // model obtained during minimal unsat core - double m_best_value; - unsigned m_restart; - unsigned m_max_restarts; + unsigned m_max_num_restarts; public: @@ -41,7 +39,6 @@ namespace sat { lbool mus1(); lbool mus2(); lbool qx(literal_set& assignment, literal_set& support, bool has_support); - void mr(); void reset(); void set_core(); void update_model(); diff --git a/src/sat/sat_par.cpp b/src/sat/sat_par.cpp new file mode 100644 index 000000000..7a185a3b5 --- /dev/null +++ b/src/sat/sat_par.cpp @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_par.cpp + +Abstract: + + Utilities for parallel SAT solving. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-1-29. + +Revision History: + +--*/ +#include "sat_par.h" + + +namespace sat { + + par::par() {} + + void par::exchange(literal_vector const& in, unsigned& limit, literal_vector& out) { + #pragma omp critical (par_solver) + { + 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(); + } + } + +}; + diff --git a/src/sat/sat_par.h b/src/sat/sat_par.h new file mode 100644 index 000000000..2b2592de7 --- /dev/null +++ b/src/sat/sat_par.h @@ -0,0 +1,39 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_par.h + +Abstract: + + Utilities for parallel SAT solving. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-1-29. + +Revision History: + +--*/ +#ifndef SAT_PAR_H_ +#define SAT_PAR_H_ + +#include"sat_types.h" +#include"hashtable.h" +#include"map.h" + +namespace sat { + + class par { + typedef hashtable index_set; + literal_vector m_units; + index_set m_unit_set; + public: + par(); + void exchange(literal_vector const& in, unsigned& limit, literal_vector& out); + }; + +}; + +#endif diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index c5ac97b4e..60708fd5c 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -7,6 +7,7 @@ def_module_params('sat', ('phase.caching.off', UINT, 100, 'phase caching off period (in number of conflicts)'), ('restart', SYMBOL, 'luby', 'restart strategy: luby or geometric'), ('restart.initial', UINT, 100, 'initial restart (number of conflicts)'), + ('restart.max', UINT, UINT_MAX, 'maximal number of restarts.'), ('restart.factor', DOUBLE, 1.5, 'restart increment factor for geometric strategy'), ('random_freq', DOUBLE, 0.01, 'frequency of random case splits'), ('random_seed', UINT, 0, 'random seed'), @@ -19,8 +20,7 @@ def_module_params('sat', ('gc.k', UINT, 7, 'learned clauses that are inactive for k gc rounds are permanently deleted (only used in dyn_psm)'), ('minimize_lemmas', BOOL, True, 'minimize learned clauses'), ('dyn_sub_res', BOOL, True, 'dynamic subsumption resolution for minimizing learned clauses'), - ('minimize_core', BOOL, False, 'minimize computed core'), - ('minimize_core_partial', BOOL, False, 'apply partial (cheap) core minimization'), - ('optimize_model', BOOL, False, 'enable optimization of soft constraints'), - ('bcd', BOOL, False, 'enable blocked clause decomposition for equality extraction'), + ('core.minimize', BOOL, False, 'minimize computed core'), + ('core.minimize_partial', BOOL, False, 'apply partial (cheap) core minimization'), + ('parallel_threads', UINT, 1, 'number of parallel threads to use'), ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'))) diff --git a/src/sat/sat_probing.cpp b/src/sat/sat_probing.cpp index 165d39ad8..f54ee9f89 100644 --- a/src/sat/sat_probing.cpp +++ b/src/sat/sat_probing.cpp @@ -95,7 +95,7 @@ namespace sat { if (updt_cache) cache_bins(l, old_tr_sz); s.pop(1); - + literal_vector::iterator it = m_to_assert.begin(); literal_vector::iterator end = m_to_assert.end(); for (; it != end; ++it) { @@ -139,17 +139,17 @@ namespace sat { if (m_probing_binary) { watch_list & wlist = s.get_wlist(~l); - watch_list::iterator it = wlist.begin(); - watch_list::iterator end = wlist.end(); - for (; it != end ; ++it) { - if (!it->is_binary_clause()) + for (unsigned i = 0; i < wlist.size(); i++) { + watched & w = wlist[i]; + if (!w.is_binary_clause()) break; - literal l2 = it->get_literal(); + 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()) @@ -178,10 +178,10 @@ namespace sat { m_num_assigned(p.m_num_assigned) { m_watch.start(); } - + ~report() { m_watch.stop(); - IF_VERBOSE(SAT_VB_LVL, + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << " (sat-probing :probing-assigned " << (m_probing.m_num_assigned - m_num_assigned) << " :cost " << m_probing.m_counter; @@ -189,7 +189,7 @@ namespace sat { verbose_stream() << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; - + bool probing::operator()(bool force) { if (!m_probing) return true; @@ -200,8 +200,8 @@ namespace sat { CASSERT("probing", s.check_invariant()); if (!force && m_counter > 0) return true; - - if (m_probing_cache && memory::get_allocation_size() > m_probing_cache_limit) + + if (m_probing_cache && memory::get_allocation_size() > m_probing_cache_limit) m_cached_bins.finalize(); report rpt(*this); @@ -239,7 +239,7 @@ namespace sat { m_counter *= 2; } CASSERT("probing", s.check_invariant()); - free_memory(); + finalize(); return r; } @@ -255,15 +255,16 @@ namespace sat { // TODO } - void probing::free_memory() { - m_assigned.cleanup(); + void probing::finalize() { + m_assigned.finalize(); m_to_assert.finalize(); + m_cached_bins.finalize(); } - + void probing::collect_statistics(statistics & st) const { st.update("probing assigned", m_num_assigned); } - + void probing::reset_statistics() { m_num_assigned = 0; } diff --git a/src/sat/sat_probing.h b/src/sat/sat_probing.h index daf6817e3..cce165a34 100644 --- a/src/sat/sat_probing.h +++ b/src/sat/sat_probing.h @@ -69,7 +69,7 @@ namespace sat { void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); - void free_memory(); + void finalize(); void collect_statistics(statistics & st) const; void reset_statistics(); diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index b200524e7..bd975115b 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -63,6 +63,7 @@ namespace sat { } simplifier::~simplifier() { + finalize(); } inline watch_list & simplifier::get_wlist(literal l) { return s.get_wlist(l); } @@ -96,7 +97,7 @@ namespace sat { inline void simplifier::remove_clause_core(clause & c) { unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) - insert_todo(c[i].var()); + insert_elim_todo(c[i].var()); m_sub_todo.erase(c); c.set_removed(true); TRACE("resolution_bug", tout << "del_clause: " << c << "\n";); @@ -116,6 +117,7 @@ namespace sat { inline void simplifier::remove_bin_clause_half(literal l1, literal l2, bool learned) { SASSERT(s.get_wlist(~l1).contains(watched(l2, learned))); s.get_wlist(~l1).erase(watched(l2, learned)); + m_sub_bin_todo.erase(bin_clause(l1, l2, learned)); } void simplifier::init_visited() { @@ -123,24 +125,42 @@ namespace sat { m_visited.resize(2*s.num_vars(), false); } - void simplifier::free_memory() { + 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(); } + 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()); + 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 && !m_elim_blocked_clauses && !m_resolution) return; + initialize(); + CASSERT("sat_solver", s.check_invariant()); TRACE("before_simplifier", s.display(tout);); + m_sub_todo.reset(); + m_sub_bin_todo.reset(); s.m_cleaner(true); m_last_sub_trail_sz = s.m_trail.size(); TRACE("after_cleanup", s.display(tout);); @@ -155,10 +175,10 @@ namespace sat { } register_clauses(s.m_clauses); + if (!learned && (m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls)) elim_blocked_clauses(); - if (!learned) m_num_calls++; @@ -197,7 +217,7 @@ namespace sat { } CASSERT("sat_solver", s.check_invariant()); TRACE("after_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); - free_memory(); + finalize(); } /** @@ -617,7 +637,7 @@ namespace sat { TRACE("elim_lit", tout << "processing: " << c << "\n";); m_need_cleanup = true; m_num_elim_lits++; - insert_todo(l.var()); + insert_elim_todo(l.var()); c.elim(l); clause_use_list & occurs = m_use_list.get(l); occurs.erase_not_removed(c); @@ -875,7 +895,7 @@ namespace sat { unsigned idx = l.index(); if (m_queue.contains(idx)) m_queue.decreased(idx); - else + else m_queue.insert(idx); } literal next() { SASSERT(!empty()); return to_literal(m_queue.erase_min()); } @@ -897,16 +917,19 @@ namespace sat { } void insert(literal l) { - bool_var v = l.var(); - if (s.is_external(v) || s.was_eliminated(v)) - return; m_queue.insert(l); } + bool process_var(bool_var v) { + return !s.is_external(v) && !s.was_eliminated(v); + } + void operator()(unsigned num_vars) { for (bool_var v = 0; v < num_vars; v++) { - insert(literal(v, false)); - insert(literal(v, true)); + if (process_var(v)) { + insert(literal(v, false)); + insert(literal(v, true)); + } } while (!m_queue.empty()) { s.checkpoint(); @@ -920,10 +943,10 @@ namespace sat { void process(literal l) { TRACE("blocked_clause", tout << "processing: " << l << "\n";); model_converter::entry * new_entry = 0; - if (s.is_external(l.var()) || s.was_eliminated(l.var())) + if (!process_var(l.var())) { return; + } { - m_to_remove.reset(); { clause_use_list & occs = s.m_use_list.get(l); @@ -942,8 +965,10 @@ namespace sat { mc.insert(*new_entry, c); unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { - if (c[i] != l) - m_queue.decreased(~c[i]); + literal lit = c[i]; + if (lit != l && process_var(lit.var())) { + m_queue.decreased(~lit); + } } } s.unmark_all(c); @@ -1235,12 +1260,14 @@ namespace sat { 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, it->is_learned())); } } TRACE("bin_clause_bug", tout << "collapsing watch_list of: " << l << "\n";); @@ -1343,7 +1370,7 @@ namespace sat { } TRACE("resolution", tout << "found var to eliminate, before: " << before_clauses << " after: " << after_clauses << "\n";); - + // eliminate variable model_converter::entry & mc_entry = s.m_mc.mk(model_converter::ELIM_VAR, v); save_clauses(mc_entry, m_pos_cls); diff --git a/src/sat/sat_simplifier.h b/src/sat/sat_simplifier.h index 85922cf7c..9ee239083 100644 --- a/src/sat/sat_simplifier.h +++ b/src/sat/sat_simplifier.h @@ -9,7 +9,7 @@ Abstract: SAT simplification procedures that use a "full" occurrence list: Subsumption, Blocked Clause Removal, Variable Elimination, ... - + Author: @@ -83,7 +83,7 @@ namespace sat { bool m_subsumption; unsigned m_subsumption_limit; bool m_elim_vars; - + // stats unsigned m_num_blocked_clauses; unsigned m_num_subsumed; @@ -97,6 +97,8 @@ namespace sat { 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; } @@ -135,7 +137,7 @@ namespace sat { void mark_as_not_learned_core(watch_list & wlist, literal l2); void mark_as_not_learned(literal l1, literal l2); void subsume(); - + void cleanup_watches(); void cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists); @@ -145,7 +147,7 @@ namespace sat { 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(); @@ -172,15 +174,20 @@ namespace sat { simplifier(solver & s, params_ref const & p); ~simplifier(); - void insert_todo(bool_var v) { m_elim_todo.insert(v); } - void reset_todo() { m_elim_todo.reset(); } + 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 free_memory(); + + void finalize(); void collect_statistics(statistics & st) const; void reset_statistics(); diff --git a/src/sat/sat_sls.cpp b/src/sat/sat_sls.cpp deleted file mode 100644 index 7efc0ce0b..000000000 --- a/src/sat/sat_sls.cpp +++ /dev/null @@ -1,686 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - sat_sls.cpp - -Abstract: - - SLS for clauses in SAT solver - -Author: - - Nikolaj Bjorner (nbjorner) 2014-12-8 - -Notes: - ---*/ - -#include "sat_sls.h" -#include "sat_solver.h" - -namespace sat { - - bool index_set::contains(unsigned idx) const { - return - (idx < m_index.size()) && - (m_index[idx] < m_elems.size()) && - (m_elems[m_index[idx]] == idx); - } - - void index_set::insert(unsigned idx) { - m_index.reserve(idx+1); - if (!contains(idx)) { - m_index[idx] = m_elems.size(); - m_elems.push_back(idx); - } - } - - void index_set::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 index_set::choose(random_gen& rnd) const { - SASSERT(!empty()); - return m_elems[rnd(num_elems())]; - } - - sls::sls(solver& s): s(s) { - m_prob_choose_min_var = 43; - m_clause_generation = 0; - } - - sls::~sls() { - for (unsigned i = 0; i < m_bin_clauses.size(); ++i) { - m_alloc.del_clause(m_bin_clauses[i]); - } - } - - lbool sls::operator()(unsigned sz, literal const* tabu, bool reuse_model) { - init(sz, tabu, reuse_model); - unsigned i; - for (i = 0; !m_false.empty() && !s.canceled() && i < m_max_tries; ++i) { - flip(); - } - IF_VERBOSE(2, verbose_stream() << "tries " << i << "\n";); - if (m_false.empty()) { - SASSERT(s.check_model(m_model)); - return l_true; - } - return l_undef; - } - - void sls::init(unsigned sz, literal const* tabu, bool reuse_model) { - bool same_generation = (m_clause_generation == s.m_stats.m_non_learned_generation); - if (!same_generation) { - init_clauses(); - init_use(); - IF_VERBOSE(0, verbose_stream() << s.m_stats.m_non_learned_generation << " " << m_clause_generation << "\n";); - } - if (!reuse_model) { - init_model(); - } - init_tabu(sz, tabu); - m_clause_generation = s.m_stats.m_non_learned_generation; - - m_max_tries = 10*(s.num_vars() + m_clauses.size()); - - } - - void sls::init_clauses() { - for (unsigned i = 0; i < m_bin_clauses.size(); ++i) { - m_alloc.del_clause(m_bin_clauses[i]); - } - m_bin_clauses.reset(); - m_clauses.reset(); - clause * const * it = s.begin_clauses(); - clause * const * end = s.end_clauses(); - for (; it != end; ++it) { - m_clauses.push_back(*it); - } - svector bincs; - s.collect_bin_clauses(bincs, false); - literal lits[2]; - for (unsigned i = 0; i < bincs.size(); ++i) { - lits[0] = bincs[i].first; - lits[1] = bincs[i].second; - clause* cl = m_alloc.mk_clause(2, lits, false); - m_clauses.push_back(cl); - m_bin_clauses.push_back(cl); - } - } - - void sls::init_model() { - m_num_true.reset(); - m_model.reset(); - m_model.append(s.get_model()); - unsigned sz = m_clauses.size(); - for (unsigned i = 0; i < sz; ++i) { - clause const& c = *m_clauses[i]; - unsigned n = 0; - unsigned csz = c.size(); - for (unsigned j = 0; j < csz; ++j) { - lbool val = value_at(c[j], m_model); - switch (val) { - case l_true: - ++n; - break; - case l_undef: - ++n; - m_model[c[j].var()] = c[j].sign()?l_false:l_true; - SASSERT(value_at(c[j], m_model) == l_true); - break; - default: - break; - } - } - m_num_true.push_back(n); - if (n == 0) { - m_false.insert(i); - } - } - } - - void sls::init_tabu(unsigned sz, literal const* tabu) { - // our main use is where m_model satisfies all the hard constraints. - // SASSERT(s.check_model(m_model)); - // SASSERT(m_false.empty()); - // ASSERT: m_num_true is correct count. - m_tabu.reset(); - m_tabu.resize(s.num_vars(), false); - for (unsigned i = 0; i < sz; ++i) { - literal lit = tabu[i]; - if (s.m_level[lit.var()] == 0) continue; - if (value_at(lit, m_model) == l_false) { - flip(lit); - } - m_tabu[lit.var()] = true; - } - for (unsigned i = 0; i < s.m_trail.size(); ++i) { - literal lit = s.m_trail[i]; - if (s.m_level[lit.var()] > 0) break; - if (value_at(lit, m_model) != l_true) { - flip(lit); - } - m_tabu[lit.var()] = true; - } - } - - void sls::init_use() { - m_use_list.reset(); - m_use_list.resize(s.num_vars()*2); - unsigned sz = m_clauses.size(); - for (unsigned i = 0; i < sz; ++i) { - clause const& c = *m_clauses[i]; - unsigned csz = c.size(); - for (unsigned j = 0; j < csz; ++j) { - m_use_list[c[j].index()].push_back(i); - } - } - DEBUG_CODE(check_use_list();); - } - - unsigned_vector const& sls::get_use(literal lit) { - SASSERT(lit.index() < m_use_list.size()); - return m_use_list[lit.index()]; - } - - unsigned sls::get_break_count(literal lit, unsigned min_break) { - SASSERT(value_at(lit, m_model) == l_false); - unsigned result = 0; - unsigned_vector const& uses = get_use(~lit); - unsigned sz = uses.size(); - for (unsigned i = 0; i < sz; ++i) { - if (m_num_true[uses[i]] == 1) { - ++result; - if (result > min_break) return result; - } - } - return result; - } - - bool sls::pick_flip(literal& lit) { - unsigned clause_idx = m_false.choose(m_rand); - clause const& c = *m_clauses[clause_idx]; - SASSERT(!c.satisfied_by(m_model)); - unsigned min_break = UINT_MAX; - unsigned sz = c.size(); - m_min_vars.reset(); - for (unsigned i = 0; i < sz; ++i) { - lit = c[i]; - if (m_tabu[lit.var()]) continue; - unsigned break_count = get_break_count(lit, min_break); - if (break_count < min_break) { - min_break = break_count; - m_min_vars.reset(); - m_min_vars.push_back(lit); - } - else if (break_count == min_break) { - m_min_vars.push_back(lit); - } - } - if (min_break == 0 || (!m_min_vars.empty() && m_rand(100) >= m_prob_choose_min_var)) { - lit = m_min_vars[m_rand(m_min_vars.size())]; - return true; - } - else if (min_break == UINT_MAX) { - return false; - } - else { - lit = c[m_rand(c.size())]; - return !m_tabu[lit.var()]; - } - } - - void sls::flip() { - literal lit; - if (pick_flip(lit)) { - flip(lit); - } - } - - void sls::flip(literal lit) { - //IF_VERBOSE(0, verbose_stream() << lit << " ";); - SASSERT(value_at(lit, m_model) == l_false); - SASSERT(!m_tabu[lit.var()]); - m_model[lit.var()] = lit.sign()?l_false:l_true; - SASSERT(value_at(lit, m_model) == l_true); - unsigned_vector const& use1 = get_use(lit); - unsigned sz = use1.size(); - for (unsigned i = 0; i < sz; ++i) { - unsigned cl = use1[i]; - m_num_true[cl]++; - SASSERT(m_num_true[cl] <= m_clauses[cl]->size()); - if (m_num_true[cl] == 1) m_false.remove(cl); - } - unsigned_vector const& use2 = get_use(~lit); - sz = use2.size(); - for (unsigned i = 0; i < sz; ++i) { - unsigned cl = use2[i]; - SASSERT(m_num_true[cl] > 0); - m_num_true[cl]--; - if (m_num_true[cl] == 0) m_false.insert(cl); - } - } - - void sls::check_invariant() { - DEBUG_CODE( - for (unsigned i = 0; i < m_clauses.size(); ++i) { - clause const& c = *m_clauses[i]; - bool is_sat = c.satisfied_by(m_model); - SASSERT(is_sat != m_false.contains(i)); - SASSERT(is_sat == (m_num_true[i] > 0)); - }); - } - - void sls::check_use_list() { - DEBUG_CODE( - for (unsigned i = 0; i < m_clauses.size(); ++i) { - clause const& c = *m_clauses[i]; - for (unsigned j = 0; j < c.size(); ++j) { - unsigned idx = c[j].index(); - SASSERT(m_use_list[idx].contains(i)); - } - } - - for (unsigned i = 0; i < m_use_list.size(); ++i) { - literal lit = to_literal(i); - for (unsigned j = 0; j < m_use_list[i].size(); ++j) { - clause const& c = *m_clauses[m_use_list[i][j]]; - bool found = false; - for (unsigned k = 0; !found && k < c.size(); ++k) { - found = c[k] == lit; - } - SASSERT(found); - } - }); - } - - void sls::display(std::ostream& out) const { - out << "Model\n"; - for (bool_var v = 0; v < m_model.size(); ++v) { - out << v << ": " << m_model[v] << "\n"; - } - out << "Clauses\n"; - unsigned sz = m_false.num_elems(); - for (unsigned i = 0; i < sz; ++i) { - out << *m_clauses[m_false[i]] << "\n"; - } - for (unsigned i = 0; i < m_clauses.size(); ++i) { - if (m_false.contains(i)) continue; - clause const& c = *m_clauses[i]; - out << c << " " << m_num_true[i] << "\n"; - } - bool has_tabu = false; - for (unsigned i = 0; !has_tabu && i < m_tabu.size(); ++i) { - has_tabu = m_tabu[i]; - } - if (has_tabu) { - out << "Tabu: "; - for (unsigned i = 0; i < m_tabu.size(); ++i) { - if (m_tabu[i]) { - literal lit(i, false); - if (value_at(lit, m_model) == l_false) lit.neg(); - out << lit << " "; - } - } - out << "\n"; - } - } - - - wsls::wsls(solver& s): - sls(s) - { - m_smoothing_probability = 1; // 1/1000 - } - - wsls::~wsls() {} - - void wsls::set_soft(unsigned sz, literal const* lits, double const* weights) { - m_soft.reset(); - m_weights.reset(); - m_soft.append(sz, lits); - m_weights.append(sz, weights); - } - - void wsls::opt(unsigned sz, literal const* tabu, bool reuse_model) { - init(sz, tabu, reuse_model); - - // - // Initialize m_clause_weights, m_hscore, m_sscore. - // - m_best_value = m_false.empty()?evaluate_model(m_model):-1.0; - m_best_model.reset(); - m_clause_weights.reset(); - m_hscore.reset(); - m_sscore.reset(); - m_H.reset(); - m_S.reset(); - m_best_model.append(s.get_model()); - m_clause_weights.resize(m_clauses.size(), 1); - m_sscore.resize(s.num_vars(), 0.0); - m_hscore.resize(s.num_vars(), 0); - for (unsigned i = 0; i < m_soft.size(); ++i) { - literal lit = m_soft[i]; - m_sscore[lit.var()] = m_weights[i]; - if (value_at(lit, m_model) == l_true) { - m_sscore[lit.var()] = -m_sscore[lit.var()]; - } - } - for (bool_var i = 0; i < s.num_vars(); ++i) { - m_hscore[i] = compute_hscore(i); - refresh_scores(i); - } - DEBUG_CODE(check_invariant();); - unsigned i = 0; - for (; !s.canceled() && m_best_value > 0 && i < m_max_tries; ++i) { - wflip(); - if (m_false.empty()) { - double val = evaluate_model(m_model); - if (val < m_best_value || m_best_value < 0.0) { - m_best_value = val; - m_best_model.reset(); - m_best_model.append(m_model); - s.set_model(m_best_model); - IF_VERBOSE(1, verbose_stream() << "new value: " << val << " @ " << i << "\n";); - if (i*2 > m_max_tries) { - m_max_tries *= 2; - } - } - } - } - TRACE("sat", display(tout);); - IF_VERBOSE(0, verbose_stream() << "tries " << i << "\n";); - } - - void wsls::wflip() { - literal lit; - if (pick_wflip(lit)) { - // IF_VERBOSE(0, verbose_stream() << lit << " ";); - wflip(lit); - } - } - - bool wsls::pick_wflip(literal & lit) { - unsigned idx; - if (!m_H.empty()) { - idx = m_H.choose(m_rand); - lit = literal(idx, false); - if (value_at(lit, m_model) == l_true) lit.neg(); - SASSERT(value_at(lit, m_model) == l_false); - TRACE("sat", tout << "flip H(" << m_H.num_elems() << ") " << lit << "\n";); - } - else if (!m_S.empty()) { - double score = 0.0; - m_min_vars.reset(); - for (unsigned i = 0; i < m_S.num_elems(); ++i) { - unsigned v = m_S[i]; - SASSERT(m_sscore[v] > 0.0); - if (m_sscore[v] > score) { - m_min_vars.reset(); - m_min_vars.push_back(literal(v, false)); - score = m_sscore[v]; - } - else if (m_sscore[v] == score) { - m_min_vars.push_back(literal(v, false)); - } - } - lit = m_min_vars[m_rand(m_min_vars.size())]; // pick with largest sscore. - SASSERT(value_at(lit, m_model) == l_false); - TRACE("sat", tout << "flip S(" << m_min_vars.size() << "," << score << ") " << lit << "\n";); - } - else { - update_hard_weights(); - if (!m_false.empty()) { - unsigned cls_idx = m_false.choose(m_rand); - clause const& c = *m_clauses[cls_idx]; - lit = c[m_rand(c.size())]; - TRACE("sat", tout << "flip hard(" << m_false.num_elems() << "," << c.size() << ") " << lit << "\n";); - } - else { - m_min_vars.reset(); - for (unsigned i = 0; i < m_soft.size(); ++i) { - lit = m_soft[i]; - if (value_at(lit, m_model) == l_false) { - m_min_vars.push_back(lit); - } - } - if (m_min_vars.empty()) { - SASSERT(m_best_value == 0.0); - UNREACHABLE(); // we should have exited the main loop before. - return false; - } - else { - lit = m_min_vars[m_rand(m_min_vars.size())]; - } - TRACE("sat", tout << "flip soft(" << m_min_vars.size() << ", " << m_sscore[lit.var()] << ") " << lit << "\n";); - - } - SASSERT(value_at(lit, m_model) == l_false); - } - return !m_tabu[lit.var()]; - } - - void wsls::wflip(literal lit) { - flip(lit); - unsigned v = lit.var(); - m_sscore[v] = -m_sscore[v]; - m_hscore[v] = compute_hscore(v); - refresh_scores(v); - recompute_hscores(lit); - } - - void wsls::update_hard_weights() { - unsigned csz = m_clauses.size(); - if (m_smoothing_probability >= m_rand(1000)) { - for (unsigned i = 0; i < csz; ++i) { - if (m_clause_weights[i] > 1 && !m_false.contains(i)) { - --m_clause_weights[i]; - if (m_num_true[i] == 1) { - clause const& c = *m_clauses[i]; - unsigned sz = c.size(); - for (unsigned j = 0; j < sz; ++j) { - if (value_at(c[j], m_model) == l_true) { - ++m_hscore[c[j].var()]; - refresh_scores(c[j].var()); - break; - } - } - } - } - } - } - else { - for (unsigned i = 0; i < csz; ++i) { - if (m_false.contains(i)) { - ++m_clause_weights[i]; - clause const& c = *m_clauses[i]; - unsigned sz = c.size(); - for (unsigned j = 0; j < sz; ++j) { - ++m_hscore[c[j].var()]; - refresh_scores(c[j].var()); - } - } - } - } - DEBUG_CODE(check_invariant();); - } - - double wsls::evaluate_model(model& mdl) { - SASSERT(m_false.empty()); - double result = 0.0; - for (unsigned i = 0; i < m_soft.size(); ++i) { - literal lit = m_soft[i]; - if (value_at(lit, mdl) != l_true) { - result += m_weights[i]; - } - } - return result; - } - - int wsls::compute_hscore(bool_var v) { - literal lit(v, false); - if (value_at(lit, m_model) == l_false) { - lit.neg(); - } - SASSERT(value_at(lit, m_model) == l_true); - int hs = 0; - unsigned_vector const& use1 = get_use(~lit); - unsigned sz = use1.size(); - for (unsigned i = 0; i < sz; ++i) { - unsigned cl = use1[i]; - if (m_num_true[cl] == 0) { - SASSERT(m_false.contains(cl)); - hs += m_clause_weights[cl]; - } - else { - SASSERT(!m_false.contains(cl)); - } - } - unsigned_vector const& use2 = get_use(lit); - sz = use2.size(); - for (unsigned i = 0; i < sz; ++i) { - unsigned cl = use2[i]; - if (m_num_true[cl] == 1) { - SASSERT(!m_false.contains(cl)); - hs -= m_clause_weights[cl]; - } - } - return hs; - } - - void wsls::recompute_hscores(literal lit) { - SASSERT(value_at(lit, m_model) == l_true); - TRACE("sat", tout << lit.var() << " := " << m_hscore[lit.var()] << "\n";); - unsigned_vector const& use1 = get_use(lit); - unsigned sz = use1.size(); - for (unsigned i = 0; i < sz; ++i) { - unsigned cl = use1[i]; - TRACE("sat", tout << *m_clauses[cl] << " " << m_num_true[cl] << "\n";); - SASSERT(m_num_true[cl] > 0); - if (m_num_true[cl] == 1) { - // num_true 0 -> 1 - // other literals don't have upside any more. - // subtract one from all other literals - adjust_all_values(lit, cl, -static_cast(m_clause_weights[cl])); - } - else if (m_num_true[cl] == 2) { - // num_true 1 -> 2, previous critical literal is no longer critical - adjust_pivot_value(lit, cl, +m_clause_weights[cl]); - } - } - unsigned_vector const& use2 = get_use(~lit); - sz = use2.size(); - for (unsigned i = 0; i < sz; ++i) { - unsigned cl = use2[i]; - TRACE("sat", tout << *m_clauses[cl] << " " << m_num_true[cl] << "\n";); - if (m_num_true[cl] == 0) { - // num_true 1 -> 0 - // all variables became critical. - adjust_all_values(~lit, cl, +m_clause_weights[cl]); - } - else if (m_num_true[cl] == 1) { - adjust_pivot_value(~lit, cl, -static_cast(m_clause_weights[cl])); - } - // else n+1 -> n >= 2 - } - } - - void wsls::adjust_all_values(literal lit, unsigned cl, int delta) { - clause const& c = *m_clauses[cl]; - unsigned sz = c.size(); - TRACE("sat", tout << lit << " " << c << " delta: " << delta << " nt: " << m_num_true[cl] << "\n";); - for (unsigned i = 0; i < sz; ++i) { - literal lit2 = c[i]; - if (lit2 != lit) { - TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); - m_hscore[lit2.var()] += delta; - TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); - refresh_scores(lit2.var()); - } - } - } - - void wsls::adjust_pivot_value(literal lit, unsigned cl, int delta) { - clause const& c = *m_clauses[cl]; - unsigned csz = c.size(); - for (unsigned j = 0; j < csz; ++j) { - literal lit2 = c[j]; - if (lit2 != lit && value_at(lit2, m_model) == l_true) { - TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); - m_hscore[lit2.var()] += delta; - TRACE("sat", tout << lit2.var() << " := " << m_hscore[lit2.var()] << "\n";); - refresh_scores(lit2.var()); - break; - } - } - } - - void wsls::refresh_scores(bool_var v) { - if (m_hscore[v] > 0 && !m_tabu[v] && m_sscore[v] == 0) { - m_H.insert(v); - } - else { - m_H.remove(v); - } - if (m_sscore[v] > 0) { - if (m_hscore[v] == 0 && !m_tabu[v]) { - m_S.insert(v); - } - else { - m_S.remove(v); - } - } - else if (m_sscore[v] < 0) { - m_S.remove(v); - } - } - - void wsls::check_invariant() { - sls::check_invariant(); - // The hscore is the reward for flipping the truth value of variable v. - // hscore(v) = Sum weight(c) for num_true(c) = 0 and v in c - // - Sum weight(c) for num_true(c) = 1 and (v in c, M(v) or !v in c and !M(v)) - DEBUG_CODE( - for (unsigned v = 0; v < s.num_vars(); ++v) { - int hs = compute_hscore(v); - CTRACE("sat", hs != m_hscore[v], display(tout << v << " - computed: " << hs << " - assigned: " << m_hscore[v] << "\n");); - SASSERT(m_hscore[v] == hs); - } - - // The score(v) is the reward on soft clauses for flipping v. - for (unsigned j = 0; j < m_soft.size(); ++j) { - unsigned v = m_soft[j].var(); - double ss = (l_true == value_at(m_soft[j], m_model))?(-m_weights[j]):m_weights[j]; - SASSERT(m_sscore[v] == ss); - } - - // m_H are values such that m_hscore > 0 and sscore = 0. - for (bool_var v = 0; v < m_hscore.size(); ++v) { - SASSERT((m_hscore[v] > 0 && !m_tabu[v] && m_sscore[v] == 0) == m_H.contains(v)); - } - - // m_S are values such that hscore = 0, sscore > 0 - for (bool_var v = 0; v < m_sscore.size(); ++v) { - SASSERT((m_hscore[v] == 0 && m_sscore[v] > 0 && !m_tabu[v]) == m_S.contains(v)); - }); - } - - void wsls::display(std::ostream& out) const { - sls::display(out); - out << "Best model\n"; - for (bool_var v = 0; v < m_best_model.size(); ++v) { - out << v << ": " << m_best_model[v] << " h: " << m_hscore[v]; - if (m_sscore[v] != 0.0) out << " s: " << m_sscore[v]; - out << "\n"; - } - } - -}; - diff --git a/src/sat/sat_sls.h b/src/sat/sat_sls.h deleted file mode 100644 index f1bf55543..000000000 --- a/src/sat/sat_sls.h +++ /dev/null @@ -1,115 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - sat_sls.h - -Abstract: - - SLS for clauses in SAT solver - -Author: - - Nikolaj Bjorner (nbjorner) 2014-12-8 - -Notes: - ---*/ -#ifndef SAT_SLS_H_ -#define SAT_SLS_H_ - -#include "util.h" -#include "sat_simplifier.h" - -namespace sat { - - 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; - void insert(unsigned idx); - void remove(unsigned idx); - unsigned choose(random_gen& rnd) const; - }; - - class sls { - protected: - solver& s; - random_gen m_rand; - unsigned m_max_tries; - unsigned m_prob_choose_min_var; // number between 0 and 99. - unsigned m_clause_generation; - ptr_vector m_clauses; // vector of all clauses. - index_set m_false; // clauses currently false - vector m_use_list; // use lists for literals - unsigned_vector m_num_true; // per clause, count of # true literals - svector m_min_vars; // literals with smallest break count - model m_model; // current model - clause_allocator m_alloc; // clause allocator - clause_vector m_bin_clauses; // binary clauses - svector m_tabu; // variables that cannot be swapped - public: - sls(solver& s); - virtual ~sls(); - lbool operator()(unsigned sz, literal const* tabu, bool reuse_model); - void set_max_tries(unsigned mx) { m_max_tries = mx; } - virtual void display(std::ostream& out) const; - protected: - void init(unsigned sz, literal const* tabu, bool reuse_model); - void init_tabu(unsigned sz, literal const* tabu); - void init_model(); - void init_use(); - void init_clauses(); - unsigned_vector const& get_use(literal lit); - void flip(literal lit); - virtual void check_invariant(); - void check_use_list(); - private: - bool pick_flip(literal& lit); - void flip(); - unsigned get_break_count(literal lit, unsigned min_break); - }; - - /** - \brief sls with weighted soft clauses. - */ - class wsls : public sls { - unsigned_vector m_clause_weights; - svector m_hscore; - svector m_sscore; - literal_vector m_soft; - svector m_weights; - double m_best_value; - model m_best_model; - index_set m_H, m_S; - unsigned m_smoothing_probability; - public: - wsls(solver& s); - virtual ~wsls(); - void set_soft(unsigned sz, literal const* lits, double const* weights); - bool has_soft() const { return !m_soft.empty(); } - void opt(unsigned sz, literal const* tabu, bool reuse_model); - virtual void display(std::ostream& out) const; - double evaluate_model(model& mdl); - private: - void wflip(); - void wflip(literal lit); - void update_hard_weights(); - bool pick_wflip(literal & lit); - virtual void check_invariant(); - void refresh_scores(bool_var v); - int compute_hscore(bool_var v); - void recompute_hscores(literal lit); - void adjust_all_values(literal lit, unsigned cl, int delta); - void adjust_pivot_value(literal lit, unsigned cl, int delta); - }; - -}; - -#endif diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 78067238a..57cdc2fb4 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -20,7 +20,7 @@ Revision History: #include"sat_integrity_checker.h" #include"luby.h" #include"trace.h" -#include"sat_bceq.h" +#include"max_cliques.h" // define to update glue during propagation #define UPDATE_GLUE @@ -35,13 +35,13 @@ namespace sat { m_rlimit(l), m_config(p), m_ext(ext), + m_par(0), m_cleaner(*this), m_simplifier(*this, p), m_scc(*this, p), m_asymm_branch(*this, p), m_probing(*this, p), m_mus(*this), - m_wsls(*this), m_inconsistent(false), m_num_frozen(0), m_activity_inc(128), @@ -54,7 +54,6 @@ namespace sat { m_conflicts = 0; m_next_simplify = 0; m_num_checkpoints = 0; - m_initializing_preferred = false; } solver::~solver() { @@ -73,7 +72,9 @@ namespace sat { } void solver::copy(solver const & src) { + pop_to_base_level(); SASSERT(m_mc.empty() && src.m_mc.empty()); + SASSERT(scope_lvl() == 0); // create new vars if (num_vars() < src.num_vars()) { for (bool_var v = num_vars(); v < src.num_vars(); v++) { @@ -83,19 +84,25 @@ namespace sat { VERIFY(v == mk_var(ext, dvar)); } } + unsigned sz = src.init_trail_size(); + for (unsigned i = 0; i < sz; ++i) { + assign(src.m_trail[i], justification()); + } + { // copy binary clauses - vector::const_iterator it = src.m_watches.begin(); - vector::const_iterator end = src.m_watches.begin(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - watch_list const & wlist = *it; + unsigned sz = src.m_watches.size(); + for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { literal l = ~to_literal(l_idx); - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (!it2->is_binary_non_learned_clause()) + watch_list const & wlist = src.m_watches[l_idx]; + watch_list::const_iterator it = wlist.begin(); + watch_list::const_iterator end = wlist.end(); + for (; it != end; ++it) { + if (!it->is_binary_non_learned_clause()) + continue; + literal l2 = it->get_literal(); + if (l.index() > l2.index()) continue; - literal l2 = it2->get_literal(); mk_clause_core(l, l2); } } @@ -113,6 +120,9 @@ namespace sat { mk_clause_core(buffer); } } + + m_user_scope_literals.reset(); + m_user_scope_literals.append(src.m_user_scope_literals); } // ----------------------- @@ -122,6 +132,7 @@ namespace sat { // ----------------------- bool_var solver::mk_var(bool ext, bool dvar) { + m_model_is_current = false; m_stats.m_mk_var++; bool_var v = m_level.size(); m_watches.push_back(watch_list()); @@ -141,17 +152,18 @@ namespace sat { m_prev_phase.push_back(PHASE_NOT_AVAILABLE); m_assigned_since_gc.push_back(false); m_case_split_queue.mk_var_eh(v); - m_simplifier.insert_todo(v); + m_simplifier.insert_elim_todo(v); SASSERT(!was_eliminated(v)); return v; } void solver::mk_clause(unsigned num_lits, literal * lits) { + m_model_is_current = false; DEBUG_CODE({ for (unsigned i = 0; i < num_lits; i++) SASSERT(m_eliminated[lits[i].var()] == false); }); - + if (m_user_scope_literals.empty()) { mk_clause_core(num_lits, lits, false); } @@ -175,12 +187,12 @@ namespace sat { void solver::del_clause(clause& c) { if (!c.is_learned()) m_stats.m_non_learned_generation++; - m_cls_allocator.del_clause(&c); - m_stats.m_del_clause++; + m_cls_allocator.del_clause(&c); + 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) << "\n";); + TRACE("sat", tout << "mk_clause: " << mk_lits_pp(num_lits, lits) << (learned?" learned":" aux") << "\n";); if (!learned) { 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";); @@ -188,7 +200,7 @@ namespace sat { return 0; // clause is equivalent to true. } ++m_stats.m_non_learned_generation; - } + } switch (num_lits) { case 0: @@ -211,7 +223,7 @@ namespace sat { if (propagate_bin_clause(l1, l2)) { if (scope_lvl() == 0) return; - if (!learned) + if (!learned) m_clauses_to_reinit.push_back(clause_wrapper(l1, l2)); } m_stats.m_mk_bin_clause++; @@ -234,19 +246,18 @@ namespace sat { } 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); + c.set_reinit_stack(true); } + clause * solver::mk_ter_clause(literal * lits, bool learned) { m_stats.m_mk_ter_clause++; clause * r = m_cls_allocator.mk_clause(3, lits, learned); - bool reinit; - attach_ter_clause(*r, reinit); - if (!learned && reinit) { - TRACE("sat_reinit", tout << "adding to reinit stack: " << *r << "\n";); - push_reinit_stack(*r); - } + bool reinit = attach_ter_clause(*r); + if (reinit && !learned) push_reinit_stack(*r); + if (learned) m_learned.push_back(r); else @@ -254,8 +265,8 @@ namespace sat { return r; } - void solver::attach_ter_clause(clause & c, bool & reinit) { - reinit = false; + bool solver::attach_ter_clause(clause & c) { + bool reinit = false; 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])); @@ -276,18 +287,15 @@ namespace sat { reinit = true; } } + return reinit; } clause * solver::mk_nary_clause(unsigned num_lits, literal * lits, bool learned) { m_stats.m_mk_clause++; clause * r = m_cls_allocator.mk_clause(num_lits, lits, learned); SASSERT(!learned || r->is_learned()); - bool reinit; - attach_nary_clause(*r, reinit); - if (!learned && reinit) { - TRACE("sat_reinit", tout << "adding to reinit stack: " << *r << "\n";); - push_reinit_stack(*r); - } + bool reinit = attach_nary_clause(*r); + if (reinit && !learned) push_reinit_stack(*r); if (learned) m_learned.push_back(r); else @@ -295,8 +303,8 @@ namespace sat { return r; } - void solver::attach_nary_clause(clause & c, bool & reinit) { - reinit = false; + bool solver::attach_nary_clause(clause & c) { + bool reinit = false; clause_offset cls_off = m_cls_allocator.get_offset(&c); if (scope_lvl() > 0) { if (c.is_learned()) { @@ -325,15 +333,16 @@ namespace sat { literal block_lit = c[some_idx]; 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 (c.size() == 3) - attach_ter_clause(c, reinit); + reinit = attach_ter_clause(c); else - attach_nary_clause(c, reinit); + reinit = attach_nary_clause(c); } /** @@ -493,7 +502,7 @@ namespace sat { void solver::assign_core(literal l, justification j) { SASSERT(value(l) == l_undef); - TRACE("sat_assign_core", tout << l << "\n";); + TRACE("sat_assign_core", tout << l << " " << j << " level: " << scope_lvl() << "\n";); if (scope_lvl() == 0) j = justification(); // erase justification for level 0 m_assignment[l.index()] = l_true; @@ -710,10 +719,13 @@ namespace sat { // Search // // ----------------------- - lbool solver::check(unsigned num_lits, literal const* lits, double const* weights, double max_weight) { + lbool solver::check(unsigned num_lits, literal const* lits) { pop_to_base_level(); IF_VERBOSE(2, verbose_stream() << "(sat.sat-solver)\n";); SASSERT(scope_lvl() == 0); + if (m_config.m_num_parallel > 1 && !m_par) { + return check_par(num_lits, lits); + } #ifdef CLONE_BEFORE_SOLVING if (m_mc.empty()) { m_clone = alloc(solver, m_params, 0 /* do not clone extension */); @@ -725,7 +737,7 @@ namespace sat { init_search(); propagate(false); if (inconsistent()) return l_false; - init_assumptions(num_lits, lits, weights, max_weight); + init_assumptions(num_lits, lits); propagate(false); if (check_inconsistent()) return l_false; cleanup(); @@ -739,13 +751,12 @@ namespace sat { m_restart_threshold = m_config.m_restart_initial; } - // iff3_finder(*this)(); + // iff3_finder(*this)(); simplify_problem(); if (check_inconsistent()) return l_false; - if (m_config.m_max_conflicts == 0) { - IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"abort: max-conflicts = 0\"\n";); + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = 0\")\n";); return l_undef; } @@ -757,14 +768,20 @@ namespace sat { return r; if (m_conflicts > m_config.m_max_conflicts) { - IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"abort: max-conflicts = " << m_conflicts << "\"\n";); + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = " << m_conflicts << "\")\n";); return l_undef; } restart(); simplify_problem(); - if (check_inconsistent()) return l_false; + if (check_inconsistent()) return l_false; gc(); + + if (m_config.m_restart_max <= m_restarts) { + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-restarts\")\n";); + return l_undef; + } + } } catch (abort_solver) { @@ -772,6 +789,139 @@ namespace sat { } } + enum par_exception_kind { + DEFAULT_EX, + ERROR_EX + }; + + lbool solver::check_par(unsigned num_lits, literal const* lits) { + int num_threads = static_cast(m_config.m_num_parallel); + int num_extra_solvers = num_threads - 1; + scoped_limits scoped_rlimit(rlimit()); + vector rlims(num_extra_solvers); + ptr_vector solvers(num_extra_solvers); + sat::par par; + symbol saved_phase = m_params.get_sym("phase", symbol("caching")); + for (int i = 0; i < num_extra_solvers; ++i) { + m_params.set_uint("random_seed", m_rand()); + if (i == 1 + num_threads/2) { + m_params.set_sym("phase", symbol("random")); + } + solvers[i] = alloc(sat::solver, m_params, rlims[i], 0); + solvers[i]->copy(*this); + solvers[i]->set_par(&par); + scoped_rlimit.push_child(&solvers[i]->rlimit()); + } + set_par(&par); + m_params.set_sym("phase", saved_phase); + int finished_id = -1; + std::string ex_msg; + par_exception_kind ex_kind; + unsigned error_code = 0; + lbool result = l_undef; + #pragma omp parallel for + for (int i = 0; i < num_threads; ++i) { + try { + lbool r = l_undef; + if (i < num_extra_solvers) { + r = solvers[i]->check(num_lits, lits); + } + else { + r = check(num_lits, lits); + } + bool first = false; + #pragma omp critical (par_solver) + { + if (finished_id == -1) { + finished_id = i; + first = true; + result = r; + } + } + if (first) { + if (r == l_true && i < num_extra_solvers) { + set_model(solvers[i]->get_model()); + } + else if (r == l_false && i < num_extra_solvers) { + m_core.reset(); + m_core.append(solvers[i]->get_core()); + } + for (int j = 0; j < num_extra_solvers; ++j) { + if (i != j) { + rlims[j].cancel(); + } + } + } + } + catch (z3_error & err) { + if (i == 0) { + error_code = err.error_code(); + ex_kind = ERROR_EX; + } + } + catch (z3_exception & ex) { + if (i == 0) { + ex_msg = ex.msg(); + ex_kind = DEFAULT_EX; + } + } + } + set_par(0); + if (finished_id != -1 && finished_id < num_extra_solvers) { + m_stats = solvers[finished_id]->m_stats; + } + + for (int i = 0; i < num_extra_solvers; ++i) { + dealloc(solvers[i]); + } + if (finished_id == -1) { + switch (ex_kind) { + case ERROR_EX: throw z3_error(error_code); + default: throw default_exception(ex_msg.c_str()); + } + } + return result; + + } + + /* + \brief import lemmas/units from parallel sat solvers. + */ + void solver::exchange_par() { + if (m_par && scope_lvl() == 0) { + 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(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(lit, justification()); + } + } + if (num_in > 0 || num_out > 0) { + IF_VERBOSE(1, verbose_stream() << "(sat-sync out: " << num_out << " in: " << num_in << ")\n";); + } + } + } + + void solver::set_par(par* p) { + m_par = p; + m_par_num_vars = num_vars(); + m_par_limit_in = 0; + m_par_limit_out = 0; + } + bool_var solver::next_var() { bool_var next; @@ -835,48 +985,66 @@ namespace sat { lbool solver::bounded_search() { while (true) { checkpoint(); - while (true) { - propagate(true); - if (!inconsistent()) - break; - if (!resolve_conflict()) - return l_false; - if (m_conflicts > m_config.m_max_conflicts) - return l_undef; - if (m_conflicts_since_restart > m_restart_threshold) - return l_undef; - if (scope_lvl() == 0) { - cleanup(); // cleaner may propagate frozen clauses - if (inconsistent()) { - TRACE("sat", tout << "conflict at level 0\n";); - return l_false; - } - gc(); - } + bool done = false; + while (!done) { + lbool is_sat = propagate_and_backjump_step(done); + if (is_sat != l_true) return is_sat; } gc(); if (!decide()) { - 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(); - } - } - else { - mk_model(); - return l_true; + lbool is_sat = final_check(); + if (is_sat != l_undef) { + return is_sat; } } } } + lbool solver::propagate_and_backjump_step(bool& done) { + done = true; + propagate(true); + if (!inconsistent()) + return l_true; + if (!resolve_conflict()) + return l_false; + if (m_conflicts > m_config.m_max_conflicts) + return l_undef; + if (m_conflicts_since_restart > m_restart_threshold) + return l_undef; + if (scope_lvl() == 0) { + cleanup(); // cleaner may propagate frozen clauses + if (inconsistent()) { + TRACE("sat", tout << "conflict at level 0\n";); + return l_false; + } + 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()) @@ -888,12 +1056,11 @@ namespace sat { } } - void solver::init_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight) { + void solver::init_assumptions(unsigned num_lits, literal const* lits) { if (num_lits == 0 && m_user_scope_literals.empty()) { return; } - retry_init_assumptions: reset_assumptions(); push(); @@ -902,10 +1069,8 @@ namespace sat { return; } - TRACE("sat", - for (unsigned i = 0; i < num_lits; ++i) - tout << lits[i] << " "; - tout << "\n"; + TRACE("sat", + tout << literal_vector(num_lits, lits) << "\n"; if (!m_user_scope_literals.empty()) { tout << "user literals: " << m_user_scope_literals << "\n"; } @@ -914,147 +1079,34 @@ namespace sat { for (unsigned i = 0; !inconsistent() && i < m_user_scope_literals.size(); ++i) { literal nlit = ~m_user_scope_literals[i]; - assign(nlit, justification()); - } - - if (weights && !inconsistent()) { - if (m_config.m_optimize_model) { - m_wsls.set_soft(num_lits, lits, weights); - } - if (!init_weighted_assumptions(num_lits, lits, weights, max_weight)) { - goto retry_init_assumptions; - } - return; + assign(nlit, justification()); } for (unsigned i = 0; !inconsistent() && i < num_lits; ++i) { literal lit = lits[i]; - SASSERT(is_external(lit.var())); - add_assumption(lit); - assign(lit, justification()); + SASSERT(is_external(lit.var())); + add_assumption(lit); + assign(lit, justification()); } } - bool solver::init_weighted_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight) { - flet _min1(m_config.m_minimize_core, false); - m_weight = 0; - m_blocker.reset(); - svector values; - unsigned num_cores = 0; - for (unsigned i = 0; !inconsistent() && i < num_lits; ++i) { - literal lit = lits[i]; - SASSERT(is_external(lit.var())); - TRACE("sat", tout << "propagate: " << lit << " " << value(lit) << "\n";); - SASSERT(m_scope_lvl == 1); - add_assumption(lit); - switch(value(lit)) { - case l_undef: - values.push_back(l_true); - assign(lit, justification()); - if (num_cores*2 >= num_lits) { - break; - } - propagate(false); - if (inconsistent()) { - flet _init(m_initializing_preferred, true); - while (inconsistent()) { - if (!resolve_conflict()) { - return true; - } - propagate(true); - } - if (m_scope_lvl == 0) { - return false; - } - // backjump to last consistent assumption: - unsigned j; - m_weight = 0; - m_blocker.reset(); - for (j = 0; j < i && value(lits[j]) == values[j]; ++j) { - if (values[j] == l_false) { - m_weight += weights[j]; - m_blocker.push_back(lits[j]); - } - } - SASSERT(value(lits[j]) != values[j]); - SASSERT(j <= i); - SASSERT(j == 0 || value(lits[j-1]) == values[j-1]); - for (unsigned k = i; k >= j; --k) { - if (is_assumption(lits[k])) { - pop_assumption(); - } - } - values.resize(j); - TRACE("sat", tout << "backjump " << (i - j + 1) << " steps " << num_cores << "\n";); - i = j - 1; - } - break; - - case l_false: - ++num_cores; - values.push_back(l_false); - SASSERT(!inconsistent()); - set_conflict(justification(), ~lit); - m_conflict_lvl = scope_lvl(); - resolve_conflict_for_unsat_core(); - IF_VERBOSE(3, verbose_stream() << "core: " << m_core << "\n";); - update_min_core(); - SASSERT(m_min_core_valid); - m_weight += weights[i]; - if (m_weight <= max_weight) { - m_blocker.push_back(lit); - } - TRACE("sat", tout << "core: " << m_core << "\nassumptions: " << m_assumptions << "\n";); - SASSERT(m_core.size() <= m_assumptions.size()); - SASSERT(m_assumptions.size() <= i+1); - if (m_core.size() <= 3) { - m_inconsistent = true; - TRACE("opt", tout << "found small core: " << m_core << "\n";); - IF_VERBOSE(11, verbose_stream() << "small core: " << m_core << "\n";); - return true; - } - pop_assumption(); - m_inconsistent = false; - break; - case l_true: - values.push_back(l_true); - SASSERT(m_justification[lit.var()].get_kind() != justification::NONE || lvl(lit) == 0); - break; - } - } - TRACE("sat", tout << "initialized\n";); - IF_VERBOSE(11, verbose_stream() << "Blocker: " << m_blocker << "\nCore: " << m_min_core << "\n";); - if (m_weight >= max_weight) { - // block the current correction set candidate. - ++m_stats.m_blocked_corr_sets; - TRACE("opt", tout << "blocking soft correction set: " << m_blocker << "\n";); - IF_VERBOSE(11, verbose_stream() << "blocking " << m_blocker << "\n";); - pop_to_base_level(); - mk_clause_core(m_blocker); - return false; - } - return true; - } - - - 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(); + m_assumption_set.reset(); } void solver::add_assumption(literal lit) { - m_assumption_set.insert(lit); - m_assumptions.push_back(lit); + m_assumption_set.insert(lit); + m_assumptions.push_back(lit); } void solver::pop_assumption() { @@ -1071,8 +1123,8 @@ namespace sat { for (unsigned i = 0; i < m_min_core.size(); ++i) { literal lit = m_min_core[i]; SASSERT(is_external(lit.var())); - add_assumption(lit); - assign(lit, justification()); + add_assumption(lit); + assign(lit, justification()); } propagate(false); SASSERT(inconsistent()); @@ -1088,6 +1140,13 @@ namespace sat { for (unsigned i = 0; !inconsistent() && i < m_assumptions.size(); ++i) { assign(m_assumptions[i], justification()); } + TRACE("sat", + for (unsigned i = 0; i < m_assumptions.size(); ++i) { + index_set s; + if (m_antecedents.find(m_assumptions[i].var(), s)) { + tout << m_assumptions[i] << ": "; display_index_set(tout, s) << "\n"; + } + }); } } @@ -1107,6 +1166,7 @@ namespace sat { m_restart_threshold = m_config.m_restart_initial; m_luby_idx = 1; m_gc_threshold = m_config.m_gc_initial; + m_restarts = 0; m_min_d_tk = 1.0; m_stopwatch.reset(); m_stopwatch.start(); @@ -1114,11 +1174,6 @@ namespace sat { m_min_core_valid = false; m_min_core.reset(); TRACE("sat", display(tout);); - - if (m_config.m_bcd) { - bceq bc(*this); - bc(); - } } /** @@ -1131,11 +1186,11 @@ namespace sat { } IF_VERBOSE(2, verbose_stream() << "(sat.simplify)\n";); - // Disable simplification during MUS computation. + // Disable simplification during MUS computation. // if (m_mus.is_active()) return; TRACE("sat", tout << "simplify\n";); - pop(scope_lvl()); + pop(scope_lvl()); SASSERT(scope_lvl() == 0); @@ -1148,7 +1203,7 @@ namespace sat { 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()); @@ -1210,9 +1265,6 @@ namespace sat { m_model[v] = value(v); } TRACE("sat_mc_bug", m_mc.display(tout);); - if (m_config.m_optimize_model) { - m_wsls.opt(0, 0, false); - } m_mc(m_model); TRACE("sat", for (bool_var v = 0; v < num; v++) tout << v << ": " << m_model[v] << "\n";); @@ -1242,7 +1294,7 @@ namespace sat { TRACE("sat", tout << "failed: " << c << "\n"; tout << "assumptions: " << m_assumptions << "\n"; tout << "trail: " << m_trail << "\n"; - tout << "model: " << m << "\n"; + tout << "model: " << m << "\n"; m_mc.display(tout); ); ok = false; @@ -1271,10 +1323,10 @@ namespace sat { } for (unsigned i = 0; i < m_assumptions.size(); ++i) { if (value_at(m_assumptions[i], m) != l_true) { - TRACE("sat", + TRACE("sat", tout << m_assumptions[i] << " does not model check\n"; tout << "trail: " << m_trail << "\n"; - tout << "model: " << m << "\n"; + tout << "model: " << m << "\n"; m_mc.display(tout); ); ok = false; @@ -1289,6 +1341,7 @@ namespace sat { void solver::restart() { m_stats.m_restart++; + m_restarts++; IF_VERBOSE(1, verbose_stream() << "(sat-restart :conflicts " << m_stats.m_conflict << " :decisions " << m_stats.m_decision << " :restarts " << m_stats.m_restart << mk_stat(*this) @@ -1638,15 +1691,11 @@ namespace sat { if (m_not_l == literal()) tout << "null literal\n"; else tout << m_not_l << "\n";); - if (m_initializing_preferred) { - SASSERT(m_conflict_lvl <= 1); - return resolve_conflict_for_init(); - } if (m_conflict_lvl <= 1 && tracking_assumptions()) { resolve_conflict_for_unsat_core(); return false; } - + if (m_conflict_lvl == 0) { return false; } @@ -1831,11 +1880,11 @@ namespace sat { bool solver::resolve_conflict_for_init() { if (m_conflict_lvl == 0) { return false; - } + } m_lemma.reset(); m_lemma.push_back(null_literal); // asserted literal if (m_not_l != null_literal) { - TRACE("sat", tout << "not_l: " << m_not_l << "\n";); + TRACE("sat", tout << "not_l: " << m_not_l << "\n";); process_antecedent_for_init(m_not_l); } literal consequent = m_not_l; @@ -1970,7 +2019,7 @@ namespace sat { SASSERT(!is_marked(m_trail[i].var())); }}); - unsigned old_size = m_unmark.size(); + unsigned old_size = m_unmark.size(); int idx = skip_literals_above_conflict_level(); if (m_not_l != null_literal) { @@ -1987,8 +2036,8 @@ namespace sat { process_consequent_for_unsat_core(m_not_l, js); } } - - literal consequent = m_not_l; + + literal consequent = m_not_l; justification js = m_conflict; @@ -2013,15 +2062,15 @@ namespace sat { SASSERT(lvl(consequent) == m_conflict_lvl); js = m_justification[c_var]; idx--; - } + } reset_unmark(old_size); - if (m_config.m_minimize_core) { + if (m_config.m_core_minimize) { if (m_min_core_valid && m_min_core.size() < m_core.size()) { IF_VERBOSE(1, verbose_stream() << "(sat.updating core " << m_min_core.size() << " " << m_core.size() << ")\n";); m_core.reset(); m_core.append(m_min_core); } - // TBD: + // TBD: // apply optional clause minimization by detecting subsumed literals. // initial experiment suggests it has no effect. m_mus(); // ignore return value on cancelation. @@ -2493,7 +2542,8 @@ namespace sat { void solver::pop_reinit(unsigned num_scopes) { pop(num_scopes); - reinit_assumptions(); + exchange_par(); + reinit_assumptions(); } void solver::pop(unsigned num_scopes) { @@ -2560,10 +2610,10 @@ namespace sat { 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; @@ -2656,7 +2706,7 @@ namespace sat { m_phase.shrink(v); m_prev_phase.shrink(v); m_assigned_since_gc.shrink(v); - m_simplifier.reset_todo(); + m_simplifier.reset_todos(); } } @@ -2665,6 +2715,9 @@ namespace sat { 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(true, lit); @@ -2677,7 +2730,7 @@ namespace sat { unassign_vars(i); break; } - } + } gc_var(lit.var()); } } @@ -2809,7 +2862,7 @@ namespace sat { } void solver::display_units(std::ostream & out) const { - unsigned end = scope_lvl() == 0 ? m_trail.size() : m_scopes[0].m_trail_lim; + unsigned end = init_trail_size(); for (unsigned i = 0; i < end; i++) { out << m_trail[i] << " "; } @@ -2895,6 +2948,7 @@ namespace sat { ++max_weight; out << "p wcnf " << num_vars() << " " << num_clauses() + sz << " " << max_weight << "\n"; + out << "c soft " << sz << "\n"; for (unsigned i = 0; i < m_trail.size(); i++) { out << max_weight << " " << dimacs_lit(m_trail[i]) << " 0\n"; @@ -2918,9 +2972,9 @@ namespace sat { clause_vector::const_iterator end = cs.end(); for (; it != end; ++it) { clause const & c = *(*it); - unsigned sz = c.size(); + unsigned clsz = c.size(); out << max_weight << " "; - for (unsigned j = 0; j < sz; j++) + for (unsigned j = 0; j < clsz; j++) out << dimacs_lit(c[j]) << " "; out << "0\n"; } @@ -2928,6 +2982,7 @@ namespace sat { for (unsigned i = 0; i < sz; ++i) { out << weights[i] << " " << lits[i] << " 0\n"; } + out.flush(); } @@ -3021,7 +3076,7 @@ namespace sat { if (scope_lvl() > 0 || inconsistent()) return; m_simplifier(learned); - m_simplifier.free_memory(); + m_simplifier.finalize(); if (m_ext) m_ext->clauses_modifed(); } @@ -3035,6 +3090,385 @@ namespace sat { 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); + collect_bin_clauses(m_user_bin_clauses, false); + hashtable, default_eq > seen_bc; + 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; + literal_pair p(l1, l2); + if (!seen_bc.contains(p)) { + seen_bc.insert(p); + mc.add_edge(l1.index(), l2.index()); + } + } + vector _mutexes; + unsigned_vector ps; + for (unsigned i = 0; i < lits.size(); ++i) { + ps.push_back(lits[i].index()); + } + mc.cliques(ps, _mutexes); + for (unsigned i = 0; i < _mutexes.size(); ++i) { + literal_vector lits; + for (unsigned j = 0; j < _mutexes[i].size(); ++j) { + lits.push_back(to_literal(_mutexes[i][j])); + } + mutexes.push_back(lits); + } + return l_true; + } + + // ----------------------- + // + // Consequence generation. + // + // ----------------------- + + 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; + } + } + 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(); + index_set::iterator it = s.begin(), end = s.end(); + for (; it != end; ++it) { + m_core.push_back(to_literal(*it)); + } + TRACE("sat", tout << m_core << "\n";); + } + + + 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 (unsigned i = 0; i < vars.size(); ++i) { + unfixed_vars.insert(vars[i]); + } + TRACE("sat", tout << asms << "\n";); + m_antecedents.reset(); + 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; + + extract_fixed_consequences(num_units, asms, unfixed_vars, conseq); + + simplify_problem(); + 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); + + if (m_conflicts > m_config.m_max_conflicts) { + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = " << m_conflicts << "\")\n";); + return l_undef; + } + + restart(); + simplify_problem(); + if (check_inconsistent()) { + fixup_consequence_core(); + return l_false; + } + gc(); + + if (m_config.m_restart_max <= num_iterations) { + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-restarts\")\n";); + 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 (unsigned i = 0; i < lits.size(); ++i) { + unfixed_vars.insert(lits[i].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; + + unsigned num_units = 0, num_iterations = 0; + extract_fixed_consequences(num_units, assumptions, unfixed_vars, conseq); + update_unfixed_literals(unfixed_lits, unfixed_vars); + while (!unfixed_lits.empty()) { + if (scope_lvl() > 1) { + pop(scope_lvl() - 1); + } + ++num_iterations; + checkpoint(); + literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); + unsigned num_resolves = 0; + lbool is_sat = l_true; + for (; it != end; ++it) { + literal lit = *it; + if (value(lit) != l_undef) { + continue; + } + push(); + assign(~lit, justification()); + 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; + } + if (scope_lvl() == 1) { + break; + } + } + if (is_sat == l_true) { + if (scope_lvl() == 1 && num_resolves > 0) { + is_sat = l_undef; + } + else { + is_sat = bounded_search(); + if (is_sat == l_undef) { + restart(); + } + } + } + if (is_sat == l_false) { + TRACE("sat", tout << "unsat\n";); + m_inconsistent = false; + } + if (is_sat == l_true) { + delete_unfixed(unfixed_lits, unfixed_vars); + } + extract_fixed_consequences(num_units, assumptions, unfixed_vars, conseq); + 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() + << " 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; + literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); + for (; it != end; ++it) { + literal lit = *it; + 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; + literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); + for (; it != end; ++it) { + literal lit = *it; + 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) { + if (!extract_fixed_consequences(m_trail[i], assumptions, unfixed, conseq)) { + for (i = 0; i < sz && lvl(m_trail[i]) <= 1; ++i) { + VERIFY(extract_fixed_consequences(m_trail[i], assumptions, unfixed, conseq)); + } + break; + } + } + start = sz; + } + + bool solver::check_domain(literal lit, literal lit2) { + return m_antecedents.contains(lit2.var()); + } + + bool solver::extract_assumptions(literal lit, index_set& s) { + justification js = m_justification[lit.var()]; + 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())) return false; + if (!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 = *(m_cls_allocator.get_clause(js.get_clause_offset())); + for (unsigned i = 0; i < c.size(); ++i) { + if (c[i] != lit) { + if (!check_domain(lit, c[i])) return false; + s |= m_antecedents.find(c[i].var()); + } + } + break; + } + case justification::EXT_JUSTIFICATION: { + fill_ext_antecedents(lit, js); + literal_vector::iterator it = m_ext_antecedents.begin(); + literal_vector::iterator end = m_ext_antecedents.end(); + for (; it != end; ++it) { + if (!check_domain(lit, *it)) return false; + s |= m_antecedents.find(it->var()); + } + break; + } + default: + UNREACHABLE(); + break; + } + TRACE("sat", display_index_set(tout << lit << ": " , s) << "\n";); + return true; + } + + std::ostream& solver::display_index_set(std::ostream& out, index_set const& s) const { + index_set::iterator it = s.begin(); + index_set::iterator end = s.end(); + for (; it != end; ++it) { + out << to_literal(*it) << " "; + } + return out; + } + + + bool solver::extract_fixed_consequences(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)) { + return false; + } + add_assumption(lit); + } + m_antecedents.insert(lit.var(), s); + if (unfixed.contains(lit.var())) { + literal_vector cons; + cons.push_back(lit); + index_set::iterator it = s.begin(), end = s.end(); + for (; it != end; ++it) { + cons.push_back(to_literal(*it)); + } + unfixed.remove(lit.var()); + conseq.push_back(cons); + } + return true; + } + void solver::asymmetric_branching() { if (scope_lvl() > 0 || inconsistent()) return; diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 95988f3ca..f910e374f 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -33,7 +33,7 @@ Revision History: #include"sat_iff3_finder.h" #include"sat_probing.h" #include"sat_mus.h" -#include"sat_sls.h" +#include"sat_par.h" #include"params.h" #include"statistics.h" #include"stopwatch.h" @@ -75,6 +75,7 @@ namespace sat { config m_config; stats m_stats; extension * m_ext; + par* m_par; random_gen m_rand; clause_allocator m_cls_allocator; cleaner m_cleaner; @@ -86,7 +87,6 @@ namespace sat { asymm_branch m_asymm_branch; probing m_probing; mus m_mus; // MUS for minimal core extraction - wsls m_wsls; // SLS facility for MaxSAT use bool m_inconsistent; // 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 @@ -130,6 +130,10 @@ namespace sat { literal_set m_assumption_set; // set of enabled assumptions literal_vector m_core; // unsat core + unsigned m_par_limit_in; + unsigned m_par_limit_out; + unsigned m_par_num_vars; + void del_clauses(clause * const * begin, clause * const * end); friend class integrity_checker; @@ -141,9 +145,6 @@ namespace sat { friend class probing; friend class iff3_finder; friend class mus; - friend class sls; - friend class wsls; - friend class bceq; friend struct mk_stat; public: solver(params_ref const & p, reslimit& l, extension * ext); @@ -189,11 +190,9 @@ namespace sat { 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); - void attach_ter_clause(clause & c, bool & reinit); - void attach_ter_clause(clause & c) { bool reinit; attach_ter_clause(c, reinit); } + bool attach_ter_clause(clause & c); clause * mk_nary_clause(unsigned num_lits, literal * lits, bool learned); - void attach_nary_clause(clause & c, bool & reinit); - void attach_nary_clause(clause & c) { bool reinit; attach_nary_clause(c, reinit); } + bool attach_nary_clause(clause & c); void attach_clause(clause & c, bool & reinit); void attach_clause(clause & c) { bool reinit; attach_clause(c, reinit); } unsigned select_watch_lit(clause const & cls, unsigned starting_at) const; @@ -216,6 +215,7 @@ namespace sat { bool inconsistent() const { return m_inconsistent; } unsigned num_vars() const { return m_level.size(); } unsigned num_clauses() const; + unsigned num_restarts() const { return m_restarts; } bool is_external(bool_var v) const { return m_external[v] != 0; } bool was_eliminated(bool_var v) const { return m_eliminated[v] != 0; } unsigned scope_lvl() const { return m_scope_lvl; } @@ -223,6 +223,7 @@ namespace sat { lbool value(bool_var v) const { return static_cast(m_assignment[literal(v, false).index()]); } unsigned lvl(bool_var v) const { return m_level[v]; } unsigned lvl(literal l) const { return m_level[l.var()]; } + unsigned init_trail_size() const { return scope_lvl() == 0 ? m_trail.size() : m_scopes[0].m_trail_lim; } void assign(literal l, justification j) { TRACE("sat_assign", tout << l << " previous value: " << value(l) << "\n";); switch (value(l)) { @@ -237,13 +238,19 @@ namespace sat { lbool status(clause const & c) const; clause_offset get_offset(clause const & c) const { return m_cls_allocator.get_offset(&c); } void checkpoint() { - if (!m_rlimit.inc()) { throw solver_exception(Z3_CANCELED_MSG); } + if (!m_rlimit.inc()) { + m_mc.reset(); + m_model_is_current = false; + throw solver_exception(Z3_CANCELED_MSG); + } ++m_num_checkpoints; if (m_num_checkpoints < 10) return; m_num_checkpoints = 0; if (memory::get_allocation_size() > m_config.m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); } + void set_par(par* p); bool canceled() { return !m_rlimit.inc(); } + config const& get_config() { return m_config; } typedef std::pair bin_clause; protected: watch_list & get_wlist(literal l) { return m_watches[l.index()]; } @@ -276,10 +283,7 @@ namespace sat { // // ----------------------- public: - lbool check(unsigned num_lits = 0, literal const* lits = 0) { - return check(num_lits, lits, 0, 0); - } - lbool check(unsigned num_lits, literal const* lits, double const* weights, double max_weight); + lbool check(unsigned num_lits = 0, literal const* lits = 0); model const & get_model() const { return m_model; } bool model_is_current() const { return m_model_is_current; } @@ -289,6 +293,7 @@ namespace sat { protected: unsigned m_conflicts; + unsigned m_restarts; unsigned m_conflicts_since_restart; unsigned m_restart_threshold; unsigned m_luby_idx; @@ -300,15 +305,13 @@ namespace sat { 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; - literal_vector m_blocker; - double m_weight; - bool m_initializing_preferred; - void init_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight); - bool init_weighted_assumptions(unsigned num_lits, literal const* lits, double const* weights, double max_weight); + void init_assumptions(unsigned num_lits, literal const* lits); void reassert_min_core(); void update_min_core(); void resolve_weighted(); @@ -323,6 +326,8 @@ namespace sat { bool check_model(model const & m) const; void restart(); void sort_watch_lits(); + void exchange_par(); + lbool check_par(unsigned num_lits, literal const* lits); // ----------------------- // @@ -409,6 +414,7 @@ namespace sat { void gc_lit(clause_vector& clauses, literal lit); void gc_bin(bool learned, literal nlit); 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); @@ -428,6 +434,43 @@ namespace sat { 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); + + private: + + typedef hashtable index_set; + + u_map m_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); + + bool extract_fixed_consequences(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 diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 6d8b8c1d9..83c31715d 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -33,6 +33,8 @@ Notes: #include "filter_model_converter.h" #include "bit_blaster_model_converter.h" #include "ast_translation.h" +#include "ast_util.h" +#include "propagate_values_tactic.h" // incremental SAT solver. class inc_sat_solver : public solver { @@ -103,51 +105,51 @@ public: virtual void set_progress_callback(progress_callback * callback) {} - virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions) { - return check_sat(num_assumptions, assumptions, 0, 0); - } void display_weighted(std::ostream& out, unsigned sz, expr * const * assumptions, unsigned const* weights) { - m_weights.reset(); if (weights != 0) { 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, assumptions, dep2asm)); + 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()); } - lbool check_sat(unsigned sz, expr * const * assumptions, double const* weights, double max_weight) { - m_weights.reset(); - if (weights != 0) { - m_weights.append(sz, weights); - } - SASSERT(m_weights.empty() == (m_weights.c_ptr() == 0)); + virtual lbool check_sat(unsigned sz, expr * const * assumptions) { m_solver.pop_to_base_level(); dep2asm_t dep2asm; m_model = 0; lbool r = internalize_formulas(); if (r != l_true) return r; r = internalize_assumptions(sz, assumptions, dep2asm); - SASSERT(sz == m_asms.size()); if (r != l_true) return r; - r = m_solver.check(m_asms.size(), m_asms.c_ptr(), m_weights.c_ptr(), max_weight); + r = m_solver.check(m_asms.size(), m_asms.c_ptr()); + switch (r) { case l_true: - if (sz > 0 && !weights) { + if (sz > 0) { check_assumptions(dep2asm); } break; case l_false: // TBD: expr_dependency core is not accounted for. - if (sz > 0) { + if (!m_asms.empty()) { extract_core(dep2asm); } break; @@ -197,7 +199,7 @@ public: assert_expr(t); } } - virtual ast_manager& get_manager() { return m; } + virtual ast_manager& get_manager() const { return m; } virtual void assert_expr(expr * t) { TRACE("sat", tout << mk_pp(t, m) << "\n";); m_fmls.push_back(t); @@ -232,6 +234,75 @@ public: return 0; } + virtual lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) { + init_preprocess(); + TRACE("sat", tout << assumptions << "\n" << vars << "\n";); + sat::literal_vector asms; + sat::bool_var_vector bvars; + vector lconseq; + dep2asm_t dep2asm; + 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); + } + 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 (unsigned i = 0; i < vars.size(); ++i) { + expr_ref cons(m); + if (extract_fixed_variable(dep2asm, asm2dep, vars[i], bool_var2conseq, lconseq, cons)) { + conseq.push_back(cons); + } + } + return r; + } + + virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { + 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 (unsigned i = 0; i < ls_mutexes.size(); ++i) { + sat::literal_vector const ls_mutex = ls_mutexes[i]; + expr_ref_vector mutex(m); + for (unsigned j = 0; j < ls_mutex.size(); ++j) { + mutex.push_back(lit2var.find(ls_mutex[j].index())); + } + mutexes.push_back(mutex); + } + return l_true; + } + + virtual std::string reason_unknown() const { return m_unknown; } @@ -269,14 +340,16 @@ public: 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_card2bv_tactic(m, m_params), using_params(mk_simplify_tactic(m), simp2_p), mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m, m_bb_rewriter.get()), //mk_aig_tactic(), + //mk_propagate_values_tactic(m, simp2_p), using_params(mk_simplify_tactic(m), simp2_p)); - for (unsigned i = 0; i < m_num_scopes; ++i) { + while (m_bb_rewriter->get_num_scopes() < m_num_scopes) { m_bb_rewriter->push(); } m_preprocess->reset(); @@ -299,6 +372,7 @@ private: } catch (tactic_exception & ex) { IF_VERBOSE(0, verbose_stream() << "exception in tactic " << ex.msg() << "\n";); + TRACE("sat", tout << "exception: " << ex.msg() << "\n";); m_preprocess = 0; m_bb_rewriter = 0; return l_undef; @@ -308,13 +382,23 @@ private: return l_undef; } g = m_subgoals[0]; + expr_ref_vector atoms(m); TRACE("sat", g->display_with_dependencies(tout);); m_goal2sat(*g, m_params, m_solver, m_map, dep2asm, true); + m_goal2sat.get_interpreted_atoms(atoms); + 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) { + if (sz == 0 && get_num_assumptions() == 0) { m_asms.shrink(0); return l_true; } @@ -322,6 +406,9 @@ private: 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); if (res == l_true) { extract_assumptions(sz, asms, dep2asm); @@ -329,16 +416,125 @@ private: return res; } + lbool internalize_vars(expr_ref_vector const& vars, sat::bool_var_vector& bvars) { + for (unsigned i = 0; i < vars.size(); ++i) { + internalize_var(vars[i], bvars); + } + return l_true; + } + + bool internalize_var(expr* v, sat::bool_var_vector& bvars) { + obj_map const& const2bits = m_bb_rewriter->const2bits(); + 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; + unsigned sz = abv->get_num_args(); + for (unsigned j = 0; j < sz; ++j) { + SASSERT(is_uninterp_const(abv->get_arg(j))); + sat::bool_var b = m_map.to_bool_var(abv->get_arg(j)); + if (b == sat::null_bool_var) { + internalized = false; + } + else { + bvars.push_back(b); + } + } + CTRACE("sat", internalized, tout << "var: "; for (unsigned j = 0; j < sz; ++j) tout << bvars[bvars.size()-sz+j] << " "; tout << "\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 (unsigned i = 0; i < bvars.size(); ++i) { + unsigned index; + if (bool_var2conseq.find(bvars[i], 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 = 0; + 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(); + } + } + 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 (; m_fmls_head < m_fmls.size(); ++m_fmls_head) { - g->assert_expr(m_fmls[m_fmls_head].get()); + for (unsigned i = m_fmls_head ; i < m_fmls.size(); ++i) { + g->assert_expr(m_fmls[i].get()); } - return internalize_goal(g, dep2asm); + lbool res = internalize_goal(g, dep2asm); + if (res != l_undef) { + m_fmls_head = m_fmls.size(); + } + return res; } void extract_assumptions(unsigned sz, expr* const* asms, dep2asm_t& dep2asm) { @@ -355,16 +551,27 @@ private: ++j; } } + for (unsigned i = 0; i < get_num_assumptions(); ++i) { + if (dep2asm.find(get_assumption(i), lit)) { + SASSERT(lit.var() <= m_solver.num_vars()); + m_asms.push_back(lit); + } + } + SASSERT(dep2asm.size() == m_asms.size()); } - void extract_core(dep2asm_t& dep2asm) { - u_map asm2dep; + void extract_asm2dep(dep2asm_t const& dep2asm, u_map& asm2dep) { dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); for (; it != end; ++it) { expr* e = it->m_key; asm2dep.insert(it->m_value.index(), e); } + } + + void extract_core(dep2asm_t& dep2asm) { + u_map asm2dep; + extract_asm2dep(dep2asm, asm2dep); sat::literal_vector const& core = m_solver.get_core(); TRACE("sat", dep2asm_t::iterator it2 = dep2asm.begin(); @@ -381,7 +588,7 @@ private: m_core.reset(); for (unsigned i = 0; i < core.size(); ++i) { - expr* e; + expr* e = 0; VERIFY(asm2dep.find(core[i].index(), e)); m_core.push_back(e); } @@ -403,7 +610,7 @@ private: } void extract_model() { - TRACE("sat", tout << "retrieve model\n";); + TRACE("sat", tout << "retrieve model " << (m_solver.model_is_current()?"present":"absent") << "\n";); if (!m_solver.model_is_current()) { m_model = 0; return; @@ -459,15 +666,6 @@ solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p) { } -lbool inc_sat_check_sat(solver& _s, unsigned sz, expr*const* soft, rational const* _weights, rational const& max_weight) { - inc_sat_solver& s = dynamic_cast(_s); - vector weights; - for (unsigned i = 0; _weights && i < sz; ++i) { - weights.push_back(_weights[i].get_double()); - } - return s.check_sat(sz, soft, weights.c_ptr(), max_weight.get_double()); -} - 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; @@ -477,6 +675,6 @@ void inc_sat_display(std::ostream& out, solver& _s, unsigned sz, expr*const* sof } weights.push_back(_weights[i].get_unsigned()); } - return s.display_weighted(out, sz, soft, weights.c_ptr()); + s.display_weighted(out, sz, soft, weights.c_ptr()); } diff --git a/src/sat/sat_solver/inc_sat_solver.h b/src/sat/sat_solver/inc_sat_solver.h index 028f71b06..4b0bea50e 100644 --- a/src/sat/sat_solver/inc_sat_solver.h +++ b/src/sat/sat_solver/inc_sat_solver.h @@ -24,7 +24,6 @@ Notes: solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p); -lbool inc_sat_check_sat(solver& s, unsigned sz, expr*const* soft, rational const* weights, rational const& max_weight); void inc_sat_display(std::ostream& out, solver& s, unsigned sz, expr*const* soft, rational const* _weights); diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index c0c087e59..28d8d761a 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -40,9 +40,9 @@ namespace sat { 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 @@ -54,33 +54,33 @@ namespace sat { 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_var var() const { + return m_val >> 1; } - + bool sign() const { - return m_val & 1; + 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); } @@ -96,6 +96,7 @@ namespace sat { }; 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; } @@ -105,6 +106,7 @@ namespace sat { inline std::ostream & operator<<(std::ostream & out, literal l) { out << (l.sign() ? "-" : "") << l.var(); return out; } typedef svector literal_vector; + typedef std::pair literal_pair; typedef unsigned clause_offset; typedef unsigned ext_constraint_idx; @@ -115,7 +117,7 @@ namespace sat { typedef approx_set_tpl literal_approx_set; typedef approx_set_tpl var_approx_set; - + enum phase { POS_PHASE, NEG_PHASE, PHASE_NOT_AVAILABLE }; @@ -127,7 +129,7 @@ namespace sat { typedef ptr_vector clause_vector; class solver_exception : public default_exception { - public: + public: solver_exception(char const * msg):default_exception(msg) {} }; @@ -137,7 +139,7 @@ namespace sat { 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++) { @@ -153,12 +155,24 @@ namespace sat { svector m_set; public: typedef svector::const_iterator iterator; - void insert(unsigned v) { + 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); + 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 = 0; + for (i = 0; i < m_set.size() && m_set[i] != v; ++i) + ; + SASSERT(i < m_set.size()); + m_set[i] = m_set.back(); + m_set.pop_back(); + } } uint_set& operator=(uint_set const& other) { @@ -166,28 +180,28 @@ namespace sat { m_set = other.m_set; return *this; } - - bool contains(unsigned v) const { - return v < m_in_set.size() && m_in_set[v] != 0; + + bool contains(unsigned v) const { + return v < m_in_set.size() && m_in_set[v] != 0; } - - bool empty() const { - return m_set.empty(); + + 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 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 cleanup() { m_set.finalize(); m_in_set.finalize(); } + void finalize() { m_set.finalize(); m_in_set.finalize(); } uint_set& operator&=(uint_set const& other) { unsigned j = 0; for (unsigned i = 0; i < m_set.size(); ++i) { @@ -227,13 +241,26 @@ namespace sat { } 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 cleanup() { m_set.cleanup(); } + void finalize() { m_set.finalize(); } class iterator { uint_set::iterator m_it; public: @@ -255,10 +282,10 @@ namespace sat { 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); out << " :memory " << std::fixed << std::setprecision(2) << mem; diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 4d5542866..742a4fb1d 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -59,6 +59,7 @@ struct goal2sat::imp { 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; imp(ast_manager & _m, params_ref const & p, sat::solver & s, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external): @@ -67,6 +68,7 @@ struct goal2sat::imp { m_map(map), m_dep2asm(dep2asm), m_trail(m), + m_interpreted_atoms(m), m_default_external(default_external) { updt_params(p); m_true = sat::null_bool_var; @@ -128,6 +130,9 @@ struct goal2sat::imp { m_map.insert(t, v); l = sat::literal(v, sign); TRACE("goal2sat", tout << "new_var: " << v << "\n" << mk_ismt2_pp(t, m) << "\n";); + if (ext && !is_uninterp_const(t)) { + m_interpreted_atoms.push_back(t); + } } } else { @@ -169,6 +174,7 @@ struct goal2sat::imp { switch (to_app(t)->get_decl_kind()) { case OP_NOT: case OP_OR: + case OP_AND: case OP_IFF: m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); return false; @@ -180,7 +186,6 @@ struct goal2sat::imp { } convert_atom(t, root, sign); return true; - case OP_AND: case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: { @@ -210,8 +215,8 @@ struct goal2sat::imp { } else { mk_clause(m_result_stack.size(), m_result_stack.c_ptr()); - m_result_stack.reset(); } + m_result_stack.reset(); } else { SASSERT(num <= m_result_stack.size()); @@ -235,6 +240,48 @@ struct goal2sat::imp { } } + 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(); + } + } + 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.mk_var(); + 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); + } + } + + void convert_ite(app * n, bool root, bool sign) { unsigned sz = m_result_stack.size(); SASSERT(sz >= 3); @@ -311,6 +358,9 @@ struct goal2sat::imp { 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; @@ -451,7 +501,6 @@ struct unsupported_bool_proc { void operator()(app * n) { if (n->get_family_id() == m.get_basic_family_id()) { switch (n->get_decl_kind()) { - case OP_AND: case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: @@ -474,7 +523,7 @@ bool goal2sat::has_unsupported_bool(goal const & g) { return test(g); } -goal2sat::goal2sat():m_imp(0) { +goal2sat::goal2sat():m_imp(0), m_interpreted_atoms(0) { } void goal2sat::collect_param_descrs(param_descrs & r) { @@ -492,10 +541,20 @@ struct goal2sat::scoped_set_imp { } }; + void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external) { imp proc(g.m(), p, t, m, dep2asm, default_external); scoped_set_imp set(this, &proc); 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); + } } diff --git a/src/sat/tactic/goal2sat.h b/src/sat/tactic/goal2sat.h index c6776f2f7..cd63cd497 100644 --- a/src/sat/tactic/goal2sat.h +++ b/src/sat/tactic/goal2sat.h @@ -38,8 +38,11 @@ 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; @@ -53,12 +56,13 @@ public: \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 & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false); + void get_interpreted_atoms(expr_ref_vector& atoms); }; diff --git a/src/sat/tactic/sat_tactic.cpp b/src/sat/tactic/sat_tactic.cpp index da513d97b..f99e46851 100644 --- a/src/sat/tactic/sat_tactic.cpp +++ b/src/sat/tactic/sat_tactic.cpp @@ -73,11 +73,11 @@ class sat_tactic : public tactic { if (r == l_false) { expr_dependency * lcore = 0; if (produce_core) { - sat::literal_vector const& core = m_solver.get_core(); + sat::literal_vector const& ucore = m_solver.get_core(); u_map asm2dep; mk_asm2dep(dep2asm, asm2dep); - for (unsigned i = 0; i < core.size(); ++i) { - sat::literal lit = core[i]; + 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)); } diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index 43ef383d2..83b900c5c 100644 --- a/src/shell/datalog_frontend.cpp +++ b/src/shell/datalog_frontend.cpp @@ -67,25 +67,27 @@ static void display_statistics( p.set_uint("profile_milliseconds_threshold", 100); ctx.updt_params(p); - out << "--------------\n"; - out << "original rules\n"; - orig_rules.display(out); + 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 << "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); + out << "--------------\n"; + out << "instructions \n"; + code.display(ex_ctx, out); + + out << "--------------\n"; + out << "big relations \n"; + ex_ctx.report_big_relations(1000, out);); } - out << "--------------\n"; - out << "relation sizes\n"; - ctx.get_rel_context()->get_rmanager().display_relation_sizes(out); + IF_VERBOSE(2, + out << "--------------\n"; + out << "relation sizes\n"; + ctx.get_rel_context()->get_rmanager().display_relation_sizes(out);); if (verbose) { out << "--------------\n"; diff --git a/src/shell/main.cpp b/src/shell/main.cpp index d1a9d21c6..fddce4f69 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -154,7 +154,18 @@ void parse_cmd_line_args(int argc, char ** argv) { exit(0); } if (strcmp(opt_name, "version") == 0) { - std::cout << "Z3 version " << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER << "\n"; + std::cout << "Z3 version " << Z3_MAJOR_VERSION << "." << Z3_MINOR_VERSION << "." << Z3_BUILD_NUMBER; + std::cout << " - "; +#ifdef _AMD64_ + 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, "smt") == 0) { diff --git a/src/shell/opt_frontend.cpp b/src/shell/opt_frontend.cpp index a45800c62..760fdcf54 100644 --- a/src/shell/opt_frontend.cpp +++ b/src/shell/opt_frontend.cpp @@ -351,6 +351,21 @@ static unsigned parse_opt(std::istream& in, bool is_wcnf) { case l_false: std::cout << "unsat\n"; break; case l_undef: std::cout << "unknown\n"; break; } + DEBUG_CODE( + if (false && r == l_true) { + model_ref mdl; + opt.get_model(mdl); + expr_ref_vector hard(m); + opt.get_hard_constraints(hard); + for (unsigned i = 0; i < hard.size(); ++i) { + std::cout << "validate: " << i << "\n"; + expr_ref tmp(m); + VERIFY(mdl->eval(hard[i].get(), tmp)); + if (!m.is_true(tmp)) { + std::cout << tmp << "\n"; + } + } + }); } catch (z3_exception & ex) { std::cerr << ex.msg() << "\n"; diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index 31c964ae0..c9fa69221 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -29,6 +29,7 @@ Revision History: #include"opt_cmds.h" #include"polynomial_cmds.h" #include"subpaving_cmds.h" +#include"smt2_extra_cmds.h" #include"smt_strategic_solver.h" #include"smt_solver.h" @@ -113,6 +114,7 @@ unsigned read_smtlib2_commands(char const * file_name) { 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); diff --git a/src/smt/arith_eq_adapter.cpp b/src/smt/arith_eq_adapter.cpp index 411588d79..ce831f9ae 100644 --- a/src/smt/arith_eq_adapter.cpp +++ b/src/smt/arith_eq_adapter.cpp @@ -264,7 +264,7 @@ namespace smt { } void arith_eq_adapter::collect_statistics(::statistics & st) const { - st.update("eq adapter", m_stats.m_num_eq_axioms); + st.update("arith eq adapter", m_stats.m_num_eq_axioms); } void arith_eq_adapter::display_already_processed(std::ostream & out) const { diff --git a/src/smt/arith_eq_solver.cpp b/src/smt/arith_eq_solver.cpp index 9a6868ff1..de88d37bb 100644 --- a/src/smt/arith_eq_solver.cpp +++ b/src/smt/arith_eq_solver.cpp @@ -113,6 +113,8 @@ unsigned arith_eq_solver::find_abs_min(vector& values) { 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] << " "; @@ -125,6 +127,7 @@ static void print_rows(std::ostream& out, vector > const& rows) print_row(out, rows[i]); } } +#endif // // The gcd of the coefficients to variables have to divide the diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index 3ba6693d9..26395f9ab 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -184,13 +184,13 @@ void asserted_formulas::get_assertions(ptr_vector & result) { } void asserted_formulas::push_scope() { - SASSERT(inconsistent() || m_asserted_qhead == m_asserted_formulas.size()); + SASSERT(inconsistent() || m_asserted_qhead == m_asserted_formulas.size() || m_manager.canceled()); TRACE("asserted_formulas_scopes", tout << "push:\n"; display(tout);); m_scopes.push_back(scope()); m_macro_manager.push_scope(); scope & s = m_scopes.back(); s.m_asserted_formulas_lim = m_asserted_formulas.size(); - SASSERT(inconsistent() || s.m_asserted_formulas_lim == m_asserted_qhead); + SASSERT(inconsistent() || s.m_asserted_formulas_lim == m_asserted_qhead || m_manager.canceled()); s.m_inconsistent_old = m_inconsistent; m_defined_names.push(); m_bv_sharing.push_scope(); @@ -543,8 +543,12 @@ void asserted_formulas::infer_patterns() { } void asserted_formulas::commit() { - m_macro_manager.mark_forbidden(m_asserted_formulas.size() - m_asserted_qhead, m_asserted_formulas.c_ptr() + m_asserted_qhead); - m_asserted_qhead = m_asserted_formulas.size(); + commit(m_asserted_formulas.size()); +} + +void asserted_formulas::commit(unsigned new_qhead) { + m_macro_manager.mark_forbidden(new_qhead - m_asserted_qhead, m_asserted_formulas.c_ptr() + m_asserted_qhead); + m_asserted_qhead = new_qhead; } void asserted_formulas::eliminate_term_ite() { diff --git a/src/smt/asserted_formulas.h b/src/smt/asserted_formulas.h index 75fb86703..9e9ecf33a 100644 --- a/src/smt/asserted_formulas.h +++ b/src/smt/asserted_formulas.h @@ -113,6 +113,7 @@ public: unsigned get_formulas_last_level() const; unsigned get_qhead() const { return m_asserted_qhead; } void commit(); + void commit(unsigned new_qhead); expr * get_formula(unsigned idx) const { return m_asserted_formulas.get(idx); } proof * get_formula_proof(unsigned idx) const { return m_manager.proofs_enabled() ? m_asserted_formula_prs.get(idx) : 0; } expr * const * get_formulas() const { return m_asserted_formulas.c_ptr(); } diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 0d7f6a3a6..5d03a3563 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -424,7 +424,7 @@ namespace smt { out << *curr; curr = curr->m_next; while (curr != 0 && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { - out << " "; + out << "\n"; out << *curr; curr = curr->m_next; } @@ -490,6 +490,7 @@ namespace smt { #ifdef _PROFILE_MAM m_counter = 0; #endif + (void)m_lbl_hasher; } #ifdef _PROFILE_MAM @@ -795,7 +796,8 @@ namespace smt { code_tree * m_tree; unsigned m_num_choices; bool m_is_tmp_tree; - svector m_mp_already_processed; + svector m_mp_already_processed; + obj_map m_matched_exprs; struct pcheck_checked { func_decl * m_label; @@ -879,6 +881,9 @@ namespace smt { */ void get_stats_core(app * n, unsigned & sz, unsigned & num_unbound_vars) { sz++; + if (n->is_ground()) { + return; + } unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); @@ -901,7 +906,7 @@ namespace smt { void get_stats(app * n, unsigned & sz, unsigned & num_unbound_vars) { sz = 0; num_unbound_vars = 0; - return get_stats_core(n, sz, num_unbound_vars); + get_stats_core(n, sz, num_unbound_vars); } /** @@ -948,7 +953,15 @@ namespace smt { 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)); @@ -1032,6 +1045,9 @@ namespace smt { */ 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); @@ -1100,7 +1116,7 @@ namespace smt { unsigned best_j = 0; bool found_bounded_mp = false; for (unsigned j = 0; j < m_mp->get_num_args(); j++) { - if (std::find(m_mp_already_processed.begin(), m_mp_already_processed.end(), j) != m_mp_already_processed.end()) + if (m_mp_already_processed[j]) continue; app * p = to_app(m_mp->get_arg(j)); bool has_unbound_vars = false; @@ -1117,7 +1133,7 @@ namespace smt { best_j = j; } } - m_mp_already_processed.push_back(best_j); + m_mp_already_processed[best_j] = true; SASSERT(best != 0); app * p = best; func_decl * lbl = p->get_decl(); @@ -1210,13 +1226,16 @@ namespace smt { */ void linearise(instruction * head, unsigned first_idx) { m_seq.reset(); - m_mp_already_processed.reset(); - m_mp_already_processed.push_back(first_idx); + m_matched_exprs.reset(); while (!m_todo.empty()) linearise_core(); - if (m_mp->get_num_args() > 1) + 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++) { @@ -1305,9 +1324,6 @@ namespace smt { unsigned reg2 = instr->m_reg2; return m_registers[reg1] != 0 && - m_registers[reg2] != 0 && - is_var(m_registers[reg1]) && - is_var(m_registers[reg2]) && m_registers[reg1] == m_registers[reg2]; } @@ -1565,12 +1581,13 @@ namespace smt { unsigned reg1 = static_cast(curr)->m_reg1; unsigned reg2 = static_cast(curr)->m_reg2; SASSERT(m_todo.contains(reg2)); - m_todo.erase(reg1); m_todo.erase(reg2); - SASSERT(is_var(m_registers[reg1])); - unsigned var_id = to_var(m_registers[reg1])->get_idx(); - if (m_vars[var_id] == -1) - m_vars[var_id] = reg1; + if (is_var(m_registers[reg1])) { + m_todo.erase(reg1); + 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 { @@ -2147,7 +2164,7 @@ namespace smt { enode_vector * best_v = 0; for (unsigned i = 0; i < num_args; i++) { enode * bare = c->m_joints[i]; - enode_vector * curr_v; + enode_vector * curr_v = 0; switch (GET_TAG(bare)) { case NULL_TAG: curr_v = 0; @@ -2865,6 +2882,7 @@ namespace smt { - 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_ast_manager; SASSERT(m_ast_manager.is_pattern(mp)); SASSERT(first_idx < mp->get_num_args()); app * p = to_app(mp->get_arg(first_idx)); diff --git a/src/smt/params/dyn_ack_params.cpp b/src/smt/params/dyn_ack_params.cpp index 15f48bad1..b62530fbf 100644 --- a/src/smt/params/dyn_ack_params.cpp +++ b/src/smt/params/dyn_ack_params.cpp @@ -28,3 +28,14 @@ void dyn_ack_params::updt_params(params_ref const & _p) { 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); +} \ No newline at end of file diff --git a/src/smt/params/dyn_ack_params.h b/src/smt/params/dyn_ack_params.h index f87e3b6df..017b7fe94 100644 --- a/src/smt/params/dyn_ack_params.h +++ b/src/smt/params/dyn_ack_params.h @@ -47,6 +47,8 @@ public: } void updt_params(params_ref const & _p); + + void display(std::ostream & out) const; }; diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index 9ad787c2a..7a0e96248 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -32,3 +32,33 @@ void preprocessor_params::updt_params(params_ref const & p) { arith_simplifier_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); + bv_simplifier_params::display(out); + arith_simplifier_params::display(out); + + DISPLAY_PARAM(m_lift_ite); + DISPLAY_PARAM(m_ng_lift_ite); + DISPLAY_PARAM(m_pull_cheap_ite_trees); + DISPLAY_PARAM(m_pull_nested_quantifiers); + DISPLAY_PARAM(m_eliminate_term_ite); + DISPLAY_PARAM(m_eliminate_and); + DISPLAY_PARAM(m_macro_finder); + DISPLAY_PARAM(m_propagate_values); + DISPLAY_PARAM(m_propagate_booleans); + 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); +} diff --git a/src/smt/params/preprocessor_params.h b/src/smt/params/preprocessor_params.h index 3806b26cf..4ffad48a2 100644 --- a/src/smt/params/preprocessor_params.h +++ b/src/smt/params/preprocessor_params.h @@ -83,6 +83,8 @@ public: 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_ */ diff --git a/src/smt/params/qi_params.cpp b/src/smt/params/qi_params.cpp index 60fcd6fc4..8182222e4 100644 --- a/src/smt/params/qi_params.cpp +++ b/src/smt/params/qi_params.cpp @@ -36,3 +36,30 @@ void qi_params::updt_params(params_ref const & _p) { m_qi_cost = p.qi_cost(); m_qi_max_eager_multipatterns = p.qi_max_multi_patterns(); } + +#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; + +void qi_params::display(std::ostream & out) const { + DISPLAY_PARAM(m_qi_ematching); + 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); +} diff --git a/src/smt/params/qi_params.h b/src/smt/params/qi_params.h index 00baea170..c9736909a 100644 --- a/src/smt/params/qi_params.h +++ b/src/smt/params/qi_params.h @@ -98,13 +98,15 @@ struct qi_params { m_mbqi_max_cexs_incr(1), m_mbqi_max_iterations(1000), m_mbqi_trace(false), - m_mbqi_force_template(10), + m_mbqi_force_template(10), m_mbqi_id(0) { updt_params(p); } void updt_params(params_ref const & p); + + void display(std::ostream & out) const; }; #endif /* QI_PARAMS_H_ */ diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index f295e260b..dcf396531 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -65,3 +65,96 @@ 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_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_abort_after_preproc); + + DISPLAY_PARAM(m_model); + DISPLAY_PARAM(m_model_compact); + DISPLAY_PARAM(m_model_on_timeout); + DISPLAY_PARAM(m_model_on_final_check); + + DISPLAY_PARAM(m_progress_sampling_freq); + + DISPLAY_PARAM(m_display_installed_theories); + 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_timeout); + DISPLAY_PARAM(m_rlimit); + DISPLAY_PARAM(m_at_labels_cex); + DISPLAY_PARAM(m_check_at_labels); + DISPLAY_PARAM(m_dump_goal_as_smt); + DISPLAY_PARAM(m_auto_config); +} \ No newline at end of file diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index a0c90a525..a86123a33 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -295,6 +295,8 @@ struct smt_params : public preprocessor_params, void updt_params(params_ref const & p); void updt_params(context_params const & p); + + void display(std::ostream & out) const; }; #endif /* SMT_PARAMS_H_ */ diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 75ee20ebd..a72d4d619 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -1,4 +1,4 @@ -def_module_params(module_name='smt', +def_module_params(module_name='smt', class_name='smt_params_helper', description='smt solver based on lazy smt', export=True, @@ -13,11 +13,11 @@ def_module_params(module_name='smt', ('restart_factor', DOUBLE, 1.1, 'when using geometric (or inner-outer-geometric) progression of restarts, it specifies the constant used to multiply the currect 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'), ('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, ingored if delay_units is false'), + ('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'), - ('timeout', UINT, UINT_MAX, 'timeout (in milliseconds) (0 means immediate timeout)'), - ('rlimit', UINT, 0, 'resource limit (0 means no limit)'), + ('timeout', UINT, UINT_MAX, 'timeout (in milliseconds) (UINT_MAX and 0 mean no timeout)'), + ('rlimit', UINT, 0, 'resource limit (0 means no limit)'), ('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts before giving up.'), ('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'), @@ -74,5 +74,8 @@ def_module_params(module_name='smt', ('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.'), ('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.overlap_priority', DOUBLE, -0.1, 'theory-aware priority for overlapping variable cases; use smt.theory_aware_branching=true'), + ('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') )) diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index fef7ca2a0..1e3f29142 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -38,3 +38,51 @@ void theory_arith_params::updt_params(params_ref const & _p) { } +#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; + +void theory_arith_params::display(std::ostream & out) const { + 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); +} \ No newline at end of file diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index 18418d1ef..943bd711e 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -156,6 +156,8 @@ struct theory_arith_params { } void updt_params(params_ref const & p); + + void display(std::ostream & out) const; }; #endif /* THEORY_ARITH_PARAMS_H_ */ diff --git a/src/smt/params/theory_array_params.cpp b/src/smt/params/theory_array_params.cpp index e3c8b2448..c0015bf2c 100644 --- a/src/smt/params/theory_array_params.cpp +++ b/src/smt/params/theory_array_params.cpp @@ -25,4 +25,16 @@ void theory_array_params::updt_params(params_ref const & _p) { 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); +} \ No newline at end of file diff --git a/src/smt/params/theory_array_params.h b/src/smt/params/theory_array_params.h index 85996078f..af51427c4 100644 --- a/src/smt/params/theory_array_params.h +++ b/src/smt/params/theory_array_params.h @@ -71,6 +71,7 @@ struct theory_array_params : public array_simplifier_params { } #endif + void display(std::ostream & out) const; }; diff --git a/src/smt/params/theory_bv_params.cpp b/src/smt/params/theory_bv_params.cpp index d3f386ab4..631c5765b 100644 --- a/src/smt/params/theory_bv_params.cpp +++ b/src/smt/params/theory_bv_params.cpp @@ -24,3 +24,14 @@ void theory_bv_params::updt_params(params_ref const & _p) { 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_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); +} \ No newline at end of file diff --git a/src/smt/params/theory_bv_params.h b/src/smt/params/theory_bv_params.h index 00565969e..5830e5176 100644 --- a/src/smt/params/theory_bv_params.h +++ b/src/smt/params/theory_bv_params.h @@ -44,6 +44,8 @@ struct theory_bv_params { } void updt_params(params_ref const & p); + + void display(std::ostream & out) const; }; #endif /* THEORY_BV_PARAMS_H_ */ diff --git a/src/smt/params/theory_datatype_params.h b/src/smt/params/theory_datatype_params.h index 4dd09a596..9f801e46c 100644 --- a/src/smt/params/theory_datatype_params.h +++ b/src/smt/params/theory_datatype_params.h @@ -31,6 +31,8 @@ struct theory_datatype_params { p.register_unsigned_param("dt_lazy_splits", m_dt_lazy_splits, "How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy"); } #endif + + void display(std::ostream & out) const { out << "m_dt_lazy_splits=" << m_dt_lazy_splits << std::endl; } }; diff --git a/src/smt/params/theory_pb_params.cpp b/src/smt/params/theory_pb_params.cpp index 6d980fe5d..a1e13a6e7 100644 --- a/src/smt/params/theory_pb_params.cpp +++ b/src/smt/params/theory_pb_params.cpp @@ -26,3 +26,12 @@ void theory_pb_params::updt_params(params_ref const & _p) { m_pb_enable_compilation = p.pb_enable_compilation(); m_pb_enable_simplex = p.pb_enable_simplex(); } + +#define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; + +void theory_pb_params::display(std::ostream & out) const { + DISPLAY_PARAM(m_pb_conflict_frequency); + DISPLAY_PARAM(m_pb_learn_complements); + DISPLAY_PARAM(m_pb_enable_compilation); + DISPLAY_PARAM(m_pb_enable_simplex); +} \ No newline at end of file diff --git a/src/smt/params/theory_pb_params.h b/src/smt/params/theory_pb_params.h index 2af4f04b7..6a129e601 100644 --- a/src/smt/params/theory_pb_params.h +++ b/src/smt/params/theory_pb_params.h @@ -35,6 +35,8 @@ struct theory_pb_params { {} void updt_params(params_ref const & p); + + void display(std::ostream & out) const; }; #endif /* THEORY_PB_PARAMS_H_ */ diff --git a/src/smt/proto_model/numeral_factory.cpp b/src/smt/proto_model/numeral_factory.cpp index a67b6c075..49ff15167 100644 --- a/src/smt/proto_model/numeral_factory.cpp +++ b/src/smt/proto_model/numeral_factory.cpp @@ -31,7 +31,7 @@ arith_factory::arith_factory(ast_manager & m): arith_factory::~arith_factory() { } -app * arith_factory::mk_value(rational const & val, bool is_int) { +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()); } @@ -47,6 +47,6 @@ app * bv_factory::mk_value_core(rational const & val, sort * s) { return m_util.mk_numeral(val, s); } -app * bv_factory::mk_value(rational const & val, unsigned bv_size) { +app * bv_factory::mk_num_value(rational const & val, unsigned bv_size) { return numeral_factory::mk_value(val, m_util.mk_sort(bv_size)); } diff --git a/src/smt/proto_model/numeral_factory.h b/src/smt/proto_model/numeral_factory.h index 3c5d41040..9b1ff6a81 100644 --- a/src/smt/proto_model/numeral_factory.h +++ b/src/smt/proto_model/numeral_factory.h @@ -38,7 +38,7 @@ public: arith_factory(ast_manager & m); virtual ~arith_factory(); - app * mk_value(rational const & val, bool is_int); + app * mk_num_value(rational const & val, bool is_int); }; class bv_factory : public numeral_factory { @@ -50,7 +50,7 @@ public: bv_factory(ast_manager & m); virtual ~bv_factory(); - app * mk_value(rational const & val, unsigned bv_size); + app * mk_num_value(rational const & val, unsigned bv_size); }; #endif /* NUMERAL_FACTORY_H_ */ diff --git a/src/smt/proto_model/value_factory.h b/src/smt/proto_model/value_factory.h index 0bf0c7c98..f841e18ea 100644 --- a/src/smt/proto_model/value_factory.h +++ b/src/smt/proto_model/value_factory.h @@ -183,7 +183,7 @@ public: sort_info* s_info = s->get_info(); sort_size const* sz = s_info?&s_info->get_num_elements():0; bool has_max = false; - Number max_size; + Number max_size(0); if (sz && sz->is_finite() && sz->size() < UINT_MAX) { unsigned usz = static_cast(sz->size()); max_size = Number(usz); diff --git a/src/smt/qi_queue.cpp b/src/smt/qi_queue.cpp index 711cdc336..530d0ec88 100644 --- a/src/smt/qi_queue.cpp +++ b/src/smt/qi_queue.cpp @@ -363,7 +363,7 @@ namespace smt { << ", scope_level: " << m_context.get_scope_level() << "\n";); if (m_params.m_qi_conservative_final_check) { bool init = false; - float min_cost; + float min_cost = 0.0; unsigned sz = m_delayed_entries.size(); for (unsigned i = 0; i < sz; i++) { entry & e = m_delayed_entries[i]; diff --git a/src/smt/smt2_extra_cmds.cpp b/src/smt/smt2_extra_cmds.cpp new file mode 100644 index 000000000..901810442 --- /dev/null +++ b/src/smt/smt2_extra_cmds.cpp @@ -0,0 +1,47 @@ +/*++ +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.h" +#include"smt2parser.h" +#include"smt2_extra_cmds.h" + +class include_cmd : public cmd { + char const * m_filename; +public: + include_cmd() : cmd("include"), m_filename(0) {} + virtual char const * get_usage() const { return ""; } + virtual char const * get_descr(cmd_context & ctx) const { return "include a file"; } + virtual unsigned get_arity() const { return 1; } + virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_STRING; } + virtual void set_next_arg(cmd_context & ctx, char const * val) { m_filename = val; } + virtual void failure_cleanup(cmd_context & ctx) {} + virtual void execute(cmd_context & ctx) { + 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(); + } + virtual void prepare(cmd_context & ctx) { reset(ctx); } + virtual void reset(cmd_context & ctx) { m_filename = 0; } + virtual void finalize(cmd_context & ctx) { reset(ctx); } +}; + +void install_smt2_extra_cmds(cmd_context & ctx) { + ctx.insert(alloc(include_cmd)); +} \ No newline at end of file diff --git a/src/smt/smt2_extra_cmds.h b/src/smt/smt2_extra_cmds.h new file mode 100644 index 000000000..947f5ed7a --- /dev/null +++ b/src/smt/smt2_extra_cmds.h @@ -0,0 +1,26 @@ +/*++ +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_ */ diff --git a/src/smt/smt_bool_var_data.h b/src/smt/smt_bool_var_data.h index e65036d9c..af0b7f9d2 100644 --- a/src/smt/smt_bool_var_data.h +++ b/src/smt/smt_bool_var_data.h @@ -24,7 +24,9 @@ Revision History: 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; @@ -45,6 +47,14 @@ namespace smt { 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; } diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index e3c2be123..7dd9144fe 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -22,13 +22,13 @@ Revision History: #include"ast_ll_pp.h" namespace smt { - + // --------------------------- // // Base class // // --------------------------- - + conflict_resolution::conflict_resolution(ast_manager & m, context & ctx, dyn_ack_manager & dyn_ack_manager, @@ -42,7 +42,7 @@ namespace smt { m_dyn_ack_manager(dyn_ack_manager), m_assigned_literals(assigned_literals), m_lemma_atoms(m), - m_todo_js_qhead(0), + m_todo_js_qhead(0), m_antecedents(0), m_watches(watches), m_new_proofs(m), @@ -67,7 +67,7 @@ namespace smt { } /** - \brief Find a common ancestor (anc) of n1 and n2 in the 'proof' tree. + \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 @@ -100,7 +100,7 @@ namespace smt { */ void conflict_resolution::eq_justification2literals(enode * lhs, enode * rhs, eq_justification js) { SASSERT(m_antecedents); - TRACE("conflict_detail", + 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()) { @@ -133,7 +133,7 @@ namespace smt { mark_eq(lhs->get_arg(1), rhs->get_arg(0)); } else { - for (unsigned i = 0; i < num_args; i++) + for (unsigned i = 0; i < num_args; i++) mark_eq(lhs->get_arg(i), rhs->get_arg(i)); } break; @@ -146,9 +146,9 @@ namespace smt { /** \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. @@ -163,7 +163,7 @@ namespace smt { /** \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. @@ -180,13 +180,17 @@ namespace smt { 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. + \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) { @@ -234,6 +238,17 @@ namespace smt { 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. */ @@ -279,13 +294,13 @@ namespace smt { 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: + case b_justification::JUSTIFICATION: r = std::max(r, get_justification_max_lvl(js.get_justification())); break; default: @@ -298,8 +313,8 @@ namespace smt { bool_var var = antecedent.var(); unsigned lvl = m_ctx.get_assign_level(var); SASSERT(var < static_cast(m_ctx.get_num_bool_vars())); - TRACE("conflict", tout << "processing antecedent (level " << lvl << "):"; - m_ctx.display_literal(tout, antecedent); + TRACE("conflict", tout << "processing antecedent (level " << lvl << "):"; + m_ctx.display_literal(tout, antecedent); m_ctx.display_detailed_literal(tout << " ", antecedent); tout << "\n";); if (!m_ctx.is_marked(var) && lvl > m_ctx.get_base_level()) { @@ -371,24 +386,23 @@ namespace smt { 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() + 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); tout << "\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()) @@ -399,7 +413,7 @@ namespace smt { } TRACE("conflict", tout << "conflict_lvl: " << m_conflict_lvl << "\n";); - + SASSERT(!m_assigned_literals.empty()); SASSERT(m_todo_js.empty()); @@ -411,32 +425,32 @@ namespace smt { /** \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", tout << "before minimization:\n"; m_ctx.display_literals(tout, m_lemma); tout << "\n";); - + TRACE("conflict_verbose", tout << "before minimization:\n"; m_ctx.display_literals_verbose(tout, m_lemma); tout << "\n";); - + if (m_params.m_minimize_lemmas) minimize_lemma(); - + TRACE("conflict", tout << "after minimization:\n"; m_ctx.display_literals(tout, m_lemma); tout << "\n";); - + TRACE("conflict_verbose", tout << "after minimization:\n"; m_ctx.display_literals_verbose(tout, m_lemma); @@ -445,7 +459,7 @@ namespace smt { TRACE("conflict_bug", m_ctx.display_literals_verbose(tout, m_lemma); tout << "\n";); - + literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); m_new_scope_lvl = m_ctx.get_search_level(); @@ -464,11 +478,11 @@ namespace smt { 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); } @@ -482,16 +496,18 @@ namespace smt { 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(0); unsigned num_marks = 0; if (not_l != null_literal) { - TRACE("conflict", tout << "not_l: "; m_ctx.display_literal(tout, not_l); tout << "\n";); + TRACE("conflict", tout << "not_l: "; m_ctx.display_literal_verbose(tout, not_l); tout << "\n";); process_antecedent(not_l, num_marks); } - + do { if (get_manager().has_trace_stream()) { @@ -500,7 +516,7 @@ namespace smt { get_manager().trace_stream() << "\n"; } - TRACE("conflict", tout << "processing consequent: "; m_ctx.display_literal(tout, consequent); tout << "\n"; + TRACE("conflict", tout << "processing consequent: "; m_ctx.display_literal_verbose(tout, consequent); tout << "\n"; tout << "num_marks: " << num_marks << ", js kind: " << js.get_kind() << "\n";); SASSERT(js != null_b_justification); switch (js.get_kind()) { @@ -528,7 +544,7 @@ namespace smt { process_antecedent(~l, num_marks); } justification * js = cls->get_justification(); - if (js) + if (js) process_justification(js, num_marks); break; } @@ -544,13 +560,13 @@ namespace smt { default: UNREACHABLE(); } - + while (true) { literal l = m_assigned_literals[idx]; - if (m_ctx.is_marked(l.var())) + 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: " << m_conflict_lvl << ", l: "; m_ctx.display_literal(tout, l); + tout << "assign_level(l): " << m_ctx.get_assign_level(l) << ", conflict_lvl: " << m_conflict_lvl << ", l: "; m_ctx.display_literal(tout, l); tout << "\n";); SASSERT(m_ctx.get_assign_level(l) == m_conflict_lvl || // it may also be an (out-of-order) asserted literal @@ -566,7 +582,7 @@ namespace smt { 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";); @@ -575,8 +591,8 @@ namespace smt { m_lemma_atoms.set(0, m_ctx.bool_var2expr(consequent.var())); // TODO: - // - // equality optimization should go here. + // + // equality optimization should go here. // finalize_resolve(conflict, not_l); @@ -586,7 +602,7 @@ namespace smt { /** \brief Return an approximation for the set of scope levels where the literals in m_lemma - were assigned. + were assigned. */ level_approx_set conflict_resolution::get_lemma_approx_level_set() { level_approx_set result; @@ -626,7 +642,7 @@ namespace smt { 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_ctx.set_mark(var); m_unmark.push_back(var); m_lemma_min_stack.push_back(var); } @@ -653,18 +669,18 @@ namespace smt { } /** - \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. + \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. + 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(); @@ -725,7 +741,7 @@ namespace smt { 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; @@ -742,7 +758,7 @@ namespace smt { j++; } } - + reset_unmark_and_justifications(0, 0); m_lemma .shrink(j); m_lemma_atoms.shrink(j); @@ -796,7 +812,7 @@ namespace smt { } if (m_manager.coarse_grain_proofs()) return pr; - TRACE("norm_eq_proof", + 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) || m_manager.is_iff(fact)); @@ -892,7 +908,7 @@ namespace smt { return 0; } } - + /** \brief Return the proof object associated with the given literal if it already exists. Otherwise, return 0 and add l to the todo-list. @@ -925,16 +941,16 @@ namespace smt { // 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. + // "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) { + 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()); @@ -1008,11 +1024,11 @@ namespace smt { 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", + 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", + 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"; @@ -1026,7 +1042,7 @@ namespace smt { } } } - + /** \brief Return the proof object associated with the given justification if it already exists. Otherwise, return 0 and add js to the todo-list. @@ -1051,7 +1067,7 @@ namespace smt { m_js2proof.reset(); literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); - for (; it != end; ++it) + for (; it != end; ++it) m_ctx.set_mark((*it).var()); } @@ -1062,6 +1078,9 @@ namespace smt { 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(); @@ -1073,14 +1092,17 @@ namespace smt { i = 1; } else { + SASSERT(cls->get_literal(1) == l); if (get_proof(~cls->get_literal(0)) == 0) visited = false; i = 2; } } - for (; i < num_lits; i++) + for (; i < num_lits; i++) { + SASSERT(cls->get_literal(i) != l); if (get_proof(~cls->get_literal(i)) == 0) visited = false; + } return visited; } else @@ -1093,7 +1115,7 @@ namespace smt { SASSERT(pr); TRACE("proof_gen_bug", tout << "lit2pr_saved: #" << l << "\n";); m_lit2proof.insert(l, pr); - TRACE("mk_proof", + 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);); @@ -1102,7 +1124,7 @@ namespace smt { /** \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 + 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) { @@ -1158,7 +1180,7 @@ namespace smt { /** \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 + 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) { @@ -1210,7 +1232,7 @@ namespace smt { if (prs1.size() == 1) pr = prs1[0]; else { - TRACE("mk_transitivity", + TRACE("mk_transitivity", unsigned sz = prs1.size(); for (unsigned i = 0; i < sz; i++) { tout << mk_ll_pp(prs1[i], m_manager) << "\n"; @@ -1235,14 +1257,19 @@ namespace smt { } tout << "\n";); init_mk_proof(); - literal consequent = false_literal; - if (not_l != null_literal) - consequent = ~not_l; - visit_b_justification(consequent, conflict); - if (not_l != null_literal) + 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; @@ -1272,7 +1299,7 @@ namespace smt { } case tp_elem::LITERAL: { literal l = to_literal(elem.m_lidx); - if (m_lit2proof.contains(l)) + if (m_lit2proof.contains(l)) m_todo_pr.pop_back(); else { b_justification js = m_ctx.get_justification(l.var()); @@ -1326,19 +1353,20 @@ namespace smt { 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";); - + 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); + } + 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(); @@ -1353,7 +1381,7 @@ namespace smt { 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(); @@ -1361,19 +1389,19 @@ namespace smt { 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 << "js.get_kind(): " << js.get_kind() << ", idx: " << idx << "\n";); + TRACE("unsat_core_bug", tout << consequent << " js.get_kind(): " << js.get_kind() << ", idx: " << idx << "\n";); switch (js.get_kind()) { case b_justification::CLAUSE: { clause * cls = js.get_clause(); @@ -1394,7 +1422,7 @@ namespace smt { process_antecedent_for_unsat_core(~l); } justification * js = cls->get_justification(); - if (js) + if (js) process_justification_for_unsat_core(js); break; } @@ -1410,18 +1438,22 @@ namespace smt { default: UNREACHABLE(); } - - while (true) { - if (idx < 0) - goto end_unsat_core; + + if (m_ctx.is_assumption(consequent.var())) { + m_assumptions.push_back(consequent); + } + while (idx >= 0) { literal l = m_assigned_literals[idx]; TRACE("unsat_core_bug", tout << "l: " << l << ", get_assign_level(l): " << m_ctx.get_assign_level(l) << ", is_marked(l): " << m_ctx.is_marked(l.var()) << "\n";); - if (m_ctx.get_assign_level(l) < search_lvl || idx == 0) + if (m_ctx.get_assign_level(l) < search_lvl) goto end_unsat_core; - if (m_ctx.is_marked(l.var())) + if (m_ctx.is_marked(l.var())) break; idx--; } + if (idx < 0) { + goto end_unsat_core; + } SASSERT(idx >= 0); consequent = m_assigned_literals[idx]; @@ -1435,12 +1467,12 @@ namespace smt { 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, + + conflict_resolution * mk_conflict_resolution(ast_manager & m, context & ctx, dyn_ack_manager & dack_manager, smt_params const & params, - literal_vector const & assigned_literals, + literal_vector const & assigned_literals, vector & watches) { return alloc(conflict_resolution, m, ctx, dack_manager, params, assigned_literals, watches); } diff --git a/src/smt/smt_conflict_resolution.h b/src/smt/smt_conflict_resolution.h index daccadbb7..fad3ab50d 100644 --- a/src/smt/smt_conflict_resolution.h +++ b/src/smt/smt_conflict_resolution.h @@ -168,6 +168,7 @@ namespace smt { 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; @@ -257,6 +258,8 @@ namespace smt { 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) { diff --git a/src/smt/smt_consequences.cpp b/src/smt/smt_consequences.cpp new file mode 100644 index 000000000..9558f6a3b --- /dev/null +++ b/src/smt/smt_consequences.cpp @@ -0,0 +1,595 @@ +/*++ +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 "smt_context.h" +#include "ast_util.h" +#include "datatype_decl_plugin.h" +#include "model_pp.h" +#include "max_cliques.h" +#include "stopwatch.h" + +namespace smt { + + expr_ref context::antecedent2fml(index_set const& vars) { + expr_ref_vector premises(m_manager); + index_set::iterator it = vars.begin(), end = vars.end(); + for (; it != end; ++it) { + expr* e = bool_var2expr(*it); + premises.push_back(get_assignment(*it) != l_false ? e : m_manager.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, obj_map& vars, index_set const& assumptions, expr_ref_vector& conseq) { + ast_manager& m = m_manager; + datatype_util dt(m); + expr* e1, *e2; + expr_ref fml(m); + if (lit == true_literal) return; + expr* e = bool_var2expr(lit.var()); + 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 (vars.contains(e)) { + found = true; + fml = lit.sign() ? m.mk_not(e) : e; + vars.erase(e); + } + else if (!lit.sign() && m.is_eq(e, e1, e2)) { + if (vars.contains(e2)) { + std::swap(e1, e2); + } + if (vars.contains(e1) && m.is_value(e2)) { + found = true; + fml = e; + vars.erase(e1); + } + } + else if (!lit.sign() && is_app(e) && dt.is_recognizer(to_app(e)->get_decl())) { + if (vars.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()))); + vars.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) { + b_justification js = get_justification(lit.var()); + switch (js.get_kind()) { + case b_justification::CLAUSE: { + clause * cls = js.get_clause(); + if (!cls) break; + unsigned num_lits = cls->get_num_literals(); + for (unsigned j = 0; j < num_lits; ++j) { + literal lit2 = cls->get_literal(j); + 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) { + s |= m_antecedents.find(literals[j].var()); + } + break; + } + } + } + + void context::extract_fixed_consequences(unsigned& start, obj_map& vars, 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], vars, 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(obj_map& var2val, expr_ref_vector& unfixed) { + ast_manager& m = m_manager; + ptr_vector to_delete; + obj_map::iterator it = var2val.begin(), end = 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) { + 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(obj_map& var2val, expr_ref_vector& conseq) { + TRACE("context", tout << "extract fixed consequences\n";); + ast_manager& m = m_manager; + ptr_vector to_delete; + expr_ref fml(m), eq(m); + obj_map::iterator it = var2val.begin(), end = 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(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(), 0); + TRACE("context", display_literals_verbose(tout, literals.size(), literals.c_ptr());); + } + } + for (unsigned i = 0; i < to_delete.size(); ++i) { + var2val.remove(to_delete[i]); + } + return to_delete.size(); + } + + literal context::mk_diseq(expr* e, expr* val) { + ast_manager& m = m_manager; + if (m.is_bool(e)) { + 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& assumptions, + expr_ref_vector const& vars, + expr_ref_vector& conseq, + expr_ref_vector& unfixed) { + + m_antecedents.reset(); + pop_to_base_lvl(); + lbool is_sat = check(assumptions.size(), assumptions.c_ptr()); + if (is_sat != l_true) { + TRACE("context", tout << is_sat << "\n";); + return is_sat; + } + + obj_map var2val; + index_set _assumptions; + for (unsigned i = 0; i < assumptions.size(); ++i) { + _assumptions.insert(get_literal(assumptions[i]).var()); + } + model_ref mdl; + get_model(mdl); + ast_manager& m = m_manager; + 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], val); + if (m.is_value(val)) { + trail.push_back(val); + var2val.insert(vars[i], val); + } + else { + unfixed.push_back(vars[i]); + } + } + unsigned num_units = 0; + extract_fixed_consequences(num_units, var2val, _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 (!var2val.empty()) { + obj_map::iterator it = var2val.begin(), end = var2val.end(); + unsigned num_vars = 0; + for (; it != end && num_vars < chunk_size; ++it) { + if (get_cancel_flag()) { + 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) { + 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(var2val, unfixed); + } + extract_fixed_consequences(num_units, var2val, _assumptions, conseq); + num_fixed_eqs += extract_fixed_eqs(var2val, conseq); + IF_VERBOSE(1, display_consequence_progress(verbose_stream(), num_iterations, var2val.size(), conseq.size(), + unfixed.size(), num_fixed_eqs);); + TRACE("context", display_consequence_progress(tout, num_iterations, var2val.size(), conseq.size(), + unfixed.size(), num_fixed_eqs);); + } + + end_search(); + DEBUG_CODE(validate_consequences(assumptions, vars, conseq, unfixed);); + 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_manager); + 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_manager.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_manager); + for (unsigned i = 0; i < vars.size(); ++i) { + expr* n = vars[i]; + bool neg = m_manager.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_manager); + 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) { + ast_manager& m = m_manager; + 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 (unsigned j = 0; j < assumptions.size(); ++j) { + assert_expr(assumptions[j]); + } + 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); + mdl->eval(unfixed[i], tmp); + 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); + } + } + +} diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 2de610772..7532bf3f8 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -263,10 +263,11 @@ namespace smt { m_assignment[false_literal.index()] = l_false; if (m_manager.proofs_enabled()) { proof * pr = m_manager.mk_true_proof(); - m_bdata[true_bool_var].m_justification = b_justification(mk_justification(justification_proof_wrapper(*this, pr))); + + 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].m_justification = b_justification::mk_axiom(); + 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. @@ -294,6 +295,12 @@ namespace smt { std::swap(lhs, rhs); return m_manager.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 << " "; @@ -304,7 +311,7 @@ namespace smt { m_assignment[l.index()] = l_true; m_assignment[(~l).index()] = l_false; bool_var_data & d = get_bdata(l.var()); - d.m_justification = j; + 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; @@ -1097,7 +1104,7 @@ namespace smt { \brief This method is invoked when a new disequality is asserted. The disequality is propagated to the theories. */ - void context::add_diseq(enode * n1, enode * n2) { + 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"; @@ -1114,7 +1121,11 @@ namespace smt { 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; // context is inconsistent + //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 @@ -1148,6 +1159,7 @@ namespace smt { l1 = l1->get_next(); } } + return true; } /** @@ -1403,7 +1415,10 @@ namespace smt { } else { TRACE("add_diseq", display_eq_detail(tout, bool_var2enode(v));); - add_diseq(get_enode(lhs), get_enode(rhs)); + 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()) { @@ -1756,8 +1771,10 @@ namespace smt { return false; if (!propagate_th_case_split()) return false; - if (get_cancel_flag()) + if (get_cancel_flag()) { + m_qhead = qhead; return true; + } SASSERT(!inconsistent()); propagate_relevancy(qhead); if (inconsistent()) @@ -1775,8 +1792,10 @@ namespace smt { m_qmanager->propagate(); if (inconsistent()) return false; - if (resource_limits_exceeded()) + 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()); @@ -1787,6 +1806,7 @@ namespace smt { void context::set_conflict(b_justification js, literal not_l) { if (!inconsistent()) { + TRACE("set_conflict", display_literal_verbose(tout, not_l); display(tout, js); ); m_conflict = js; m_not_l = not_l; } @@ -1841,7 +1861,7 @@ namespace smt { m_stats.m_num_decisions++; push_scope(); - TRACE("context", tout << "splitting, lvl: " << m_scope_lvl << "\n";); + TRACE("decide", tout << "splitting, lvl: " << m_scope_lvl << "\n";); TRACE("decide_detail", tout << mk_pp(bool_var2expr(var), m_manager) << "\n";); @@ -2022,11 +2042,13 @@ namespace smt { v.shrink(old_size); } +#if 0 void context::mark_as_deleted(clause * cls) { SASSERT(!cls->deleted()); remove_cls_occs(cls); cls->mark_as_deleted(m_manager); } +#endif /** \brief Undo variable assignments. @@ -2042,7 +2064,7 @@ namespace smt { m_assignment[(~l).index()] = l_undef; bool_var v = l.var(); bool_var_data & d = get_bdata(v); - d.m_justification = null_b_justification; + d.set_null_justification(); m_case_split_queue->unassign_var_eh(v); } @@ -2480,6 +2502,13 @@ namespace smt { 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. @@ -2595,10 +2624,10 @@ namespace smt { cls->set_justification(0); m_justifications.push_back(js); } - m_bdata[v0].m_justification = b_justification(js); + set_justification(v0, m_bdata[v0], b_justification(js)); } else - m_bdata[v0].m_justification = b_justification::mk_axiom(); + m_bdata[v0].set_axiom(); } } del_clause(cls); @@ -3085,7 +3114,11 @@ namespace smt { if (!m_asserted_formulas.inconsistent()) { unsigned sz = m_asserted_formulas.get_num_formulas(); unsigned qhead = m_asserted_formulas.get_qhead(); - while (qhead < sz && !m_manager.canceled()) { + 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); @@ -3142,6 +3175,8 @@ namespace smt { // in a consistent context. if (inconsistent()) return; + if (get_cancel_flag()) + return; push_scope(); for (unsigned i = 0; i < num_assumptions; i++) { expr * curr_assumption = assumptions[i]; @@ -3181,7 +3216,7 @@ namespace smt { SASSERT(m_assumptions.empty()); return; } - obj_hashtable already_found_assumptions; + 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) { @@ -3190,10 +3225,9 @@ namespace smt { SASSERT(get_bdata(l.var()).m_assumption); if (!m_literal2assumption.contains(l.index())) l.neg(); SASSERT(m_literal2assumption.contains(l.index())); - expr * a = m_literal2assumption[l.index()]; - if (!already_found_assumptions.contains(a)) { - already_found_assumptions.insert(a); - m_unsat_core.push_back(a); + if (!already_found_assumptions.contains(l.index())) { + already_found_assumptions.insert(l.index()); + m_unsat_core.push_back(m_literal2assumption[l.index()]); } } reset_assumptions(); @@ -3438,19 +3472,6 @@ namespace smt { m_num_conflicts_since_restart = 0; } - struct context::scoped_mk_model { - context & m_ctx; - scoped_mk_model(context & ctx):m_ctx(ctx) { - m_ctx.m_proto_model = 0; - m_ctx.m_model = 0; - } - ~scoped_mk_model() { - if (m_ctx.m_proto_model.get() != 0) { - m_ctx.m_model = m_ctx.m_proto_model->mk_model(); - m_ctx.m_proto_model = 0; // proto_model is not needed anymore. - } - } - }; lbool context::search() { #ifndef _EXTERNAL_RELEASE @@ -3478,78 +3499,10 @@ namespace smt { 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 (m_last_search_failure != OK) { - if (status != l_false) { - // build candidate model before returning - mk_proto_model(status); - // status = l_undef; - } + + if (!restart(status, curr_lvl)) { break; } - - bool force_restart = false; - - if (status == l_false) { - break; - } - else 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 - break; - } - if (cmr == quantifier_manager::UNKNOWN) { - // giving up - m_last_search_failure = QUANTIFIERS; - status = l_undef; - break; - } - status = l_undef; - force_restart = true; - } - - SASSERT(status == l_undef); - inc_limits(); - if (force_restart || !m_fparams.m_restart_adaptive || m_agility < m_fparams.m_restart_agility_threshold) { - SASSERT(!inconsistent()); - IF_VERBOSE(1, 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() << ")" << std::endl; verbose_stream().flush();); - // execute the restart - m_stats.m_num_restarts++; - if (m_scope_lvl > curr_lvl) { - pop_scope(m_scope_lvl - curr_lvl); - SASSERT(at_search_level()); - } - ptr_vector::iterator it = m_theory_set.begin(); - ptr_vector::iterator end = m_theory_set.end(); - for (; it != end && !inconsistent(); ++it) - (*it)->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; - break; - } - } - if (m_fparams.m_simplify_clauses) - simplify_clauses(); - if (m_fparams.m_lemma_gc_strategy == LGC_AT_RESTART) - del_inactive_lemmas(); } TRACE("search_lite", tout << "status: " << status << "\n";); @@ -3563,6 +3516,80 @@ namespace smt { 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(1, 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(1, 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() << ")" << std::endl; verbose_stream().flush();); + // execute the restart + m_stats.m_num_restarts++; + if (m_scope_lvl > curr_lvl) { + pop_scope(m_scope_lvl - curr_lvl); + SASSERT(at_search_level()); + } + ptr_vector::iterator it = m_theory_set.begin(); + ptr_vector::iterator end = m_theory_set.end(); + for (; it != end && !inconsistent(); ++it) + (*it)->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_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++; @@ -3579,7 +3606,6 @@ namespace smt { } lbool context::bounded_search() { - SASSERT(!inconsistent()); unsigned counter = 0; TRACE("bounded_search", tout << "starting bounded search...\n";); @@ -3653,6 +3679,7 @@ namespace smt { } if (resource_limits_exceeded() && !inconsistent()) { + m_last_search_failure = RESOURCE_LIMIT; return l_undef; } } @@ -3923,15 +3950,15 @@ namespace smt { #ifdef Z3DEBUG for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; - if (m_manager.is_not(expr_lits.get(i))) { + if (expr_signs[i] != l.sign()) { + expr* real_atom; + VERIFY(m_manager.is_not(expr_lits.get(i), real_atom)); // the sign must have flipped when internalizing - expr * real_atom = to_app(expr_lits.get(i))->get_arg(0); + CTRACE("resolve_conflict_bug", real_atom != bool_var2expr(l.var()), tout << mk_pp(real_atom, m_manager) << "\n" << mk_pp(bool_var2expr(l.var()), m_manager) << "\n";); SASSERT(real_atom == bool_var2expr(l.var())); - SASSERT(expr_signs[i] != l.sign()); } else { SASSERT(expr_lits.get(i) == bool_var2expr(l.var())); - SASSERT(expr_signs[i] == l.sign()); } } #endif @@ -4275,7 +4302,7 @@ namespace smt { m_fingerprints.display(tout); ); failure fl = get_last_search_failure(); - if (fl == MEMOUT || fl == CANCELED || fl == TIMEOUT || fl == NUM_CONFLICTS) { + if (fl == MEMOUT || fl == CANCELED || fl == TIMEOUT || fl == NUM_CONFLICTS || fl == RESOURCE_LIMIT) { return; } diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 2aae6c8a5..edc122bd6 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -50,9 +50,9 @@ Revision History: #include"statistics.h" #include"progress_callback.h" -// there is a significant space overhead with allocating 1000+ contexts in +// 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 +// Using a map instead of a vector for the literals can compress space // consumption. #ifdef SPARSE_MAP #define USE_BOOL_VAR_VECTOR 0 @@ -98,7 +98,7 @@ namespace smt { // 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. + expr_ref_vector m_e_internalized_stack; // stack of the expressions already internalized as enodes. ptr_vector m_justifications; @@ -116,7 +116,7 @@ namespace smt { 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) - cg_table m_cg_table; + cg_table m_cg_table; dyn_ack_manager m_dyn_ack_manager; struct new_eq { enode * m_lhs; @@ -140,7 +140,7 @@ namespace smt { svector m_propagated_th_eqs; svector m_propagated_th_diseqs; svector m_diseq_vector; -#endif +#endif enode * m_is_diseq_tmp; // auxiliary enode used to find congruent equality atoms. tmp_enode m_tmp_enode; @@ -161,8 +161,8 @@ namespace smt { 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; + svector m_activity; + clause_vector m_aux_clauses; clause_vector m_lemmas; vector m_clauses_to_reinit; expr_ref_vector m_units_to_reassert; @@ -176,7 +176,7 @@ namespace smt { 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; @@ -200,17 +200,30 @@ namespace smt { model_ref m_model; std::string m_unknown; void mk_proto_model(lbool r); - struct scoped_mk_model; + struct scoped_mk_model { + context & m_ctx; + scoped_mk_model(context & ctx):m_ctx(ctx) { + m_ctx.m_proto_model = 0; + m_ctx.m_model = 0; + } + ~scoped_mk_model() { + if (m_ctx.m_proto_model.get() != 0) { + m_ctx.m_model = m_ctx.m_proto_model->mk_model(); + m_ctx.m_proto_model = 0; // proto_model is not needed anymore. + } + } + }; + // ----------------------------------- // // Unsat core extraction // // ----------------------------------- - typedef u_map literal2assumption; + 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; + expr_ref_vector m_unsat_core; // ----------------------------------- // @@ -245,7 +258,6 @@ namespace smt { return m_params; } - bool get_cancel_flag() { return !m_manager.limit().inc(); } region & get_region() { @@ -260,7 +272,7 @@ namespace smt { SASSERT(e_internalized(n)); return m_app2enode[n->get_id()]; } - + /** \brief Similar to get_enode, but returns 0 if n is to e_internalized. */ @@ -322,7 +334,7 @@ namespace smt { 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(); @@ -335,7 +347,7 @@ namespace smt { 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]); } @@ -348,8 +360,8 @@ namespace smt { return get_assignment(literal(v)); } - literal_vector const & assigned_literals() const { - return m_assigned_literals; + literal_vector const & assigned_literals() const { + return m_assigned_literals; } lbool get_assignment(expr * n) const; @@ -362,9 +374,11 @@ namespace smt { void get_assignments(expr_ref_vector& assignments); b_justification get_justification(bool_var v) const { - return get_bdata(v).m_justification; + 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; @@ -422,7 +436,7 @@ namespace smt { unsigned get_assign_level(literal l) const { return get_assign_level(l.var()); } - + /** \brief Return the scope level when v was internalized. */ @@ -433,7 +447,7 @@ namespace smt { theory * get_theory(theory_id th_id) const { return m_theories.get_plugin(th_id); } - + ptr_vector::const_iterator begin_theories() const { return m_theories.begin(); } @@ -447,7 +461,7 @@ namespace smt { } unsigned get_base_level() const { - return m_base_lvl; + return m_base_lvl; } bool at_base_level() const { @@ -467,11 +481,11 @@ namespace smt { } expr * bool_var2expr(bool_var v) const { - return m_bool_var2expr[v]; + return m_bool_var2expr[v]; } - + void literal2expr(literal l, expr_ref & result) const { - if (l == true_literal) + if (l == true_literal) result = m_manager.mk_true(); else if (l == false_literal) result = m_manager.mk_false(); @@ -498,7 +512,7 @@ namespace smt { unsigned id = decl->get_decl_id(); return id < m_decl2enodes.size() ? m_decl2enodes[id].begin() : 0; } - + 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() : 0; @@ -588,7 +602,7 @@ namespace smt { void push_scope(); unsigned pop_scope_core(unsigned num_scopes); - + void pop_scope(unsigned num_scopes); void undo_trail_stack(unsigned old_size); @@ -614,13 +628,13 @@ namespace smt { 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); @@ -629,14 +643,14 @@ namespace smt { // ----------------------------------- // - // Internalization + // 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_manager.is_false(n) || (m_manager.is_not(n) ? b_internalized(to_app(n)->get_arg(0)) : b_internalized(n)); } @@ -645,7 +659,7 @@ namespace smt { return m_app2enode.get(n->get_id(), 0) != 0; } - unsigned get_num_b_internalized() const { + unsigned get_num_b_internalized() const { return m_b_internalized_stack.size(); } @@ -653,7 +667,7 @@ namespace smt { return m_b_internalized_stack.get(idx); } - unsigned get_num_e_internalized() const { + unsigned get_num_e_internalized() const { return m_e_internalized_stack.size(); } @@ -690,9 +704,9 @@ namespace smt { 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); @@ -720,7 +734,7 @@ namespace smt { void internalize_term(app * n); void internalize_ite_term(app * n); - + bool internalize_theory_term(app * n); void internalize_uninterpreted(app * n); @@ -751,7 +765,7 @@ namespace smt { 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; @@ -783,7 +797,7 @@ namespace smt { 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); @@ -795,7 +809,7 @@ namespace smt { 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); @@ -838,7 +852,7 @@ namespace smt { bool propagate_th_case_split(); 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); @@ -847,7 +861,7 @@ namespace smt { justification * mk_justification(Justification const & j) { justification * js = new (m_region) Justification(j); SASSERT(js->in_region()); - if (js->has_del_eh()) + if (js->has_del_eh()) m_justifications.push_back(js); return js; } @@ -868,10 +882,10 @@ namespace smt { unsigned m_num_conflicts_since_lemma_gc; unsigned m_restart_threshold; unsigned m_restart_outer_threshold; - unsigned m_luby_idx; + 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; @@ -897,7 +911,7 @@ namespace smt { 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); @@ -918,13 +932,13 @@ namespace smt { d.m_phase = phase; } - void force_phase(literal l) { + 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, unsigned max_generation, + bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, ptr_vector & used_enodes); void set_global_generation(unsigned generation) { m_generation = generation; } @@ -973,11 +987,11 @@ namespace smt { push_eq(n1, n2, eq_justification::mk_cg(used_commutativity)); } - void add_diseq(enode * n1, enode * n2); + bool add_diseq(enode * n1, enode * n2); void assign_quantifier(quantifier * q); - void set_conflict(b_justification js, literal not_l); + void set_conflict(b_justification js, literal not_l); void set_conflict(b_justification js) { set_conflict(js, null_literal); @@ -1016,12 +1030,12 @@ namespace smt { #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) + if (act > ACTIVITY_LIMIT) rescale_bool_var_activity(); m_case_split_queue->activity_increased_eh(v); } @@ -1050,7 +1064,7 @@ namespace smt { } return false; } - + bool can_delete(clause * cls) const { if (cls->in_reinit_stack()) return false; @@ -1072,7 +1086,7 @@ namespace smt { bool validate_assumptions(unsigned num_assumptions, expr * const * assumptions); void init_assumptions(unsigned num_assumptions, expr * const * assumptions); - + void reset_assumptions(); void mk_unsat_core(); @@ -1087,12 +1101,14 @@ namespace smt { 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(); @@ -1119,7 +1135,7 @@ namespace smt { public: // event handler for relevancy_propagator class - void relevant_eh(expr * n); + void relevant_eh(expr * n); bool is_relevant(expr * n) const { return !relevancy() || is_relevant_core(n); @@ -1143,9 +1159,9 @@ namespace smt { 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); @@ -1166,9 +1182,9 @@ namespace smt { void propagate_th_eqs(); void propagate_th_diseqs(); - + bool can_theories_propagate() const; - + bool propagate(); public: @@ -1184,7 +1200,7 @@ namespace smt { // ----------------------------------- // - // Pretty Printing + // Pretty Printing // // ----------------------------------- protected: @@ -1232,7 +1248,7 @@ namespace smt { void 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; @@ -1252,15 +1268,15 @@ namespace smt { 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; void 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, + 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; - void display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, - unsigned num_antecedent_eqs, enode_pair const * antecedent_eqs, + void 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_assignment_as_smtlib2(std::ostream& out, symbol const& logic = symbol::null) const; void display_normalized_enodes(std::ostream & out) const; @@ -1306,13 +1322,13 @@ namespace smt { 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; @@ -1353,6 +1369,7 @@ namespace smt { virtual void setup_context(bool use_static_features); void setup_components(void); 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; @@ -1374,6 +1391,38 @@ namespace smt { 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; + void extract_fixed_consequences(literal lit, obj_map& var2val, index_set const& assumptions, expr_ref_vector& conseq); + void extract_fixed_consequences(unsigned& idx, obj_map& var2val, 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(obj_map& var2val, expr_ref_vector& unfixed); + + unsigned extract_fixed_eqs(obj_map& var2val, 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()); @@ -1412,12 +1461,18 @@ namespace smt { void pop(unsigned num_scopes); - lbool check(unsigned num_assumptions = 0, expr * const * assumptions = 0, bool reset_cancel = true); - + lbool check(unsigned num_assumptions = 0, expr * const * assumptions = 0, bool reset_cancel = true); + + 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 reduce_assertions(); bool resource_limits_exceeded(); @@ -1437,22 +1492,20 @@ namespace smt { void internalize_instance(expr * body, proof * pr, unsigned generation) { internalize_assertion(body, pr, generation); -#ifndef SMTCOMP if (relevancy()) m_case_split_queue->internalize_instance_eh(body, generation); -#endif } 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_model(model_ref & m) const; bool update_model(bool refinalize); @@ -1460,17 +1513,17 @@ namespace smt { 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); } - + expr * const * get_asserted_formulas() const { return m_asserted_formulas.get_formulas(); } - + proof * const * get_asserted_formula_proofs() const { return m_asserted_formulas.get_formula_proofs(); } void get_assumptions_core(ptr_vector & result); @@ -1482,7 +1535,7 @@ namespace smt { 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; diff --git a/src/smt/smt_context_inv.cpp b/src/smt/smt_context_inv.cpp index 6b626c2de..7d009a037 100644 --- a/src/smt/smt_context_inv.cpp +++ b/src/smt/smt_context_inv.cpp @@ -322,6 +322,9 @@ namespace smt { 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); @@ -399,6 +402,20 @@ namespace smt { #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(); + unsigned num_lits = cls->get_num_literals(); + 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; diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index a3079f52b..ff45c5089 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -39,6 +39,8 @@ namespace smt { return out << "CANCELED"; case NUM_CONFLICTS: return out << "NUM_CONFLICTS"; + case RESOURCE_LIMIT: + return out << "RESOURCE_LIMIT"; case THEORY: if (!m_incomplete_theories.empty()) { ptr_vector::const_iterator it = m_incomplete_theories.begin(); @@ -78,6 +80,7 @@ namespace smt { r += "))"; break; } + case RESOURCE_LIMIT: r = "(resource limits reached)"; break; case QUANTIFIERS: r = "(incomplete quantifiers)"; break; case UNKNOWN: r = m_unknown; break; } @@ -250,7 +253,7 @@ namespace smt { void context::display_app_enode_map(std::ostream & out) const { if (!m_e_internalized_stack.empty()) { - out << "expresion -> enode:\n"; + 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); @@ -262,7 +265,7 @@ namespace smt { void context::display_expr_bool_var_map(std::ostream & out) const { if (!m_b_internalized_stack.empty()) { - out << "expresion -> bool_var:\n"; + 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); diff --git a/src/smt/smt_context_stat.cpp b/src/smt/smt_context_stat.cpp index 5e82923f0..3838a88b5 100644 --- a/src/smt/smt_context_stat.cpp +++ b/src/smt/smt_context_stat.cpp @@ -32,7 +32,7 @@ namespace smt { return static_cast(acc / m_lemmas.size()); } - void acc_num_occs(clause * cls, unsigned_vector & lit2num_occs) { + static void acc_num_occs(clause * cls, unsigned_vector & lit2num_occs) { unsigned num_lits = cls->get_num_literals(); for (unsigned i = 0; i < num_lits; i++) { literal l = cls->get_literal(i); @@ -40,7 +40,7 @@ namespace smt { } } - void acc_num_occs(clause_vector const & v, unsigned_vector & lit2num_occs) { + static void acc_num_occs(clause_vector const & v, unsigned_vector & lit2num_occs) { clause_vector::const_iterator it = v.begin(); clause_vector::const_iterator end = v.end(); for (; it != end; ++it) @@ -79,7 +79,7 @@ namespace smt { out << (m_assigned_literals.size() - n) << "]"; } - void acc_var_num_occs(clause * cls, unsigned_vector & var2num_occs) { + static void acc_var_num_occs(clause * cls, unsigned_vector & var2num_occs) { unsigned num_lits = cls->get_num_literals(); for (unsigned i = 0; i < num_lits; i++) { literal l = cls->get_literal(i); @@ -87,7 +87,7 @@ namespace smt { } } - void acc_var_num_occs(clause_vector const & v, unsigned_vector & var2num_occs) { + static void acc_var_num_occs(clause_vector const & v, unsigned_vector & var2num_occs) { clause_vector::const_iterator it = v.begin(); clause_vector::const_iterator end = v.end(); for (; it != end; ++it) @@ -114,7 +114,7 @@ namespace smt { out << "\n"; } - void acc_var_num_min_occs(clause * cls, unsigned_vector & var2num_min_occs) { + 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->get_literal(0).var(); for (unsigned i = 1; i < num_lits; i++) { @@ -125,7 +125,7 @@ namespace smt { var2num_min_occs[min_var]++; } - void acc_var_num_min_occs(clause_vector const & v, unsigned_vector & var2num_min_occs) { + static void acc_var_num_min_occs(clause_vector const & v, unsigned_vector & var2num_min_occs) { clause_vector::const_iterator it = v.begin(); clause_vector::const_iterator end = v.end(); for (; it != end; ++it) diff --git a/src/smt/smt_failure.h b/src/smt/smt_failure.h index 171c0bf6d..9e332ac89 100644 --- a/src/smt/smt_failure.h +++ b/src/smt/smt_failure.h @@ -32,6 +32,7 @@ namespace smt { 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. }; diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index df80d3281..f7936eacd 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -321,10 +321,10 @@ namespace smt { void context::internalize(expr * n, bool gate_ctx) { TRACE("internalize", tout << "internalizing:\n" << mk_pp(n, m_manager) << "\n";); TRACE("internalize_bug", tout << "internalizing:\n" << mk_bounded_pp(n, m_manager) << "\n";); + if (is_var(n)) { + throw default_exception("Formulas should not contain unbound variables"); + } if (m_manager.is_bool(n)) { - if (is_var(n)) { - throw default_exception("Formulas should not contain unbound variables"); - } SASSERT(is_quantifier(n) || is_app(n)); internalize_formula(n, gate_ctx); } @@ -335,23 +335,6 @@ namespace smt { } } - bool find_arg(app * n, expr * t, expr * & other) { - SASSERT(n->get_num_args() == 2); - if (n->get_arg(0) == t) { - other = n->get_arg(1); - return true; - } - else if (n->get_arg(1) == t) { - other = n->get_arg(0); - return true; - } - return false; - } - - bool check_args(app * n, expr * t1, expr * t2) { - SASSERT(n->get_num_args() == 2); - return (n->get_arg(0) == t1 && n->get_arg(1) == t2) || (n->get_arg(1) == t1 && n->get_arg(0) == t2); - } /** \brief Internalize the given formula into the logical context. @@ -840,7 +823,7 @@ namespace smt { } #endif TRACE("mk_bool_var", tout << "creating boolean variable: " << v << " for:\n" << mk_pp(n, m_manager) << "\n";); - TRACE("mk_var_bug", tout << "mk_bool: " << v << "\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); @@ -1045,15 +1028,13 @@ namespace smt { 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 i = 0; unsigned j = 0; - for (; i < num_lits; i++) { + for (unsigned i = 0; i < num_lits; i++) { literal curr = lits[i]; lbool val = get_assignment(curr); - if (val == l_false) - simp_lits.push_back(~curr); switch(val) { case l_false: + simp_lits.push_back(~curr); break; // ignore literal case l_undef: if (curr == ~prev) @@ -1283,10 +1264,9 @@ namespace smt { 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", tout << "creating clause:\n"; display_literals(tout, num_lits, lits); tout << "\n";); + TRACE("mk_clause", tout << "creating clause:\n"; display_literals_verbose(tout, num_lits, lits); tout << "\n";); switch (k) { case CLS_AUX: { - unsigned old_num_lits = num_lits; literal_buffer simp_lits; if (!simplify_aux_clause_literals(num_lits, lits, simp_lits)) return 0; // clause is equivalent to true; @@ -1295,8 +1275,9 @@ namespace smt { SASSERT(get_assignment(simp_lits[i]) == l_true); } }); - if (old_num_lits != num_lits) + if (!simp_lits.empty()) { j = mk_justification(unit_resolution_justification(m_region, j, simp_lits.size(), simp_lits.c_ptr())); + } break; } case CLS_AUX_LEMMA: { diff --git a/src/smt/smt_justification.cpp b/src/smt/smt_justification.cpp index de3594fcc..c091f6973 100644 --- a/src/smt/smt_justification.cpp +++ b/src/smt/smt_justification.cpp @@ -52,6 +52,7 @@ namespace smt { tout << lits[i] << " "; } tout << "\n";); + SASSERT(m_num_literals > 0); } unit_resolution_justification::unit_resolution_justification(justification * js, @@ -68,6 +69,7 @@ namespace smt { tout << lits[i] << " "; } tout << "\n";); + SASSERT(num_lits != 0); } unit_resolution_justification::~unit_resolution_justification() { diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp index 418dfdb04..9bdf962ec 100644 --- a/src/smt/smt_kernel.cpp +++ b/src/smt/smt_kernel.cpp @@ -55,6 +55,18 @@ namespace smt { 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(); + expr * const * fms = m_kernel.get_asserted_formulas(); + out << "(kernel"; + for (unsigned i = 0; i < num; i++) { + out << "\n " << mk_ismt2_pp(fms[i], m(), 2); + } + out << ")"; + } void assert_expr(expr * e) { TRACE("smt_kernel", tout << "assert:\n" << mk_ismt2_pp(e, m()) << "\n";); @@ -98,6 +110,19 @@ namespace smt { lbool check(unsigned num_assumptions, expr * const * assumptions) { return m_kernel.check(num_assumptions, assumptions); } + + 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); @@ -146,18 +171,6 @@ namespace smt { void get_guessed_literals(expr_ref_vector & result) { m_kernel.get_guessed_literals(result); } - - 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(); - expr * const * fms = m_kernel.get_asserted_formulas(); - out << "(kernel"; - for (unsigned i = 0; i < num; i++) { - out << "\n " << mk_ismt2_pp(fms[i], m(), 2); - } - out << ")"; - } void collect_statistics(::statistics & st) const { m_kernel.collect_statistics(st); @@ -214,6 +227,12 @@ namespace smt { 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); } @@ -264,6 +283,18 @@ namespace smt { return r; } + 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); } diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index bf559a775..264fae011 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -70,7 +70,8 @@ namespace smt { 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. */ @@ -126,6 +127,22 @@ namespace smt { lbool check(app_ref_vector const& asms) { return check(asms.size(), (expr* const*)asms.c_ptr()); } + /** + \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. */ diff --git a/src/smt/smt_literal.cpp b/src/smt/smt_literal.cpp index d510027f0..e6e795ed4 100644 --- a/src/smt/smt_literal.cpp +++ b/src/smt/smt_literal.cpp @@ -27,6 +27,8 @@ namespace smt { out << "true"; else if (*this == false_literal) out << "false"; + else if (*this == null_literal) + out << "null"; else if (sign()) out << "(not " << mk_pp(bool_var2expr_map[var()], m) << ")"; else diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index 0d9c361f8..5329cd80f 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -112,7 +112,6 @@ namespace smt { if (!m_curr_model->eval(q->get_expr(), tmp, true)) { return; } - //std::cout << tmp << "\n"; 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(); @@ -138,8 +137,10 @@ namespace smt { } bool model_checker::add_instance(quantifier * q, model * cex, expr_ref_vector & sks, bool use_inv) { - if (cex == 0) - return false; // no model available. + if (cex == 0) { + TRACE("model_checker", tout << "no model is available\n";); + return false; + } unsigned num_decls = q->get_num_decls(); // Remark: sks were created for the flat version of q. SASSERT(sks.size() >= num_decls); @@ -153,19 +154,24 @@ namespace smt { sk_value = cex->get_const_interp(sk_d); if (sk_value == 0) { sk_value = cex->get_some_value(sk_d->get_range()); - if (sk_value == 0) + if (sk_value == 0) { + 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 != 0) { + TRACE("model_checker", tout << "Found inverse " << mk_pp(sk_term, m) << "\n";); SASSERT(!m.is_model_value(sk_term)); if (sk_term_gen > max_generation) max_generation = sk_term_gen; sk_value = sk_term; } else { + TRACE("model_checker", tout << "no inverse value for " << sk_value << "\n";); return false; } } @@ -175,8 +181,10 @@ namespace smt { sk_value = sk_term; } } - if (contains_model_value(sk_value)) + if (contains_model_value(sk_value)) { + TRACE("model_checker", tout << "value is private to model: " << sk_value << "\n";); return false; + } bindings.set(num_decls - i - 1, sk_value); } @@ -186,6 +194,7 @@ namespace smt { } tout << "\n";); + max_generation = std::max(m_qm->get_generation(q), max_generation); add_instance(q, bindings, max_generation); return true; } @@ -286,18 +295,15 @@ namespace smt { break; model_ref cex; m_aux_context->get_model(cex); - if (add_instance(q, cex.get(), sks, true)) { - num_new_instances++; - if (num_new_instances < m_max_cexs) { - if (!add_blocking_clause(cex.get(), sks)) - break; // add_blocking_clause failed... stop the search for new counter-examples... - } - } - else { + if (!add_instance(q, cex.get(), sks, true)) { break; } - if (num_new_instances >= m_max_cexs) - 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\n";); + // add_blocking_clause failed... stop the search for new counter-examples... + break; + } } if (num_new_instances == 0) { @@ -357,9 +363,6 @@ namespace smt { } } - struct scoped_set_relevancy { - }; - bool model_checker::check(proto_model * md, obj_map const & root2value) { SASSERT(md != 0); m_root2value = &root2value; @@ -368,8 +371,11 @@ namespace smt { if (it == end) return true; - if (m_iteration_idx >= m_params.m_mbqi_max_iterations) + 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(); diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index 5dc0dd8ec..ff84992e1 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -315,6 +315,7 @@ namespace smt { void mk_instantiation_set(ast_manager & m) { SASSERT(is_root()); + SASSERT(!m_set); m_set = alloc(instantiation_set, m); } @@ -497,7 +498,7 @@ namespace smt { m_bvsimp = static_cast(s.get_plugin(m.mk_family_id("bv"))); } - ~auf_solver() { + virtual ~auf_solver() { flush_nodes(); reset_eval_cache(); } @@ -1001,10 +1002,13 @@ namespace smt { void add_elem_to_empty_inst_sets() { ptr_vector::const_iterator it = m_root_nodes.begin(); ptr_vector::const_iterator end = m_root_nodes.end(); + obj_map sort2elems; + ptr_vector need_fresh; for (; it != end; ++it) { node * n = *it; 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 @@ -1013,11 +1017,29 @@ namespace smt { // If these module values "leak" inside the logical context, they may affect satisfiability. // sort * ns = n->get_sort(); - if (m_manager.is_fully_interp(ns)) + if (m_manager.is_fully_interp(ns)) { n->insert(m_model->get_some_value(ns), 0); - else - n->insert(m_manager.mk_fresh_const("elem", ns), 0); + } + else { + need_fresh.push_back(n); + } } + else { + sort2elems.insert(n->get_sort(), elems.begin()->m_key); + } + } + expr_ref_vector trail(m_manager); + for (unsigned i = 0; i < need_fresh.size(); ++i) { + expr * e; + node* n = need_fresh[i]; + sort* s = n->get_sort(); + if (!sort2elems.find(s, e)) { + e = m_manager.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_manager) << "\n";); } } diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index 21a310a42..b9c1ac453 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -20,6 +20,7 @@ Revision History: #include"smt_context.h" #include"smt_model_generator.h" #include"proto_model.h" +#include"ref_util.h" #include"for_each_expr.h" #include"ast_ll_pp.h" #include"ast_pp.h" @@ -36,6 +37,7 @@ namespace smt { } model_generator::~model_generator() { + dec_ref_collection_values(m_manager, m_hidden_ufs); } void model_generator::reset() { @@ -386,6 +388,7 @@ namespace smt { enode * n = *it3; if (is_uninterp_const(n->get_owner()) && m_context->is_relevant(n)) { func_decl * d = n->get_owner()->get_decl(); + if (m_hidden_ufs.contains(d)) continue; expr * val = get_value(n); m_model->register_decl(d, val); } @@ -404,9 +407,9 @@ namespace smt { */ bool model_generator::include_func_interp(func_decl * f) const { family_id fid = f->get_family_id(); - if (fid == null_family_id) return true; + if (fid == null_family_id) return !m_hidden_ufs.contains(f); if (fid == m_manager.get_basic_family_id()) return false; - theory * th = m_context->get_theory(fid); + theory * th = m_context->get_theory(fid); if (!th) return true; return th->include_func_interp(f); } diff --git a/src/smt/smt_model_generator.h b/src/smt/smt_model_generator.h index 6017176e5..f360dbd7e 100644 --- a/src/smt/smt_model_generator.h +++ b/src/smt/smt_model_generator.h @@ -182,6 +182,7 @@ namespace smt { obj_map m_root2value; ast_ref_vector m_asts; proto_model * m_model; + obj_hashtable m_hidden_ufs; void init_model(); void mk_bool_model(); @@ -220,6 +221,13 @@ namespace smt { 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_manager.inc_ref(f); + } + } }; }; diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp index ad2c25e63..2a56168f2 100644 --- a/src/smt/smt_quantifier.cpp +++ b/src/smt/smt_quantifier.cpp @@ -27,7 +27,7 @@ Revision History: #include"ast_smt2_pp.h" namespace smt { - + quantifier_manager_plugin * mk_default_plugin(); struct quantifier_manager::imp { @@ -40,7 +40,7 @@ namespace smt { 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), @@ -85,7 +85,7 @@ namespace smt { out << max_generation << " : " << max_cost << "\n"; } } - + void del(quantifier * q) { if (m_params.m_qi_profile) { display_stats(verbose_stream(), q); @@ -99,15 +99,15 @@ namespace smt { } bool is_shared(enode * n) const { - return m_plugin->is_shared(n); + return m_plugin->is_shared(n); } - bool add_instance(quantifier * q, app * pat, - unsigned num_bindings, - enode * const * bindings, - unsigned max_generation, - unsigned min_top_generation, - unsigned max_top_generation, + bool add_instance(quantifier * q, app * pat, + unsigned num_bindings, + enode * const * bindings, + unsigned max_generation, + unsigned min_top_generation, + unsigned max_top_generation, ptr_vector & used_enodes) { max_generation = std::max(max_generation, get_generation(q)); if (m_num_instances > m_params.m_qi_max_instances) @@ -136,7 +136,7 @@ namespace smt { } return false; } - + void init_search_eh() { m_num_instances = 0; ptr_vector::iterator it2 = m_quantifiers.begin(); @@ -147,8 +147,9 @@ namespace smt { } 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); } @@ -174,7 +175,7 @@ namespace smt { 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(); } @@ -183,9 +184,9 @@ namespace smt { 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; // TBD: && !m_context->get_manager().is_rec_fun_def(q); + return m_context.is_relevant(q) && m_context.get_assignment(q) == l_true; // && !m_context.get_manager().is_rec_fun_def(q); } bool quick_check_quantifiers() { @@ -250,7 +251,7 @@ namespace smt { quantifier_manager::~quantifier_manager() { dealloc(m_imp); } - + context & quantifier_manager::get_context() const { return m_imp->m_context; } @@ -284,16 +285,16 @@ namespace smt { return m_imp->get_generation(q); } - bool quantifier_manager::add_instance(quantifier * q, app * pat, - unsigned num_bindings, - enode * const * bindings, - unsigned max_generation, - unsigned min_top_generation, - unsigned max_top_generation, + bool quantifier_manager::add_instance(quantifier * q, app * pat, + unsigned num_bindings, + enode * const * bindings, + unsigned max_generation, + unsigned min_top_generation, + unsigned max_top_generation, ptr_vector & used_enodes) { return m_imp->add_instance(q, pat, num_bindings, bindings, max_generation, min_top_generation, max_generation, used_enodes); } - + bool quantifier_manager::add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned generation) { ptr_vector tmp; return add_instance(q, 0, num_bindings, bindings, generation, generation, generation, tmp); @@ -346,7 +347,7 @@ namespace smt { 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(); } @@ -356,17 +357,14 @@ namespace smt { } void quantifier_manager::reset() { - #pragma omp critical (quantifier_manager) - { - 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); - } + 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 { } @@ -381,12 +379,12 @@ namespace smt { 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::begin_quantifiers() const { + return m_imp->m_quantifiers.begin(); } - - ptr_vector::const_iterator quantifier_manager::end_quantifiers() const { - return m_imp->m_quantifiers.end(); + + ptr_vector::const_iterator quantifier_manager::end_quantifiers() const { + return m_imp->m_quantifiers.end(); } // The default plugin uses E-matching, MBQI and quick-checker @@ -403,13 +401,13 @@ namespace smt { bool m_active; public: default_qm_plugin(): - m_qm(0), - m_context(0), - m_new_enode_qhead(0), + m_qm(0), + m_context(0), + m_new_enode_qhead(0), m_lazy_matching_idx(0), m_active(false) { } - + virtual ~default_qm_plugin() { } @@ -433,20 +431,18 @@ namespace smt { virtual bool model_based() const { return m_fparams->m_mbqi; } - virtual bool mbqi_enabled(quantifier *q) const { - if(!m_fparams->m_mbqi_id) return true; + virtual bool mbqi_enabled(quantifier *q) const { + 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()) + if (s == symbol::null || s.is_numerical()) return len == 0; - return strncmp(s.bare_str(),m_fparams->m_mbqi_id,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. - */ + /* 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. */ virtual void add(quantifier * q) { if (m_fparams->m_mbqi && mbqi_enabled(q)) { m_active = true; @@ -454,8 +450,7 @@ namespace smt { } } - virtual void del(quantifier * q) { - } + virtual void del(quantifier * q) { } virtual void push() { m_mam->push_scope(); @@ -464,7 +459,7 @@ namespace smt { m_model_finder->push_scope(); } } - + virtual void pop(unsigned num_scopes) { m_mam->pop_scope(num_scopes); m_lazy_mam->pop_scope(num_scopes); @@ -472,7 +467,7 @@ namespace smt { m_model_finder->pop_scope(num_scopes); } } - + virtual void init_search_eh() { m_lazy_matching_idx = 0; if (m_fparams->m_mbqi) { @@ -483,39 +478,43 @@ namespace smt { virtual void assign_eh(quantifier * q) { m_active = true; - if (m_fparams->m_ematching) { - 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_context->get_manager().is_pattern(mp)); - bool unary = (mp->get_num_args() == 1); - if (!unary && j >= num_eager_multi_patterns) { - TRACE("assign_quantifier", tout << "delaying (too many multipatterns):\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\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("assign_quantifier", tout << "adding:\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n";); - m_mam->add_pattern(q, mp); - } - if (!unary) - j++; + if (!m_fparams->m_ematching) { + return; + } + if (false && m_context->get_manager().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_context->get_manager().is_pattern(mp)); + bool unary = (mp->get_num_args() == 1); + if (!unary && j >= num_eager_multi_patterns) { + TRACE("assign_quantifier", tout << "delaying (too many multipatterns):\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\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("assign_quantifier", tout << "adding:\n" << mk_ismt2_pp(mp, m_context->get_manager()) << "\n";); + m_mam->add_pattern(q, mp); + } + if (!unary) + j++; + } } - + bool use_ematching() const { return m_fparams->m_ematching && !m_qm->empty(); } @@ -537,7 +536,7 @@ namespace smt { } virtual void restart_eh() { - if (m_fparams->m_mbqi) { + if (m_fparams->m_mbqi) { m_model_finder->restart_eh(); m_model_checker->restart_eh(); } @@ -612,7 +611,7 @@ namespace smt { } return FC_DONE; } - + }; quantifier_manager_plugin * mk_default_plugin() { diff --git a/src/smt/smt_quantifier.h b/src/smt/smt_quantifier.h index e814afcb1..bc731bf9c 100644 --- a/src/smt/smt_quantifier.h +++ b/src/smt/smt_quantifier.h @@ -37,7 +37,7 @@ namespace smt { 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); @@ -51,12 +51,12 @@ namespace smt { quantifier_stat * get_stat(quantifier * q) const; unsigned get_generation(quantifier * q) const; - bool add_instance(quantifier * q, app * pat, - unsigned num_bindings, - enode * const * bindings, - unsigned max_generation, - unsigned min_top_generation, - unsigned max_top_generation, + bool add_instance(quantifier * q, app * pat, + unsigned num_bindings, + enode * const * bindings, + unsigned max_generation, + unsigned min_top_generation, + unsigned max_top_generation, ptr_vector & used_enodes); bool add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned generation = 0); @@ -78,11 +78,11 @@ namespace smt { 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; @@ -97,7 +97,7 @@ namespace smt { public: quantifier_manager_plugin() {} virtual ~quantifier_manager_plugin() {} - + virtual void set_manager(quantifier_manager & qm) = 0; virtual quantifier_manager_plugin * mk_fresh() = 0; @@ -106,7 +106,7 @@ namespace smt { 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. */ @@ -131,9 +131,9 @@ namespace smt { \brief This method is invoked whenever the solver restarts. */ virtual void restart_eh() = 0; - + /** - \brief Return true if the quantifier_manager can propagate information + \brief Return true if the quantifier_manager can propagate information information back into the core. */ virtual bool can_propagate() const = 0; @@ -143,11 +143,11 @@ namespace smt { \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;} + virtual bool mbqi_enabled(quantifier *q) const {return true;} /** \brief Give a change to the plugin to adjust the interpretation of unintepreted functions. @@ -161,10 +161,10 @@ namespace smt { 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; - + }; }; diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 7cbfd0b2e..3a4f7f981 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -193,7 +193,7 @@ namespace smt { } } - void check_no_arithmetic(static_features const & st, char const * logic) { + static void check_no_arithmetic(static_features const & st, char const * logic) { if (st.m_num_arith_ineqs > 0 || st.m_num_arith_terms > 0 || st.m_num_arith_eqs > 0) throw default_exception("Benchmark constains arithmetic, but specified loging does not support it."); } @@ -236,21 +236,21 @@ namespace smt { (st.m_num_arith_eqs + st.m_num_arith_ineqs) > st.m_num_uninterpreted_constants * 9; } - bool is_in_diff_logic(static_features const & st) { + 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; } - bool is_diff_logic(static_features const & st) { + 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) ; } - void check_no_uninterpreted_functions(static_features const & st, char const * logic) { + 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."); } diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 1778ce076..59a5ddf8f 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -20,36 +20,67 @@ Notes: #include"smt_kernel.h" #include"reg_decl_plugins.h" #include"smt_params.h" +#include"smt_params_helper.hpp" +#include"mus.h" +#include"for_each_expr.h" +#include"ast_smt2_pp.h" +#include"func_decl_dependencies.h" +#include"dec_ref_util.h" namespace smt { class solver : public solver_na2as { - smt_params m_params; - smt::kernel m_context; - progress_callback * m_callback; - symbol m_logic; + smt_params m_smt_params; + params_ref m_params; + smt::kernel m_context; + progress_callback * m_callback; + symbol m_logic; + bool m_minimizing_core; + bool m_core_extend_patterns; + unsigned m_core_extend_patterns_max_distance; + obj_map m_name2assertion; + public: - solver(ast_manager & m, params_ref const & p, symbol const & l): + solver(ast_manager & m, params_ref const & p, symbol const & l) : solver_na2as(m), + m_smt_params(p), m_params(p), - m_context(m, m_params) { + m_context(m, m_smt_params), + m_minimizing_core(false), + m_core_extend_patterns(false), + m_core_extend_patterns_max_distance(UINT_MAX) { m_logic = l; if (m_logic != symbol::null) m_context.set_logic(m_logic); + smt_params_helper smth(p); + m_core_extend_patterns = smth.core_extend_patterns(); + m_core_extend_patterns_max_distance = smth.core_extend_patterns_max_distance(); } - virtual solver* translate(ast_manager& m, params_ref const& p) { - solver* result = alloc(solver, m, p, m_logic); + virtual solver * translate(ast_manager & m, params_ref const & p) { + solver * result = alloc(solver, m, p, m_logic); smt::kernel::copy(m_context, result->m_context); + + ast_translation translator(get_manager(), m); + obj_map::iterator it = m_name2assertion.begin(); + obj_map::iterator end = m_name2assertion.end(); + for (; it != end; it++) + result->m_name2assertion.insert(translator(it->m_key), + translator(it->m_value)); + return result; } - + virtual ~solver() { + dec_ref_values(get_manager(), m_name2assertion); } virtual void updt_params(params_ref const & p) { - m_params.updt_params(p); + m_smt_params.updt_params(p); + m_params.copy(p); m_context.updt_params(p); + smt_params_helper smth(p); + m_core_extend_patterns = smth.core_extend_patterns(); } virtual void collect_param_descrs(param_descrs & r) { @@ -60,15 +91,46 @@ namespace smt { m_context.collect_statistics(st); } + virtual lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) { + expr_ref_vector unfixed(m_context.m()); + return m_context.get_consequences(assumptions, vars, conseq, unfixed); + } + + virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { + return m_context.find_mutexes(vars, mutexes); + } + virtual void assert_expr(expr * t) { m_context.assert_expr(t); } + virtual void assert_expr(expr * t, expr * a) { + solver_na2as::assert_expr(t, a); + SASSERT(!m_name2assertion.contains(a)); + get_manager().inc_ref(t); + m_name2assertion.insert(a, t); + } + virtual void push_core() { m_context.push(); } virtual void pop_core(unsigned n) { + 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); } @@ -77,10 +139,39 @@ namespace smt { return m_context.check(num_assumptions, assumptions); } + struct scoped_minimize_core { + solver& s; + expr_ref_vector m_assumptions; + scoped_minimize_core(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); + } + }; + virtual void get_unsat_core(ptr_vector & r) { unsigned sz = m_context.get_unsat_core_size(); - for (unsigned i = 0; i < sz; i++) + for (unsigned i = 0; i < sz; i++) { r.push_back(m_context.get_unsat_core_expr(i)); + } + + if (m_minimizing_core && smt_params_helper(m_params).core_minimize()) { + scoped_minimize_core scm(*this); + mus mus(*this); + mus.add_soft(r.size(), r.c_ptr()); + ptr_vector r2; + if (l_true == mus.get_mus(r2)) { + r.reset(); + r.append(r2); + } + } + + if (m_core_extend_patterns) + add_pattern_literals_to_core(r); } virtual void get_model(model_ref & m) { @@ -105,8 +196,7 @@ namespace smt { r.append(tmp.size(), tmp.c_ptr()); } - virtual ast_manager& get_manager() { return m_context.m(); } - + virtual ast_manager & get_manager() const { return m_context.m(); } virtual void set_progress_callback(progress_callback * callback) { m_callback = callback; @@ -116,18 +206,118 @@ namespace smt { virtual unsigned get_num_assertions() const { return m_context.size(); } - + virtual expr * get_assertion(unsigned idx) const { SASSERT(idx < get_num_assertions()); return m_context.get_formulas()[idx]; } - virtual void display(std::ostream & out) const { - m_context.display(out); - } - - }; + 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_func_decls(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(ptr_vector & core, vector & assrtn_fds) { + assrtn_fds.resize(m_name2assertion.size()); + obj_map::iterator ait = m_name2assertion.begin(); + obj_map::iterator aend = m_name2assertion.end(); + for (unsigned i = 0; ait != aend; ait++, i++) { + if (core.contains(ait->m_key)) + continue; + collect_fds_proc p(m, assrtn_fds[i]); + expr_fast_mark1 visited; + quick_for_each_expr(p, visited, ait->m_value); + } + } + + bool fds_intersect(func_decl_set & pattern_fds, func_decl_set & assrtn_fds) { + func_decl_set::iterator it = pattern_fds.begin(); + func_decl_set::iterator end = pattern_fds.end(); + for (; it != end; it++) { + func_decl * fd = *it; + if (assrtn_fds.contains(fd)) + return true; + } + return false; + } + + void add_pattern_literals_to_core(ptr_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(); + + unsigned sz = core.size(); + for (unsigned i = 0; i < sz; i++) { + expr_ref name(core[i], m); + SASSERT(m_name2assertion.contains(name)); + expr_ref assrtn(m_name2assertion.find(name), m); + collect_pattern_func_decls(assrtn, pattern_fds); + } + + if (!pattern_fds.empty()) { + if (assrtn_fds.empty()) + compute_assrtn_fds(core, assrtn_fds); + + obj_map::iterator ait = m_name2assertion.begin(); + obj_map::iterator aend = m_name2assertion.end(); + for (unsigned i = 0; ait != aend; ait++, i++) { + if (!core.contains(ait->m_key) && + fds_intersect(pattern_fds, assrtn_fds[i])) + new_core_literals.push_back(ait->m_key); + } + } + + core.append(new_core_literals.size(), new_core_literals.c_ptr()); + + if (new_core_literals.empty()) + break; + } + } + }; }; solver * mk_smt_solver(ast_manager & m, params_ref const & p, symbol const & logic) { diff --git a/src/smt/tactic/smt_tactic.cpp b/src/smt/tactic/smt_tactic.cpp index 7fa3328fb..bd2c72c3e 100644 --- a/src/smt/tactic/smt_tactic.cpp +++ b/src/smt/tactic/smt_tactic.cpp @@ -24,63 +24,10 @@ Notes: #include"rewriter_types.h" #include"filter_model_converter.h" #include"ast_util.h" +#include"solver2tactic.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 == 0 || !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 = 0; - if (!dep2bool.find(d, b)) { - b = m.mk_fresh_const(0, m.mk_bool_sort()); - dep2bool.insert(d, b); - bool2dep.insert(b, d); - assumptions.push_back(b); - if (!fmc) { - fmc = alloc(filter_model_converter, m); - } - fmc->insert(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 smt_tactic : public tactic { smt_params m_params; @@ -191,6 +138,7 @@ public: proof_converter_ref & pc, expr_dependency_ref & core) { try { + mc = 0; pc = 0; core = 0; 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 << " " @@ -199,6 +147,7 @@ public: 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); @@ -255,11 +204,11 @@ public: if (in->models_enabled()) { model_ref md; m_ctx->get_model(md); - mc = model2model_converter(md.get()); + buffer r; + m_ctx->get_relevant_labels(0, r); + mc = model_and_labels2model_converter(md.get(), r); mc = concat(fmc.get(), mc.get()); } - pc = 0; - core = 0; return; } case l_false: { @@ -284,17 +233,17 @@ public: } in->assert_expr(m.mk_false(), pr, lcore); result.push_back(in.get()); - mc = 0; - pc = 0; - core = 0; return; } case l_undef: if (m_ctx->canceled()) { throw tactic_exception(Z3_CANCELED_MSG); } - if (m_fail_if_inconclusive) - throw tactic_exception("smt tactic failed to show goal to be sat/unsat"); + if (m_fail_if_inconclusive) { + 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().c_str()); + } result.push_back(in.get()); if (m_candidate_models) { switch (m_ctx->last_failure()) { @@ -304,10 +253,10 @@ public: if (in->models_enabled()) { model_ref md; m_ctx->get_model(md); - mc = model2model_converter(md.get()); + buffer r; + m_ctx->get_relevant_labels(0, r); + mc = model_and_labels2model_converter(md.get(), r); } - pc = 0; - core = 0; return; default: break; diff --git a/src/smt/tactic/smt_tactic.h b/src/smt/tactic/smt_tactic.h index dd113634b..a23695fd1 100644 --- a/src/smt/tactic/smt_tactic.h +++ b/src/smt/tactic/smt_tactic.h @@ -32,8 +32,6 @@ tactic * mk_smt_tactic(params_ref const & p = params_ref()); tactic * mk_smt_tactic_using(bool auto_config = true, params_ref const & p = params_ref()); -void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, obj_map& bool2dep, ref& fmc); - /* ADD_TACTIC("smt", "apply a SAT based SMT solver.", "mk_smt_tactic(p)") */ diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 7e594abe5..7a5d46e7d 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -958,6 +958,7 @@ namespace smt { // // ----------------------------------- 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 @@ -1092,7 +1093,7 @@ namespace smt { virtual inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared); virtual inf_eps_rational value(theory_var v); virtual theory_var add_objective(app* term); - virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_numeral const& val); + expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_numeral const& val); void enable_record_conflict(expr* bound); void record_conflict(unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 163452d47..de357c8d3 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -417,8 +417,8 @@ namespace smt { 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 << ":"; + // out << "v" << bound::get_var() << " " << bound::get_bound_kind() << " " << get_k() << " "; + // out << l << ":"; th.get_context().display_detailed_literal(out, l); } @@ -1056,6 +1056,11 @@ namespace smt { 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; @@ -1300,6 +1305,7 @@ namespace smt { */ + template bool theory_arith::pick_var_to_leave( @@ -1331,7 +1337,7 @@ namespace smt { 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; + a_ij = coeff_ij; } has_shared |= ctx.is_shared(get_enode(s)); } @@ -1709,7 +1715,7 @@ namespace smt { SASSERT(!maintain_integrality || valid_assignment()); SASSERT(satisfy_bounds()); } - TRACE("opt", display(tout);); + TRACE("opt_verbose", display(tout);); return (best_efforts>0 || ctx.get_cancel_flag())?BEST_EFFORT:result; } diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 1ce56ffe8..81ceb114c 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -268,7 +268,7 @@ namespace smt { } rational _val; expr* arg1, *arg2; - if (m_util.is_mul(m, arg1, arg2) && m_util.is_numeral(arg1, _val)) { + if (m_util.is_mul(m, arg1, arg2) && m_util.is_numeral(arg1, _val) && is_app(arg1) && is_app(arg2)) { SASSERT(m->get_num_args() == 2); numeral val(_val); theory_var v = internalize_term_core(to_app(arg2)); @@ -297,6 +297,11 @@ namespace smt { scoped_row_vars _sc(m_row_vars, m_row_vars_top); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { + if (is_var(n->get_arg(i))) { + std::ostringstream strm; + strm << mk_pp(n, get_manager()) << " contains a free variable"; + throw default_exception(strm.str()); + } internalize_internal_monomial(to_app(n->get_arg(i)), r_id); } enode * e = mk_enode(n); @@ -356,6 +361,11 @@ namespace smt { SASSERT(!val.is_one()); unsigned r_id = mk_row(); scoped_row_vars _sc(m_row_vars, m_row_vars_top); + if (is_var(m->get_arg(1))) { + 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(m->get_arg(0))); theory_var v = internalize_mul_core(to_app(m->get_arg(1))); @@ -455,7 +465,7 @@ namespace smt { tout << s_ante << "\n" << s_conseq << "\n";); literal lits[2] = {l_ante, l_conseq}; - ctx.mk_th_axiom(get_id(), 2, lits); + mk_clause(l_ante, l_conseq, 0, 0); if (ctx.relevancy()) { if (l_ante == false_literal) { ctx.mark_as_relevant(l_conseq); @@ -747,17 +757,25 @@ namespace smt { enode * e = mk_enode(n); return mk_var(e); } - else { - 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()); + 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()); } /** @@ -916,11 +934,13 @@ namespace smt { 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); } @@ -1051,7 +1071,7 @@ namespace smt { template void theory_arith::flush_bound_axioms() { - CTRACE("arith", !m_new_atoms.empty(), tout << "flush bound axioms\n";); + CTRACE("arith_verbose", !m_new_atoms.empty(), tout << "flush bound axioms\n";); while (!m_new_atoms.empty()) { ptr_vector atoms; @@ -1066,7 +1086,7 @@ namespace smt { --i; } } - CTRACE("arith", !atoms.empty(), + CTRACE("arith", atoms.size() > 1, for (unsigned i = 0; i < atoms.size(); ++i) { atoms[i]->display(*this, tout); tout << "\n"; }); @@ -1196,6 +1216,9 @@ namespace smt { 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; @@ -1214,6 +1237,14 @@ namespace smt { 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); @@ -1237,10 +1268,11 @@ namespace smt { template void theory_arith::internalize_eq_eh(app * atom, bool_var v) { - if (m_params.m_arith_eager_eq_axioms) { + 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(atom->get_arg(0)); - app * rhs = to_app(atom->get_arg(1)); + 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. @@ -1262,7 +1294,7 @@ namespace smt { template void theory_arith::assign_eh(bool_var v, bool is_true) { - TRACE("arith", tout << "p" << v << " := " << (is_true?"true":"false") << "\n";); + 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); @@ -1373,26 +1405,27 @@ namespace smt { 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 { - TRACE("final_check_arith", tout << "m_final_check_idx: " << m_final_check_idx << ", result: " << result << "\n";); + 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("final_check_arith", tout << "check_int_feasibility(), ok: " << ok << "\n";); + TRACE("arith", tout << "check_int_feasibility(), ok: " << ok << "\n";); break; case 1: if (assume_eqs_core()) ok = FC_CONTINUE; else ok = FC_DONE; - TRACE("final_check_arith", tout << "assume_eqs(), ok: " << ok << "\n";); + TRACE("arith", tout << "assume_eqs(), ok: " << ok << "\n";); break; default: ok = process_non_linear(); - TRACE("final_check_arith", tout << "non_linear(), ok: " << ok << "\n";); + TRACE("arith", tout << "non_linear(), ok: " << ok << "\n";); break; } m_final_check_idx = (m_final_check_idx + 1) % 3; @@ -1403,7 +1436,7 @@ namespace smt { result = FC_GIVEUP; break; case FC_CONTINUE: - TRACE("final_check_arith", + TRACE("arith", tout << "continue arith..." << (get_context().inconsistent()?"inconsistent\n":"\n");); return FC_CONTINUE; @@ -1420,7 +1453,7 @@ namespace smt { template final_check_status theory_arith::final_check_eh() { TRACE("arith_eq_adapter_info", m_arith_eq_adapter.display_already_processed(tout);); - TRACE("arith_final_check", display(tout);); + TRACE("arith", display(tout);); if (!propagate_core()) return FC_CONTINUE; @@ -1437,7 +1470,7 @@ namespace smt { m_liberal_final_check = false; m_changed_assignment = false; result = final_check_core(); - TRACE("final_check_arith", tout << "result: " << result << "\n";); + TRACE("arith", tout << "result: " << result << "\n";); return result; } @@ -1637,6 +1670,7 @@ namespace smt { 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), @@ -2394,6 +2428,7 @@ namespace smt { 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); @@ -2434,7 +2469,7 @@ namespace smt { template bool theory_arith::assert_bound(bound * b) { - TRACE("assert_bound", display_bound(tout, b);); + TRACE("assert_bound", display_bound(tout, b); display(tout);); theory_var v = b->get_var(); if (b->is_atom()) { @@ -2456,7 +2491,7 @@ namespace smt { break; } - TRACE("arith_assert", tout << "result: " << result << "\n"; display(tout);); + TRACE("arith_bound", tout << "result: " << result << "\n"; display(tout);); return result; } @@ -3012,12 +3047,14 @@ namespace smt { 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() << " "; @@ -3185,7 +3222,9 @@ namespace smt { m_factory = alloc(arith_factory, get_manager()); m.register_factory(m_factory); compute_epsilon(); - refine_epsilon(); + if (!m_model_depends_on_computed_epsilon) { + refine_epsilon(); + } } template @@ -3198,7 +3237,7 @@ namespace smt { 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_value(num, m_util.is_int(var2expr(v)))); + return alloc(expr_wrapper_proc, m_factory->mk_num_value(num, m_util.is_int(var2expr(v)))); } // ----------------------------------- diff --git a/src/smt/theory_arith_eq.h b/src/smt/theory_arith_eq.h index 8e1b52ee5..ae0f17757 100644 --- a/src/smt/theory_arith_eq.h +++ b/src/smt/theory_arith_eq.h @@ -44,6 +44,7 @@ namespace smt { 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) { @@ -64,7 +65,7 @@ namespace smt { lower(v2)->push_justification(ante, numeral::zero(), proofs_enabled()); upper(v)->push_justification(ante, numeral::zero(), proofs_enabled()); - TRACE("arith_fixed_propagate_eq", tout << "propagate eq: v" << v << " = v" << v2 << "\n"; + TRACE("arith_eq", tout << "propagate eq: v" << v << " = v" << v2 << "\n"; display_var(tout, v); display_var(tout, v2);); m_stats.m_fixed_eqs++; @@ -175,7 +176,7 @@ namespace smt { timer.stop(); ok++; if (ok % 100000 == 0) { - TRACE("propagate_cheap_eq", + TRACE("arith_eq", tout << total << " " << ok << " " << static_cast(ok)/static_cast(total) << " " << timer.get_seconds() << "\n"; @@ -216,7 +217,7 @@ namespace smt { void theory_arith::propagate_cheap_eq(unsigned rid) { if (!propagate_eqs()) return; - TRACE("propagate_cheap_eq", tout << "checking if row " << rid << " can propagate equality.\n"; + 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; @@ -247,6 +248,7 @@ namespace smt { // // 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++; @@ -258,7 +260,7 @@ namespace smt { // found equality x = y antecedents ante(*this); collect_fixed_var_justifications(r, ante); - TRACE("propagate_cheap_eq", tout << "propagate eq using x-y=0 row:\n"; display_row_info(tout, r);); + 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); } @@ -299,7 +301,7 @@ namespace smt { antecedents ante(*this); collect_fixed_var_justifications(r, ante); collect_fixed_var_justifications(r2, ante); - TRACE("propagate_cheap_eq", tout << "propagate eq two rows:\n"; + TRACE("arith_eq", tout << "propagate eq two rows:\n"; tout << "swapped: " << swapped << "\n"; tout << "x : v" << x << "\n"; tout << "x2 : v" << x2 << "\n"; @@ -324,28 +326,42 @@ namespace smt { 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 (get_manager().get_sort(var2expr(x)) != get_manager().get_sort(var2expr(y))) { - TRACE("arith", tout << mk_pp(var2expr(x), get_manager()) << " = " << mk_pp(var2expr(y), get_manager()) << "\n";); + 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, - antecedents.lits().size(), antecedents.lits().c_ptr(), - antecedents.eqs().size(), antecedents.eqs().c_ptr(), + lits.size(), lits.c_ptr(), + eqs.size(), eqs.c_ptr(), _x, _y, antecedents.num_params(), antecedents.params("eq-propagate"))); - TRACE("propagate_eq_to_core", tout << "detected equality: #" << _x->get_owner_id() << " = #" << _y->get_owner_id() << "\n"; + 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)); } }; diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index 72e1f1bc2..c06f82c8b 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -1267,11 +1267,11 @@ namespace smt { final_check_status theory_arith::check_int_feasibility() { TRACE("arith_int_detail", get_context().display(tout);); if (!has_infeasible_int_var()) { - TRACE("arith_int_incomp", tout << "FC_DONE 1...\n"; display(tout);); + TRACE("arith", tout << "FC_DONE 1...\n"; display(tout);); return FC_DONE; } - TRACE("arith_int_fracs", + TRACE("arith", int num = get_num_vars(); for (theory_var v = 0; v < num; v++) { if (is_int(v) && !get_value(v).is_int()) { @@ -1385,7 +1385,7 @@ namespace smt { m_branch_cut_counter++; // TODO: add giveup code if (m_branch_cut_counter % m_params.m_arith_branch_cut_ratio == 0) { - TRACE("opt", display(tout);); + 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";); diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index 311a62a38..52a117cd5 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -23,9 +23,9 @@ Revision History: namespace smt { - template + template expr * theory_arith::mk_nary_mul(unsigned sz, expr * const * args, bool is_int) { - if (sz == 0) + if (sz == 0) return m_util.mk_numeral(rational(1), is_int); if (sz == 1) return args[0]; @@ -36,21 +36,21 @@ namespace smt { return m_util.mk_mul(sz, args); } - template + template expr * theory_arith::mk_nary_add(unsigned sz, expr * const * args, bool is_int) { - if (sz == 0) + 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 + 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. */ @@ -92,21 +92,21 @@ namespace smt { 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; + 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; + 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)) + 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. @@ -123,7 +123,7 @@ namespace smt { for (; it != end; ++it) { theory_var v = *it; expr * n = var2expr(v); - if (ctx.is_relevant(n)) + if (ctx.is_relevant(n)) mark_var(v, vars, already_found); } for (unsigned idx = 0; idx < vars.size(); idx++) { @@ -134,7 +134,7 @@ namespace smt { svector::const_iterator it = vars.begin(); svector::const_iterator end = vars.end(); for (; it != end; ++it) tout << "v" << *it << " "; - tout << "\n";); + tout << "\n";); } /** @@ -148,7 +148,7 @@ namespace smt { \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) @@ -168,24 +168,24 @@ namespace smt { 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 == 0) { - 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 (var == 0) { + 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++; @@ -257,17 +257,17 @@ namespace smt { unsigned j; for (j = 0; j < to_app(m)->get_num_args(); j++) { expr * arg = to_app(m)->get_arg(j); - if (var == 0) { - var = arg; - power = 1; - } - else if (var == arg) { - power++; - } - else { - if (curr_idx == i) - return var_power_pair(var, power); - curr_idx++; + if (var == 0) { + 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; } @@ -289,24 +289,24 @@ namespace smt { bound * l = lower(v); bound * u = upper(v); if (l && u) { - return interval(m_dep_manager, - l->get_value().get_rational().to_rational(), + 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_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_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_rational().to_rational(), !u->get_value().get_infinitesimal().to_rational().is_zero(), false, m_dep_manager.mk_leaf(u)); @@ -333,12 +333,12 @@ namespace smt { 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", + + 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";); + display_interval(tout << "target before: ", target); tout << "\n";); i.expt(power); target *= i; TRACE("non_linear", display_interval(tout << "target after: ", target); tout << "\n";); @@ -348,7 +348,7 @@ namespace smt { \brief Evaluate the given expression using interval arithmetic. - If a subexpression is internalized, then mk_interval_for is used to - compute its interval. + compute its interval. - Only +, *, and numerals are handled. */ @@ -382,7 +382,7 @@ namespace smt { interval it = evaluate_as_interval(var); it.expt(power); r *= it; - } + } TRACE("cross_nested_eval_bug", display_nested_form(tout, n); tout << "\ninterval: " << r << "\n";); return r; } @@ -424,7 +424,7 @@ namespace smt { ptr_vector::const_iterator end = bounds.end(); for (; it != end; ++it) { bound * b = static_cast(*it); - accumulate_justification(*b, new_bound, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); + accumulate_justification(*b, new_bound, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); } } @@ -476,7 +476,7 @@ namespace smt { } return r; } - + template bool theory_arith::update_bounds_using_interval(expr * n, interval const & i) { SASSERT(expr2var(n) != null_theory_var); @@ -510,7 +510,7 @@ namespace smt { } /** - \brief Propagate a bound to the i-th variable of the given monomial + \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 @@ -523,7 +523,7 @@ namespace smt { var_power_pair p = get_var_and_degree(m, i); expr * v = p.first; unsigned power = p.second; - TRACE("propagate_nl_downward", tout << "m: " << mk_ismt2_pp(m, get_manager()) << "\nv: " << mk_ismt2_pp(v, get_manager()) << + TRACE("propagate_nl_downward", tout << "m: " << mk_ismt2_pp(m, get_manager()) << "\nv: " << mk_ismt2_pp(v, get_manager()) << "\npower: " << power << "\n";); if (power != 1) return false; // TODO: remove, when the n-th root is implemented in interval. @@ -556,7 +556,7 @@ namespace smt { 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) + if (i == -1) return propagate_nl_upward(m); else return propagate_nl_downward(m, i); @@ -589,10 +589,8 @@ namespace smt { m_dep_manager.reset(); bool propagated = 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) { - theory_var v = *it; + 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; @@ -640,6 +638,7 @@ namespace smt { 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(); } @@ -654,16 +653,20 @@ namespace smt { 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); + rational val(1), v_val; for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); - theory_var curr = expr2var(arg); - SASSERT(curr != null_theory_var); - val *= get_value(curr, computed_epsilon); + 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; } - return get_value(v, computed_epsilon) == 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, @@ -691,11 +694,11 @@ namespace smt { /** \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 @@ -705,44 +708,42 @@ namespace smt { theory_var target = null_theory_var; bool bounded = false; unsigned n = 0; - numeral range; - svector::const_iterator it = m_nl_monomials.begin(); - svector::const_iterator end = m_nl_monomials.end(); - for (; it != end; ++it) { - theory_var v = *it; - if (is_real(v)) + 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); + bool r = check_monomial_assignment(v, computed_epsilon); SASSERT(!computed_epsilon); // integer variables do not use 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";); + 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";); } } } @@ -762,7 +763,7 @@ namespace smt { m_stats.m_nl_branching++; SASSERT(is_int(v)); expr * bound = 0; - if (lower(v)) + 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)); @@ -787,14 +788,14 @@ namespace smt { unsigned num_nl_vars = 0; 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)) { - num_nl_vars++; - } - else { - if (lower_bound(_var).is_zero()) - return true; - } + 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; } @@ -809,9 +810,9 @@ namespace smt { 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(); + theory_var _var = expr2var(arg); + if (is_fixed(_var)) + r *= lower_bound(_var).get_rational(); } return r; } @@ -825,9 +826,9 @@ namespace smt { 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; + theory_var _var = expr2var(arg); + if (!is_fixed(_var)) + return arg; } return 0; } @@ -886,7 +887,7 @@ namespace smt { } else { // One of the x_i variables is zero, - // or all of them are assigned. + // or all of them are assigned. // Assert the equality // (= (* x_1 ... x_n) k) @@ -908,45 +909,45 @@ namespace smt { 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(); - } + 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", + + TRACE("non_linear", for (unsigned j = 0; j < new_lower->m_lits.size(); ++j) { ctx.display_detailed_literal(tout, new_lower->m_lits[j]); tout << " "; } tout << "\n";); - + accumulate_justification(*u, *new_lower, numeral::zero(), m_tmp_lit_set, m_tmp_eq_set); - - TRACE("non_linear", + + TRACE("non_linear", for (unsigned j = 0; j < new_lower->m_lits.size(); ++j) { ctx.display_detailed_literal(tout, new_lower->m_lits[j]); tout << " "; } tout << "\n";); - - } - } + + } + } new_upper->m_lits.append(new_lower->m_lits); new_upper->m_eqs.append(new_lower->m_eqs); - TRACE("non_linear", + TRACE("non_linear", tout << "lower: " << new_lower << " upper: " << new_upper << "\n"; for (unsigned j = 0; j < new_upper->m_lits.size(); ++j) { ctx.display_detailed_literal(tout, new_upper->m_lits[j]); @@ -965,10 +966,19 @@ namespace smt { bool theory_arith::propagate_linear_monomials() { TRACE("non_linear", tout << "propagating linear monomials...\n";); bool p = false; - svector::const_iterator it = m_nl_monomials.begin(); - svector::const_iterator end = m_nl_monomials.end(); - for (; it != end; ++it) { - theory_var v = *it; + // 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; } @@ -979,9 +989,9 @@ namespace smt { /* 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: @@ -995,11 +1005,11 @@ namespace smt { 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: @@ -1009,13 +1019,13 @@ namespace smt { h_p(x) = x(2 + x^3(1 + x)) The horner extension evaluates tighter intervals when - condensation is not used. + 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 + 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 @@ -1027,13 +1037,13 @@ namespace smt { 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: @@ -1047,7 +1057,7 @@ namespace smt { If we compute the bounds for x^2 - x we obtain (-oo, oo). - On the other hand, if we compute the bounds for + 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. @@ -1055,8 +1065,8 @@ namespace smt { 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, + + 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 @@ -1066,7 +1076,7 @@ namespace smt { /** \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 @@ -1208,7 +1218,7 @@ namespace smt { UNREACHABLE(); } } - + // Update the number of occurrences in the result vector. typename var2num_occs::iterator it2 = m_var2num_occs.begin(); typename var2num_occs::iterator end2 = m_var2num_occs.end(); @@ -1263,14 +1273,14 @@ namespace smt { 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, + 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) { \ @@ -1289,7 +1299,7 @@ namespace smt { 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) { @@ -1396,7 +1406,7 @@ namespace smt { SASSERT(d != UINT_MAX); return d; } - + /** \brief Divide m by var^d. */ @@ -1416,19 +1426,19 @@ namespace smt { ptr_buffer new_args; for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); - if (arg == var) { - if (idx < d) - idx++; - else - new_args.push_back(arg); - } - else { - new_args.push_back(arg); - } + 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)); + 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; @@ -1442,7 +1452,7 @@ namespace smt { SASSERT(!p.empty()); SASSERT(var != 0); unsigned d = get_min_degree(p, var); - TRACE("horner_bug", tout << "poly:\n"; + 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";); @@ -1467,7 +1477,7 @@ namespace smt { // TODO: improve here s = m_util.mk_add(q, s); } - + expr * result = s; if (d != 0) { expr * xd = power(var, d); @@ -1513,9 +1523,9 @@ namespace smt { unsigned nm = UINT_MAX; if (in_monovariate_monomials(p, var, i1, a, n, i2, b, 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()); - } + 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"; @@ -1556,7 +1566,7 @@ namespace smt { sbuffer rest; unsigned sz = p.size(); for (unsigned i = 0; i < sz; i++) { - if (i != i1 && i != i2) + if (i != i1 && i != i2) rest.push_back(p[i]); } if (rest.empty()) @@ -1620,7 +1630,7 @@ namespace smt { /** \brief Check whether the polynomial represented by the current row is - consistent with respect to the known bound when converted into a + consistent with respect to the known bound when converted into a equivalent cross nested form. */ template @@ -1630,17 +1640,17 @@ namespace smt { 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 + 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. @@ -1654,10 +1664,10 @@ namespace smt { 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. + 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'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. @@ -1696,7 +1706,7 @@ namespace smt { svector::const_iterator end = nl_cluster.end(); for (; it != end; ++it) { theory_var v = *it; - if (!is_base(v)) + if (!is_base(v)) continue; m_stats.m_nl_cross_nested++; row const & r = m_rows[get_var_row(v)]; @@ -1719,8 +1729,8 @@ namespace smt { /** \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" > + "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 @@ -1801,7 +1811,7 @@ namespace smt { } if (!coeff.is_zero()) return gb.mk_monomial(coeff, vars.size(), vars.c_ptr()); - else + else return 0; } @@ -1918,7 +1928,7 @@ namespace smt { derived_bound b(null_theory_var, inf_numeral(0), B_LOWER); dependency2new_bound(d, b); set_conflict(b, ante, "arith_nl"); - TRACE("non_linear", + TRACE("non_linear", for (unsigned i = 0; i < b.m_lits.size(); ++i) { tout << b.m_lits[i] << " "; }); @@ -2027,7 +2037,7 @@ namespace smt { unsigned num1 = m1_sq->get_degree(); unsigned num2 = m2_sq->get_degree(); unsigned num12 = m1m2->get_degree(); - if (num1 + num2 != num12 * 2) + if (num1 + num2 != num12 * 2) return false; unsigned i1, i2, i12; i1 = i2 = i12 = 0; @@ -2072,8 +2082,8 @@ namespace smt { */ 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. + // 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; @@ -2117,7 +2127,7 @@ namespace smt { 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"; + 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";); @@ -2131,7 +2141,7 @@ namespace smt { deleted[i] = true; deleted[j] = true; deleted[k] = true; - break; + break; } } if (k < num) @@ -2184,7 +2194,7 @@ namespace smt { grobner::monomial const * m = eq->get_monomial(i); if (m->get_degree() == 0) k -= m->get_coeff(); - else + else args.push_back(monomial2expr(eq->get_monomial(i), is_int)); } context & ctx = get_context(); @@ -2213,7 +2223,7 @@ namespace smt { 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. */ @@ -2227,7 +2237,7 @@ namespace smt { 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; @@ -2267,7 +2277,7 @@ namespace smt { 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, + // 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) { @@ -2277,7 +2287,7 @@ namespace smt { 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)) + if (internalize_gb_eq(eq)) result = GB_NEW_EQ; } } @@ -2331,10 +2341,8 @@ namespace smt { bool theory_arith::max_min_nl_vars() { var_set already_found; svector vars; - 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 j = 0; j < m_nl_monomials.size(); ++j) { + theory_var v = m_nl_monomials[j]; mark_var(v, vars, already_found); expr * n = var2expr(v); SASSERT(is_pure_monomial(n)); @@ -2353,6 +2361,7 @@ namespace smt { */ template final_check_status theory_arith::process_non_linear() { + m_model_depends_on_computed_epsilon = false; if (m_nl_monomials.empty()) return FC_DONE; @@ -2372,10 +2381,10 @@ namespace smt { 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", tout << "processing non linear constraints...\n"; get_context().display(tout);); @@ -2384,8 +2393,8 @@ namespace smt { failed(); return FC_CONTINUE; } - - if (!max_min_nl_vars()) + + if (!max_min_nl_vars()) return FC_CONTINUE; if (check_monomial_assignments()) { @@ -2409,20 +2418,20 @@ namespace smt { } break; case 1: - if (!is_cross_nested_consistent(vars)) + 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: + case GB_PROGRESS: progress = true; break; - case GB_NEW_EQ: + case GB_NEW_EQ: progress = true; propagate_core(); break; - case GB_FAIL: + case GB_FAIL: break; } } diff --git a/src/smt/theory_arith_pp.h b/src/smt/theory_arith_pp.h index ec14d8374..7b657d9c2 100644 --- a/src/smt/theory_arith_pp.h +++ b/src/smt/theory_arith_pp.h @@ -27,22 +27,22 @@ namespace smt { template void theory_arith::collect_statistics(::statistics & st) const { st.update("arith conflicts", m_stats.m_conflicts); - st.update("add rows", m_stats.m_add_rows); - st.update("pivots", m_stats.m_pivots); - st.update("assert lower", m_stats.m_assert_lower); - st.update("assert upper", m_stats.m_assert_upper); - st.update("assert diseq", m_stats.m_assert_diseq); - st.update("bound prop", m_stats.m_bound_props); - st.update("fixed eqs", m_stats.m_fixed_eqs); - st.update("offset eqs", m_stats.m_offset_eqs); - st.update("gcd tests", m_stats.m_gcd_tests); - st.update("ineq splits", m_stats.m_branches); - st.update("gomory cuts", m_stats.m_gomory_cuts); - st.update("max-min", m_stats.m_max_min); - st.update("grobner", m_stats.m_gb_compute_basis); - st.update("pseudo nonlinear", m_stats.m_nl_linear); - st.update("nonlinear bounds", m_stats.m_nl_bounds); - st.update("nonlinear horner", m_stats.m_nl_cross_nested); + 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 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); } diff --git a/src/smt/theory_array.cpp b/src/smt/theory_array.cpp index 5e0539787..b06e50d2a 100644 --- a/src/smt/theory_array.cpp +++ b/src/smt/theory_array.cpp @@ -404,7 +404,7 @@ namespace smt { r = assert_delayed_axioms(); } } - TRACE("as_array", tout << "m_found_unsupported_op: " << m_found_unsupported_op << " " << r << "\n";); + TRACE("array", tout << "m_found_unsupported_op: " << m_found_unsupported_op << " " << r << "\n";); if (r == FC_DONE && m_found_unsupported_op) r = FC_GIVEUP; return r; diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index 498f8aeec..fa48fea57 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -33,7 +33,7 @@ namespace smt { } void theory_array_base::found_unsupported_op(expr * n) { - TRACE("theory_array_unsup", tout << mk_ll_pp(n, get_manager()) << "\n";); + TRACE("array", tout << mk_ll_pp(n, get_manager()) << "\n";); if (!m_found_unsupported_op) { get_context().push_trail(value_trail(m_found_unsupported_op)); m_found_unsupported_op = true; diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index 2945c8c93..a886c8a1e 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -312,6 +312,7 @@ namespace smt { 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); @@ -809,6 +810,7 @@ namespace smt { 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); @@ -830,6 +832,7 @@ namespace smt { 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); @@ -1533,6 +1536,7 @@ namespace smt { } 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. @@ -1579,7 +1583,7 @@ namespace smt { #endif get_fixed_value(v, val); SASSERT(r); - return alloc(expr_wrapper_proc, m_factory->mk_value(val, get_bv_size(v))); + 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 { diff --git a/src/smt/theory_datatype.h b/src/smt/theory_datatype.h index 95c729dc2..b97adacfe 100644 --- a/src/smt/theory_datatype.h +++ b/src/smt/theory_datatype.h @@ -97,6 +97,7 @@ namespace smt { virtual void pop_scope_eh(unsigned num_scopes); virtual final_check_status final_check_eh(); virtual void reset_eh(); + virtual void restart_eh() { m_util.reset(); } virtual bool is_shared(theory_var v) const; public: theory_datatype(ast_manager & m, theory_datatype_params & p); diff --git a/src/smt/theory_dense_diff_logic.h b/src/smt/theory_dense_diff_logic.h index 1b1653eb7..c2be89bc3 100644 --- a/src/smt/theory_dense_diff_logic.h +++ b/src/smt/theory_dense_diff_logic.h @@ -198,7 +198,7 @@ namespace smt { 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_rational const& val, bool is_strict); + 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; @@ -270,8 +270,8 @@ namespace smt { virtual inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared); virtual inf_eps_rational value(theory_var v); virtual theory_var add_objective(app* term); - virtual expr_ref mk_gt(theory_var v, inf_rational const& val); - virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val); + virtual expr_ref mk_gt(theory_var v, inf_eps const& val); + expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val); // ----------------------------------- // diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index fd388b5bd..877d4f659 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -828,7 +828,7 @@ namespace smt { 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_value(num, is_int(v))); + 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. @@ -1002,9 +1002,10 @@ namespace smt { m_assignment[i] = a; // TBD: if epsilon is != 0, then adjust a by some small fraction. } - blocker = mk_gt(v, r); + inf_eps result(rational(0), r); + blocker = mk_gt(v, result); IF_VERBOSE(10, verbose_stream() << blocker << "\n";); - return inf_eps(rational(0), r); + return result; } default: TRACE("opt", tout << "unbounded\n"; ); @@ -1034,18 +1035,18 @@ namespace smt { } template - expr_ref theory_dense_diff_logic::mk_gt(theory_var v, inf_rational const& val) { + 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( - filter_model_converter& fm, theory_var v, inf_rational const& val) { + filter_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_rational const& val, bool is_strict) { + 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); diff --git a/src/smt/theory_diff_logic.h b/src/smt/theory_diff_logic.h index 6fb9e6454..f47e61548 100644 --- a/src/smt/theory_diff_logic.h +++ b/src/smt/theory_diff_logic.h @@ -324,14 +324,15 @@ namespace smt { virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared); virtual inf_eps value(theory_var v); virtual theory_var add_objective(app* term); - virtual expr_ref mk_gt(theory_var v, inf_rational const& val); - virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val); + expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val); bool internalize_objective(expr * n, rational const& m, rational& r, objective_term & objective); private: - expr_ref mk_ineq(theory_var v, inf_rational const& val, bool is_strict); + 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); diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 98f94c6d2..372786c01 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -905,7 +905,7 @@ model_value_proc * theory_diff_logic::mk_value(enode * n, model_generator & 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_value(num, m_util.is_int(n->get_owner()))); + return alloc(expr_wrapper_proc, m_factory->mk_num_value(num, m_util.is_int(n->get_owner()))); } template @@ -1242,7 +1242,8 @@ theory_diff_logic::maximize(theory_var v, expr_ref& blocker, bool& has_shar rational r = rational(val.first) + m_delta*rational(val.second); m_graph.set_assignment(i, numeral(r)); } - blocker = mk_gt(v, r); + inf_eps r1(rational(0), r); + blocker = mk_gt(v, r1); return inf_eps(rational(0), r + m_objective_consts[v]); } default: @@ -1273,7 +1274,7 @@ theory_var theory_diff_logic::add_objective(app* term) { } template -expr_ref theory_diff_logic::mk_ineq(theory_var v, inf_rational const& val, bool is_strict) { +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); @@ -1304,7 +1305,7 @@ expr_ref theory_diff_logic::mk_ineq(theory_var v, inf_rational const& val, return f; } - inf_rational new_val = val; // - inf_rational(m_objective_consts[v]); + 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()) { @@ -1328,12 +1329,12 @@ expr_ref theory_diff_logic::mk_ineq(theory_var v, inf_rational const& val, } template -expr_ref theory_diff_logic::mk_gt(theory_var v, inf_rational const& val) { +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(filter_model_converter& fm, theory_var v, inf_rational const& val) { +expr_ref theory_diff_logic::mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val) { return mk_ineq(v, val, false); } diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index 6038867aa..a3bf77180 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -21,6 +21,7 @@ Revision History: #include"theory_fpa.h" #include"theory_bv.h" #include"smt_model_generator.h" +#include"bv2fpa_converter.h" namespace smt { @@ -83,27 +84,6 @@ namespace smt { } } - void theory_fpa::fpa2bv_converter_wrapped::mk_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { - // Note: this introduces new UFs that should be filtered afterwards. - return fpa2bv_converter::mk_function(f, num, args, result); - } - - expr_ref theory_fpa::fpa2bv_converter_wrapped::mk_min_max_unspecified(func_decl * f, expr * x, expr * y) { - expr_ref a(m), wrapped(m), wu(m), wu_eq(m); - a = m.mk_app(f, x, y); - wrapped = m_th.wrap(a); - wu = m_th.unwrap(wrapped, f->get_range()); - wu_eq = m.mk_eq(wu, a); - m_extra_assertions.push_back(wu_eq); - - unsigned bv_sz = m_bv_util.get_bv_size(wrapped); - expr_ref sc(m); - sc = m.mk_eq(m_bv_util.mk_extract(bv_sz-2, 0, wrapped), m_bv_util.mk_numeral(0, bv_sz-1)); - m_extra_assertions.push_back(sc); - - return wu; - } - theory_fpa::theory_fpa(ast_manager & m) : theory(m.mk_family_id("fpa")), m_converter(m, this), @@ -137,15 +117,15 @@ namespace smt { SASSERT(m_trail_stack.get_num_scopes() == 0); SASSERT(m_conversions.empty()); - SASSERT(m_is_added_to_model.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, ptr_vector & values) { - TRACE("t_fpa_detail", + 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;); @@ -222,7 +202,7 @@ namespace smt { app * theory_fpa::fpa_rm_value_proc::mk_value(model_generator & mg, ptr_vector & values) { SASSERT(values.size() == 1); - TRACE("t_fpa_detail", + 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;); @@ -256,12 +236,12 @@ namespace smt { 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 * cargs[3] = { to_app(e)->get_arg(0), to_app(e)->get_arg(1), to_app(e)->get_arg(2) }; res = m_bv_util.mk_concat(3, cargs); m_th_rw((expr_ref&)res); } else { - sort * es = m.get_sort(e); + sort * es = m.get_sort(e); sort_ref bv_srt(m); if (m_converter.is_rm(es)) @@ -285,7 +265,7 @@ namespace smt { 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(); + ast_manager & m = get_manager(); app_ref res(m); unsigned bv_sz = m_bv_util.get_bv_size(e); @@ -306,7 +286,7 @@ namespace smt { m_bv_util.mk_extract(bv_sz - 2, sbits - 1, e), m_bv_util.mk_extract(sbits - 2, 0, e)); } - + return res; } @@ -328,12 +308,13 @@ namespace smt { 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)) { + 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); @@ -350,7 +331,7 @@ namespace smt { } else UNREACHABLE(); - + return res; } @@ -383,7 +364,7 @@ namespace smt { res = convert_atom(e); else if (m_fpa_util.is_float(e) || m_fpa_util.is_rm(e)) res = convert_term(e); - else + else res = convert_conversion_term(e); TRACE("t_fpa_detail", tout << "converted; caching:" << std::endl; @@ -416,6 +397,7 @@ namespace smt { 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;); @@ -469,7 +451,7 @@ namespace smt { 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(); @@ -515,7 +497,7 @@ namespace smt { SASSERT(n->get_owner()->get_decl()->get_range() == s); ast_manager & m = get_manager(); - context & ctx = get_context(); + context & ctx = get_context(); app_ref owner(n->get_owner(), m); if (!is_attached_to_var(n)) { @@ -531,7 +513,7 @@ namespace smt { assert_cnstr(valid); } } - + if (!ctx.relevancy()) relevant_eh(owner); } @@ -571,6 +553,7 @@ namespace smt { 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); @@ -621,7 +604,7 @@ namespace smt { 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(c_eq_iff); assert_cnstr(mk_side_conditions()); return; @@ -701,8 +684,15 @@ namespace smt { // These are the conversion functions fp.to_* */ SASSERT(!m_fpa_util.is_float(n) && !m_fpa_util.is_rm(n)); } - else - UNREACHABLE(); + 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() { @@ -710,9 +700,12 @@ namespace smt { 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 = 0; + m_th_rw.reset(); + m_trail_stack.pop_scope(m_trail_stack.get_num_scopes()); + if (m_factory) { + dealloc(m_factory); + m_factory = 0; + } ast_manager & m = get_manager(); dec_ref_map_key_values(m, m_conversions); dec_ref_collection_values(m, m_is_added_to_model); @@ -727,8 +720,9 @@ namespace smt { void theory_fpa::init_model(model_generator & mg) { TRACE("t_fpa", tout << "initializing model" << std::endl; display(tout);); - m_factory = alloc(fpa_value_factory, get_manager(), get_family_id()); - mg.register_factory(m_factory); + 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) { @@ -814,7 +808,33 @@ namespace smt { return res; } - void theory_fpa::finalize_model(model_generator & mg) {} + 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 { @@ -865,8 +885,8 @@ namespace smt { } bool theory_fpa::include_func_interp(func_decl * f) { - TRACE("t_fpa", tout << "f = " << mk_ismt2_pp(f, get_manager()) << std::endl;); - + TRACE("t_fpa", tout << "f = " << mk_ismt2_pp(f, get_manager()) << std::endl;); + if (f->get_family_id() == get_family_id()) { bool include = m_fpa_util.is_min_unspecified(f) || @@ -882,8 +902,6 @@ namespace smt { } return false; } - else if (m_converter.is_uf2bvuf(f) || m_converter.is_special(f)) - return false; else return true; } diff --git a/src/smt/theory_fpa.h b/src/smt/theory_fpa.h index 32fcfcffc..998f2be3c 100644 --- a/src/smt/theory_fpa.h +++ b/src/smt/theory_fpa.h @@ -83,9 +83,6 @@ namespace smt { virtual ~fpa2bv_converter_wrapped() {} virtual void mk_const(func_decl * f, expr_ref & result); virtual void mk_rm_const(func_decl * f, expr_ref & result); - virtual void mk_function(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - - virtual expr_ref mk_min_max_unspecified(func_decl * f, expr * x, expr * y); }; class fpa_value_proc : public model_value_proc { @@ -123,7 +120,7 @@ namespace smt { 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) {} + 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)); } diff --git a/src/smt/theory_opt.h b/src/smt/theory_opt.h index 58e039140..421e6feca 100644 --- a/src/smt/theory_opt.h +++ b/src/smt/theory_opt.h @@ -33,7 +33,6 @@ namespace smt { 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; - virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val) { UNREACHABLE(); return expr_ref(*((ast_manager*)0)); } bool is_linear(ast_manager& m, expr* term); bool is_numeral(arith_util& a, expr* term); }; diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index a85f9aa80..507cb6d43 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -321,7 +321,8 @@ namespace smt { if (m_simplex.upper_valid(v)) { m_simplex.get_upper(v, last_bound); if (m_mpq_inf_mgr.gt(bound, last_bound)) { - literal lit = m_explain_upper.get(v, null_literal); + literal lit = m_explain_upper.get(v, null_literal); + TRACE("pb", tout << ~lit << " " << ~explain << "\n";); get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain)); return false; } @@ -342,6 +343,7 @@ namespace smt { m_simplex.get_lower(v, last_bound); if (m_mpq_inf_mgr.gt(last_bound, bound)) { literal lit = m_explain_lower.get(v, null_literal); + TRACE("pb", tout << ~lit << " " << ~explain << "\n";); get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain)); return false; } @@ -405,6 +407,7 @@ namespace smt { if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); } + TRACE("pb", tout << lits << "\n";); ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); return false; @@ -515,16 +518,17 @@ namespace smt { ++log; n *= 2; } - unsigned th = args.size()*log; // 10* + unsigned th = args.size()*log*log; c->m_compilation_threshold = th; - IF_VERBOSE(2, verbose_stream() << "(smt.pb setting compilation threhshold to " << th << ")\n";); + IF_VERBOSE(2, verbose_stream() << "(smt.pb setting compilation threshold to " << th << ")\n";); TRACE("pb", tout << "compilation threshold: " << th << "\n";); } else { c->m_compilation_threshold = UINT_MAX; } init_watch_var(*c); - m_ineqs.insert(abv, c); + init_watch(abv); + m_var_infos[abv].m_ineq = c; m_ineqs_trail.push_back(abv); if (m_enable_simplex) { @@ -684,35 +688,43 @@ namespace smt { 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) { - ptr_vector* ineqs; - if (!m_lwatch.find(lit.index(), ineqs)) { + init_watch(lit.var()); + ptr_vector* ineqs = m_var_infos[lit.var()].m_lit_watch[lit.sign()]; + if (ineqs == 0) { ineqs = alloc(ptr_vector); - m_lwatch.insert(lit.index(), ineqs); + m_var_infos[lit.var()].m_lit_watch[lit.sign()] = ineqs; } ineqs->push_back(c); } void theory_pb::watch_var(bool_var v, ineq* c) { - ptr_vector* ineqs; - if (!m_vwatch.find(v, ineqs)) { + init_watch(v); + ptr_vector* ineqs = m_var_infos[v].m_var_watch; + if (ineqs == 0) { ineqs = alloc(ptr_vector); - m_vwatch.insert(v, ineqs); + m_var_infos[v].m_var_watch = ineqs; } ineqs->push_back(c); } void theory_pb::unwatch_var(bool_var v, ineq* c) { - ptr_vector* ineqs = 0; - if (m_vwatch.find(v, ineqs)) { + ptr_vector* ineqs = m_var_infos[v].m_var_watch; + if (ineqs) { remove(*ineqs, c); } } - void theory_pb::unwatch_literal(literal w, ineq* c) { - ptr_vector* ineqs = 0; - if (m_lwatch.find(w.index(), ineqs)) { - remove(*ineqs, c); + void theory_pb::unwatch_literal(literal lit, ineq* c) { + ptr_vector* ineqs = m_var_infos[lit.var()].m_lit_watch[lit.sign()]; + if (ineqs) { + remove(*ineqs, c); } } @@ -738,22 +750,9 @@ namespace smt { void theory_pb::reset_eh() { - // m_watch; - u_map*>::iterator it = m_lwatch.begin(), end = m_lwatch.end(); - for (; it != end; ++it) { - dealloc(it->m_value); + for (unsigned i = 0; i < m_var_infos.size(); ++i) { + m_var_infos[i].reset(); } - it = m_vwatch.begin(), end = m_vwatch.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - u_map::iterator itc = m_ineqs.begin(), endc = m_ineqs.end(); - for (; itc != endc; ++itc) { - dealloc(itc->m_value); - } - m_lwatch.reset(); - m_vwatch.reset(); - m_ineqs.reset(); m_ineqs_trail.reset(); m_ineqs_lim.reset(); m_stats.reset(); @@ -773,8 +772,10 @@ namespace smt { void theory_pb::assign_eh(bool_var v, bool is_true) { ptr_vector* ineqs = 0; literal nlit(v, is_true); + init_watch(v); TRACE("pb", tout << "assign: " << ~nlit << "\n";); - if (m_lwatch.find(nlit.index(), ineqs)) { + ineqs = m_var_infos[v].m_lit_watch[nlit.sign()]; + if (ineqs != 0) { if (m_enable_simplex) { mpq_inf num(mpq(is_true?1:0),mpq(0)); if (!update_bound(v, ~nlit, is_true, num)) { @@ -794,14 +795,15 @@ namespace smt { } } } - if (m_vwatch.find(v, ineqs)) { + ineqs = m_var_infos[v].m_var_watch; + if (ineqs != 0) { for (unsigned i = 0; i < ineqs->size(); ++i) { ineq* c = (*ineqs)[i]; assign_watch(v, is_true, *c); } } - ineq* c = 0; - if (m_ineqs.find(v, c)) { + ineq* c = m_var_infos[v].m_ineq; + if (c != 0) { if (m_enable_simplex) { row_info const& info = m_ineq_row_info.find(v); scoped_eps_numeral coeff(m_mpq_inf_mgr); @@ -1216,7 +1218,7 @@ namespace smt { void theory_pb::inc_propagations(ineq& c) { ++c.m_num_propagations; - if (c.m_compiled == l_false && c.m_num_propagations > c.m_compilation_threshold) { + if (c.m_compiled == l_false && c.m_num_propagations >= c.m_compilation_threshold) { c.m_compiled = l_undef; m_to_compile.push_back(&c); } @@ -1246,9 +1248,9 @@ namespace smt { literal_vector in; for (unsigned i = 0; i < num_args; ++i) { rational n = c.coeff(i); - lbool val = ctx.get_assignment(c.lit()); - if (val != l_undef && - ctx.get_assign_level(thl) == ctx.get_base_level()) { + literal lit = c.lit(i); + lbool val = ctx.get_assignment(lit); + if (val != l_undef && ctx.get_assign_level(lit) == ctx.get_base_level()) { if (val == l_true) { unsigned m = n.get_unsigned(); if (k < m) { @@ -1263,31 +1265,35 @@ namespace smt { n -= rational::one(); } } + + TRACE("pb", tout << in << " >= " << k << "\n";); + + + psort_expr ps(ctx, *this); + psort_nw sortnw(ps); + sortnw.m_stats.reset(); + if (ctx.get_assignment(thl) == l_true && ctx.get_assign_level(thl) == ctx.get_base_level()) { - psort_expr ps(ctx, *this); - psort_nw sortnw(ps); - sortnw.m_stats.reset(); - at_least_k = sortnw.ge(false, k, in.size(), in.c_ptr()); + at_least_k = sortnw.ge(false, k, in.size(), in.c_ptr()); + TRACE("pb", tout << ~thl << " " << at_least_k << "\n";); ctx.mk_clause(~thl, at_least_k, justify(~thl, at_least_k)); - m_stats.m_num_compiled_vars += sortnw.m_stats.m_num_compiled_vars; - m_stats.m_num_compiled_clauses += sortnw.m_stats.m_num_compiled_clauses; } else { - psort_expr ps(ctx, *this); - psort_nw sortnw(ps); - sortnw.m_stats.reset(); literal at_least_k = sortnw.ge(true, k, in.size(), in.c_ptr()); + TRACE("pb", tout << ~thl << " " << at_least_k << "\n";); ctx.mk_clause(~thl, at_least_k, justify(~thl, at_least_k)); ctx.mk_clause(~at_least_k, thl, justify(thl, ~at_least_k)); - m_stats.m_num_compiled_vars += sortnw.m_stats.m_num_compiled_vars; - m_stats.m_num_compiled_clauses += sortnw.m_stats.m_num_compiled_clauses; } - IF_VERBOSE(1, verbose_stream() - << "(smt.pb compile sorting network bound: " - << k << " literals: " << in.size() << ")\n";); + m_stats.m_num_compiled_vars += sortnw.m_stats.m_num_compiled_vars; + m_stats.m_num_compiled_clauses += sortnw.m_stats.m_num_compiled_clauses; + + IF_VERBOSE(2, verbose_stream() + << "(smt.pb compile sorting network bound: " + << k << " literals: " << in.size() + << " clauses: " << sortnw.m_stats.m_num_compiled_clauses + << " vars: " << sortnw.m_stats.m_num_compiled_vars << ")\n";); - TRACE("pb", tout << thl << "\n";); // auxiliary clauses get removed when popping scopes. // we have to recompile the circuit after back-tracking. c.m_compiled = l_false; @@ -1297,7 +1303,6 @@ namespace smt { void theory_pb::init_search_eh() { - m_to_compile.reset(); } void theory_pb::push_scope_eh() { @@ -1311,10 +1316,9 @@ namespace smt { unsigned sz = m_ineqs_lim[new_lim]; while (m_ineqs_trail.size() > sz) { bool_var v = m_ineqs_trail.back(); - ineq* c = 0; - VERIFY(m_ineqs.find(v, c)); + ineq* c = m_var_infos[v].m_ineq; clear_watch(*c); - m_ineqs.remove(v); + m_var_infos[v].m_ineq = 0; m_ineqs_trail.pop_back(); if (m_enable_simplex) { row_info r_info; @@ -1326,6 +1330,7 @@ namespace smt { m_ineq_rep.erase(r_info.m_rep); } } + m_to_compile.erase(c); dealloc(c); } m_ineqs_lim.resize(new_lim); @@ -1451,6 +1456,7 @@ namespace smt { if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); } + TRACE("pb", tout << lits << "\n";); ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); } @@ -1746,7 +1752,7 @@ namespace smt { // verbose_stream() << "(pb.conflict min size: " << l_size << ")\n"; // s_min_l_size = l_size; //} - //IF_VERBOSE(1, verbose_stream() << "(pb.conflict " << m_ineq_literals.size() << " " << m_lemma.size() << "\n";); + IF_VERBOSE(1, verbose_stream() << "(pb.conflict " << m_ineq_literals.size() << " " << m_lemma.size() << ")\n";); switch(is_true) { case l_true: UNREACHABLE(); @@ -1757,6 +1763,7 @@ namespace smt { for (unsigned i = 0; i < m_ineq_literals.size(); ++i) { m_ineq_literals[i].neg(); } + TRACE("pb", tout << m_ineq_literals << "\n";); ctx.mk_clause(m_ineq_literals.size(), m_ineq_literals.c_ptr(), justify(m_ineq_literals), CLS_AUX_LEMMA, 0); break; default: { @@ -1857,9 +1864,11 @@ namespace smt { } void theory_pb::validate_final_check() { - u_map::iterator itc = m_ineqs.begin(), endc = m_ineqs.end(); - for (; itc != endc; ++itc) { - validate_final_check(*itc->m_value); + for (unsigned i = 0; i < m_var_infos.size(); ++i) { + ineq* c = m_var_infos[i].m_ineq; + if (c) { + validate_final_check(*c); + } } } @@ -2067,29 +2076,37 @@ namespace smt { return p; } + void theory_pb::display_watch(std::ostream& out, bool_var v, bool sign) const { + watch_list const* w = m_var_infos[v].m_lit_watch[sign]; + if (!w) return; + watch_list 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 { - u_map*>::iterator it = m_lwatch.begin(), end = m_lwatch.end(); - for (; it != end; ++it) { - out << "watch: " << to_literal(it->m_key) << " |-> "; - watch_list const& wl = *it->m_value; + 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) { + watch_list const* w = m_var_infos[vi].m_var_watch; + if (!w) continue; + out << "watch (v): " << literal(vi) << " |-> "; + watch_list const& wl = *w; for (unsigned i = 0; i < wl.size(); ++i) { out << wl[i]->lit() << " "; } out << "\n"; } - it = m_vwatch.begin(), end = m_vwatch.end(); - for (; it != end; ++it) { - out << "watch (v): " << literal(it->m_key) << " |-> "; - watch_list const& wl = *it->m_value; - for (unsigned i = 0; i < wl.size(); ++i) { - out << wl[i]->lit() << " "; + for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { + ineq* c = m_var_infos[vi].m_ineq; + if (c) { + display(out, *c, true); } - out << "\n"; - } - u_map::iterator itc = m_ineqs.begin(), endc = m_ineqs.end(); - for (; itc != endc; ++itc) { - ineq& c = *itc->m_value; - display(out, c, true); } } diff --git a/src/smt/theory_pb.h b/src/smt/theory_pb.h index b9fddba38..ee68bd26e 100644 --- a/src/smt/theory_pb.h +++ b/src/smt/theory_pb.h @@ -193,10 +193,29 @@ namespace smt { typedef ptr_vector watch_list; typedef map arg_map; + struct var_info { + watch_list* m_lit_watch[2]; + watch_list* m_var_watch; + ineq* m_ineq; + + var_info(): m_var_watch(0), m_ineq(0) + { + m_lit_watch[0] = 0; + m_lit_watch[1] = 0; + } + + void reset() { + dealloc(m_lit_watch[0]); + dealloc(m_lit_watch[1]); + dealloc(m_var_watch); + dealloc(m_ineq); + } + }; + + theory_pb_params m_params; - u_map m_lwatch; // per literal. - u_map m_vwatch; // per variable. - u_map m_ineqs; // per inequality. + + svector m_var_infos; arg_map m_ineq_rep; // Simplex: representative inequality u_map m_ineq_row_info; // Simplex: row information per variable uint_set m_vars; // Simplex: 0-1 variables. @@ -221,6 +240,7 @@ namespace smt { literal compile_arg(expr* arg); void add_watch(ineq& c, unsigned index); void del_watch(watch_list& watch, unsigned index, ineq& c, unsigned ineq_index); + void init_watch(bool_var v); void init_watch_literal(ineq& c); void init_watch_var(ineq& c); void clear_watch(ineq& c); @@ -242,6 +262,7 @@ namespace smt { 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; virtual void display(std::ostream& out) const; + 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); @@ -316,6 +337,7 @@ namespace smt { virtual void collect_statistics(::statistics & st) const; virtual model_value_proc * mk_value(enode * n, model_generator & mg); virtual void init_model(model_generator & m); + virtual bool include_func_interp(func_decl* f) { return false; } static literal assert_ge(context& ctx, unsigned k, unsigned n, literal const* xs); }; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 89f08d3df..d5251c56b 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -196,6 +196,7 @@ theory_seq::theory_seq(ast_manager& m): theory(m.mk_family_id("seq")), m(m), m_rep(m, m_dm), + m_reset_cache(false), m_eq_id(0), m_find(*this), m_factory(0), @@ -242,8 +243,13 @@ void theory_seq::init(context* ctx) { } final_check_status theory_seq::final_check_eh() { + if (m_reset_cache) { + m_rep.reset_cache(); + m_reset_cache = false; + } m_new_propagation = false; TRACE("seq", display(tout << "level: " << get_context().get_scope_level() << "\n");); + TRACE("seq_verbose", get_context().display(tout);); if (simplify_and_solve_eqs()) { ++m_stats.m_solve_eqs; TRACE("seq", tout << ">>solve_eqs\n";); @@ -1004,6 +1010,17 @@ bool theory_seq::is_nth(expr* e) const { return is_skolem(m_nth, e); } +bool theory_seq::is_nth(expr* e, expr*& e1, expr*& e2) const { + if (is_nth(e)) { + e1 = to_app(e)->get_arg(0); + e2 = to_app(e)->get_arg(1); + return true; + } + else { + return false; + } +} + bool theory_seq::is_tail(expr* e, expr*& s, unsigned& idx) const { rational r; return @@ -1033,6 +1050,10 @@ expr_ref theory_seq::mk_nth(expr* s, expr* idx) { return mk_skolem(m_nth, s, idx, 0, char_sort); } +expr_ref theory_seq::mk_sk_ite(expr* c, expr* t, expr* e) { + return mk_skolem(symbol("seq.if"), c, t, e, 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) { @@ -1128,7 +1149,7 @@ bool theory_seq::check_extensionality() { continue; } TRACE("seq", tout << m_lhs << " = " << m_rhs << "\n";); - ctx.assume_eq(n1, n2); + ctx.assume_eq(n1, n2); return false; } } @@ -1366,7 +1387,7 @@ bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps) { bool theory_seq::occurs(expr* a, expr_ref_vector const& b) { for (unsigned i = 0; i < b.size(); ++i) { - if (a == b[i]) return true; + if (a == b[i] || m.is_ite(b[i])) return true; } return false; } @@ -1379,7 +1400,7 @@ bool theory_seq::occurs(expr* a, expr* b) { m_todo.push_back(b); while (!m_todo.empty()) { b = m_todo.back(); - if (a == b) { + if (a == b || m.is_ite(b)) { m_todo.reset(); return true; } @@ -1794,7 +1815,10 @@ bool theory_seq::solve_ne(unsigned idx) { for (unsigned i = 0; i < n.lits().size(); ++i) { switch (ctx.get_assignment(n.lits(i))) { case l_false: - TRACE("seq", display_disequation(tout << "has false literal\n", n);); + TRACE("seq", display_disequation(tout << "has false literal\n", n); + ctx.display_literal_verbose(tout, n.lits(i)); + tout << "\n" << n.lits(i) << " " << ctx.is_relevant(n.lits(i)) << "\n"; + ); return true; case l_true: break; @@ -1820,7 +1844,10 @@ bool theory_seq::solve_ne(unsigned idx) { change = canonize(n.rs(i), rs, deps) || change; if (!m_seq_rewrite.reduce_eq(ls, rs, lhs, rhs, change)) { - TRACE("seq", display_disequation(tout << "reduces to false: ", n);); + 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) { @@ -2197,42 +2224,130 @@ bool theory_seq::check_int_string() { if (m_util.str.is_itos(e) && add_itos_axiom(e)) { change = true; } - else if (m_util.str.is_stoi(e, n)) { - // not (yet) handled. - // we would check that in the current proto-model - // the string at 'n', when denoting integer would map to the - // proper integer. + else if (m_util.str.is_stoi(e, n) && add_stoi_axiom(e)) { + change = true; } } return change; } +bool theory_seq::add_stoi_axiom(expr* e) { + context& ctx = get_context(); + expr* n; + rational val; + TRACE("seq", tout << mk_pp(e, m) << "\n";); + VERIFY(m_util.str.is_stoi(e, n)); + if (get_num_value(e, val) && !m_stoi_axioms.contains(val)) { + m_stoi_axioms.insert(val); + if (!val.is_minus_one()) { + app_ref e1(m_util.str.mk_string(symbol(val.to_string().c_str())), m); + expr_ref n1(arith_util(m).mk_numeral(val, true), m); + literal eq1 = mk_eq(e, n1, false); + literal eq2 = mk_eq(n, e1, false); + add_axiom(~eq1, eq2); + add_axiom(~eq2, eq1); + ctx.force_phase(eq1); + ctx.force_phase(eq2); + m_trail_stack.push(insert_map(m_stoi_axioms, val)); + m_trail_stack.push(push_replay(alloc(replay_axiom, m, e))); + return true; + } + } + if (upper_bound(n, val) && get_length(n, val) && val.is_pos() && !m_stoi_axioms.contains(val)) { + zstring s; + SASSERT(val.is_unsigned()); + unsigned sz = val.get_unsigned(); + expr_ref len1(m), len2(m), ith_char(m), num(m), coeff(m); + expr_ref_vector nums(m); + len1 = m_util.str.mk_length(n); + len2 = m_autil.mk_int(sz); + literal lit = mk_eq(len1, len2, false); + literal_vector lits; + lits.push_back(~lit); + for (unsigned i = 0; i < sz; ++i) { + ith_char = mk_nth(n, m_autil.mk_int(i)); + lits.push_back(~is_digit(ith_char)); + nums.push_back(digit2int(ith_char)); + } + for (unsigned i = sz-1, c = 1; i > 0; c *= 10) { + --i; + coeff = m_autil.mk_int(c); + nums[i] = m_autil.mk_mul(coeff, nums[i].get()); + } + num = m_autil.mk_add(nums.size(), nums.c_ptr()); + lits.push_back(mk_eq(e, num, false)); + ++m_stats.m_add_axiom; + m_new_propagation = true; + for (unsigned i = 0; i < lits.size(); ++i) { + ctx.mark_as_relevant(lits[i]); + } + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + m_stoi_axioms.insert(val); + m_trail_stack.push(insert_map(m_stoi_axioms, val)); + m_trail_stack.push(push_replay(alloc(replay_axiom, m, e))); + return true; + } + + return false; +} + +literal theory_seq::is_digit(expr* ch) { + bv_util bv(m); + literal isd = mk_literal(mk_skolem(symbol("seq.is_digit"), ch, 0, 0, m.mk_bool_sort())); + expr_ref d2i = digit2int(ch); + expr_ref _lo(bv.mk_ule(bv.mk_numeral(rational('0'), bv.mk_sort(8)), ch), m); + expr_ref _hi(bv.mk_ule(ch, bv.mk_numeral(rational('9'), bv.mk_sort(8))), m); + literal lo = mk_literal(_lo); + literal hi = mk_literal(_hi); + add_axiom(~lo, ~hi, isd); + add_axiom(~isd, lo); + add_axiom(~isd, hi); + for (unsigned i = 0; i < 10; ++i) { + add_axiom(~mk_eq(ch, bv.mk_numeral(rational('0'+i), bv.mk_sort(8)), false), mk_eq(d2i, m_autil.mk_int(i), false)); + } + return isd; +} + +expr_ref theory_seq::digit2int(expr* ch) { + return expr_ref(mk_skolem(symbol("seq.digit2int"), ch, 0, 0, m_autil.mk_int()), m); +} + bool theory_seq::add_itos_axiom(expr* e) { + context& ctx = get_context(); rational val; expr* n; + TRACE("seq", tout << mk_pp(e, m) << "\n";); VERIFY(m_util.str.is_itos(e, n)); - if (get_value(n, val)) { + if (get_num_value(n, val)) { if (!m_itos_axioms.contains(val)) { m_itos_axioms.insert(val); app_ref e1(m_util.str.mk_string(symbol(val.to_string().c_str())), m); expr_ref n1(arith_util(m).mk_numeral(val, true), m); -#if 1 // itos(n) = "25" <=> n = 25 - add_axiom(~mk_eq(n1, n , false), mk_eq(e, e1, false)); - add_axiom(mk_eq(n1, n, false), ~mk_eq(e, e1, false)); -#else - // "25" = itos(25) - // stoi(itos(n)) = n - app_ref e2(m_util.str.mk_stoi(e), m); - add_axiom(mk_eq(e2, n, false)); - add_axiom(mk_eq(m_util.str.mk_itos(n1), e1, false)); -#endif + literal eq1 = mk_eq(n1, n , false); + literal eq2 = mk_eq(e, e1, false); + add_axiom(~eq1, eq2); + add_axiom(~eq2, eq1); + ctx.force_phase(eq1); + ctx.force_phase(eq2); + m_trail_stack.push(insert_map(m_itos_axioms, val)); m_trail_stack.push(push_replay(alloc(replay_axiom, m, e))); return true; - } + } } + else { + // stoi(itos(n)) = n + app_ref e2(m_util.str.mk_stoi(e), m); + if (ctx.e_internalized(e2) && ctx.get_enode(e2)->get_root() == ctx.get_enode(n)->get_root()) { + return false; + } + add_axiom(mk_eq(e2, n, false)); + m_trail_stack.push(push_replay(alloc(replay_axiom, m, e))); + return true; + } + return false; } @@ -2394,6 +2509,11 @@ void theory_seq::init_model(expr_ref_vector const& es) { void theory_seq::init_model(model_generator & mg) { m_factory = alloc(seq_factory, get_manager(), get_family_id(), mg.get_model()); mg.register_factory(m_factory); + for (unsigned j = 0; j < m_nqs.size(); ++j) { + ne const& n = m_nqs[j]; + m_factory->register_value(n.l()); + m_factory->register_value(n.r()); + } for (unsigned j = 0; j < m_nqs.size(); ++j) { ne const& n = m_nqs[j]; for (unsigned i = 0; i < n.ls().size(); ++i) { @@ -2405,26 +2525,38 @@ void theory_seq::init_model(model_generator & mg) { 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; + svector m_source; public: seq_value_proc(theory_seq& th, sort* s): th(th), m_sort(s) { } virtual ~seq_value_proc() {} - void add_dependency(enode* n) { + void add_unit(enode* n) { m_dependencies.push_back(model_value_dependency(n)); - m_source.push_back(true); + 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(false); + m_source.push_back(string_source); } virtual void get_dependencies(buffer & result) { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } + + void add_buffer(svector& sbuffer, zstring const& zs) { + for (unsigned l = 0; l < zs.length(); ++l) { + sbuffer.push_back(zs[l]); + } + } + virtual app * mk_value(model_generator & mg, ptr_vector & values) { SASSERT(values.size() == m_dependencies.size()); expr_ref_vector args(th.m); @@ -2438,28 +2570,51 @@ public: unsigned sz; for (unsigned i = 0; i < m_source.size(); ++i) { - if (m_source[i]) { + switch (m_source[i]) { + case unit_source: { VERIFY(bv.is_numeral(values[j++], val, sz)); sbuffer.push_back(val.get_unsigned()); + break; } - else { + case string_source: { + dependency* deps = 0; + expr_ref tmp = th.canonize(m_strings[k], deps); zstring zs; - if (th.m_util.str.is_string(m_strings[k++], zs)) { - for (unsigned l = 0; l < zs.length(); ++l) { - sbuffer.push_back(zs[l]); - } + 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 << "-"; + strm << abs(val); + zstring zs(strm.str().c_str()); + add_buffer(sbuffer, zs); + break; + } } } result = th.m_util.str.mk_string(zstring(sbuffer.size(), sbuffer.c_ptr())); } else { for (unsigned i = 0; i < m_source.size(); ++i) { - if (m_source[i]) { + switch (m_source[i]) { + case unit_source: args.push_back(th.m_util.str.mk_unit(values[j++])); - } - else { + break; + case string_source: args.push_back(m_strings[k++]); + break; + case int_source: + UNREACHABLE(); + break; } } result = th.mk_concat(args, m_sort); @@ -2472,18 +2627,37 @@ public: model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) { - if (m_util.is_seq(n->get_owner())) { + app* e = n->get_owner(); + context& ctx = get_context(); + expr* e1, *e2, *e3; + if (m.is_ite(e, e1, e2, e3) && ctx.e_internalized(e2) && ctx.e_internalized(e3) && + (ctx.get_enode(e2)->get_root() == n->get_root() || + ctx.get_enode(e3)->get_root() == n->get_root())) { + if (ctx.get_enode(e2)->get_root() == n->get_root()) { + return mk_value(ctx.get_enode(e2), mg); + } + else { + return mk_value(ctx.get_enode(e3), mg); + } + } + else if (m_util.is_seq(e)) { ptr_vector concats; - get_concat(n->get_owner(), concats); - context& ctx = get_context(); - sort* srt = m.get_sort(n->get_owner()); + get_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 (unsigned i = 0; i < concats.size(); ++i) { expr* c = concats[i], *c1; + TRACE("seq", tout << mk_pp(c, m) << "\n";); if (m_util.str.is_unit(c, c1)) { if (ctx.e_internalized(c1)) { - sv->add_dependency(ctx.get_enode(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)) { @@ -2496,7 +2670,7 @@ model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) { return sv; } else { - return alloc(expr_wrapper_proc, mk_value(n->get_owner())); + return alloc(expr_wrapper_proc, mk_value(e)); } } @@ -2619,7 +2793,23 @@ expr_ref theory_seq::expand(expr* e0, dependency*& eqs) { result = m_util.str.mk_index(expand(e1, deps), expand(e2, deps), e3); } else if (m.is_ite(e, e1, e2, e3)) { + if (ctx.e_internalized(e) && ctx.e_internalized(e2) && ctx.get_enode(e)->get_root() == ctx.get_enode(e2)->get_root()) { + result = expand(e2, deps); + add_dependency(deps, ctx.get_enode(e), ctx.get_enode(e2)); + } + else if (ctx.e_internalized(e) && ctx.e_internalized(e2) && ctx.get_enode(e)->get_root() == ctx.get_enode(e3)->get_root()) { + result = expand(e3, deps); + add_dependency(deps, ctx.get_enode(e), ctx.get_enode(e3)); + } + else { literal lit(mk_literal(e1)); +#if 0 + expr_ref sk_ite = mk_sk_ite(e1, e2, e3); + add_axiom(~lit, mk_eq(e2, sk_ite, false)); + add_axiom( lit, mk_eq(e3, sk_ite, false)); + result = sk_ite; + +#else switch (ctx.get_assignment(lit)) { case l_true: deps = m_dm.mk_join(deps, m_dm.mk_leaf(assumption(lit))); @@ -2630,13 +2820,19 @@ expr_ref theory_seq::expand(expr* e0, dependency*& eqs) { result = expand(e3, deps); break; case l_undef: - result = e; + result = e; + m_reset_cache = true; + TRACE("seq", tout << "undef: " << result << "\n"; + tout << lit << "@ level: " << ctx.get_scope_level() << "\n";); break; } +#endif + } } else if (m_util.str.is_itos(e, e1)) { rational val; - if (get_value(e1, val)) { + TRACE("seq", tout << mk_pp(e, m) << "\n";); + if (get_num_value(e1, val)) { TRACE("seq", tout << mk_pp(e, m) << " -> " << val << "\n";); expr_ref num(m), res(m); num = m_autil.mk_numeral(val, true); @@ -2744,8 +2940,8 @@ void theory_seq::deque_axiom(expr* n) { encode that s is not contained in of xs1 where s1 is all of s, except the last element. - lit or s = "" or s = s1*(unit c) - lit or s = "" or !contains(x*s1, s) + 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); @@ -2762,46 +2958,45 @@ void theory_seq::tightest_prefix(expr* s, expr* x) { let i = Index(t, s, offset): offset >= len(t) => i = -1 - - offset fixed to 0: - len(t) != 0 & !contains(t, s) => i = -1 - len(t) != 0 & contains(t, s) => t = xsy & i = len(x) + + + offset = 0 & len(t) != 0 & contains(t, s) => t = xsy & i = len(x) tightest_prefix(x, s) - offset not fixed: 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) - if offset < 0 - under specified + 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, *t, *offset = 0; + expr* s = 0, *t = 0, *offset = 0; 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); + add_axiom(cnt, i_eq_m1); + literal s_eq_empty = mk_eq_empty(s); + add_axiom(~s_eq_empty, mk_eq(i, zero, false)); + add_axiom(s_eq_empty, ~mk_eq_empty(t), 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(m_util.str.mk_length(x), m); - literal cnt = mk_literal(m_util.str.mk_contains(t, s)); - literal s_eq_empty = mk_eq_empty(s); - add_axiom(cnt, mk_eq(i, minus_one, false)); - add_axiom(~s_eq_empty, mk_eq(i, zero, false)); - add_axiom(s_eq_empty, ~mk_eq_empty(t), mk_eq(i, minus_one, false)); add_axiom(~cnt, s_eq_empty, mk_seq_eq(t, xsy)); add_axiom(~cnt, s_eq_empty, mk_eq(i, lenx, false)); tightest_prefix(s, x); @@ -2811,7 +3006,7 @@ void theory_seq::add_indexof_axiom(expr* i) { expr_ref len_t(m_util.str.mk_length(t), m); literal offset_ge_len = mk_literal(m_autil.mk_ge(mk_sub(offset, len_t), zero)); - add_axiom(offset_ge_len, mk_eq(i, minus_one, false)); + add_axiom(~offset_ge_len, mk_eq(i, minus_one, false)); expr_ref x = mk_skolem(m_indexof_left, t, s, offset); expr_ref y = mk_skolem(m_indexof_right, t, s, offset); @@ -2828,10 +3023,13 @@ void theory_seq::add_indexof_axiom(expr* i) { 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(m_util.str.mk_length(x), offset, false)); add_axiom(~offset_ge_0, offset_ge_len, - ~mk_eq(indexof0, minus_one, false), mk_eq(i, minus_one, false)); + ~mk_eq(indexof0, minus_one, false), i_eq_m1); add_axiom(~offset_ge_0, offset_ge_len, ~mk_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); } } @@ -2850,10 +3048,14 @@ void theory_seq::add_replace_axiom(expr* r) { 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 cnt = mk_literal(m_util.str.mk_contains(a ,s)); + literal cnt = mk_literal(m_util.str.mk_contains(a, s)); + literal a_emp = mk_eq_empty(a); + literal s_emp = mk_eq_empty(s); + add_axiom(~a_emp, s_emp, mk_seq_eq(r, a)); add_axiom(cnt, mk_seq_eq(r, a)); - add_axiom(~cnt, mk_seq_eq(a, xsy)); - add_axiom(~cnt, mk_seq_eq(r, xty)); + 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)); tightest_prefix(s, x); } @@ -2885,7 +3087,7 @@ void theory_seq::add_elim_string_axiom(expr* n) { */ void theory_seq::add_length_axiom(expr* n) { context& ctx = get_context(); - expr* x; + expr* x = 0; VERIFY(m_util.str.is_length(n, x)); if (m_util.str.is_concat(x) || m_util.str.is_unit(x) || @@ -2908,58 +3110,65 @@ void theory_seq::add_length_axiom(expr* n) { } void theory_seq::add_itos_length_axiom(expr* len) { - expr* x, *n; + expr* x = 0, *n = 0; VERIFY(m_util.str.is_length(len, x)); VERIFY(m_util.str.is_itos(x, n)); - add_axiom(mk_literal(m_autil.mk_ge(len, m_autil.mk_int(1)))); - rational val; - if (get_value(n, val)) { - bool neg = val.is_neg(); - rational ten(10); - if (neg) val.neg(); - unsigned num_char = neg?2:1; - // 0 < x < 10 - // 10 < x < 100 - // 100 < x < 1000 - rational hi(10); - while (val > hi) { - ++num_char; - hi *= ten; + unsigned num_char1 = 1, num_char2 = 1; + rational len1, len2; + rational ten(10); + if (get_num_value(n, len1)) { + bool neg = len1.is_neg(); + if (neg) len1.neg(); + num_char1 = neg?2:1; + // 0 <= x < 10 + // 10 <= x < 100 + // 100 <= x < 1000 + rational upper(10); + while (len1 > upper) { + ++num_char1; + upper *= ten; } - rational lo(div(hi - rational(1), ten)); - - literal len_le(mk_literal(m_autil.mk_ge(len, m_autil.mk_int(num_char)))); - literal len_ge(mk_literal(m_autil.mk_le(len, m_autil.mk_int(num_char)))); - literal n_le_mlo(mk_literal(m_autil.mk_le(n, m_autil.mk_numeral(-lo, true)))); - literal n_ge_lo(mk_literal(m_autil.mk_ge(n, m_autil.mk_numeral(lo, true)))); - - // len >= num_char => n <= -lo or n >= lo - // len <= num_char => -hi < n < hi - - add_axiom(~len_ge, n_le_mlo, n_ge_lo); - if (neg) { - // n <= -lo => len >= num_char - // -hi < n <= 0 => len <= num_char - // n <= -hi or ~(n <= 0) or len <= num_char - - add_axiom(~n_le_mlo, len_ge); - literal n_le_mhi(mk_literal(m_autil.mk_le(n, m_autil.mk_numeral(-hi, true)))); - literal n_le_0(mk_literal(m_autil.mk_le(n, m_autil.mk_int(0)))); - add_axiom(n_le_mhi, ~n_le_0, len_le); - add_axiom(~len_le, ~n_le_mhi); - } - else { - // n >= lo => len >= num_char - // 0 <= n < hi => len <= num_char - add_axiom(~n_ge_lo, len_ge); - literal n_ge_hi(mk_literal(m_autil.mk_ge(n, m_autil.mk_numeral(hi, true)))); - literal n_ge_0(mk_literal(m_autil.mk_ge(n, m_autil.mk_int(0)))); - add_axiom(n_ge_hi, ~n_ge_0, len_le); - add_axiom(~len_le, ~n_ge_hi); - } + 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)))); + + if (num_char == 1) { + add_axiom(len_ge); + literal n_ge_0(mk_literal(m_autil.mk_ge(n, m_autil.mk_int(0)))); + 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_0); + add_axiom(~len_le, ~n_ge_10); + return; + } + rational hi(1); + for (unsigned i = 2; i < num_char; ++i) { + hi *= ten; + } + // n <= -hi or n >= hi*10 <=> len >= num_chars + // -10*hi < n < 100*hi <=> len <= num_chars + literal n_le_hi = mk_literal(m_autil.mk_le(n, m_autil.mk_numeral(-hi, true))); + literal n_ge_10hi = mk_literal(m_autil.mk_ge(n, m_autil.mk_numeral(ten*hi, true))); + literal n_le_m10hi = mk_literal(m_autil.mk_le(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_le_hi, len_ge); + add_axiom(~n_ge_10hi, len_ge); + add_axiom(n_le_hi, n_ge_10hi, ~len_ge); + + add_axiom(n_le_m10hi, n_ge_100hi, len_le); + add_axiom(~n_le_m10hi, ~len_le); + add_axiom(~n_ge_100hi, ~len_le); + + add_axiom(mk_literal(m_autil.mk_ge(len, m_autil.mk_int(1)))); } @@ -3056,12 +3265,20 @@ static theory_mi_arith* get_th_arith(context& ctx, theory_id afid, expr* e) { } } -bool theory_seq::get_value(expr* e, rational& val) const { +bool theory_seq::get_num_value(expr* e, rational& val) const { context& ctx = get_context(); theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); expr_ref _val(m); - if (!tha || !tha->get_value(ctx.get_enode(e), _val)) return false; - return m_autil.is_numeral(_val, val) && val.is_int(); + if (!tha) return false; + enode* next = ctx.get_enode(e), *n = next; + do { + if (tha->get_value(next, _val) && m_autil.is_numeral(_val, val) && val.is_int()) { + return true; + } + next = next->get_next(); + } + while (next != n); + return false; } bool theory_seq::lower_bound(expr* _e, rational& lo) const { @@ -3134,15 +3351,18 @@ bool theory_seq::get_length(expr* e, rational& val) const { let e = extract(s, i, l) - 0 <= i <= len(s) -> prefix(xe,s) - 0 <= i <= len(s) -> len(x) = i - 0 <= i <= len(s) & 0 <= l <= len(s) - i -> len(e) = l - 0 <= i <= len(s) & len(s) < l + i -> len(e) = len(s) - i - 0 <= i <= len(s) & l < 0 -> len(e) = 0 - * i < 0 -> e = empty - * i > len(s) -> e = empty + 0 <= i <= |s| -> prefix(xe,s) + 0 <= i <= |s| -> len(x) = i + 0 <= i <= |s| & 0 <= l <= |s| - i -> |e| = l + 0 <= i <= |s| & |s| < l + i -> |e| = |s| - i + 0 <= i <= |s| & l < 0 -> |e| = 0 + i >= |s| => |e| = 0 + i < 0 => |e| = 0 + l <= 0 => |e| = 0 + + It follows that: + |e| = min(l, |s| - i) for 0 <= i < |s| and 0 < |l| - */ @@ -3176,16 +3396,20 @@ void theory_seq::add_extract_axiom(expr* e) { expr_ref zero(m_autil.mk_int(0), m); literal i_ge_0 = mk_literal(m_autil.mk_ge(i, zero)); - literal i_le_ls = mk_literal(m_autil.mk_le(mk_sub(i, ls), zero)); + literal ls_le_i = mk_literal(m_autil.mk_le(mk_sub(i, ls), zero)); literal li_ge_ls = mk_literal(m_autil.mk_ge(ls_minus_i_l, zero)); literal l_ge_zero = mk_literal(m_autil.mk_ge(l, zero)); + literal ls_le_0 = mk_literal(m_autil.mk_le(ls, zero)); -// add_axiom(~i_ge_0, ~i_le_ls, mk_literal(m_util.str.mk_prefix(xe, s))); - 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_zero, ~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_zero, mk_eq(le, zero, false)); +// add_axiom(~i_ge_0, ~ls_le_i, mk_literal(m_util.str.mk_prefix(xe, s))); + add_axiom(~i_ge_0, ~ls_le_i, mk_seq_eq(xey, s)); + add_axiom(~i_ge_0, ~ls_le_i, mk_eq(lx, i, false)); + add_axiom(~i_ge_0, ~ls_le_i, ~l_ge_zero, ~li_ge_ls, mk_eq(le, l, false)); + add_axiom(~i_ge_0, ~ls_le_i, li_ge_ls, mk_eq(le, mk_sub(ls, i), false)); + add_axiom(~i_ge_0, ~ls_le_i, l_ge_zero, mk_eq(le, zero, false)); + add_axiom(i_ge_0, mk_eq(le, zero, false)); + add_axiom(ls_le_i, mk_eq(le, zero, false)); + add_axiom(~ls_le_0, mk_eq(le, zero, false)); } void theory_seq::add_tail_axiom(expr* e, expr* s) { @@ -3273,7 +3497,7 @@ void theory_seq::add_extract_suffix_axiom(expr* e, expr* s, expr* i) { */ void theory_seq::add_at_axiom(expr* e) { - expr* s, *i; + expr* s = 0, *i = 0; VERIFY(m_util.str.is_at(e, s, i)); expr_ref len_e(m_util.str.mk_length(e), m); expr_ref len_s(m_util.str.mk_length(s), m); @@ -3560,7 +3784,10 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { else if (is_skolem(symbol("seq.split"), e)) { // propagate equalities } + else if (is_skolem(symbol("seq.is_digit"), e)) { + } else { + TRACE("seq", tout << mk_pp(e, m) << "\n";); UNREACHABLE(); } } @@ -3592,6 +3819,15 @@ void theory_seq::new_eq_eh(dependency* deps, enode* n1, enode* n2) { solve_eqs(m_eqs.size()-1); enforce_length_coherence(n1, n2); } + else if (n1 != n2 && m_util.is_re(n1->get_owner())) { + warning_msg("equality between regular expressions is not yet supported"); + eautomaton* a1 = get_automaton(n1->get_owner()); + eautomaton* a2 = get_automaton(n2->get_owner()); + // eautomaton* b1 = mk_difference(*a1, *a2); + // eautomaton* b2 = mk_difference(*a2, *a1); + // eautomaton* c = mk_union(*b1, *b2); + // then some emptiness check. + } } void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { @@ -3601,16 +3837,17 @@ void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { expr_ref e2(n2->get_owner(), m); m_exclude.update(e1, e2); expr_ref eq(m.mk_eq(e1, e2), m); + TRACE("seq", tout << "new disequality " << get_context().get_scope_level() << ": " << eq << "\n";); m_rewrite(eq); if (!m.is_false(eq)) { - TRACE("seq", tout << "new disequality " << get_context().get_scope_level() << ": " << eq << "\n";); literal lit = mk_eq(e1, e2, false); - // propagate x != "" into x = (++ (unit (nth x 0) (tail x 0))) + if (m_util.str.is_empty(e2)) { std::swap(e1, e2); } + if (false && m_util.str.is_empty(e1)) { expr_ref head(m), tail(m), conc(m); mk_decompose(e2, head, tail); @@ -3618,13 +3855,13 @@ void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { propagate_eq(~lit, e2, conc, true); } #if 0 + // (e1 = "" & e2 = xdz) or (e2 = "" & e1 = xcy) or (e1 = xcy & e2 = xdz & c != d) or (e1 = x & e2 = xdz) or (e2 = x & e1 = xcy) // e1 = "" or e1 = xcy or e1 = x // e2 = "" or e2 = xdz or e2 = x // e1 = xcy or e2 = xdz // c != d - literal lit = mk_seq_eq(e1, e2); sort* char_sort = 0; expr_ref emp(m); VERIFY(m_util.is_seq(m.get_sort(e1), char_sort)); @@ -4068,7 +4305,7 @@ void theory_seq::propagate_not_prefix2(expr* e) { void theory_seq::propagate_not_suffix(expr* e) { context& ctx = get_context(); - expr* e1, *e2; + expr* e1 = 0, *e2 = 0; VERIFY(m_util.str.is_suffix(e, e1, e2)); literal lit = ctx.get_literal(e); SASSERT(ctx.get_assignment(lit) == l_false); @@ -4097,7 +4334,7 @@ void theory_seq::propagate_not_suffix(expr* e) { */ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { context& ctx = get_context(); - expr* e1, *e2; + expr* e1 = 0, *e2 = 0; VERIFY(m_util.str.is_prefix(e, e1, e2)); SASSERT(ctx.get_assignment(e) == l_false); if (canonizes(false, e)) { @@ -4169,7 +4406,7 @@ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { */ bool theory_seq::add_suffix2suffix(expr* e, bool& change) { context& ctx = get_context(); - expr* e1, *e2; + expr* e1 = 0, *e2 = 0; VERIFY(m_util.str.is_suffix(e, e1, e2)); SASSERT(ctx.get_assignment(e) == l_false); if (canonizes(false, e)) { @@ -4254,7 +4491,7 @@ bool theory_seq::canonizes(bool sign, expr* e) { bool theory_seq::add_contains2contains(expr* e, bool& change) { context& ctx = get_context(); - expr* e1, *e2; + expr* e1 = 0, *e2 = 0; VERIFY(m_util.str.is_contains(e, e1, e2)); SASSERT(ctx.get_assignment(e) == l_false); if (canonizes(false, e)) { @@ -4324,7 +4561,7 @@ bool theory_seq::propagate_automata() { } void theory_seq::get_concat(expr* e, ptr_vector& concats) { - expr* e1, *e2; + expr* e1 = 0, *e2 = 0; while (true) { e = m_rep.find(e); if (m_util.str.is_concat(e, e1, e2)) { diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 136a84520..aa7ddec1b 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -294,6 +294,7 @@ namespace smt { ast_manager& m; dependency_manager m_dm; solution_map m_rep; // unification representative. + bool m_reset_cache; // invalidate cache. 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. @@ -308,6 +309,7 @@ namespace smt { bool m_incomplete; // is the solver (clearly) incomplete for the fragment. expr_ref_vector m_int_string; rational_set m_itos_axioms; + rational_set m_stoi_axioms; obj_hashtable m_length; // is length applied scoped_ptr_vector m_replay; // set of actions to replay model_generator* m_mg; @@ -447,10 +449,12 @@ namespace smt { bool is_var(expr* b); bool add_solution(expr* l, expr* r, dependency* dep); bool is_nth(expr* a) const; + bool is_nth(expr* a, expr*& e1, expr*& e2) 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); @@ -493,7 +497,10 @@ namespace smt { void add_elim_string_axiom(expr* n); void add_at_axiom(expr* n); void add_in_re_axiom(expr* n); + bool add_stoi_axiom(expr* n); bool add_itos_axiom(expr* n); + literal is_digit(expr* ch); + expr_ref digit2int(expr* ch); void add_itos_length_axiom(expr* n); literal mk_literal(expr* n); literal mk_eq_empty(expr* n, bool phase = true); @@ -507,7 +514,7 @@ namespace smt { // arithmetic integration - bool get_value(expr* s, rational& val) const; + bool get_num_value(expr* s, rational& val) const; bool lower_bound(expr* s, rational& lo) const; bool upper_bound(expr* s, rational& hi) const; bool get_length(expr* s, rational& val) const; diff --git a/src/smt/theory_seq_empty.h b/src/smt/theory_seq_empty.h index 27c72cc97..3f6c7f3e2 100644 --- a/src/smt/theory_seq_empty.h +++ b/src/smt/theory_seq_empty.h @@ -126,6 +126,22 @@ namespace smt { 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; + } } } }; diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index d5ed4e825..5f370c29c 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -250,7 +250,7 @@ namespace smt { std::stringstream msg; msg << "found non utvpi logic expression:\n" << mk_pp(n, get_manager()) << "\n"; TRACE("utvpi", tout << msg.str();); - warning_msg(msg.str().c_str()); + warning_msg("%s", msg.str().c_str()); get_context().push_trail(value_trail(m_non_utvpi_exprs)); m_non_utvpi_exprs = true; } @@ -901,7 +901,7 @@ namespace smt { 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_value(num, is_int)); + return alloc(expr_wrapper_proc, m_factory->mk_num_value(num, is_int)); } /** diff --git a/src/smt/theory_wmaxsat.cpp b/src/smt/theory_wmaxsat.cpp index e52475d06..3f153f500 100644 --- a/src/smt/theory_wmaxsat.cpp +++ b/src/smt/theory_wmaxsat.cpp @@ -21,291 +21,352 @@ Notes: #include "smt_context.h" #include "ast_pp.h" #include "theory_wmaxsat.h" +#include "smt_justification.h" namespace smt { -theory_wmaxsat::theory_wmaxsat(ast_manager& m, filter_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) -{} - -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(); + theory_wmaxsat::theory_wmaxsat(ast_manager& m, filter_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) + {} - if (!m_found_optimal) { - for (unsigned i = 0; i < m_vars.size(); ++i) { - result.push_back(false); - } + theory_wmaxsat::~theory_wmaxsat() { + m_old_values.reset(); } - 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)) { + + /** + \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); - ++j; - } - else { - result.push_back(true); } } + 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";); } - 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; -} - -bool_var 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.insert(var->get_decl()); - 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_rmin_cost += w; - m_normalize = true; - return register_var(var, true); -} - -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); + void theory_wmaxsat::init_search_eh() { + m_propagate = true; } - else { - bv = ctx.mk_bool_var(var); + + 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.insert(var->get_decl()); + 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 << "enable: v" << m_bool2var[bv] << " b" << bv << " " << mk_pp(var, get_manager()) << "\n"; + tout << wfml << "\n";); + return 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); - SASSERT(v == static_cast(m_var2bool.size())); - m_var2bool.push_back(bv); - SASSERT(ctx.bool_var2enode(bv)); - } - return bv; -} -rational const& theory_wmaxsat::get_min_cost() { - unsynch_mpq_manager mgr; - scoped_mpq q(mgr); - mgr.set(q, m_zmin_cost, m_den.to_mpq().numerator()); - m_rmin_cost = rational(q); - return m_rmin_cost; -} - -void theory_wmaxsat::assign_eh(bool_var v, bool is_true) { - TRACE("opt", tout << "Assign " << mk_pp(m_vars[m_bool2var[v]].get(), get_manager()) << " " << is_true << "\n";); - if (is_true) { - if (m_normalize) normalize(); + void theory_wmaxsat::disable_var(expr* var) { context& ctx = get_context(); - theory_var tv = m_bool2var[v]; - if (m_assigned[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; - m_costs.push_back(tv); - m_assigned[tv] = true; - if (m_zcost > m_zmin_cost) { - block(); + 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); } - } -} - -final_check_status theory_wmaxsat::final_check_eh() { - if (m_normalize) normalize(); - 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_rcost.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_found_optimal = false; - m_assigned.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); + 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); + SASSERT(v == static_cast(m_var2bool.size())); + m_var2bool.push_back(bv); + SASSERT(ctx.bool_var2enode(bv)); + } + return bv; } - m_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) { - weight += m_zweights[costs[i]]; - disj.push_back(m.mk_not(m_vars[costs[i]].get())); - } - if (is_optimal()) { + + rational theory_wmaxsat::get_cost() { unsynch_mpq_manager mgr; scoped_mpq q(mgr); - mgr.set(q, m_zmin_cost, m_den.to_mpq().numerator()); - rational rw = rational(q); - m_zmin_cost = weight; - m_found_optimal = true; + 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_cost_save.append(m_costs); + 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 << "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); - ); + tout << result << " weight: " << weight << "\n"; + tout << "cost: " << m_zcost << " min-cost: " << m_zmin_cost << "\n";); + return result; } - 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; -} -expr_ref theory_wmaxsat::mk_optimal_block(svector const& ws, rational const& weight) { - ast_manager& m = get_manager(); - expr_ref_vector disj(m); - rational new_w = weight*m_den; - m_zmin_cost = new_w.to_mpq().numerator(); - m_cost_save.reset(); - for (unsigned i = 0; i < ws.size(); ++i) { - bool_var bv = ws[i]; - theory_var v = m_bool2var[bv]; - m_cost_save.push_back(v); - disj.push_back(m.mk_not(m_vars[v].get())); - } - expr_ref result(m.mk_or(disj.size(), disj.c_ptr()), m); - return result; -} + void theory_wmaxsat::restart_eh() {} -void theory_wmaxsat::block() { - if (m_vars.empty()) { - return; + 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, 0, 0, 0))); + } + + 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; } - ++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", - ast_manager& m = get_manager(); - tout << "block: "; - for (unsigned i = 0; i < lits.size(); ++i) { - expr_ref tmp(m); - ctx.literal2expr(lits[i], tmp); - tout << tmp << " "; - } - tout << "\n"; - ); - - ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); -} + 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, 0, lit, 0, 0))); + } -void theory_wmaxsat::normalize() { - m_den = rational::one(); - for (unsigned i = 0; i < m_rweights.size(); ++i) { - m_den = lcm(m_den, denominator(m_rweights[i])); + 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; } - m_den = lcm(m_den, denominator(m_rmin_cost)); - SASSERT(!m_den.is_zero()); - m_zweights.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()); - } - rational r = m_rcost* m_den; - m_zcost = r.to_mpq().numerator(); - r = m_rmin_cost * m_den; - m_zmin_cost = r.to_mpq().numerator(); - m_normalize = false; -} }; diff --git a/src/smt/theory_wmaxsat.h b/src/smt/theory_wmaxsat.h index b0c556c0e..0f711b9f8 100644 --- a/src/smt/theory_wmaxsat.h +++ b/src/smt/theory_wmaxsat.h @@ -28,6 +28,7 @@ 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(); } }; @@ -39,27 +40,31 @@ namespace smt { 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_rcost; // current sum of asserted costs 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_propagate; + bool m_can_propagate; bool m_normalize; rational m_den; // lcm of denominators for rational weights. - svector m_assigned; + svector m_assigned, m_enabled; stats m_stats; public: theory_wmaxsat(ast_manager& m, filter_model_converter& mc); virtual ~theory_wmaxsat(); void get_assignment(svector& result); - virtual void init_search_eh(); - bool_var assert_weighted(expr* fml, rational const& w); + expr* assert_weighted(expr* fml, rational const& w); + void disable_var(expr* var); bool_var register_var(app* var, bool attach); - rational const& get_min_cost(); + rational get_cost(); + void init_min_cost(rational const& r); + class numeral_trail : public trail { typedef scoped_mpz T; T & m_value; @@ -79,6 +84,8 @@ namespace smt { m_old_values.shrink(m_old_values.size() - 1); } }; + + virtual void init_search_eh(); virtual void assign_eh(bool_var v, bool is_true); virtual final_check_status final_check_eh(); virtual bool use_diseqs() const { @@ -95,23 +102,30 @@ namespace smt { virtual void new_eq_eh(theory_var v1, theory_var v2) { } virtual void new_diseq_eh(theory_var v1, theory_var v2) { } virtual void display(std::ostream& out) const {} + virtual void restart_eh(); virtual void collect_statistics(::statistics & st) const { st.update("wmaxsat num blocks", m_stats.m_num_blocks); + st.update("wmaxsat num props", m_stats.m_num_propagations); } virtual bool can_propagate() { - return m_propagate; + return m_propagate || m_can_propagate; } virtual void propagate(); + bool is_optimal() const; expr_ref mk_block(); - expr_ref mk_optimal_block(svector const& ws, rational const& weight); + + private: void block(); + void propagate(bool_var v); void normalize(); + + bool max_unassigned_is_blocked(); class compare_cost { theory_wmaxsat& m_th; diff --git a/src/solver/check_sat_result.h b/src/solver/check_sat_result.h index e6586d492..36eadd97d 100644 --- a/src/solver/check_sat_result.h +++ b/src/solver/check_sat_result.h @@ -48,12 +48,18 @@ public: lbool status() const { return m_status; } virtual void collect_statistics(statistics & st) const = 0; virtual void get_unsat_core(ptr_vector & r) = 0; + virtual void get_unsat_core(expr_ref_vector & r) { + ptr_vector core; + get_unsat_core(core); + r.append(core.size(), core.c_ptr()); + } virtual void get_model(model_ref & m) = 0; virtual proof * get_proof() = 0; virtual std::string reason_unknown() const = 0; virtual void set_reason_unknown(char const* msg) = 0; virtual void get_labels(svector & r) = 0; - virtual ast_manager& get_manager() = 0; + virtual ast_manager& get_manager() const = 0; + }; /** @@ -69,7 +75,7 @@ struct simple_check_sat_result : public check_sat_result { simple_check_sat_result(ast_manager & m); virtual ~simple_check_sat_result(); - virtual ast_manager& get_manager() { return m_proof.get_manager(); } + virtual ast_manager& get_manager() const { return m_proof.get_manager(); } virtual void collect_statistics(statistics & st) const; virtual void get_unsat_core(ptr_vector & r); virtual void get_model(model_ref & m); diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index 4e645116c..1eb6be08e 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -21,6 +21,7 @@ Notes: #include"solver.h" #include"scoped_timer.h" #include"combined_solver_params.hpp" +#include"common_msgs.h" #define PS_VB_LVL 15 /** @@ -101,7 +102,7 @@ private: m_inc_unknown_behavior = static_cast(p.solver2_unknown()); } - virtual ast_manager& get_manager() { return m_solver1->get_manager(); } + virtual ast_manager& get_manager() const { return m_solver1->get_manager(); } bool has_quantifiers() const { unsigned sz = get_num_assertions(); @@ -194,12 +195,29 @@ public: return m_solver1->get_scope_level(); } + virtual lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { + 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()) { + set_reason_unknown(Z3_CANCELED_MSG); + } + else { + set_reason_unknown(ex.msg()); + } + } + return l_undef; + } + virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions) { m_check_sat_executed = true; m_use_solver1_results = false; if (get_num_assumptions() != 0 || - num_assumptions > 0 || // assumptions were provided + num_assumptions > 0 || // assumptions were provided m_ignore_solver1) { // must use incremental solver switch_inc_mode(); @@ -209,7 +227,7 @@ public: 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(0, 0); + lbool r = m_solver2->check_sat(num_assumptions, assumptions); if (r != l_undef || !use_solver1_when_undef()) { return r; } @@ -220,7 +238,7 @@ public: lbool r = l_undef; try { scoped_timer timer(m_inc_timeout, &eh); - r = m_solver2->check_sat(0, 0); + r = m_solver2->check_sat(num_assumptions, assumptions); } catch (z3_exception&) { if (!eh.m_canceled) { @@ -236,7 +254,7 @@ public: IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"using solver 1\")\n";); m_use_solver1_results = true; - return m_solver1->check_sat(0, 0); + return m_solver1->check_sat(num_assumptions, assumptions); } virtual void set_progress_callback(progress_callback * callback) { @@ -262,8 +280,8 @@ public: return m_solver2->get_assumption(idx - c1); } - virtual void display(std::ostream & out) const { - m_solver1->display(out); + virtual std::ostream& display(std::ostream & out) const { + return m_solver1->display(out); } virtual void collect_statistics(statistics & st) const { diff --git a/src/solver/mus.cpp b/src/solver/mus.cpp new file mode 100644 index 000000000..91d47386e --- /dev/null +++ b/src/solver/mus.cpp @@ -0,0 +1,395 @@ +/*++ +Copyright (c) 2014 Microsoft Corporation + +Module Name: + + mus.cpp + +Abstract: + + MUS extraction. + +Author: + + Nikolaj Bjorner (nbjorner) 2014-20-7 + +Notes: + + +--*/ + +#include "solver.h" +#include "mus.h" +#include "ast_pp.h" +#include "ast_util.h" +#include "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) { + SASSERT(is_literal(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_mus(ptr_vector& mus) { + mus.reset(); + expr_ref_vector result(m); + lbool r = get_mus(result); + mus.append(result.size(), result.c_ptr()); + return r; + } + + lbool get_mus1(expr_ref_vector& mus) { + ptr_vector unknown(m_lit2expr.size(), m_lit2expr.c_ptr()); + ptr_vector core_exprs; + 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 (unsigned i = 0; i < core_exprs.size(); ++i) { + if (!mus.contains(core_exprs[i])) { + unknown.push_back(core_exprs[i]); + } + } + TRACE("mus", display_vec(tout << "core exprs:", core_exprs); + 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 = 0; + 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 = 0; + 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 (unsigned i = 0; i < mss.size(); ++i) { + mss_set.insert(mss[i]); + } + expr_set::iterator it = min_core.begin(), end = min_core.end(); + for (; it != end; ++it) { + if (mss_set.contains(*it) && min_lit != *it) { + unknown.push_back(*it); + } + } + core_literal = min_lit; + + return l_false; + } + + void get_core(expr_set& core) { + core.reset(); + ptr_vector core_exprs; + m_solver.get_unsat_core(core_exprs); + for (unsigned i = 0; i < core_exprs.size(); ++i) { + if (m_expr2lit.contains(core_exprs[i])) { + core.insert(core_exprs[i]); + } + } + } + + 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) { + mdl->eval(m_soft[i].get(), tmp); + if (!m.is_true(tmp)) { + 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(ptr_vector& mus) { + return m_imp->get_mus(mus); +} + +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); +} diff --git a/src/solver/mus.h b/src/solver/mus.h new file mode 100644 index 000000000..f2e543f04 --- /dev/null +++ b/src/solver/mus.h @@ -0,0 +1,69 @@ +/*++ +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(ptr_vector& mus); + + 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 diff --git a/src/solver/smt_logics.cpp b/src/solver/smt_logics.cpp new file mode 100644 index 000000000..c4ead74df --- /dev/null +++ b/src/solver/smt_logics.cpp @@ -0,0 +1,157 @@ +/*++ +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 "symbol.h" +#include "smt_logics.h" + + + +bool smt_logics::supported_logic(symbol const & s) { + return logic_has_uf(s) || logic_is_all(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 == "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 == "QF_FPBV" || + s == "QF_BVFP" || + s == "QF_S" || + s == "ALL" || + s == "QF_FD" || + s == "HORN"; +} + +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 == "QF_BVFP" || + s == "ALL" || + 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" || + s == "ALL" || + s == "QF_ABV" || + s == "QF_AUFBV" || + s == "HORN"; +} + +bool smt_logics::logic_has_seq(symbol const & s) { + return s == "QF_BVRE" || s == "QF_S" || s == "ALL"; +} + +bool smt_logics::logic_has_str(symbol const & s) { + return s == "QF_S" || s == "ALL"; +} + +bool smt_logics::logic_has_fpa(symbol const & s) { + return s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "ALL"; +} + +bool smt_logics::logic_has_uf(symbol const & s) { + return s == "QF_UF" || s == "UF"; +} + +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" || s == "ALL"; +} + +bool smt_logics::logic_has_datatype(symbol const& s) { + return s == "QF_FD"; +} diff --git a/src/solver/smt_logics.h b/src/solver/smt_logics.h new file mode 100644 index 000000000..702431cdd --- /dev/null +++ b/src/solver/smt_logics.h @@ -0,0 +1,42 @@ +/*++ +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_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_ */ + diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 59118c5ca..9163cfeda 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -17,6 +17,12 @@ Notes: --*/ #include"solver.h" +#include"model_evaluator.h" +#include"ast_util.h" +#include"ast_pp.h" +#include"ast_pp_util.h" +#include "common_msgs.h" + unsigned solver::get_num_assertions() const { NOT_IMPLEMENTED_YET(); @@ -28,7 +34,131 @@ expr * solver::get_assertion(unsigned idx) const { return 0; } -void solver::display(std::ostream & out) const { - out << "(solver)"; +std::ostream& solver::display(std::ostream & out) const { + expr_ref_vector fmls(get_manager()); + get_assertions(fmls); + ast_pp_util visitor(get_manager()); + visitor.collect(fmls); + visitor.display_decls(out); + visitor.display_asserts(out, fmls, true); + return out; +} + +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)); + } +} + +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, 0); +} + +bool solver::is_literal(ast_manager& m, expr* e) { + return is_uninterp_const(e) || (m.is_not(e, e) && is_uninterp_const(e)); } diff --git a/src/solver/solver.h b/src/solver/solver.h index 5a1514795..6b9d38f29 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -54,7 +54,7 @@ public: /** \brief Update the solver internal settings. */ - virtual void updt_params(params_ref const & p) {} + virtual void updt_params(params_ref const & p) { } /** \brief Store in \c r a description of the configuration @@ -79,6 +79,10 @@ public: for (unsigned i = 0; i < ts.size(); ++i) assert_expr(ts[i]); } + void assert_expr(ptr_vector const& ts) { + for (unsigned i = 0; i < ts.size(); ++i) assert_expr(ts[i]); + } + /** \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 @@ -108,6 +112,10 @@ public: */ virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions) = 0; + 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()); } + /** \brief Set a progress callback procedure that is invoked by this solver during check_sat. @@ -126,6 +134,11 @@ public: */ virtual expr * get_assertion(unsigned idx) const; + /** + \brief Retrieves assertions as a vector. + */ + void get_assertions(expr_ref_vector& fmls) const; + /** \brief The number of tracked assumptions (see assert_expr(t, a)). */ @@ -136,12 +149,33 @@ public: */ 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 Display the content of this solver. */ - virtual void display(std::ostream & out) const; + virtual std::ostream& display(std::ostream & out) const; class scoped_push { solver& s; @@ -151,6 +185,13 @@ public: ~scoped_push() { if (!m_nopop) s.pop(1); } void disable_pop() { m_nopop = true; } }; + +protected: + + virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences); + + bool is_literal(ast_manager& m, expr* e); + }; #endif diff --git a/src/solver/solver2tactic.cpp b/src/solver/solver2tactic.cpp new file mode 100644 index 000000000..1a02c97e6 --- /dev/null +++ b/src/solver/solver2tactic.cpp @@ -0,0 +1,177 @@ +/*++ +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.h" +#include "tactic.h" +#include"filter_model_converter.h" +#include "solver2tactic.h" +#include "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 == 0 || !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 = 0; + if (!dep2bool.find(d, b)) { + b = m.mk_fresh_const(0, m.mk_bool_sort()); + dep2bool.insert(d, b); + bool2dep.insert(b, d); + assumptions.push_back(b); + if (!fmc) { + fmc = alloc(filter_model_converter, m); + } + fmc->insert(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) + {} + + virtual void updt_params(params_ref const & p) { + m_params.append(p); + m_solver->updt_params(p); + } + + virtual void collect_param_descrs(param_descrs & r) { + m_solver->collect_param_descrs(r); + } + + virtual void operator()(/* in */ goal_ref const & in, + /* out */ goal_ref_buffer & result, + /* out */ model_converter_ref & mc, + /* out */ proof_converter_ref & pc, + /* out */ expr_dependency_ref & core) { + pc = 0; mc = 0; core = 0; + 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); + lbool r = local_solver->check_sat(assumptions.size(), assumptions.c_ptr()); + switch (r) { + case l_true: + if (in->models_enabled()) { + model_ref mdl; + local_solver->get_model(mdl); + mc = model2model_converter(mdl.get()); + mc = concat(fmc.get(), mc.get()); + } + in->reset(); + result.push_back(in.get()); + break; + case l_false: { + in->reset(); + proof* pr = 0; + expr_dependency* lcore = 0; + if (in->proofs_enabled()) { + pr = local_solver->get_proof(); + pc = proof2proof_converter(m, pr); + } + if (in->unsat_core_enabled()) { + ptr_vector core; + local_solver->get_unsat_core(core); + for (unsigned i = 0; i < core.size(); ++i) { + lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(core[i]))); + } + } + in->assert_expr(m.mk_false(), pr, lcore); + result.push_back(in.get()); + core = lcore; + break; + } + case l_undef: + if (m.canceled()) { + throw tactic_exception(Z3_CANCELED_MSG); + } + throw tactic_exception(local_solver->reason_unknown().c_str()); + } + local_solver->collect_statistics(m_st); + } + + virtual void collect_statistics(statistics & st) const { + st.copy(m_st); + } + virtual void reset_statistics() { m_st.reset(); } + + virtual void cleanup() { } + virtual void reset() { cleanup(); } + + virtual void set_logic(symbol const & l) {} + + virtual void set_progress_callback(progress_callback * callback) { + m_solver->set_progress_callback(callback); + } + + virtual tactic * translate(ast_manager & m) { + return alloc(solver2tactic, m_solver->translate(m, m_params)); + } +}; + +tactic* mk_solver2tactic(solver* s) { return alloc(solver2tactic, s); } diff --git a/src/solver/solver2tactic.h b/src/solver/solver2tactic.h new file mode 100644 index 000000000..65fbd37b1 --- /dev/null +++ b/src/solver/solver2tactic.h @@ -0,0 +1,30 @@ +/*++ +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.h" +#include "filter_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 diff --git a/src/solver/solver_na2as.cpp b/src/solver/solver_na2as.cpp index 2ca6e187c..31895b8ef 100644 --- a/src/solver/solver_na2as.cpp +++ b/src/solver/solver_na2as.cpp @@ -22,6 +22,7 @@ Notes: #include"solver_na2as.h" #include"ast_smt2_pp.h" + solver_na2as::solver_na2as(ast_manager & m): m(m), m_assumptions(m) { @@ -62,9 +63,20 @@ struct append_assumptions { lbool solver_na2as::check_sat(unsigned num_assumptions, expr * const * assumptions) { append_assumptions app(m_assumptions, num_assumptions, assumptions); + TRACE("solver_na2as", display(tout);); return check_sat_core(m_assumptions.size(), m_assumptions.c_ptr()); } +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(); diff --git a/src/solver/solver_na2as.h b/src/solver/solver_na2as.h index 019468fd7..aaa48efe7 100644 --- a/src/solver/solver_na2as.h +++ b/src/solver/solver_na2as.h @@ -25,6 +25,7 @@ Notes: #include"solver.h" class solver_na2as : public solver { + protected: ast_manager & m; expr_ref_vector m_assumptions; unsigned_vector m_scopes; @@ -44,6 +45,8 @@ public: virtual unsigned get_num_assumptions() const { return m_assumptions.size(); } virtual expr * get_assumption(unsigned idx) const { return m_assumptions[idx]; } + virtual lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences); + virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes); protected: virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) = 0; virtual void push_core() = 0; diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index 5235be230..b0a4c0e4f 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -21,8 +21,8 @@ Notes: --*/ #include"solver_na2as.h" #include"tactic.h" -#include"ast_pp_util.h" #include"ast_translation.h" +#include"mus.h" /** \brief Simulates the incremental solver interface using a tactic. @@ -42,6 +42,7 @@ class tactic2solver : public solver_na2as { 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); virtual ~tactic2solver(); @@ -73,11 +74,10 @@ public: virtual unsigned get_num_assertions() const; virtual expr * get_assertion(unsigned idx) const; - virtual void display(std::ostream & out) const; - virtual ast_manager& get_manager(); + virtual ast_manager& get_manager() const; }; -ast_manager& tactic2solver::get_manager() { return m_assertions.get_manager(); } +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), @@ -96,7 +96,7 @@ tactic2solver::~tactic2solver() { } void tactic2solver::updt_params(params_ref const & p) { - m_params = p; + m_params.append(p); } void tactic2solver::collect_param_descrs(param_descrs & r) { @@ -137,15 +137,18 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass g->assert_expr(m_assertions.get(i)); } for (unsigned i = 0; i < num_assumptions; i++) { - g->assert_expr(assumptions[i], m.mk_asserted(assumptions[i]), m.mk_leaf(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, pr, core, reason_unknown)) { + switch (::check_sat(*m_tactic, g, md, labels, pr, core, reason_unknown)) { case l_true: m_result->set_status(l_true); break; @@ -160,6 +163,7 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass } } catch (z3_error & ex) { + TRACE("tactic2solver", tout << "exception: " << ex.msg() << "\n";); throw ex; } catch (z3_exception & ex) { @@ -203,8 +207,9 @@ void tactic2solver::collect_statistics(statistics & st) const { } void tactic2solver::get_unsat_core(ptr_vector & r) { - if (m_result.get()) + if (m_result.get()) { m_result->get_unsat_core(r); + } } void tactic2solver::get_model(model_ref & m) { @@ -240,21 +245,6 @@ expr * tactic2solver::get_assertion(unsigned idx) const { return m_assertions.get(idx); } -void tactic2solver::display(std::ostream & out) const { - ast_pp_util visitor(m_assertions.m()); - visitor.collect(m_assertions); - visitor.display_decls(out); - visitor.display_asserts(out, m_assertions, true); -#if 0 - ast_manager & m = m_assertions.m(); - unsigned num = m_assertions.size(); - out << "(solver"; - for (unsigned i = 0; i < num; i++) { - out << "\n " << mk_ismt2_pp(m_assertions.get(i), m, 2); - } - out << ")"; -#endif -} solver * mk_tactic2solver(ast_manager & m, tactic * t, diff --git a/src/tactic/aig/aig.cpp b/src/tactic/aig/aig.cpp index b84ae68f0..6e289d969 100644 --- a/src/tactic/aig/aig.cpp +++ b/src/tactic/aig/aig.cpp @@ -53,8 +53,10 @@ struct aig { aig() {} }; +#if Z3DEBUG inline bool is_true(aig_lit const & r) { return !r.is_inverted() && r.ptr_non_inverted()->m_id == 0; } -inline bool is_false(aig_lit const & r) { return r.is_inverted() && r.ptr()->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; } @@ -66,13 +68,8 @@ 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; } -void unmark(unsigned sz, aig_lit const * ns) { - for (unsigned i = 0; i < sz; i++) { - ns[i].ptr()->m_mark = false; - } -} -void unmark(unsigned sz, aig * const * ns) { +static void unmark(unsigned sz, aig * const * ns) { for (unsigned i = 0; i < sz; i++) { ns[i]->m_mark = false; } diff --git a/src/tactic/arith/add_bounds_tactic.cpp b/src/tactic/arith/add_bounds_tactic.cpp index 950248698..8ff82af17 100644 --- a/src/tactic/arith/add_bounds_tactic.cpp +++ b/src/tactic/arith/add_bounds_tactic.cpp @@ -51,6 +51,7 @@ public: virtual result operator()(goal const & g) { return is_unbounded(g); } + virtual ~is_unbounded_probe() {} }; probe * mk_is_unbounded_probe() { @@ -109,11 +110,11 @@ class add_bounds_tactic : public tactic { void operator()(quantifier*) {} }; - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; tactic_report report("add-bounds", *g); bound_manager bm(m); diff --git a/src/tactic/arith/bound_manager.cpp b/src/tactic/arith/bound_manager.cpp index ed77467c3..97d93658c 100644 --- a/src/tactic/arith/bound_manager.cpp +++ b/src/tactic/arith/bound_manager.cpp @@ -79,12 +79,23 @@ 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; @@ -99,10 +110,10 @@ void bound_manager::operator()(expr * f, expr_dependency * d) { expr * lhs = t->get_arg(0); expr * rhs = t->get_arg(1); bool is_int; - if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, n, is_int)) { + if (is_uninterp_const(lhs) && is_numeral(rhs, n, is_int)) { v = lhs; } - else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, n, is_int)) { + else if (is_uninterp_const(rhs) && is_numeral(lhs, n, is_int)) { v = rhs; k = swap_decl(k); } @@ -165,6 +176,26 @@ void bound_manager::insert_lower(expr * v, bool strict, numeral const & n, expr_ } } +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; @@ -176,14 +207,14 @@ bool bound_manager::is_disjunctive_bound(expr * f, expr_dependency * d) { expr * e = to_app(f)->get_arg(i); if (!m().is_eq(e, x, y)) return false; if (is_uninterp_const(x) && - m_util.is_numeral(y, n, is_int) && is_int && + is_numeral(y, n, is_int) && is_int && (x == v || v == 0)) { if (v == 0) { v = x; lo = hi = n; } if (n < lo) lo = n; if (n > hi) hi = n; } else if (is_uninterp_const(y) && - m_util.is_numeral(x, n, is_int) && is_int && + is_numeral(x, n, is_int) && is_int && (y == v || v == 0)) { if (v == 0) { v = y; lo = hi = n; } if (n < lo) lo = n; diff --git a/src/tactic/arith/bound_manager.h b/src/tactic/arith/bound_manager.h index cc0d693e9..6a4dc2c96 100644 --- a/src/tactic/arith/bound_manager.h +++ b/src/tactic/arith/bound_manager.h @@ -36,6 +36,8 @@ private: 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: diff --git a/src/tactic/arith/bv2int_rewriter.h b/src/tactic/arith/bv2int_rewriter.h index 0f68257f1..15a425857 100644 --- a/src/tactic/arith/bv2int_rewriter.h +++ b/src/tactic/arith/bv2int_rewriter.h @@ -34,7 +34,7 @@ class bv2int_rewriter_ctx { public: bv2int_rewriter_ctx(ast_manager& m, params_ref const& p) : - m_side_conditions(m), m_trail(m) { update_params(p); } + m_max_size(UINT_MAX), 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); } diff --git a/src/tactic/arith/card2bv_tactic.cpp b/src/tactic/arith/card2bv_tactic.cpp index 7d7097958..096e52981 100644 --- a/src/tactic/arith/card2bv_tactic.cpp +++ b/src/tactic/arith/card2bv_tactic.cpp @@ -18,434 +18,22 @@ Notes: --*/ #include"tactical.h" #include"cooperate.h" -#include"rewriter_def.h" #include"ast_smt2_pp.h" -#include"expr_substitution.h" #include"card2bv_tactic.h" -#include"pb_rewriter.h" +#include"pb2bv_rewriter.h" #include"ast_util.h" #include"ast_pp.h" - -namespace pb { - unsigned card2bv_rewriter::get_num_bits(func_decl* f) { - rational r(0); - unsigned sz = f->get_arity(); - for (unsigned i = 0; i < sz; ++i) { - r += pb.get_coeff(f, i); - } - r = r > pb.get_k(f)? r : pb.get_k(f); - return r.get_num_bits(); - } - - card2bv_rewriter::card2bv_rewriter(ast_manager& m): - m(m), - au(m), - pb(m), - bv(m), - m_sort(*this), - m_lemmas(m), - m_trail(m) - {} - - void card2bv_rewriter::mk_assert(func_decl * f, unsigned sz, expr * const* args, expr_ref & result, expr_ref_vector& lemmas) { - m_lemmas.reset(); - SASSERT(f->get_family_id() == pb.get_family_id()); - if (is_or(f)) { - result = m.mk_or(sz, args); - } - else if (is_and(f)) { - result = m.mk_and(sz, args); - } - else if (pb.is_eq(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { - result = m_sort.eq(pb.get_k(f).get_unsigned(), sz, args); - } - else if (pb.is_le(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { - result = m_sort.le(false, pb.get_k(f).get_unsigned(), sz, args); - } - else if (pb.is_ge(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { - result = m_sort.ge(false, pb.get_k(f).get_unsigned(), sz, args); - } - else { - br_status st = mk_shannon(f, sz, args, result); - if (st == BR_FAILED) { - mk_bv(f, sz, args, result); - } - } - lemmas.append(m_lemmas); - } - - std::ostream& card2bv_rewriter::pp(std::ostream& out, literal lit) { - return out << mk_ismt2_pp(lit, m); - } - - card2bv_rewriter::literal card2bv_rewriter::trail(literal l) { - m_trail.push_back(l); - return l; - } - card2bv_rewriter::literal card2bv_rewriter::fresh() { - return trail(m.mk_fresh_const("sn", m.mk_bool_sort())); - } - - void card2bv_rewriter::mk_clause(unsigned n, literal const* lits) { - m_lemmas.push_back(mk_or(m, n, lits)); - } - - - br_status card2bv_rewriter::mk_app_core(func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { - if (f->get_family_id() == null_family_id) { - if (sz == 1) { - // Expecting minimize/maximize. - func_decl_ref fd(m); - fd = m.mk_func_decl(f->get_name(), m.get_sort(args[0]), f->get_range()); - result = m.mk_app(fd.get(), args[0]); - return BR_DONE; - } - else - return BR_FAILED; - } - else if (f->get_family_id() == m.get_basic_family_id()) { - result = m.mk_app(f, sz, args); - return BR_DONE; - } - else if (f->get_family_id() == pb.get_family_id()) { - if (is_or(f)) { - result = m.mk_or(sz, args); - return BR_DONE; - } - if (is_and(f)) { - result = m.mk_and(sz, args); - return BR_DONE; - } - br_status st = mk_shannon(f, sz, args, result); - if (st == BR_FAILED) { - mk_bv(f, sz, args, result); - return BR_DONE; - } - else { - return st; - } - } - // NSB: review - // we should remove this code and rely on a layer above to deal with - // whatever it accomplishes. It seems to break types. - // - else if (f->get_family_id() == au.get_family_id()) { - if (f->get_decl_kind() == OP_ADD) { - unsigned bits = 0; - for (unsigned i = 0; i < sz; i++) { - rational val1, val2; - if (au.is_int(args[i]) && au.is_numeral(args[i], val1)) { - bits += val1.get_num_bits(); - } - else if (m.is_ite(args[i]) && - au.is_numeral(to_app(args[i])->get_arg(1), val1) && val1.is_one() && - au.is_numeral(to_app(args[i])->get_arg(2), val2) && val2.is_zero()) { - bits++; - } - else - return BR_FAILED; - } - - result = 0; - for (unsigned i = 0; i < sz; i++) { - rational val1, val2; - expr * q; - if (au.is_int(args[i]) && au.is_numeral(args[i], val1)) - q = bv.mk_numeral(val1, bits); - else - q = mk_ite(to_app(args[i])->get_arg(0), bv.mk_numeral(1, bits), bv.mk_numeral(0, bits)); - result = (i == 0) ? q : bv.mk_bv_add(result.get(), q); - } - return BR_DONE; - } - else - return BR_FAILED; - } - else - return BR_FAILED; - } - - bool card2bv_rewriter::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; - } - } - - bool card2bv_rewriter::is_and(func_decl* f) { - return false; - } - - void card2bv_rewriter::mk_bv(func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { - expr_ref zero(m), a(m), b(m); - expr_ref_vector es(m); - unsigned bw = get_num_bits(f); - zero = bv.mk_numeral(rational(0), bw); - for (unsigned i = 0; i < sz; ++i) { - es.push_back(mk_ite(args[i], bv.mk_numeral(pb.get_coeff(f, i), bw), zero)); - } - switch (es.size()) { - case 0: a = zero; break; - case 1: a = es[0].get(); break; - default: - a = es[0].get(); - for (unsigned i = 1; i < es.size(); ++i) { - a = bv.mk_bv_add(a, es[i].get()); - } - break; - } - b = bv.mk_numeral(pb.get_k(f), bw); - - switch (f->get_decl_kind()) { - case OP_AT_MOST_K: - case OP_PB_LE: - UNREACHABLE(); - result = bv.mk_ule(a, b); - break; - case OP_AT_LEAST_K: - UNREACHABLE(); - case OP_PB_GE: - result = bv.mk_ule(b, a); - break; - case OP_PB_EQ: - result = m.mk_eq(a, b); - break; - default: - UNREACHABLE(); - } - TRACE("card2bv", tout << result << "\n";); - } - - struct argc_t { - expr* m_arg; - rational m_coeff; - argc_t():m_arg(0), m_coeff(0) {} - argc_t(expr* arg, rational const& r): m_arg(arg), m_coeff(r) {} - }; - struct argc_gt { - bool operator()(argc_t const& a, argc_t const& b) const { - return a.m_coeff > b.m_coeff; - } - }; - struct argc_entry { - unsigned m_index; - rational m_k; - expr* m_value; - argc_entry(unsigned i, rational const& k): m_index(i), m_k(k), m_value(0) {} - argc_entry():m_index(0), m_k(0), m_value(0) {} - - struct eq { - bool operator()(argc_entry const& a, argc_entry const& b) const { - return a.m_index == b.m_index && a.m_k == b.m_k; - } - }; - struct hash { - unsigned operator()(argc_entry const& a) const { - return a.m_index ^ a.m_k.hash(); - } - }; - }; - typedef hashtable argc_cache; - - br_status card2bv_rewriter::mk_shannon( - func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { - - unsigned max_clauses = sz*10; - vector argcs; - for (unsigned i = 0; i < sz; ++i) { - argcs.push_back(argc_t(args[i], pb.get_coeff(f, i))); - } - std::sort(argcs.begin(), argcs.end(), argc_gt()); - DEBUG_CODE( - for (unsigned i = 0; i + 1 < sz; ++i) { - SASSERT(argcs[i].m_coeff >= argcs[i+1].m_coeff); - } - ); - result = m.mk_app(f, sz, args); - TRACE("card2bv", tout << result << "\n";); - argc_cache cache; - expr_ref_vector trail(m); - vector todo_k; - unsigned_vector todo_i; - todo_k.push_back(pb.get_k(f)); - todo_i.push_back(0); - decl_kind kind = f->get_decl_kind(); - argc_entry entry1; - while (!todo_i.empty()) { - SASSERT(todo_i.size() == todo_k.size()); - if (cache.size() > max_clauses) { - return BR_FAILED; - } - unsigned i = todo_i.back(); - rational k = todo_k.back(); - argc_entry entry(i, k); - if (cache.contains(entry)) { - todo_i.pop_back(); - todo_k.pop_back(); - continue; - } - SASSERT(i < sz); - SASSERT(!k.is_neg()); - rational const& coeff = argcs[i].m_coeff; - expr* arg = argcs[i].m_arg; - if (i + 1 == sz) { - switch(kind) { - case OP_AT_MOST_K: - case OP_PB_LE: - if (coeff <= k) { - entry.m_value = m.mk_true(); - } - else { - entry.m_value = negate(arg); - trail.push_back(entry.m_value); - } - break; - case OP_AT_LEAST_K: - case OP_PB_GE: - if (k.is_zero()) { - entry.m_value = m.mk_true(); - } - else if (coeff < k) { - entry.m_value = m.mk_false(); - } - else if (coeff.is_zero()) { - entry.m_value = m.mk_true(); - } - else { - SASSERT(coeff >= k && k.is_pos()); - entry.m_value = arg; - } - break; - case OP_PB_EQ: - if (coeff == k) { - entry.m_value = arg; - } - else if (k.is_zero()) { - entry.m_value = negate(arg); - trail.push_back(entry.m_value); - } - else { - entry.m_value = m.mk_false(); - } - break; - } - todo_i.pop_back(); - todo_k.pop_back(); - cache.insert(entry); - continue; - } - entry.m_index++; - expr* lo = 0, *hi = 0; - if (cache.find(entry, entry1)) { - lo = entry1.m_value; - } - else { - todo_i.push_back(i+1); - todo_k.push_back(k); - } - entry.m_k -= coeff; - if (kind != OP_PB_EQ && !entry.m_k.is_pos()) { - switch (kind) { - case OP_AT_MOST_K: - case OP_PB_LE: - hi = m.mk_false(); - break; - case OP_AT_LEAST_K: - case OP_PB_GE: - hi = m.mk_true(); - break; - default: - UNREACHABLE(); - } - } - else if (cache.find(entry, entry1)) { - hi = entry1.m_value; - } - else { - todo_i.push_back(i+1); - todo_k.push_back(entry.m_k); - } - if (hi && lo) { - todo_i.pop_back(); - todo_k.pop_back(); - entry.m_index = i; - entry.m_k = k; - entry.m_value = mk_ite(arg, hi, lo); - trail.push_back(entry.m_value); - cache.insert(entry); - } - } - argc_entry entry(0, pb.get_k(f)); - VERIFY(cache.find(entry, entry)); - result = entry.m_value; - TRACE("card2bv", tout << result << "\n";); - return BR_DONE; - } - - expr* card2bv_rewriter::negate(expr* e) { - if (m.is_not(e, e)) return e; - return m.mk_not(e); - } - - expr* card2bv_rewriter::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); - } - - void card_pb_rewriter::rewrite(expr* e, expr_ref& result) { - if (pb.is_eq(e)) { - app* a = to_app(e); - ast_manager& m = m_lemmas.get_manager(); - unsigned sz = a->get_num_args(); - expr_ref_vector args(m); - expr_ref tmp(m); - for (unsigned i = 0; i < sz; ++i) { - (*this)(a->get_arg(i), tmp); - args.push_back(tmp); - } - m_cfg.m_r.mk_assert(a->get_decl(), sz, args.c_ptr(), result, m_lemmas); - } - else { - (*this)(e, result); - } - } - -}; - -template class rewriter_tpl; - +#include"filter_model_converter.h" class card2bv_tactic : public tactic { ast_manager & m; params_ref m_params; - th_rewriter m_rw1; - pb::card_pb_rewriter m_rw2; public: card2bv_tactic(ast_manager & m, params_ref const & p): m(m), - m_params(p), - m_rw1(m), - m_rw2(m) { + m_params(p) { } virtual tactic * translate(ast_manager & m) { @@ -472,9 +60,8 @@ public: SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; result.reset(); tactic_report report("card2bv", *g); - m_rw1.reset(); - m_rw2.reset(); - m_rw2.lemmas().reset(); + th_rewriter rw1(m, m_params); + pb2bv_rewriter rw2(m, m_params); if (g->inconsistent()) { result.push_back(g.get()); @@ -484,18 +71,28 @@ public: 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++) { - m_rw1(g->form(idx), new_f1, new_pr1); + rw1(g->form(idx), new_f1, new_pr1); TRACE("card2bv", tout << "Rewriting " << mk_ismt2_pp(new_f1.get(), m) << std::endl;); - m_rw2.rewrite(new_f1, new_f2); + rw2(new_f1, new_f2, new_pr2); if (m.proofs_enabled()) { new_pr1 = m.mk_modus_ponens(g->pr(idx), new_pr1); - new_pr2 = m.mk_rewrite(new_f1, new_f2); new_pr1 = m.mk_modus_ponens(new_pr1, new_pr2); } g->update(idx, new_f2, new_pr1, g->dep(idx)); } - for (unsigned i = 0; i < m_rw2.lemmas().size(); ++i) { - g->assert_expr(m_rw2.lemmas()[i].get()); + expr_ref_vector fmls(m); + rw2.flush_side_constraints(fmls); + for (unsigned i = 0; !g->inconsistent() && i < fmls.size(); ++i) { + g->assert_expr(fmls[i].get()); + } + + func_decl_ref_vector const& fns = rw2.fresh_constants(); + if (!fns.empty()) { + filter_model_converter* filter = alloc(filter_model_converter, m); + for (unsigned i = 0; i < fns.size(); ++i) { + filter->insert(fns[i]); + } + mc = filter; } g->inc_depth(); diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h index 30a91416d..9bf21d2c3 100644 --- a/src/tactic/arith/card2bv_tactic.h +++ b/src/tactic/arith/card2bv_tactic.h @@ -52,6 +52,9 @@ namespace pb { 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, literal const* xs, expr_ref_vector& result, expr_ref_vector& ors); public: card2bv_rewriter(ast_manager& m); diff --git a/src/tactic/arith/diff_neq_tactic.cpp b/src/tactic/arith/diff_neq_tactic.cpp index 410185cf8..d63c2fcf4 100644 --- a/src/tactic/arith/diff_neq_tactic.cpp +++ b/src/tactic/arith/diff_neq_tactic.cpp @@ -312,11 +312,11 @@ class diff_neq_tactic : public tactic { return md; } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); m_produce_models = g->models_enabled(); mc = 0; pc = 0; core = 0; result.reset(); diff --git a/src/tactic/arith/diff_neq_tactic.h b/src/tactic/arith/diff_neq_tactic.h index 5a5c24000..205fb7bb5 100644 --- a/src/tactic/arith/diff_neq_tactic.h +++ b/src/tactic/arith/diff_neq_tactic.h @@ -29,6 +29,6 @@ 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 numberal, and all constants are bounded.", "mk_diff_neq_tactic(m, p)") + 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 diff --git a/src/tactic/arith/fm_tactic.cpp b/src/tactic/arith/fm_tactic.cpp index eae3ab5e6..6db0f5ad3 100644 --- a/src/tactic/arith/fm_tactic.cpp +++ b/src/tactic/arith/fm_tactic.cpp @@ -993,7 +993,7 @@ class fm_tactic : public tactic { sbuffer xs; buffer as; rational c; - bool strict; + bool strict = false; unsigned num; expr * const * args; if (m.is_or(f)) { @@ -1549,11 +1549,11 @@ class fm_tactic : public tactic { throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("fm", *g); diff --git a/src/tactic/arith/lia2pb_tactic.cpp b/src/tactic/arith/lia2pb_tactic.cpp index 8d637bada..2b54dd463 100644 --- a/src/tactic/arith/lia2pb_tactic.cpp +++ b/src/tactic/arith/lia2pb_tactic.cpp @@ -188,11 +188,11 @@ class lia2pb_tactic : public tactic { return true; } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("lia2pb", g); m_produce_models = g->models_enabled(); diff --git a/src/tactic/arith/normalize_bounds_tactic.cpp b/src/tactic/arith/normalize_bounds_tactic.cpp index b3ec505ea..06f1368a2 100644 --- a/src/tactic/arith/normalize_bounds_tactic.cpp +++ b/src/tactic/arith/normalize_bounds_tactic.cpp @@ -80,11 +80,11 @@ class normalize_bounds_tactic : public tactic { return false; } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; bool produce_models = in->models_enabled(); bool produce_proofs = in->proofs_enabled(); diff --git a/src/tactic/arith/pb2bv_tactic.cpp b/src/tactic/arith/pb2bv_tactic.cpp index 4c3790af3..3931a1ea4 100644 --- a/src/tactic/arith/pb2bv_tactic.cpp +++ b/src/tactic/arith/pb2bv_tactic.cpp @@ -29,10 +29,11 @@ Notes: #include"filter_model_converter.h" #include"pb2bv_model_converter.h" #include"pb2bv_tactic.h" +#include"ast_pp.h" class pb2bv_tactic : public tactic { public: - struct non_pb {}; + struct non_pb { expr* e; non_pb(expr* e) : e(e) {}}; struct only_01_visitor { typedef rational numeral; @@ -48,7 +49,7 @@ public: void throw_non_pb(expr * n) { TRACE("pb2bv", tout << "Not pseudo-Boolean: " << mk_ismt2_pp(n, m) << "\n";); - throw non_pb(); + throw non_pb(n); } void operator()(var * n) { @@ -575,7 +576,7 @@ private: void throw_non_pb(expr * n) { TRACE("pb2bv", tout << "Not pseudo-Boolean: " << mk_ismt2_pp(n, m) << "\n";); - throw non_pb(); + throw non_pb(n); } // check if polynomial is encoding @@ -884,11 +885,11 @@ private: r.erase("elim_and"); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { TRACE("pb2bv", g->display(tout);); SASSERT(g->is_well_sorted()); fail_if_proof_generation("pb2bv", g); @@ -910,8 +911,8 @@ private: try { quick_pb_check(g); } - catch (non_pb) { - throw tactic_exception("goal is in a fragment unsupported by pb2bv"); + catch (non_pb& p) { + throw_tactic(p.e); } unsigned size = g->size(); @@ -940,8 +941,8 @@ private: new_exprs.push_back(new_f); } } - catch (non_pb) { - throw tactic_exception("goal is in a fragment unsupported by pb2bv"); + catch (non_pb& p) { + throw_tactic(p.e); } for (unsigned idx = 0; idx < size; idx++) @@ -966,6 +967,12 @@ private: 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().c_str()); + } }; imp * m_imp; diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index a41c6939e..463e1a9e8 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -28,6 +28,7 @@ Revision History: #include"simplify_tactic.h" #include"th_rewriter.h" #include"filter_model_converter.h" +#include"extension_model_converter.h" #include"ast_smt2_pp.h" #include"expr_replacer.h" @@ -102,17 +103,26 @@ Rules 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; - purify_arith_proc(arith_util & u, bool produce_proofs, bool elim_root_objs, bool elim_inverses, bool 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_complete(complete), + m_unsafe_found(false), + m_pinned(m()) { } arith_util & u() { @@ -123,6 +133,56 @@ struct purify_arith_proc { 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(0, u().mk_real()); + pair.second = m().mk_fresh_const(0, 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 rw_cfg : public default_rewriter_cfg { purify_arith_proc & m_owner; obj_map m_app2fresh; @@ -167,6 +227,8 @@ struct purify_arith_proc { 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)) { @@ -316,12 +378,13 @@ struct purify_arith_proc { cache_result(t, result, result_pr); expr * x = args[0]; - // to-real(k) - x >= 0 - expr * diff = u().mk_add(u().mk_to_real(k), u().mk_mul(u().mk_numeral(rational(-1), false), x)); + // 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(to-real(k) - x >= 1) + // not(x - to-real(k) >= 1) push_cnstr(NOT(u().mk_ge(diff, u().mk_numeral(rational(1), false)))); push_cnstr_pr(result_pr); } @@ -347,14 +410,12 @@ struct purify_arith_proc { expr * x = args[0]; expr * zero = u().mk_numeral(rational(0), is_int); expr * one = u().mk_numeral(rational(1), is_int); - if (y.is_zero() && complete()) { + 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); - if (complete()) { - push_cnstr(OR(NOT(EQ(x, zero)), EQ(k, is_int ? u().mk_0_pw_0_int() : u().mk_0_pw_0_real()))); - push_cnstr_pr(result_pr); - } + push_cnstr(OR(NOT(EQ(x, zero)), EQ(k, is_int ? u().mk_0_pw_0_int() : u().mk_0_pw_0_real()))); + push_cnstr_pr(result_pr); } else if (!is_int) { SASSERT(!y.is_int()); @@ -436,6 +497,30 @@ struct purify_arith_proc { 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 { + 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; @@ -566,6 +651,10 @@ struct purify_arith_proc { 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: @@ -590,6 +679,8 @@ struct purify_arith_proc { } }; + 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): @@ -649,26 +740,27 @@ struct purify_arith_proc { } } - void operator()(goal & g, model_converter_ref & mc, bool produce_models) { + 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 = g.size(); + unsigned sz = m_goal.size(); for (unsigned i = 0; i < sz; i++) { - expr * curr = g.form(i); + expr * curr = m_goal.form(i); r(curr, new_curr, new_pr); if (m_produce_proofs) { - proof * pr = g.pr(i); + proof * pr = m_goal.pr(i); new_pr = m().mk_modus_ponens(pr, new_pr); } - g.update(i, new_curr, new_pr, g.dep(i)); + 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++) { - g.assert_expr(r.cfg().m_new_cnstrs.get(i), m_produce_proofs ? r.cfg().m_new_cnstr_prs.get(i) : 0, 0); + m_goal.assert_expr(r.cfg().m_new_cnstrs.get(i), m_produce_proofs ? r.cfg().m_new_cnstr_prs.get(i) : 0, 0); } // add filter_model_converter to eliminate auxiliary variables from model @@ -684,7 +776,20 @@ struct purify_arith_proc { fmc->insert(v->get_decl()); } } + if (produce_models && !m_sin_cos.empty()) { + extension_model_converter* emc = alloc(extension_model_converter, m()); + mc = concat(mc.get(), emc); + obj_map >::iterator it = m_sin_cos.begin(), end = m_sin_cos.end(); + for (; it != end; ++it) { + emc->insert(it->m_key->get_decl(), + m().mk_ite(u().mk_ge(it->m_value.first, mk_real_zero()), u().mk_acos(it->m_value.second), + u().mk_add(u().mk_acos(u().mk_uminus(it->m_value.second)), u().mk_pi()))); + } + + } } + + }; class purify_arith_tactic : public tactic { @@ -726,14 +831,15 @@ public: SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; 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(m_util, produce_proofs, elim_root_objs, elim_inverses, complete); + purify_arith_proc proc(*(g.get()), m_util, produce_proofs, elim_root_objs, elim_inverses, complete); - proc(*(g.get()), mc, produce_models); + proc(mc, produce_models); g->inc_depth(); result.push_back(g.get()); diff --git a/src/tactic/bv/bv_bound_chk_tactic.cpp b/src/tactic/bv/bv_bound_chk_tactic.cpp new file mode 100644 index 000000000..1b90e05b1 --- /dev/null +++ b/src/tactic/bv/bv_bound_chk_tactic.cpp @@ -0,0 +1,244 @@ +/*++ + Copyright (c) 2016 Microsoft Corporation + + Module Name: + + bv_bound_chk_tactic.cpp + + Abstract: + + + Author: + + Mikolas Janota + + Revision History: +--*/ +#include"bv_bound_chk_tactic.h" +#include"ast.h" +#include"rewriter.h" +#include"rewriter_def.h" +#include"bv_bounds.h" +#include"rewriter_params.hpp" +#include"bool_rewriter.h" +#include"cooperate.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 = 0; + 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().size()) 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 { + cooperate("bv-bound-chk"); + 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); + } + + virtual ~bv_bound_chk_rewriter() {} + + 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); + virtual ~bv_bound_chk_tactic(); + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core); + virtual tactic * translate(ast_manager & m); + virtual void updt_params(params_ref const & p); + void cleanup(); + void collect_statistics(statistics & st) const; + void reset_statistics(); +}; + +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, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + 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;); + mc = 0; pc = 0; core = 0; 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); +} diff --git a/src/tactic/bv/bv_bound_chk_tactic.h b/src/tactic/bv/bv_bound_chk_tactic.h new file mode 100644 index 000000000..f134d42ab --- /dev/null +++ b/src/tactic/bv/bv_bound_chk_tactic.h @@ -0,0 +1,30 @@ + /*++ + 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"tactical.h" +#include"params.h" +#include"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_*/ diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index e67c2470b..1b324b2eb 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -148,10 +148,12 @@ struct interval { } }; +#ifdef _TRACE std::ostream& operator<<(std::ostream& o, const interval& I) { o << "[" << I.l << ", " << I.h << "]"; return o; } +#endif struct undo_bound { diff --git a/src/tactic/bv/dt2bv_tactic.cpp b/src/tactic/bv/dt2bv_tactic.cpp new file mode 100644 index 000000000..2ccbe9712 --- /dev/null +++ b/src/tactic/bv/dt2bv_tactic.cpp @@ -0,0 +1,183 @@ +/*++ +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 "dt2bv_tactic.h" +#include "tactical.h" +#include "filter_model_converter.h" +#include "datatype_decl_plugin.h" +#include "bv_decl_plugin.h" +#include "rewriter_def.h" +#include "filter_model_converter.h" +#include "extension_model_converter.h" +#include "var_subst.h" +#include "ast_util.h" +#include "enum2bv_rewriter.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)) { + return; + } + if (m.is_distinct(a)) { + return; + } + 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))); + return; + } + + if (m_t.is_fd(a)) { + m_t.m_fd_sorts.insert(get_sort(a)); + } + else { + unsigned sz = a->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + if (m_t.is_fd(a->get_arg(i))) { + m_t.m_non_fd_sorts.insert(get_sort(a->get_arg(i))); + } + } + } + } + + 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) {} + virtual ~sort_pred() {} + virtual bool operator()(sort* s) { + 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) {} + + virtual tactic * translate(ast_manager & m) { + return alloc(dt2bv_tactic, m, m_params); + } + + virtual void updt_params(params_ref const & p) { + } + + virtual void collect_param_descrs(param_descrs & r) { + } + + virtual void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + mc = 0; pc = 0; core = 0; + 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)); + } + obj_hashtable::iterator it = m_non_fd_sorts.begin(), end = m_non_fd_sorts.end(); + for (; it != end; ++it) { + m_fd_sorts.remove(*it); + } + if (!m_fd_sorts.empty()) { + ref ext = alloc(extension_model_converter, m); + ref filter = alloc(filter_model_converter, m); + 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 (unsigned i = 0; i < bounds.size(); ++i) { + g->assert_expr(bounds[i].get()); + } + { + obj_map::iterator it = rw.enum2bv().begin(), end = rw.enum2bv().end(); + for (; it != end; ++it) { + filter->insert(it->m_value); + } + } + { + obj_map::iterator it = rw.enum2def().begin(), end = rw.enum2def().end(); + for (; it != end; ++it) { + ext->insert(it->m_key, it->m_value); + } + } + + mc = concat(filter.get(), ext.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()); + } + + virtual void cleanup() { + 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); +} diff --git a/src/tactic/bv/dt2bv_tactic.h b/src/tactic/bv/dt2bv_tactic.h new file mode 100644 index 000000000..10ce0724f --- /dev/null +++ b/src/tactic/bv/dt2bv_tactic.h @@ -0,0 +1,33 @@ +/*++ +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"params.h" +#include"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 diff --git a/src/tactic/bv/elim_small_bv_tactic.cpp b/src/tactic/bv/elim_small_bv_tactic.cpp index 7a83ee403..e395433f5 100644 --- a/src/tactic/bv/elim_small_bv_tactic.cpp +++ b/src/tactic/bv/elim_small_bv_tactic.cpp @@ -119,9 +119,8 @@ class elim_small_bv_tactic : public tactic { return res; } - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result = m.mk_app(f, num, args); - TRACE("elim_small_bv_app", tout << "reduce " << mk_ismt2_pp(result, m) << std::endl; ); + 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; } @@ -147,11 +146,10 @@ class elim_small_bv_tactic : public tactic { 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); - symbol const & name = q->get_decl_name(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 " << name << + 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;); diff --git a/src/tactic/core/cofactor_term_ite_tactic.cpp b/src/tactic/core/cofactor_term_ite_tactic.cpp index 24b381476..2286281f4 100644 --- a/src/tactic/core/cofactor_term_ite_tactic.cpp +++ b/src/tactic/core/cofactor_term_ite_tactic.cpp @@ -36,7 +36,7 @@ class cofactor_term_ite_tactic : public tactic { expr * f = g.form(i); expr_ref new_f(m); m_elim_ite(f, new_f); - g.update(i, new_f); + g.update(i, new_f, 0, g.dep(i)); } } diff --git a/src/tactic/core/collect_statistics_tactic.cpp b/src/tactic/core/collect_statistics_tactic.cpp new file mode 100644 index 000000000..8e8879fef --- /dev/null +++ b/src/tactic/core/collect_statistics_tactic.cpp @@ -0,0 +1,187 @@ +/*++ +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.h" +#include"params.h" +#include"arith_decl_plugin.h" +#include"array_decl_plugin.h" +#include"bv_decl_plugin.h" +#include"datatype_decl_plugin.h" +#include"fpa_decl_plugin.h" +#include"tactical.h" +#include"stats.h" + +#include"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) { + } + + virtual ~collect_statistics_tactic() {} + + virtual tactic * translate(ast_manager & m_) { + return alloc(collect_statistics_tactic, m_, m_params); + } + + virtual void updt_params(params_ref const & p) { + m_params = p; + } + + virtual void collect_param_descrs(param_descrs & r) {} + + virtual void operator()(goal_ref const & g, goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc, + expr_dependency_ref & core) { + mc = 0; + 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; + stats_type::iterator it = m_stats.begin(); + stats_type::iterator end = m_stats.end(); + for (; it != end; it++) + std::cout << " :" << it->first << " " << it->second << std::endl; + std::cout << ")" << std::endl; + + g->inc_depth(); + result.push_back(g.get()); + } + + virtual void cleanup() {} + + virtual void collect_statistics(statistics & st) const { + } + + virtual void reset_statistics() { reset(); } + virtual void reset() { cleanup(); } + +protected: + class collect_proc { + public: + ast_manager & m; + stats_type & m_stats; + obj_hashtable m_seen_sorts; + obj_hashtable m_seen_func_decls; + + collect_proc(ast_manager & m, stats_type & s) : m(m), m_stats(s) {} + + 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()); + this->operator()(body); + } + + 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)); +} diff --git a/src/tactic/core/collect_statistics_tactic.h b/src/tactic/core/collect_statistics_tactic.h new file mode 100644 index 000000000..5734af3c7 --- /dev/null +++ b/src/tactic/core/collect_statistics_tactic.h @@ -0,0 +1,33 @@ +/*++ +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"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 diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 1cda9cc6f..3e2790fe3 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -46,7 +46,7 @@ public: 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; @@ -83,8 +83,8 @@ void ctx_propagate_assertions::assert_eq_core(expr * t, app * val) { // because the simplifier stopped at depth m_max_depth return; } - - CTRACE("assert_eq_bug", m_assertions.contains(t), + + 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); @@ -114,11 +114,11 @@ void ctx_propagate_assertions::pop(unsigned num_scopes) { m_trail.pop_back(); } SASSERT(m_trail.size() == old_trail_size); - m_scopes.shrink(scope_lvl - num_scopes); + m_scopes.shrink(scope_lvl - num_scopes); } -bool ctx_simplify_tactic::simplifier::shared(expr * t) const { +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; } @@ -139,7 +139,7 @@ struct ctx_simplify_tactic::imp { unsigned m_lvl; cached_result * m_next; cached_result(expr * t, unsigned lvl, cached_result * next): - m_to(t), + m_to(t), m_lvl(lvl), m_next(next) { } @@ -156,7 +156,7 @@ struct ctx_simplify_tactic::imp { small_object_allocator m_allocator; svector m_cache; vector > m_cache_undo; - unsigned m_depth; + unsigned m_depth; unsigned m_num_steps; goal_num_occurs m_occs; mk_simplified_app m_mk_app; @@ -183,7 +183,7 @@ struct ctx_simplify_tactic::imp { dealloc(m_simp); DEBUG_CODE({ for (unsigned i = 0; i < m_cache.size(); i++) { - CTRACE("ctx_simplify_tactic_bug", m_cache[i].m_from, + 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";); @@ -209,7 +209,7 @@ struct ctx_simplify_tactic::imp { throw tactic_exception(m.limit().get_cancel_msg()); } - bool shared(expr * t) const { + 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; } @@ -233,7 +233,7 @@ struct ctx_simplify_tactic::imp { 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]; + cache_cell & cell = m_cache[id]; void * mem = m_allocator.allocate(sizeof(cached_result)); if (cell.m_from == 0) { // new_entry @@ -243,7 +243,7 @@ struct ctx_simplify_tactic::imp { m.inc_ref(to); } else { - // update + // update cell.m_result = new (mem) cached_result(to, scope_level(), cell.m_result); m.inc_ref(to); } @@ -255,7 +255,7 @@ struct ctx_simplify_tactic::imp { if (shared(from)) cache_core(from, to); } - + unsigned scope_level() const { return m_simp->scope_level(); } @@ -276,7 +276,7 @@ struct ctx_simplify_tactic::imp { 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" << + 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";); @@ -287,7 +287,7 @@ struct ctx_simplify_tactic::imp { } m_allocator.deallocate(sizeof(cached_result), to_delete); } - keys.reset(); + keys.reset(); } void pop(unsigned num_scopes) { @@ -295,7 +295,7 @@ struct ctx_simplify_tactic::imp { return; SASSERT(num_scopes <= scope_level()); - unsigned lvl = scope_level(); + unsigned lvl = scope_level(); m_simp->pop(num_scopes); // restore cache @@ -339,7 +339,7 @@ struct ctx_simplify_tactic::imp { } m_num_steps++; m_depth++; - if (m.is_or(t)) + 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); @@ -351,7 +351,7 @@ struct ctx_simplify_tactic::imp { 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 @@ -374,7 +374,7 @@ struct ctx_simplify_tactic::imp { continue; } if ((OR && m.is_true(new_arg)) || - (!OR && m.is_false(new_arg))) { + (!OR && m.is_false(new_arg))) { r = new_arg; pop(scope_level() - old_lvl); cache(t, r); @@ -403,7 +403,7 @@ struct ctx_simplify_tactic::imp { continue; } if ((OR && m.is_true(new_arg)) || - (!OR && m.is_false(new_arg))) { + (!OR && m.is_false(new_arg))) { r = new_arg; pop(scope_level() - old_lvl); cache(t, r); @@ -428,7 +428,7 @@ struct ctx_simplify_tactic::imp { } cache(t, r); } - + void simplify_ite(app * ite, expr_ref & r) { expr * c = ite->get_arg(0); expr * t = ite->get_arg(1); @@ -467,8 +467,8 @@ struct ctx_simplify_tactic::imp { } 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) + 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); } @@ -529,7 +529,7 @@ struct ctx_simplify_tactic::imp { unsigned sz = g.size(); expr_ref r(m); for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) { - m_depth = 0; + 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(); @@ -538,6 +538,8 @@ struct ctx_simplify_tactic::imp { } pop(scope_level() - old_lvl); + m_occs(g); + // go backwards sz = g.size(); for (unsigned i = sz; !g.inconsistent() && i > 0; ) { @@ -590,7 +592,7 @@ struct ctx_simplify_tactic::imp { 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): @@ -618,9 +620,9 @@ void ctx_simplify_tactic::get_param_descrs(param_descrs & r) { 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, - model_converter_ref & mc, +void ctx_simplify_tactic::operator()(goal_ref const & in, + goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { mc = 0; pc = 0; core = 0; @@ -628,12 +630,12 @@ void ctx_simplify_tactic::operator()(goal_ref const & in, 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); + std::swap(d, m_imp); dealloc(d); } diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 156f69388..df3b9f0f5 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -819,7 +819,7 @@ class elim_uncnstr_tactic : public tactic { m_rw = alloc(rw, m(), produce_proofs, m_vars, m_mc.get(), m_max_memory, m_max_steps); } - virtual void operator()(goal_ref const & g, + void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, diff --git a/src/tactic/core/pb_preprocess_tactic.cpp b/src/tactic/core/pb_preprocess_tactic.cpp index b4f335db5..a7e381576 100644 --- a/src/tactic/core/pb_preprocess_tactic.cpp +++ b/src/tactic/core/pb_preprocess_tactic.cpp @@ -151,9 +151,6 @@ public: SASSERT(g->is_well_sorted()); pc = 0; core = 0; - if (g->unsat_core_enabled()) { - throw tactic_exception("pb-preprocess does not support cores"); - } if (g->proofs_enabled()) { throw tactic_exception("pb-preprocess does not support proofs"); } @@ -202,6 +199,7 @@ public: 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); mc.set_value(e, false); @@ -249,11 +247,11 @@ public: unsigned_vector const& pos = neg?r.neg:r.pos; for (unsigned j = 0; j < pos.size(); ++j) { unsigned k = pos[j]; - if (k == i) continue; + 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()); + g->update(k, m.mk_true(), 0, m.mk_join(g->dep(m_ge[i]), g->dep(k))); m_progress = true; } } @@ -299,7 +297,7 @@ private: 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->update(i, tmp, g->pr(i), g->dep(i)); } } } @@ -331,12 +329,12 @@ private: 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); + g->assert_expr(fml1, 0, 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); + g->update(i, fml2, 0, g->dep(i)); TRACE("pb", tout << fml2 << "\n";); } } @@ -577,8 +575,12 @@ private: 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";); - g->update(idx1, m.mk_true()); // proof & dependencies - g->update(idx2, tmp2); // proof & dependencies + 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, 0, m.mk_join(g->dep(idx1), g->dep(idx2))); + g->update(idx1, m.mk_true(), 0, 0); m_progress = true; //IF_VERBOSE(0, if (!g->inconsistent()) display_annotation(verbose_stream(), g);); } @@ -634,14 +636,18 @@ private: 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); + 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";); - g->update(idx, tmp); // proof & dependencies. + 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; } } diff --git a/src/tactic/core/symmetry_reduce_tactic.cpp b/src/tactic/core/symmetry_reduce_tactic.cpp index 0ee606da9..873dc55bc 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -176,7 +176,7 @@ private: app_map coloring; app_map depth; inv_app_map inv_color; - unsigned num_occs; + unsigned num_occs = 0; compute_sort_colors(fml, coloring); compute_max_depth(fml, depth); merge_colors(occs, coloring); @@ -233,7 +233,7 @@ private: typedef map pair_map; bool merge_colors(app_map const& colors1, app_map& colors2) { pair_map recolor; - unsigned num_colors = 0, v1, v2, w, old_max = 0; + 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; @@ -545,7 +545,7 @@ private: term_set& cts, term_set const& consts, app_map const& occs) { SASSERT(!T.empty()); app* t = T[0]; - unsigned weight, weight1; + 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";); @@ -559,9 +559,9 @@ private: 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; + cts_delta = cts_delta1; + weight = weight1; + t = t1; } } return t; diff --git a/src/tactic/extension_model_converter.cpp b/src/tactic/extension_model_converter.cpp index cdd096455..ee6b8f691 100644 --- a/src/tactic/extension_model_converter.cpp +++ b/src/tactic/extension_model_converter.cpp @@ -25,7 +25,7 @@ Notes: extension_model_converter::~extension_model_converter() { } - +#ifdef _TRACE static void display_decls_info(std::ostream & out, model_ref & md) { ast_manager & m = md->get_manager(); unsigned sz = md->get_num_decls(); @@ -42,6 +42,7 @@ static void display_decls_info(std::ostream & out, model_ref & md) { out << " :id " << d->get_id() << "\n"; } } +#endif void extension_model_converter::operator()(model_ref & md, unsigned goal_idx) { SASSERT(goal_idx == 0); @@ -71,7 +72,7 @@ void extension_model_converter::operator()(model_ref & md, unsigned goal_idx) { void extension_model_converter::insert(func_decl * v, expr * def) { m_vars.push_back(v); - m_defs.push_back(def); + m_defs.push_back(def); } diff --git a/src/tactic/filter_model_converter.cpp b/src/tactic/filter_model_converter.cpp index ba6ee4f0d..247f2e91d 100644 --- a/src/tactic/filter_model_converter.cpp +++ b/src/tactic/filter_model_converter.cpp @@ -51,6 +51,9 @@ void filter_model_converter::operator()(model_ref & old_model, unsigned goal_idx TRACE("filter_mc", tout << "after filter_model_converter\n"; model_v2_pp(tout, *old_model);); } +void filter_model_converter::operator()(svector & labels, unsigned goal_idx) { +} + void filter_model_converter::display(std::ostream & out) { out << "(filter-model-converter"; for (unsigned i = 0; i < m_decls.size(); i++) { diff --git a/src/tactic/filter_model_converter.h b/src/tactic/filter_model_converter.h index 6fc8fdd77..0b67a6c5a 100644 --- a/src/tactic/filter_model_converter.h +++ b/src/tactic/filter_model_converter.h @@ -32,6 +32,8 @@ public: virtual void operator()(model_ref & md, unsigned goal_idx); + virtual void operator()(svector & labels, unsigned goal_idx); + virtual void operator()(model_ref & md) { operator()(md, 0); } // TODO: delete virtual void cancel() {} diff --git a/src/tactic/fpa/fpa2bv_model_converter.cpp b/src/tactic/fpa/fpa2bv_model_converter.cpp index 20d8bbbcc..88224d2f2 100644 --- a/src/tactic/fpa/fpa2bv_model_converter.cpp +++ b/src/tactic/fpa/fpa2bv_model_converter.cpp @@ -22,448 +22,50 @@ Notes: void fpa2bv_model_converter::display(std::ostream & out) { out << "(fpa2bv-model-converter"; - for (obj_map::iterator it = m_const2bv.begin(); - it != m_const2bv.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value, m, indent) << ")"; - } - for (obj_map::iterator it = m_rm_const2bv.begin(); - it != m_rm_const2bv.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value, m, indent) << ")"; - } - for (obj_map::iterator it = m_uf2bvuf.begin(); - it != m_uf2bvuf.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value, m, indent) << ")"; - } - for (obj_map >::iterator it = m_specials.begin(); - it != m_specials.end(); - it++) { - const symbol & n = it->m_key->get_name(); - out << "\n (" << n << " "; - unsigned indent = n.size() + 4; - out << mk_ismt2_pp(it->m_value.first, m, indent) << "; " << - mk_ismt2_pp(it->m_value.second, m, indent) << ")"; - } + m_bv2fp->display(out); out << ")"; } model_converter * fpa2bv_model_converter::translate(ast_translation & translator) { fpa2bv_model_converter * res = alloc(fpa2bv_model_converter, translator.to()); - for (obj_map::iterator it = m_const2bv.begin(); - it != m_const2bv.end(); - it++) - { - func_decl * k = translator(it->m_key); - expr * v = translator(it->m_value); - res->m_const2bv.insert(k, v); - translator.to().inc_ref(k); - translator.to().inc_ref(v); - } - for (obj_map::iterator it = m_rm_const2bv.begin(); - it != m_rm_const2bv.end(); - it++) - { - func_decl * k = translator(it->m_key); - expr * v = translator(it->m_value); - res->m_rm_const2bv.insert(k, v); - translator.to().inc_ref(k); - translator.to().inc_ref(v); - } - for (obj_map::iterator it = m_uf2bvuf.begin(); - it != m_uf2bvuf.end(); - it++) { - func_decl * k = translator(it->m_key); - func_decl * v = translator(it->m_value); - res->m_uf2bvuf.insert(k, v); - translator.to().inc_ref(k); - translator.to().inc_ref(v); - } - for (obj_map >::iterator it = m_specials.begin(); - it != m_specials.end(); - it++) { - func_decl * k = translator(it->m_key); - app * v1 = translator(it->m_value.first); - app * v2 = translator(it->m_value.second); - res->m_specials.insert(k, std::pair(v1, v2)); - translator.to().inc_ref(k); - translator.to().inc_ref(v1); - translator.to().inc_ref(v2); - } + res->m_bv2fp = m_bv2fp->translate(translator); return res; } -expr_ref fpa2bv_model_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("fpa2bv_mc", 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 fpa2bv_model_converter::convert_bv2fp(model * bv_mdl, 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 sgn(m), exp(m), sig(m); - sgn = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, bv); - exp = m_bv_util.mk_extract(bv_sz - 2, sbits - 1, bv); - sig = m_bv_util.mk_extract(sbits - 2, 0, bv); - - expr_ref v_sgn(m), v_exp(m), v_sig(m); - bv_mdl->eval(sgn, v_sgn); - bv_mdl->eval(exp, v_exp); - bv_mdl->eval(sig, v_sig); - - return convert_bv2fp(s, v_sgn, v_exp, v_sig); -} - -expr_ref fpa2bv_model_converter::convert_bv2rm(expr * bv_rm) { - expr_ref res(m); - rational bv_val(0); - unsigned sz = 0; - - if (m_bv_util.is_numeral(bv_rm, bv_val, sz)) { - 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(); - } - } - - return res; -} - -expr_ref fpa2bv_model_converter::convert_bv2rm(model * bv_mdl, expr * val) { - expr_ref res(m); - expr_ref eval_v(m); - - if (val && bv_mdl->eval(val, eval_v, true)) - res = convert_bv2rm(eval_v); - - return res; -} - -expr_ref fpa2bv_model_converter::rebuild_floats(model * bv_mdl, sort * s, expr * e) { - expr_ref result(m); - TRACE("fpa2bv_mc", tout << "rebuild floats in " << mk_ismt2_pp(s, m) << " for " << mk_ismt2_pp(e, m) << std::endl;); - - if (m_fpa_util.is_float(s)) { - 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(bv_mdl, s, e); - } - } - else if (m_fpa_util.is_rm(s)) { - 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(bv_mdl, e); - } - } - else if (is_app(e)) { - app * a = to_app(e); - expr_ref_vector new_args(m); - for (unsigned i = 0; i < a->get_num_args(); i++) - new_args.push_back(rebuild_floats(bv_mdl, a->get_decl()->get_domain()[i], a->get_arg(i))); - result = m.mk_app(a->get_decl(), new_args.size(), new_args.c_ptr()); - } - - return result; -} - -fpa2bv_model_converter::array_model fpa2bv_model_converter::convert_array_func_interp(func_decl * f, func_decl * bv_f, model * bv_mdl) { - 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 = bv_mdl->get_const_interp(bv_f); - if (as_arr_mdl == 0) return am; - TRACE("fpa2bv_mc", 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(am.new_float_fd, bv_f, bv_mdl); - am.bv_fd = bv_f; - am.result = arr_util.mk_as_array(f->get_range(), am.new_float_fd); - return am; -} - -func_interp * fpa2bv_model_converter::convert_func_interp(func_decl * f, func_decl * bv_f, model * bv_mdl) { - SASSERT(f->get_arity() > 0); - func_interp * result = 0; - sort * rng = f->get_range(); - sort * const * dmn = f->get_domain(); - - unsigned arity = bv_f->get_arity(); - func_interp * bv_fi = bv_mdl->get_func_interp(bv_f); - - if (bv_fi != 0) { - fpa_rewriter rw(m); - expr_ref ai(m); - result = alloc(func_interp, m, arity); - - 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]; - ai = rebuild_floats(bv_mdl, ft_dj, bv_aj); - m_th_rw(ai); - new_args.push_back(ai); - } - - expr_ref bv_fres(m), ft_fres(m); - bv_fres = bv_fe->get_result(); - ft_fres = rebuild_floats(bv_mdl, rng, bv_fres); - m_th_rw(ft_fres); - result->insert_new_entry(new_args.c_ptr(), ft_fres); - } - - expr_ref bv_els(m), ft_els(m); - bv_els = bv_fi->get_else(); - ft_els = rebuild_floats(bv_mdl, rng, bv_els); - m_th_rw(ft_els); - result->set_else(ft_els); - } - - return result; -} - -void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { - TRACE("fpa2bv_mc", tout << "BV Model: " << std::endl; - for (unsigned i = 0; i < bv_mdl->get_num_constants(); i++) - tout << bv_mdl->get_constant(i)->get_name() << " --> " << - mk_ismt2_pp(bv_mdl->get_const_interp(bv_mdl->get_constant(i)), m) << std::endl; - for (unsigned i = 0; i < bv_mdl->get_num_functions(); i++) { - func_decl * f = bv_mdl->get_function(i); - tout << f->get_name() << "(...) := " << std::endl; - func_interp * fi = bv_mdl->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; - }); - +void fpa2bv_model_converter::convert(model_core * mc, model * float_mdl) { obj_hashtable seen; - - for (obj_map >::iterator it = m_specials.begin(); - it != m_specials.end(); - it++) { - func_decl * f = it->m_key; - 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); - bv_mdl->eval(it->m_value.first, pn, true); - bv_mdl->eval(it->m_value.second, np, true); - seen.insert(it->m_value.first->get_decl()); - seen.insert(it->m_value.second->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); - - float_mdl->register_decl(f, flt_fi); - TRACE("fpa2bv_mc", tout << "fp.min/fp.max special: " << std::endl << - mk_ismt2_pp(f, m) << " == " << mk_ismt2_pp(flt_fi->get_interp(), m) << std::endl;); - } - - for (obj_map::iterator it = m_const2bv.begin(); - it != m_const2bv.end(); - it++) - { - func_decl * var = it->m_key; - app * val = to_app(it->m_value); - SASSERT(m_fpa_util.is_float(var->get_range())); - SASSERT(var->get_range()->get_num_parameters() == 2); - - expr_ref sgn(m), sig(m), exp(m); - bv_mdl->eval(val->get_arg(0), sgn, true); - bv_mdl->eval(val->get_arg(1), exp, true); - bv_mdl->eval(val->get_arg(2), sig, true); - - 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(m); - cv = convert_bv2fp(var->get_range(), sgn, exp, sig); - float_mdl->register_decl(var, cv); - - TRACE("fpa2bv_mc", tout << var->get_name() << " == " << mk_ismt2_pp(cv, m) << std::endl;); - } - - for (obj_map::iterator it = m_rm_const2bv.begin(); - it != m_rm_const2bv.end(); - it++) - { - func_decl * var = it->m_key; - SASSERT(m_fpa_util.is_rm(var->get_range())); - expr * val = it->m_value; - SASSERT(m_fpa_util.is_bv2rm(val)); - expr * bvval = to_app(val)->get_arg(0); - expr_ref fv(m); - fv = convert_bv2rm(bv_mdl, bvval); - TRACE("fpa2bv_mc", tout << var->get_name() << " == " << mk_ismt2_pp(fv, m) << ")" << std::endl;); - float_mdl->register_decl(var, fv); - seen.insert(to_app(bvval)->get_decl()); - } - - for (obj_map::iterator it = m_uf2bvuf.begin(); - it != m_uf2bvuf.end(); - it++) { - seen.insert(it->m_value); - - func_decl * f = it->m_key; - if (f->get_arity() == 0) - { - array_util au(m); - if (au.is_array(f->get_range())) { - array_model am = convert_array_func_interp(f, it->m_value, bv_mdl); - if (am.new_float_fd) float_mdl->register_decl(am.new_float_fd, am.new_float_fi); - if (am.result) float_mdl->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); - bv_mdl->eval(it->m_value, val); - if (val) float_mdl->register_decl(f, val); - } - } - else { - func_interp * fmv = convert_func_interp(f, it->m_value, bv_mdl); - if (fmv) float_mdl->register_decl(f, fmv); - } - } - + 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 = bv_mdl->get_num_constants(); - for (unsigned i = 0; i < sz; i++) - { - func_decl * c = bv_mdl->get_constant(i); + 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, bv_mdl->get_const_interp(c)); + float_mdl->register_decl(c, mc->get_const_interp(c)); } - + // And keep everything else - sz = bv_mdl->get_num_functions(); - for (unsigned i = 0; i < sz; i++) - { - func_decl * f = bv_mdl->get_function(i); - if (!seen.contains(f)) - { + 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 = bv_mdl->get_func_interp(f)->copy(); + func_interp * val = mc->get_func_interp(f)->copy(); float_mdl->register_decl(f, val); } } - - sz = bv_mdl->get_num_uninterpreted_sorts(); - for (unsigned i = 0; i < sz; i++) - { - sort * s = bv_mdl->get_uninterpreted_sort(i); - ptr_vector u = bv_mdl->get_universe(s); + + 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 const & conv) { +model_converter * mk_fpa2bv_model_converter(ast_manager & m, fpa2bv_converter & conv) { return alloc(fpa2bv_model_converter, m, conv); } diff --git a/src/tactic/fpa/fpa2bv_model_converter.h b/src/tactic/fpa/fpa2bv_model_converter.h index 0e92841de..1f482478b 100644 --- a/src/tactic/fpa/fpa2bv_model_converter.h +++ b/src/tactic/fpa/fpa2bv_model_converter.h @@ -19,78 +19,27 @@ Notes: #ifndef FPA2BV_MODEL_CONVERTER_H_ #define FPA2BV_MODEL_CONVERTER_H_ -#include"th_rewriter.h" #include"fpa2bv_converter.h" #include"model_converter.h" +#include"bv2fpa_converter.h" class fpa2bv_model_converter : public model_converter { ast_manager & m; - fpa_util m_fpa_util; - bv_util m_bv_util; - th_rewriter m_th_rw; + bv2fpa_converter * m_bv2fp; - obj_map m_const2bv; - obj_map m_rm_const2bv; - obj_map m_uf2bvuf; - obj_map > m_specials; - public: - fpa2bv_model_converter(ast_manager & m, fpa2bv_converter const & conv) : + fpa2bv_model_converter(ast_manager & m, fpa2bv_converter & conv): m(m), - m_fpa_util(m), - m_bv_util(m), - m_th_rw(m) { - for (obj_map::iterator it = conv.m_const2bv.begin(); - it != conv.m_const2bv.end(); - it++) - { - m_const2bv.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - m.inc_ref(it->m_value); - } - for (obj_map::iterator it = conv.m_rm_const2bv.begin(); - it != conv.m_rm_const2bv.end(); - it++) - { - m_rm_const2bv.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - m.inc_ref(it->m_value); - } - for (obj_map::iterator it = conv.m_uf2bvuf.begin(); - it != conv.m_uf2bvuf.end(); - it++) - { - m_uf2bvuf.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - m.inc_ref(it->m_value); - } - for (obj_map >::iterator it = conv.m_specials.begin(); - it != conv.m_specials.end(); - it++) { - m_specials.insert(it->m_key, it->m_value); - m.inc_ref(it->m_key); - m.inc_ref(it->m_value.first); - m.inc_ref(it->m_value.second); - } + m_bv2fp(alloc(bv2fpa_converter, m, conv)) { } virtual ~fpa2bv_model_converter() { - dec_ref_map_key_values(m, m_const2bv); - dec_ref_map_key_values(m, m_rm_const2bv); - dec_ref_map_key_values(m, m_uf2bvuf); - for (obj_map >::iterator it = m_specials.begin(); - it != m_specials.end(); - it++) { - m.dec_ref(it->m_key); - m.dec_ref(it->m_value.first); - m.dec_ref(it->m_value.second); - } + dealloc(m_bv2fp); } virtual void operator()(model_ref & md, unsigned goal_idx) { SASSERT(goal_idx == 0); model * new_model = alloc(model, m); - obj_hashtable bits; convert(md.get(), new_model); md = new_model; } @@ -106,32 +55,12 @@ public: protected: fpa2bv_model_converter(ast_manager & m) : m(m), - m_fpa_util(m), - m_bv_util(m), - m_th_rw(m) {} - - void convert(model * bv_mdl, model * float_mdl); - expr_ref convert_bv2fp(sort * s, expr * sgn, expr * exp, expr * sig); - expr_ref convert_bv2fp(model * bv_mdl, sort * s, expr * bv); - expr_ref convert_bv2rm(expr * eval_v); - expr_ref convert_bv2rm(model * bv_mdl, expr * val); - - func_interp * convert_func_interp(func_decl * f, func_decl * bv_f, model * bv_mdl); - expr_ref rebuild_floats(model * bv_mdl, 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(0), new_float_fi(0), bv_fd(0), result(m) {} - }; - - array_model convert_array_func_interp(func_decl * f, func_decl * bv_f, model * bv_mdl); + m_bv2fp(0) {} + + void convert(model_core * mc, model * float_mdl); }; -model_converter * mk_fpa2bv_model_converter(ast_manager & m, fpa2bv_converter const & conv); +model_converter * mk_fpa2bv_model_converter(ast_manager & m, fpa2bv_converter & conv); #endif diff --git a/src/tactic/fpa/fpa2bv_tactic.cpp b/src/tactic/fpa/fpa2bv_tactic.cpp index 28597c6ec..d55a2e25c 100644 --- a/src/tactic/fpa/fpa2bv_tactic.cpp +++ b/src/tactic/fpa/fpa2bv_tactic.cpp @@ -46,11 +46,11 @@ class fpa2bv_tactic : public tactic { m_rw.cfg().updt_params(p); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { SASSERT(g->is_well_sorted()); m_proofs_enabled = g->proofs_enabled(); m_produce_models = g->models_enabled(); diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index 8897a83f8..b14d2676c 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -237,6 +237,9 @@ void goal::slow_process(expr * f, proof * pr, expr_dependency * d) { } 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()); SASSERT(proofs_enabled() == (pr != 0 && !m().is_undef_proof(pr))); if (m_inconsistent) return; diff --git a/src/tactic/model_converter.cpp b/src/tactic/model_converter.cpp index 7ac3e898a..6f6dd3da1 100644 --- a/src/tactic/model_converter.cpp +++ b/src/tactic/model_converter.cpp @@ -33,6 +33,12 @@ public: this->m_c1->operator()(m, 0); } + virtual void operator()(labels_vec & r, unsigned goal_idx) { + this->m_c2->operator()(r, goal_idx); + this->m_c1->operator()(r, 0); + } + + virtual char const * get_name() const { return "concat-model-converter"; } virtual model_converter * translate(ast_translation & translator) { @@ -76,6 +82,24 @@ public: } UNREACHABLE(); } + + virtual void operator()(labels_vec & r, unsigned goal_idx) { + unsigned num = this->m_c2s.size(); + for (unsigned i = 0; i < num; i++) { + if (goal_idx < this->m_szs[i]) { + // found the model converter that should be used + model_converter * c2 = this->m_c2s[i]; + if (c2) + c2->operator()(r, goal_idx); + if (m_c1) + this->m_c1->operator()(r, i); + return; + } + // invalid goal + goal_idx -= this->m_szs[i]; + } + UNREACHABLE(); + } virtual char const * get_name() const { return "concat-star-model-converter"; } @@ -102,8 +126,12 @@ model_converter * concat(model_converter * mc1, unsigned num, model_converter * class model2mc : public model_converter { model_ref m_model; + buffer m_labels; public: model2mc(model * m):m_model(m) {} + + model2mc(model * m, buffer const & r):m_model(m), m_labels(r) {} + virtual ~model2mc() {} virtual void operator()(model_ref & m) { @@ -114,7 +142,11 @@ public: m = m_model; } - virtual void cancel() { + virtual void operator()(labels_vec & r, unsigned goal_idx) { + r.append(m_labels.size(), m_labels.c_ptr()); + } + + virtual void cancel() { } virtual void display(std::ostream & out) { @@ -135,6 +167,12 @@ model_converter * model2model_converter(model * m) { return alloc(model2mc, m); } +model_converter * model_and_labels2model_converter(model * m, buffer & r) { + if (m == 0) + return 0; + return alloc(model2mc, m, r); +} + void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m) { if (mc) { m = alloc(model, mng); diff --git a/src/tactic/model_converter.h b/src/tactic/model_converter.h index 00c50a15f..e2bc3cf83 100644 --- a/src/tactic/model_converter.h +++ b/src/tactic/model_converter.h @@ -23,6 +23,8 @@ Notes: #include"converter.h" #include"ref.h" +class labels_vec : public svector {}; + class model_converter : public converter { public: virtual void operator()(model_ref & m) {} // TODO: delete @@ -32,6 +34,8 @@ public: SASSERT(goal_idx == 0); operator()(m); } + + virtual void operator()(labels_vec & r, unsigned goal_idx) {} virtual model_converter * translate(ast_translation & translator) = 0; }; @@ -49,6 +53,8 @@ model_converter * concat(model_converter * mc1, unsigned num, model_converter * model_converter * model2model_converter(model * m); +model_converter * model_and_labels2model_converter(model * m, buffer &r); + void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m); void apply(model_converter_ref & mc, model_ref & m, unsigned gidx); diff --git a/src/tactic/nlsat_smt/nl_purify_tactic.cpp b/src/tactic/nlsat_smt/nl_purify_tactic.cpp index 030d0bd8c..46b5a51b0 100644 --- a/src/tactic/nlsat_smt/nl_purify_tactic.cpp +++ b/src/tactic/nlsat_smt/nl_purify_tactic.cpp @@ -59,6 +59,7 @@ Revision History: #include "model_smt2_pp.h" #include "expr_safe_replace.h" #include "ast_util.h" +#include "solver2tactic.h" class nl_purify_tactic : public tactic { @@ -154,8 +155,7 @@ public: void mk_interface_bool(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref& pr) { expr_ref old_pred(m.mk_app(f, num, args), m); - polarity_t pol; - VERIFY(m_polarities.find(old_pred, pol)); + polarity_t pol = m_polarities.find(old_pred); result = m.mk_fresh_const(0, m.mk_bool_sort()); m_polarities.insert(result, pol); m_new_preds.push_back(to_app(result)); diff --git a/src/tactic/portfolio/bounded_int2bv_solver.cpp b/src/tactic/portfolio/bounded_int2bv_solver.cpp new file mode 100644 index 000000000..53c20b253 --- /dev/null +++ b/src/tactic/portfolio/bounded_int2bv_solver.cpp @@ -0,0 +1,311 @@ +/*++ +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 "bounded_int2bv_solver.h" +#include "solver_na2as.h" +#include "tactic.h" +#include "pb2bv_rewriter.h" +#include "filter_model_converter.h" +#include "extension_model_converter.h" +#include "ast_pp.h" +#include "model_smt2_pp.h" +#include "bound_manager.h" +#include "bv2int_rewriter.h" +#include "expr_safe_replace.h" +#include "bv_decl_plugin.h" +#include "arith_decl_plugin.h" + +class bounded_int2bv_solver : public solver_na2as { + ast_manager& m; + params_ref m_params; + bv_util m_bv; + arith_util m_arith; + expr_ref_vector m_assertions; + ref m_solver; + ptr_vector m_bounds; + func_decl_ref_vector m_bv_fns; + func_decl_ref_vector m_int_fns; + unsigned_vector m_bv_fns_lim; + obj_map m_int2bv; + obj_map m_bv2int; + obj_map m_bv2offset; + bv2int_rewriter_ctx m_rewriter_ctx; + bv2int_rewriter_star m_rewriter; + +public: + + bounded_int2bv_solver(ast_manager& m, params_ref const& p, solver* s): + solver_na2as(m), + m(m), + m_params(p), + m_bv(m), + m_arith(m), + m_assertions(m), + m_solver(s), + m_bv_fns(m), + m_int_fns(m), + m_rewriter_ctx(m, p), + m_rewriter(m, m_rewriter_ctx) + { + m_bounds.push_back(alloc(bound_manager, m)); + } + + virtual ~bounded_int2bv_solver() { + while (!m_bounds.empty()) { + dealloc(m_bounds.back()); + m_bounds.pop_back(); + } + } + + virtual solver* translate(ast_manager& m, params_ref const& p) { + return alloc(bounded_int2bv_solver, m, p, m_solver->translate(m, p)); + } + + virtual void assert_expr(expr * t) { + m_assertions.push_back(t); + } + + virtual void push_core() { + flush_assertions(); + m_solver->push(); + m_bv_fns_lim.push_back(m_bv_fns.size()); + m_bounds.push_back(alloc(bound_manager, m)); + } + + virtual void pop_core(unsigned n) { + 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; + } + } + + virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + flush_assertions(); + return m_solver->check_sat(num_assumptions, assumptions); + } + + virtual void updt_params(params_ref const & p) { m_solver->updt_params(p); } + virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } + virtual void set_produce_models(bool f) { m_solver->set_produce_models(f); } + virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } + virtual void collect_statistics(statistics & st) const { m_solver->collect_statistics(st); } + virtual void get_unsat_core(ptr_vector & r) { m_solver->get_unsat_core(r); } + virtual void get_model(model_ref & mdl) { + m_solver->get_model(mdl); + if (mdl) { + extend_model(mdl); + filter_model(mdl); + } + } + virtual proof * get_proof() { return m_solver->get_proof(); } + virtual std::string reason_unknown() const { return m_solver->reason_unknown(); } + virtual void set_reason_unknown(char const* msg) { m_solver->set_reason_unknown(msg); } + virtual void get_labels(svector & r) { m_solver->get_labels(r); } + virtual ast_manager& get_manager() const { return m; } + virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_solver->find_mutexes(vars, mutexes); } + virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { + 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, *b, *u, *v; + 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 filter_model(model_ref& mdl) const { + if (m_bv_fns.empty()) { + return; + } + filter_model_converter filter(m); + for (unsigned i = 0; i < m_bv_fns.size(); ++i) { + filter.insert(m_bv_fns[i]); + } + filter(mdl, 0); + } + + void extend_model(model_ref& mdl) { + extension_model_converter ext(m); + obj_map::iterator it = m_int2bv.begin(), end = m_int2bv.end(); + for (; it != end; ++it) { + rational offset; + VERIFY (m_bv2offset.find(it->m_value, offset)); + expr_ref value(m_bv.mk_bv2int(m.mk_const(it->m_value)), m); + if (!offset.is_zero()) { + value = m_arith.mk_add(value, m_arith.mk_numeral(offset, true)); + } + TRACE("int2bv", tout << mk_pp(it->m_key, m) << " " << value << "\n";); + ext.insert(it->m_key, value); + } + ext(mdl, 0); + } + + void accumulate_sub(expr_safe_replace& sub) { + 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) { + bound_manager::iterator it = bm.begin(), end = bm.end(); + for (; it != end; ++it) { + expr* e = *it; + rational lo, hi; + bool s1, s2; + 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 { + IF_VERBOSE(1, + verbose_stream() << "unprocessed entry: " << mk_pp(e, m) << "\n"; + if (bm.has_lower(e, lo, s1)) { + verbose_stream() << "lower: " << lo << " " << s1 << "\n"; + } + if (bm.has_upper(e, hi, s2)) { + verbose_stream() << "upper: " << hi << " " << s2 << "\n"; + }); + } + } + } + + unsigned get_num_bits(rational const& k) { + 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() { + bound_manager& bm = *m_bounds.back(); + for (unsigned i = 0; i < m_assertions.size(); ++i) { + bm(m_assertions[i].get()); + } + 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 (unsigned i = 0; i < m_assertions.size(); ++i) { + sub(m_assertions[i].get(), 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(); + } +}; + +solver * mk_bounded_int2bv_solver(ast_manager & m, params_ref const & p, solver* s) { + return alloc(bounded_int2bv_solver, m, p, s); +} diff --git a/src/tactic/portfolio/bounded_int2bv_solver.h b/src/tactic/portfolio/bounded_int2bv_solver.h new file mode 100644 index 000000000..5fcf2cd65 --- /dev/null +++ b/src/tactic/portfolio/bounded_int2bv_solver.h @@ -0,0 +1,29 @@ +/*++ +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.h" +#include"params.h" + +class solver; + +solver * mk_bounded_int2bv_solver(ast_manager & m, params_ref const & p, solver* s); + +#endif diff --git a/src/tactic/portfolio/enum2bv_solver.cpp b/src/tactic/portfolio/enum2bv_solver.cpp new file mode 100644 index 000000000..f3288d8d6 --- /dev/null +++ b/src/tactic/portfolio/enum2bv_solver.cpp @@ -0,0 +1,170 @@ +/*++ +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 "solver_na2as.h" +#include "tactic.h" +#include "bv_decl_plugin.h" +#include "datatype_decl_plugin.h" +#include "enum2bv_rewriter.h" +#include "extension_model_converter.h" +#include "filter_model_converter.h" +#include "ast_pp.h" +#include "model_smt2_pp.h" +#include "enum2bv_solver.h" + +class enum2bv_solver : public solver_na2as { + ast_manager& m; + params_ref m_params; + ref m_solver; + enum2bv_rewriter m_rewriter; + +public: + + enum2bv_solver(ast_manager& m, params_ref const& p, solver* s): + solver_na2as(m), + m(m), + m_params(p), + m_solver(s), + m_rewriter(m, p) + { + } + + virtual ~enum2bv_solver() {} + + virtual solver* translate(ast_manager& m, params_ref const& p) { + return alloc(enum2bv_solver, m, p, m_solver->translate(m, p)); + } + + virtual void assert_expr(expr * t) { + 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); + } + + virtual void push_core() { + m_rewriter.push(); + m_solver->push(); + } + + virtual void pop_core(unsigned n) { + m_solver->pop(n); + m_rewriter.pop(n); + } + + virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + return m_solver->check_sat(num_assumptions, assumptions); + } + + virtual void updt_params(params_ref const & p) { m_solver->updt_params(p); } + virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } + virtual void set_produce_models(bool f) { m_solver->set_produce_models(f); } + virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } + virtual void collect_statistics(statistics & st) const { m_solver->collect_statistics(st); } + virtual void get_unsat_core(ptr_vector & r) { m_solver->get_unsat_core(r); } + virtual void get_model(model_ref & mdl) { + m_solver->get_model(mdl); + if (mdl) { + extend_model(mdl); + filter_model(mdl); + } + } + virtual proof * get_proof() { return m_solver->get_proof(); } + virtual std::string reason_unknown() const { return m_solver->reason_unknown(); } + virtual void set_reason_unknown(char const* msg) { m_solver->set_reason_unknown(msg); } + virtual void get_labels(svector & r) { m_solver->get_labels(r); } + virtual ast_manager& get_manager() const { return m; } + virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_solver->find_mutexes(vars, mutexes); } + + virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { + 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 (unsigned i = 0; i < vars.size(); ++i) { + expr_ref tmp(m.mk_eq(vars[i], vars[i]), 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 (unsigned i = 0; i < vars.size(); ++i) { + func_decl* f; + if (is_app(vars[i]) && is_uninterp_const(vars[i]) && m_rewriter.enum2bv().find(to_app(vars[i])->get_decl(), f)) { + bvars.push_back(m.mk_const(f)); + } + else { + bvars.push_back(vars[i]); + } + } + 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, *b, *u, *v; + 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()); + 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 filter_model(model_ref& mdl) { + filter_model_converter filter(m); + obj_map::iterator it = m_rewriter.enum2bv().begin(), end = m_rewriter.enum2bv().end(); + for (; it != end; ++it) { + filter.insert(it->m_value); + } + filter(mdl, 0); + } + + void extend_model(model_ref& mdl) { + extension_model_converter ext(m); + obj_map::iterator it = m_rewriter.enum2def().begin(), end = m_rewriter.enum2def().end(); + for (; it != end; ++it) { + ext.insert(it->m_key, it->m_value); + + } + ext(mdl, 0); + } + +}; + +solver * mk_enum2bv_solver(ast_manager & m, params_ref const & p, solver* s) { + return alloc(enum2bv_solver, m, p, s); +} diff --git a/src/tactic/portfolio/enum2bv_solver.h b/src/tactic/portfolio/enum2bv_solver.h new file mode 100644 index 000000000..b113c6747 --- /dev/null +++ b/src/tactic/portfolio/enum2bv_solver.h @@ -0,0 +1,29 @@ +/*++ +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.h" +#include"params.h" + +class solver; + +solver * mk_enum2bv_solver(ast_manager & m, params_ref const & p, solver* s); + +#endif diff --git a/src/tactic/portfolio/fd_solver.cpp b/src/tactic/portfolio/fd_solver.cpp new file mode 100644 index 000000000..a534337bc --- /dev/null +++ b/src/tactic/portfolio/fd_solver.cpp @@ -0,0 +1,33 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + fd_solver.cpp + +Abstract: + + Finite domain solver. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-10-17 + +Notes: + +--*/ + +#include "fd_solver.h" +#include "tactic.h" +#include "inc_sat_solver.h" +#include "enum2bv_solver.h" +#include "pb2bv_solver.h" +#include "bounded_int2bv_solver.h" + +solver * mk_fd_solver(ast_manager & m, params_ref const & p) { + solver* s = mk_inc_sat_solver(m, p); + s = mk_enum2bv_solver(m, p, s); + s = mk_pb2bv_solver(m, p, s); + s = mk_bounded_int2bv_solver(m, p, s); + return s; +} diff --git a/src/tactic/portfolio/fd_solver.h b/src/tactic/portfolio/fd_solver.h new file mode 100644 index 000000000..51abb087f --- /dev/null +++ b/src/tactic/portfolio/fd_solver.h @@ -0,0 +1,29 @@ +/*++ +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.h" +#include"params.h" + +class solver; + +solver * mk_fd_solver(ast_manager & m, params_ref const & p); + +#endif diff --git a/src/tactic/portfolio/pb2bv_solver.cpp b/src/tactic/portfolio/pb2bv_solver.cpp new file mode 100644 index 000000000..bfd533e8a --- /dev/null +++ b/src/tactic/portfolio/pb2bv_solver.cpp @@ -0,0 +1,127 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + pb2bv_solver.cpp + +Abstract: + + +Author: + + Nikolaj Bjorner (nbjorner) 2016-10-23 + +Notes: + +--*/ + +#include "pb2bv_solver.h" +#include "solver_na2as.h" +#include "tactic.h" +#include "pb2bv_rewriter.h" +#include "filter_model_converter.h" +#include "ast_pp.h" +#include "model_smt2_pp.h" + +class pb2bv_solver : public solver_na2as { + ast_manager& m; + params_ref m_params; + expr_ref_vector m_assertions; + ref m_solver; + pb2bv_rewriter m_rewriter; + +public: + + pb2bv_solver(ast_manager& m, params_ref const& p, solver* s): + solver_na2as(m), + m(m), + m_params(p), + m_assertions(m), + m_solver(s), + m_rewriter(m, p) + { + } + + virtual ~pb2bv_solver() {} + + virtual solver* translate(ast_manager& m, params_ref const& p) { + return alloc(pb2bv_solver, m, p, m_solver->translate(m, p)); + } + + virtual void assert_expr(expr * t) { + m_assertions.push_back(t); + } + + virtual void push_core() { + flush_assertions(); + m_rewriter.push(); + m_solver->push(); + } + + virtual void pop_core(unsigned n) { + m_assertions.reset(); + m_solver->pop(n); + m_rewriter.pop(n); + } + + virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + flush_assertions(); + return m_solver->check_sat(num_assumptions, assumptions); + } + + virtual void updt_params(params_ref const & p) { m_solver->updt_params(p); } + virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } + virtual void set_produce_models(bool f) { m_solver->set_produce_models(f); } + virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } + virtual void collect_statistics(statistics & st) const { + m_rewriter.collect_statistics(st); + m_solver->collect_statistics(st); + } + virtual void get_unsat_core(ptr_vector & r) { m_solver->get_unsat_core(r); } + virtual void get_model(model_ref & mdl) { + m_solver->get_model(mdl); + if (mdl) { + filter_model(mdl); + } + } + virtual proof * get_proof() { return m_solver->get_proof(); } + virtual std::string reason_unknown() const { return m_solver->reason_unknown(); } + virtual void set_reason_unknown(char const* msg) { m_solver->set_reason_unknown(msg); } + virtual void get_labels(svector & r) { m_solver->get_labels(r); } + virtual ast_manager& get_manager() const { return m; } + virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_solver->find_mutexes(vars, mutexes); } + virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { + flush_assertions(); + return m_solver->get_consequences(asms, vars, consequences); } + + void filter_model(model_ref& mdl) { + if (m_rewriter.fresh_constants().empty()) { + return; + } + filter_model_converter filter(m); + func_decl_ref_vector const& fns = m_rewriter.fresh_constants(); + for (unsigned i = 0; i < fns.size(); ++i) { + filter.insert(fns[i]); + } + filter(mdl, 0); + } + +private: + void flush_assertions() { + proof_ref proof(m); + expr_ref fml(m); + expr_ref_vector fmls(m); + for (unsigned i = 0; i < m_assertions.size(); ++i) { + m_rewriter(m_assertions[i].get(), 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); +} diff --git a/src/tactic/portfolio/pb2bv_solver.h b/src/tactic/portfolio/pb2bv_solver.h new file mode 100644 index 000000000..e861e769b --- /dev/null +++ b/src/tactic/portfolio/pb2bv_solver.h @@ -0,0 +1,29 @@ +/*++ +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.h" +#include"params.h" + +class solver; + +solver * mk_pb2bv_solver(ast_manager & m, params_ref const & p, solver* s); + +#endif diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index f9334bcb2..a4a579ddd 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -38,7 +38,9 @@ Notes: #include"horn_tactic.h" #include"smt_solver.h" #include"inc_sat_solver.h" +#include"fd_solver.h" #include"bv_rewriter.h" +#include"solver2tactic.h" tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const & logic) { @@ -88,17 +90,28 @@ tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const return mk_qffpbv_tactic(m, p); else if (logic=="HORN") return mk_horn_tactic(m, p); + else if (logic == "QF_FD") + return mk_solver2tactic(mk_fd_solver(m, p)); //else if (logic=="QF_UFNRA") // return mk_qfufnra_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) { + if (logic == "QF_FD") + return mk_fd_solver(m, p); + return 0; +} + static solver* mk_solver_for_logic(ast_manager & m, params_ref const & p, symbol const& logic) { bv_rewriter rw(m); - if (logic == "QF_BV" && rw.hi_div0()) - return mk_inc_sat_solver(m, p); - return mk_smt_solver(m, p, logic); + 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 { @@ -113,10 +126,11 @@ public: l = m_logic; else l = logic; + solver* s = mk_special_solver_for_logic(m, p, l); + if (s) return s; tactic * t = mk_tactic_for_logic(m, p, l); return mk_combined_solver(mk_tactic2solver(m, t, p, proofs_enabled, models_enabled, unsat_core_enabled, l), mk_solver_for_logic(m, p, l), - //mk_smt_solver(m, p, l), p); } }; diff --git a/src/tactic/probe.cpp b/src/tactic/probe.cpp index ecc27eccf..677438cdf 100644 --- a/src/tactic/probe.cpp +++ b/src/tactic/probe.cpp @@ -287,14 +287,14 @@ struct is_non_qfbv_predicate { 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 (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(); diff --git a/src/tactic/probe.h b/src/tactic/probe.h index a4754f8ed..3f7229d36 100644 --- a/src/tactic/probe.h +++ b/src/tactic/probe.h @@ -62,7 +62,7 @@ probe * mk_depth_probe(); probe * mk_size_probe(); /* - ADD_PROBE("memory", "ammount of used memory in megabytes.", "mk_memory_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()") */ diff --git a/src/tactic/sine_filter.cpp b/src/tactic/sine_filter.cpp new file mode 100644 index 000000000..f2d6a9653 --- /dev/null +++ b/src/tactic/sine_filter.cpp @@ -0,0 +1,245 @@ +/*++ +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 "sine_filter.h" +#include "tactical.h" +#include "filter_model_converter.h" +#include "datatype_decl_plugin.h" +#include "rewriter_def.h" +#include "filter_model_converter.h" +#include "extension_model_converter.h" +#include "var_subst.h" +#include "ast_util.h" +#include "obj_pair_hashtable.h" +#include "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) {} + + virtual tactic * translate(ast_manager & m) { + return alloc(sine_tactic, m, m_params); + } + + virtual void updt_params(params_ref const & p) { + } + + virtual void collect_param_descrs(param_descrs & r) { + } + + virtual void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + mc = 0; pc = 0; core = 0; + + 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), 0, 0); + } + g->inc_depth(); + g->updt_prec(goal::OVER); + result.push_back(g.get()); + TRACE("sine", result[0]->display(tout);); + SASSERT(g->is_well_sorted()); + filter_model_converter * fmc = alloc(filter_model_converter, m); + mc = fmc; + } + + virtual void cleanup() { + } + +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)) { + app *a = to_app(curr); + if (is_uninterp(a)) { + 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 (obj_hashtable::iterator constit = consts.begin(), constend = consts.end(); constit != constend; constit++) { + TRACE("sine", tout << *constit; tout << "\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; + for (unsigned i = 0; i < g->size(); i++) { + stack.push_back(work_item(g->form(i), g->form(i))); + } + t_work_item curr; + while (!stack.empty()) { + curr = stack.back(); + stack.pop_back(); + if (is_app(curr.first)) { + app *a = to_app(curr.first); + if (is_uninterp(a)) { + func_decl *f = a->get_decl(); + if (!consts.contains(f)) { + consts.insert(f); + if (const2quantifier.contains(f)) { + for (obj_pair_hashtable::iterator it = const2quantifier[f].begin(), end = const2quantifier[f].end(); it != end; it++) { + stack.push_back(*it); + } + 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 (q->is_forall()) { + 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 (q->is_exists()) { + 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 (obj_hashtable::iterator constit = exp2const[visiting].begin(), constend = exp2const[visiting].end(); constit != constend; constit++) { + for (obj_hashtable::iterator exprit = const2exp[*constit].begin(), exprend = const2exp[*constit].end(); exprit != exprend; exprit++) { + if (!visited.contains(*exprit)) { + to_visit.push_back(*exprit); + } + } + } + } + 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); +} diff --git a/src/tactic/sine_filter.h b/src/tactic/sine_filter.h new file mode 100644 index 000000000..769ef474f --- /dev/null +++ b/src/tactic/sine_filter.h @@ -0,0 +1,32 @@ +/*++ +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"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 diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index 4e49e0d76..f4ef300e4 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -94,13 +94,13 @@ public: }; -tactic * mk_sls_tactic(ast_manager & m, params_ref const & p) { +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))); } -tactic * mk_preamble(ast_manager & m, params_ref const & 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); diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h index 39d76bee6..dfdbd4685 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/tactic/sls/sls_tracker.h @@ -464,7 +464,7 @@ public: if (!m_weights.contains(e)) m_weights.insert(e, m_paws_init); - // positive/negative occurences used for early pruning + // positive/negative occurrences used for early pruning setup_occs(as[i]); } diff --git a/src/tactic/smtlogics/qfbv_tactic.cpp b/src/tactic/smtlogics/qfbv_tactic.cpp index 7cc875fba..918e0fc6d 100644 --- a/src/tactic/smtlogics/qfbv_tactic.cpp +++ b/src/tactic/smtlogics/qfbv_tactic.cpp @@ -32,7 +32,7 @@ Notes: #define MEMLIMIT 300 -tactic * mk_qfbv_preamble(ast_manager& m, params_ref const& p) { +static tactic * mk_qfbv_preamble(ast_manager& m, params_ref const& p) { params_ref solve_eq_p; // conservative guassian elimination. @@ -80,7 +80,7 @@ static tactic * main_p(tactic* t) { } -tactic * mk_qfbv_tactic(ast_manager& m, params_ref const & p, tactic* sat, tactic* smt) { +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); diff --git a/src/tactic/smtlogics/qfnia_tactic.cpp b/src/tactic/smtlogics/qfnia_tactic.cpp index 950291221..5a71281fb 100644 --- a/src/tactic/smtlogics/qfnia_tactic.cpp +++ b/src/tactic/smtlogics/qfnia_tactic.cpp @@ -29,7 +29,7 @@ Notes: #include"ctx_simplify_tactic.h" #include"cofactor_term_ite_tactic.h" -tactic * mk_qfnia_bv_solver(ast_manager & m, params_ref const & p_ref) { +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); @@ -51,7 +51,7 @@ tactic * mk_qfnia_bv_solver(ast_manager & m, params_ref const & p_ref) { return r; } -tactic * mk_qfnia_premable(ast_manager & m, params_ref const & p_ref) { +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); @@ -77,7 +77,7 @@ tactic * mk_qfnia_premable(ast_manager & m, params_ref const & p_ref) { using_params(mk_simplify_tactic(m), simp_p)); } -tactic * mk_qfnia_sat_solver(ast_manager & m, params_ref const & 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); diff --git a/src/tactic/smtlogics/qfuf_tactic.cpp b/src/tactic/smtlogics/qfuf_tactic.cpp index 567325f6a..7648e20fe 100644 --- a/src/tactic/smtlogics/qfuf_tactic.cpp +++ b/src/tactic/smtlogics/qfuf_tactic.cpp @@ -33,7 +33,7 @@ tactic * mk_qfuf_tactic(ast_manager & m, params_ref const & p) { mk_propagate_values_tactic(m, p), mk_solve_eqs_tactic(m, p), using_params(mk_simplify_tactic(m, p), s2_p), - mk_symmetry_reduce_tactic(m, p), + if_no_proofs(if_no_unsat_cores(mk_symmetry_reduce_tactic(m, p))), mk_smt_tactic(p)); } diff --git a/src/tactic/smtlogics/qfufbv_tactic.cpp b/src/tactic/smtlogics/qfufbv_tactic.cpp index 3ee97308a..e0e1a60e4 100644 --- a/src/tactic/smtlogics/qfufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfufbv_tactic.cpp @@ -39,6 +39,7 @@ Notes: #include"qfaufbv_tactic.h" #include"qfbv_tactic.h" #include"tactic2solver.h" +#include"bv_bound_chk_tactic.h" /////////////// class qfufbv_ackr_tactic : public tactic { @@ -149,6 +150,7 @@ static tactic * mk_qfufbv_preamble1(ast_manager & m, params_ref const & p) { 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), diff --git a/src/tactic/smtlogics/quant_tactics.cpp b/src/tactic/smtlogics/quant_tactics.cpp index 045b0ec87..044511c33 100644 --- a/src/tactic/smtlogics/quant_tactics.cpp +++ b/src/tactic/smtlogics/quant_tactics.cpp @@ -104,19 +104,12 @@ tactic * mk_aufnira_tactic(ast_manager & m, params_ref const & p) { } tactic * mk_lra_tactic(ast_manager & m, params_ref const & p) { -#if 0 - tactic * st = and_then(mk_quant_preprocessor(m), - or_else(try_for(mk_smt_tactic(), 100), - try_for(qe::mk_sat_tactic(m), 1000), - try_for(mk_smt_tactic(), 1000), - and_then(mk_qe_tactic(m), mk_smt_tactic()) - )); -#else tactic * st = and_then(mk_quant_preprocessor(m), mk_qe_lite_tactic(m, p), - or_else(mk_qsat_tactic(m, p), - and_then(mk_qe_tactic(m), mk_smt_tactic()))); -#endif + cond(mk_has_quantifier_probe(), + or_else(mk_qsat_tactic(m, p), + and_then(mk_qe_tactic(m), mk_smt_tactic())), + mk_smt_tactic())); st->updt_params(p); return st; } diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index 92e916d80..67fce1486 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -126,7 +126,7 @@ tactic * mk_report_verbose_tactic(char const * msg, unsigned lvl) { class trace_tactic : public skip_tactic { char const * m_tag; public: - trace_tactic(char const * tag):m_tag(tag) {} + trace_tactic(char const * tag): m_tag(tag) {} virtual void operator()(goal_ref const & in, goal_ref_buffer & result, @@ -134,6 +134,7 @@ public: proof_converter_ref & pc, expr_dependency_ref & core) { TRACE(m_tag, in->display(tout);); + (void)m_tag; skip_tactic::operator()(in, result, mc, pc, core); } }; @@ -174,7 +175,7 @@ void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result, model_conve } } -lbool check_sat(tactic & t, goal_ref & g, model_ref & md, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown) { +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 proofs_enabled = g->proofs_enabled(); bool cores_enabled = g->unsat_core_enabled(); @@ -199,6 +200,8 @@ lbool check_sat(tactic & t, goal_ref & g, model_ref & md, proof_ref & pr, expr_d if (is_decided_sat(r)) { if (models_enabled) { + if (mc) + (*mc)(labels, 0); model_converter2model(m, mc.get(), md); if (!md) { // create empty model. @@ -215,7 +218,11 @@ lbool check_sat(tactic & t, goal_ref & g, model_ref & md, proof_ref & pr, expr_d return l_false; } else { - if (models_enabled) model_converter2model(m, mc.get(), md); + if (models_enabled) { + model_converter2model(m, mc.get(), md); + if (mc) + (*mc)(labels, 0); + } reason_unknown = "incomplete"; return l_undef; } diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index a9e50ff10..645b53681 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -153,7 +153,7 @@ public: #define MK_SIMPLE_TACTIC_FACTORY(NAME, ST) MK_TACTIC_FACTORY(NAME, return ST;) void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); -lbool check_sat(tactic & t, goal_ref & g, model_ref & md, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown); +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); diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index 0d276ba72..33f8325fb 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -132,8 +132,7 @@ public: if (r1_size == 1) { if (r1[0]->is_decided()) { result.push_back(r1[0]); - if (models_enabled) mc = mc1; - SASSERT(!pc); SASSERT(!core); + if (models_enabled) mc = mc1; return; } goal_ref r1_0 = r1[0]; @@ -462,13 +461,6 @@ enum par_exception_kind { class par_tactical : public or_else_tactical { - 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; } - }; public: par_tactical(unsigned num, tactic * const * ts):or_else_tactical(num, ts) {} @@ -964,7 +956,7 @@ class repeat_tactical : public unary_tactical { pc = 0; core = 0; { - goal orig_in(in->m()); + goal orig_in(in->m(), proofs_enabled, models_enabled, cores_enabled); orig_in.copy_from(*(in.get())); m_t->operator()(in, r1, mc1, pc1, core1); if (is_equal(orig_in, *(in.get()))) { diff --git a/src/tactic/tactical.h b/src/tactic/tactical.h index 9aac3d42d..4b4274b0c 100644 --- a/src/tactic/tactical.h +++ b/src/tactic/tactical.h @@ -48,7 +48,7 @@ tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5 tactic * repeat(tactic * t, unsigned max = UINT_MAX); /** \brief Fails if \c t produeces more than \c threshold subgoals. - Otherwise, it behabes like \c t. + Otherwise, it behaves like \c t. */ tactic * fail_if_branching(tactic * t, unsigned threshold = 1); diff --git a/src/tactic/ufbv/ufbv_tactic.cpp b/src/tactic/ufbv/ufbv_tactic.cpp index 9d16d7f9a..19d41be68 100644 --- a/src/tactic/ufbv/ufbv_tactic.cpp +++ b/src/tactic/ufbv/ufbv_tactic.cpp @@ -31,32 +31,33 @@ Notes: #include"ufbv_tactic.h" -tactic * mk_der_fp_tactic(ast_manager & m, params_ref const & p) { +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))); } -tactic * mk_ufbv_preprocessor_tactic(ast_manager & m, params_ref const & 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(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))), - 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")); + 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) { diff --git a/src/tactic/ufbv/ufbv_tactic.h b/src/tactic/ufbv/ufbv_tactic.h index 2d5454de5..300fdd84a 100644 --- a/src/tactic/ufbv/ufbv_tactic.h +++ b/src/tactic/ufbv/ufbv_tactic.h @@ -23,8 +23,6 @@ Notes: class ast_manager; class tactic; -tactic * mk_ufbv_preprocessor_tactic(ast_manager & m, params_ref const & p = params_ref()); - tactic * mk_ufbv_tactic(ast_manager & m, params_ref const & p = params_ref()); /* diff --git a/src/test/fuzzing/expr_rand.cpp b/src/test/fuzzing/expr_rand.cpp index f56704e45..ccc4a9e5c 100644 --- a/src/test/fuzzing/expr_rand.cpp +++ b/src/test/fuzzing/expr_rand.cpp @@ -13,9 +13,6 @@ Copyright (c) 2015 Microsoft Corporation expr_rand::expr_rand(ast_manager& m): m_manager(m), - m_num_vars(0), - m_num_apps(0), - m_num_nodes(0), m_max_steps(10), m_funcs(m) {} diff --git a/src/test/fuzzing/expr_rand.h b/src/test/fuzzing/expr_rand.h index 971a3ec60..bbadd587e 100644 --- a/src/test/fuzzing/expr_rand.h +++ b/src/test/fuzzing/expr_rand.h @@ -24,9 +24,6 @@ Revision History: class expr_rand { ast_manager& m_manager; - unsigned m_num_vars; - unsigned m_num_apps; - unsigned m_num_nodes; unsigned m_max_steps; random_gen m_random; diff --git a/src/test/get_consequences.cpp b/src/test/get_consequences.cpp new file mode 100644 index 000000000..febff0151 --- /dev/null +++ b/src/test/get_consequences.cpp @@ -0,0 +1,115 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +--*/ + +#include "inc_sat_solver.h" +#include "bv_decl_plugin.h" +#include "datatype_decl_plugin.h" +#include "reg_decl_plugins.h" +#include "ast_pp.h" +#include "dt2bv_tactic.h" +#include "tactic.h" +#include "model_smt2_pp.h" +#include "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, 0); + constructor_decl* G = mk_constructor_decl(symbol("G"), symbol("is-G"), 0, 0); + constructor_decl* B = mk_constructor_decl(symbol("B"), symbol("is-B"), 0, 0); + constructor_decl* constrs[3] = { R, G, B }; + datatype_decl * enum_sort = mk_datatype_decl(symbol("RGB"), 3, constrs); + VERIFY(dt.mk_datatypes(1, &enum_sort, new_sorts)); + del_constructor_decls(3, constrs); + 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,0)); + 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,0)); + fd_solver->get_model(mr); + SASSERT(mr.get()); + model_smt2_pp(std::cout, m, *mr.get(), 0); + +} + +void tst_get_consequences() { + test1(); + test2(); +} diff --git a/src/test/main.cpp b/src/test/main.cpp index 8fc0a2de6..9239d0119 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -193,7 +193,6 @@ int main(int argc, char ** argv) { TST(polynomial); TST(upolynomial); TST(algebraic); - TST(polynomial_factorization); TST(prime_generator); TST(permutation); TST(nlsat); @@ -228,6 +227,8 @@ int main(int argc, char ** argv) { TST(pdr); TST_ARGV(ddnf); TST(model_evaluator); + TST(get_consequences); + TST(pb2bv); //TST_ARGV(hs); } diff --git a/src/test/model_based_opt.cpp b/src/test/model_based_opt.cpp index b5b8e2953..64b2df285 100644 --- a/src/test/model_based_opt.cpp +++ b/src/test/model_based_opt.cpp @@ -287,9 +287,76 @@ static void test7() { 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); + mbo.project(1, &y); + 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); + mbo.project(1, &y); + 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); + mbo.project(1, &y); + mbo.display(std::cout); + mbo.project(1, &x0); + mbo.display(std::cout); +} + // test with mix of upper and lower bounds void tst_model_based_opt() { + test10(); + return; check_random_ineqs(); test1(); test2(); @@ -298,5 +365,6 @@ void tst_model_based_opt() { test5(); test6(); test7(); - + test8(); + test9(); } diff --git a/src/test/mpff.cpp b/src/test/mpff.cpp index 35afcccf3..44930f2bd 100644 --- a/src/test/mpff.cpp +++ b/src/test/mpff.cpp @@ -207,7 +207,7 @@ static void tst_set64(unsigned N, unsigned prec) { mpff_manager fm(prec); scoped_mpff a(fm); - fm.set(a, INT64_MAX); + fm.set(a, static_cast(INT64_MAX)); SASSERT(fm.is_int64(a)); SASSERT(fm.is_uint64(a)); fm.inc(a); @@ -221,7 +221,7 @@ static void tst_set64(unsigned N, unsigned prec) { SASSERT(fm.is_int64(a)); SASSERT(fm.is_uint64(a)); - fm.set(a, INT64_MIN); + fm.set(a, static_cast(INT64_MIN)); SASSERT(fm.is_int64(a)); SASSERT(!fm.is_uint64(a)); fm.dec(a); @@ -235,7 +235,7 @@ static void tst_set64(unsigned N, unsigned prec) { SASSERT(fm.is_int64(a)); SASSERT(!fm.is_uint64(a)); - fm.set(a, UINT64_MAX); + fm.set(a, static_cast(UINT64_MAX)); SASSERT(fm.is_uint64(a)); SASSERT(!fm.is_int64(a)); fm.inc(a); @@ -600,7 +600,7 @@ static void tst_div(unsigned prec) { scoped_mpff a(m), b(m), c(m); m.round_to_plus_inf(); m.set(a, 1); - m.set(b, UINT64_MAX); + 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"; diff --git a/src/test/mpz.cpp b/src/test/mpz.cpp index d7944ae95..ee7cf39f1 100644 --- a/src/test/mpz.cpp +++ b/src/test/mpz.cpp @@ -280,7 +280,7 @@ void tst_int_min_bug() { mpz big; mpz expected; mpz r; - m.set(big, UINT64_MAX); + 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"; diff --git a/src/test/pb2bv.cpp b/src/test/pb2bv.cpp new file mode 100644 index 000000000..c114997c5 --- /dev/null +++ b/src/test/pb2bv.cpp @@ -0,0 +1,195 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +--*/ + +#include "trace.h" +#include "vector.h" +#include "ast.h" +#include "ast_pp.h" +#include "statistics.h" +#include "reg_decl_plugins.h" +#include "pb2bv_rewriter.h" +#include "smt_kernel.h" +#include "model_smt2_pp.h" +#include "smt_params.h" +#include "ast_util.h" +#include "pb_decl_plugin.h" +#include "th_rewriter.h" +#include "fd_solver.h" +#include "solver.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(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(fml1, result1, proof); + rw.flush_side_constraints(lemmas); + std::cout << lemmas << "\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); + SASSERT(m.is_true(result2) || m.is_false(result2)); + lbool res = solver.check(); + SASSERT(res == l_true); + solver.assert_expr(m.is_true(result2) ? m.mk_not(result1) : result1.get()); + res = solver.check(); + SASSERT(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); + SASSERT(m.is_true(result2) || m.is_false(result2)); + lbool res = slv->check_sat(0,0); + SASSERT(res == l_true); + slv->assert_expr(m.is_true(result2) ? m.mk_not(result1) : result1.get()); + res = slv->check_sat(0,0); + SASSERT(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); + } + } +} + +void tst_pb2bv() { + test1(); + test2(); + test3(); +} + diff --git a/src/test/polynomial.cpp b/src/test/polynomial.cpp index 56eb61a11..03eb321cd 100644 --- a/src/test/polynomial.cpp +++ b/src/test/polynomial.cpp @@ -18,7 +18,6 @@ Notes: --*/ #if !defined(__clang__) #include"polynomial.h" -#include"polynomial_factorization.h" #include"polynomial_var2value.h" #include"polynomial_cache.h" #include"linear_eq_solver.h" diff --git a/src/test/polynomial_factorization.cpp b/src/test/polynomial_factorization.cpp deleted file mode 100644 index 361ca4630..000000000 --- a/src/test/polynomial_factorization.cpp +++ /dev/null @@ -1,746 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - polynomial_factorization.cpp - -Abstract: - - Testing of factorization. - -Author: - - Dejan (t-dejanj) 2011-11-29 - -Notes: - ---*/ -#include"upolynomial_factorization_int.h" -#include"timeit.h" -#include"polynomial.h" -#include"rlimit.h" -#if 0 -#include"polynomial_factorization.h" -#endif - -using std::cout; -using std::endl; - -// some prime numbers -unsigned primes[] = { - 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 -}; - -// [i,l]: how many factors the Knuth example has over p_i, when i = 0 it's Z, p_1 = 2, for l=0 distinct, for l = 1 total -unsigned knuth_factors[2][11] = { - // x^8 + x^6 + 10*x^4 + 10*x^3 + 8*x^2 + 2*x + 8 - {2, 2, 3, 3, 2, 3, 1, 4, 3, 1, 1}, - {8, 2, 3, 3, 2, 3, 1, 4, 3, 1, 1}, -}; - -// [k,l,i]: how many factors the S_k has over p_i, when i = 0 it's Z, p_1 = 2, for l=0 distinct, for l = 1 total -unsigned swinnerton_dyer_factors[5][2][11] = { - // S1 = (x^2) - 2 - { - // 2, 3, 5, 7,11,13,17,19,23,29, Z - {1, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1}, - {2, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1} - }, - // S2 = (x^4) - 10*(x^2) + 1 - { - {1, 1, 2, 2, 2, 2, 2, 2, 4, 2, 1}, - {4, 2, 2, 2, 2, 2, 2, 2, 4, 2, 1} - }, - // S3 = (x^8) - 40*(x^6) + 352*(x^4) - 960*(x^2) + 576 - { - {1, 2, 2, 4, 4, 4, 4, 4, 4, 4, 1}, - {8, 6, 4, 4, 4, 4, 4, 4, 4, 4, 1} - }, - // S4 = (x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225 - { - {1, 4, 3, 4, 8, 8, 8, 8, 8, 8, 1}, - {16, 12, 10, 8, 8, 8, 8, 8, 8, 8, 1} - }, - // SA = S1*S2*S3*S4 - { - //p = 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, Z - { 2, 6, 3, 6, 15, 11, 16, 15, 18, 15, 1}, - {30, 21, 17, 16, 15, 15, 16, 15, 18, 15, 1} - } -}; - -int random_polynomial[20][2][11] = { - { - // 3*x^10 + 2*x^9 + 4*x^8 + 4*x^7 + 4*x^6 + x^5 + 3*x^2 + 3*x - { 4, 3, 4, 4, 3, 4, 4, 4, 3, 4, 2 }, - { 7, 7, 4, 4, 3, 4, 4, 4, 3, 4, 2 }, - }, - { - // 4*x^9 + 4*x^8 + x^7 + x^6 + 2*x^5 + 3*x^4 + 4*x^2 + 4*x - { 2, 2, 3, 3, 4, 2, 5, 3, 4, 2, 2 }, - { 5, 2, 3, 3, 4, 2, 5, 3, 5, 2, 2 }, - }, - { - // 3*x^10 + 4*x^9 + 3*x^8 + x^6 + 4*x^5 + 4*x^4 + x^2 - { 3, 2, 4, 4, 5, 3, 4, 2, 4, 5, 2 }, - { 6, 3, 5, 5, 6, 4, 5, 3, 5, 7, 3 }, - }, - { - // x^10 + 4*x^9 + x^8 + 3*x^7 + 3*x^4 + 3*x^3 + x^2 + 4*x - { 3, 4, 4, 3, 3, 3, 4, 4, 5, 3, 2 }, - { 8, 4, 4, 3, 3, 3, 4, 4, 5, 3, 2 }, - }, - { - // x^9 + 2*x^8 + 3*x^7 + x^6 + 2*x^5 + 4*x^4 + 3*x^2 - { 3, 3, 3, 3, 4, 4, 4, 3, 3, 4, 2 }, - { 5, 6, 4, 5, 5, 6, 5, 4, 4, 5, 3 }, - }, - { - // x^10 + x^9 + 4*x^7 + x^6 + 3*x^5 + x^4 + x^3 + x - { 3, 2, 3, 3, 3, 5, 3, 2, 4, 4, 2 }, - { 3, 2, 3, 3, 3, 5, 3, 2, 4, 4, 2 }, - }, - { - // 4*x^10 + 4*x^9 + x^8 + 2*x^7 + 3*x^6 + 4*x^5 + 3*x^4 + x^3 + 2*x^2 + 4*x - { 3, 3, 2, 5, 3, 4, 2, 4, 5, 5, 2 }, - { 5, 3, 2, 5, 3, 4, 2, 4, 5, 5, 2 }, - }, - { - // 3*x^10 + 4*x^9 + 3*x^8 + x^7 + x^6 + 2*x^5 + x^4 + 2*x^3 + 2*x^2 + x - { 3, 4, 6, 4, 4, 4, 4, 6, 6, 4, 3 }, - { 4, 4, 7, 4, 4, 4, 4, 6, 6, 4, 3 }, - }, - { - // 4*x^10 + x^9 + x^7 + 2*x^5 + 3*x^3 + x^2 + 4*x - { 3, 3, 3, 4, 4, 5, 4, 5, 2, 4, 2 }, - { 4, 4, 3, 4, 4, 5, 4, 5, 2, 4, 2 }, - }, - { - // x^10 + 3*x^9 + 3*x^8 + x^7 + 3*x^6 + 3*x^5 + 3*x^4 + x^2 + 3*x - { 2, 3, 4, 4, 3, 3, 4, 3, 3, 4, 2 }, - { 2, 4, 5, 4, 3, 3, 4, 3, 3, 4, 2 }, - }, - { - // x^10 + x^9 + 2*x^8 + x^7 + 4*x^6 + 2*x^5 + 3*x^4 + 4*x^3 + x^2 + 2*x - { 3, 4, 4, 3, 3, 3, 3, 4, 5, 3, 2 }, - { 4, 4, 4, 3, 3, 3, 3, 4, 5, 3, 2 }, - }, - { - // 3*x^9 + x^8 + 3*x^7 + 3*x^6 + x^5 + 2*x^4 + 4*x^3 + 4*x^2 + 3*x - { 4, 3, 3, 3, 5, 3, 6, 4, 2, 2, 2 }, - { 6, 4, 3, 3, 5, 3, 6, 4, 2, 2, 2 }, - }, - { - // 2*x^10 + 3*x^9 + 2*x^8 + 4*x^7 + x^6 + 3*x^5 + 2*x^3 + 3*x^2 + 2*x + 2 - { 3, 3, 3, 5, 4, 5, 6, 7, 4, 6, 3 }, - { 8, 4, 3, 7, 4, 5, 6, 7, 4, 7, 3 }, - }, - { - // 3*x^10 + x^9 + 4*x^8 + 2*x^7 + x^6 + 4*x^5 + x^4 + 3*x^3 + x + 2 - { 3, 3, 3, 2, 6, 4, 4, 4, 3, 3, 2 }, - { 3, 3, 3, 2, 6, 5, 4, 5, 3, 3, 2 }, - }, - { - // 4*x^10 + 2*x^9 + x^8 + x^6 + x^5 + 3*x^4 + 4*x^3 + x^2 + x - { 3, 4, 2, 4, 4, 4, 4, 2, 3, 3, 2 }, - { 6, 4, 2, 4, 4, 4, 4, 2, 3, 3, 2 }, - }, - { - // 4*x^10 + 2*x^7 + 4*x^6 + 2*x^3 + x - { 1, 3, 3, 3, 4, 4, 4, 3, 3, 2, 2 }, - { 1, 3, 3, 3, 4, 4, 4, 3, 3, 2, 2 }, - }, - { - // 4*x^10 + x^9 + x^8 + 4*x^7 + 4*x^4 + 2*x^2 + x + 4 - { 3, 4, 2, 5, 3, 6, 3, 6, 3, 3, 2 }, - { 3, 6, 2, 5, 3, 6, 3, 6, 3, 3, 2 }, - }, - { - // 3*x^10 + 2*x^8 + x^7 + x^6 + 3*x^4 + 3*x^3 + 4*x^2 + 3*x - { 4, 3, 4, 3, 3, 3, 2, 4, 4, 3, 2 }, - { 5, 4, 4, 3, 3, 3, 2, 4, 4, 3, 2 }, - }, - { - // x^10 + 2*x^9 + 2*x^6 + 4*x^3 + 4*x^2 - { 1, 2, 2, 3, 3, 4, 3, 3, 3, 3, 2 }, - { 10, 3, 3, 4, 4, 6, 4, 4, 4, 4, 3 }, - }, - { - // x^10 + 2*x^9 + 2*x^8 + 4*x^7 + 4*x^6 + x^5 + x^3 + x^2 + 3*x - { 2, 4, 2, 3, 3, 3, 5, 5, 6, 2, 2 }, - { 2, 5, 2, 3, 3, 3, 5, 5, 6, 2, 2 }, - } -}; - -#if 0 -static void tst_square_free_finite_1() { - polynomial::numeral_manager nm; - reslimit rl; polynomial::manager pm(rl, nm); - - // example from Knuth, p. 442 - polynomial_ref x(pm); - x = pm.mk_polynomial(pm.mk_var()); - - // polynomials \prod_{i < p} (x - i)^i - for (unsigned prime_i = 0; prime_i < 5; ++ prime_i) - { - int p = primes[prime_i]; - - // make the polynomial - polynomial_ref f(pm); - f = x - 1; - for (int i = 2; i < p; ++ i) { - f = f*((x + (-i))^i); - } - cout << "Factoring " << f << " into square-free over Z_" << p << endl; - - // convert to univariate over Z_p - upolynomial::zp_manager upm(nm); - upm.set_zp(p); - upolynomial::numeral_vector f_u; - upm.to_numeral_vector(f, f_u); - - cout << "Input: "; upm.display(cout, f_u); cout << endl; - - // factor it - upolynomial::zp_factors f_factors(upm); - cout << "Start: " << f_factors << endl; - - upolynomial::zp_square_free_factor(upm, f_u, f_factors); - - upolynomial::numeral_vector mult; - f_factors.multiply(mult); - cout << "Multiplied: "; upm.display(cout, mult); cout << endl; - - SASSERT(upm.eq(mult, f_u)); - - // remove the temps - upm.reset(f_u); - upm.reset(mult); - } -} - -static void tst_factor_finite_1() { - - polynomial::numeral_manager nm; - reslimit rl; polynomial::manager pm(rl, nm); - - // example from Knuth, p. 442 - polynomial_ref x(pm); - x = pm.mk_polynomial(pm.mk_var()); - polynomial_ref K(pm); - K = (x^8) + (x^6) + 10*(x^4) + 10*(x^3) + 8*(x^2) + 2*x + 8; - - // factor them for all the prime numbers - for (unsigned prime_i = 0; prime_i < sizeof(primes)/sizeof(unsigned); ++ prime_i) - { - // make the Z_p - unsigned prime = primes[prime_i]; - upolynomial::zp_manager upm(nm); - upm.set_zp(prime); - - // make the polynomial in Z_p - upolynomial::numeral_vector K_u; - upm.to_numeral_vector(K, K_u); - - cout << "Factoring " << K << "("; upm.display(cout, K_u); cout << ") in Z_" << prime << endl; - cout << "Expecting " << knuth_factors[0][prime_i] << " distinct factors, " << knuth_factors[1][prime_i] << " total" << endl; - - // factor it - upolynomial::zp_factors factors(upm); - /* bool factorized = */ upolynomial::zp_factor(upm, K_u, factors); - - // check the result - unsigned distinct = factors.distinct_factors(); - unsigned total = factors.total_factors(); - - cout << "Got " << factors << endl; - cout << "Thats " << distinct << " distinct factors, " << total << " total" << endl; - - SASSERT(knuth_factors[0][prime_i] == distinct); - SASSERT(knuth_factors[1][prime_i] == total); - - upolynomial::numeral_vector multiplied; - factors.multiply(multiplied); - SASSERT(upm.eq(K_u, multiplied)); - upm.reset(multiplied); - - // remove the temp - upm.reset(K_u); - } -} - -static void tst_factor_finite_2() { - - polynomial::numeral_manager nm; - reslimit rl; polynomial::manager pm(rl, nm); - - polynomial_ref x(pm); - x = pm.mk_polynomial(pm.mk_var()); - - // Swinnerton-Dyer polynomials (irreducible, modular factors of degree at most 2) - polynomial_ref S1 = (x^2) - 2; - polynomial_ref S2 = (x^4) - 10*(x^2) + 1; - polynomial_ref S3 = (x^8) - 40*(x^6) + 352*(x^4) - 960*(x^2) + 576; - polynomial_ref S4 = (x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225; - - vector S; - S.push_back(S1); - S.push_back(S2); - S.push_back(S3); - S.push_back(S4); - S.push_back(S1*S2*S3*S4); - - // factor all the S_i them for all the prime numbers - for (unsigned S_i = 0; S_i < S.size(); ++ S_i) { - for (unsigned prime_i = 0; prime_i < sizeof(primes)/sizeof(unsigned); ++ prime_i) { - unsigned prime = primes[prime_i]; - - upolynomial::zp_manager upm(nm); - upm.set_zp(prime); - - upolynomial::numeral_vector S_i_u; - upm.to_numeral_vector(S[S_i], S_i_u); - - cout << "Factoring "; upm.display(cout, S_i_u); cout << " over Z_" << prime << endl; - cout << "Expecting " << swinnerton_dyer_factors[S_i][0][prime_i] << " distinct factors, " << swinnerton_dyer_factors[S_i][1][prime_i] << " total" << endl; - - upolynomial::zp_factors factors(upm); - upolynomial::zp_factor(upm, S_i_u, factors); - - // check the result - unsigned distinct = factors.distinct_factors(); - unsigned total = factors.total_factors(); - - cout << "Got " << factors << endl; - cout << "Thats " << distinct << " distinct factors, " << total << " total" << endl; - - SASSERT(swinnerton_dyer_factors[S_i][0][prime_i] == distinct); - SASSERT(swinnerton_dyer_factors[S_i][1][prime_i] == total); - - upolynomial::numeral_vector multiplied; - factors.multiply(multiplied); - SASSERT(upm.eq(S_i_u, multiplied)); - upm.reset(multiplied); - - // remove the temp - upm.reset(S_i_u); - } - } -} - -static void tst_factor_finite_3() { - - polynomial::numeral_manager nm; - reslimit rl; polynomial::manager pm(rl, nm); - - polynomial_ref x(pm); - x = pm.mk_polynomial(pm.mk_var()); - - // random polynomials - vector random_p; - random_p.push_back( 3*(x^10) + 2*(x^9) + 4*(x^8) + 4*(x^7) + 4*(x^6) + 1*(x^5) + 3*(x^2) + 3*x + 0 ); - random_p.push_back( 4*(x^9) + 4*(x^8) + 1*(x^7) + 1*(x^6) + 2*(x^5) + 3*(x^4) + 4*(x^2) + 4*x + 0 ); - random_p.push_back( 3*(x^10) + 4*(x^9) + 3*(x^8) + 1*(x^6) + 4*(x^5) + 4*(x^4) + 1*(x^2) + 0 ); - random_p.push_back( 1*(x^10) + 4*(x^9) + 1*(x^8) + 3*(x^7) + 3*(x^4) + 3*(x^3) + 1*(x^2) + 4*x + 0 ); - random_p.push_back( 1*(x^9) + 2*(x^8) + 3*(x^7) + 1*(x^6) + 2*(x^5) + 4*(x^4) + 3*(x^2) + 0 ); - random_p.push_back( 1*(x^10) + 1*(x^9) + 4*(x^7) + 1*(x^6) + 3*(x^5) + 1*(x^4) + 1*(x^3) + 1*x + 0 ); - random_p.push_back( 4*(x^10) + 4*(x^9) + 1*(x^8) + 2*(x^7) + 3*(x^6) + 4*(x^5) + 3*(x^4) + 1*(x^3) + 2*(x^2) + 4*x + 0 ); - random_p.push_back( 3*(x^10) + 4*(x^9) + 3*(x^8) + 1*(x^7) + 1*(x^6) + 2*(x^5) + 1*(x^4) + 2*(x^3) + 2*(x^2) + 1*x + 0 ); - random_p.push_back( 4*(x^10) + 1*(x^9) + 1*(x^7) + 2*(x^5) + 3*(x^3) + 1*(x^2) + 4*x + 0 ); - random_p.push_back( 1*(x^10) + 3*(x^9) + 3*(x^8) + 1*(x^7) + 3*(x^6) + 3*(x^5) + 3*(x^4) + 1*(x^2) + 3*x + 0 ); - random_p.push_back( 1*(x^10) + 1*(x^9) + 2*(x^8) + 1*(x^7) + 4*(x^6) + 2*(x^5) + 3*(x^4) + 4*(x^3) + 1*(x^2) + 2*x + 0 ); - random_p.push_back( 3*(x^9) + 1*(x^8) + 3*(x^7) + 3*(x^6) + 1*(x^5) + 2*(x^4) + 4*(x^3) + 4*(x^2) + 3*x + 0 ); - random_p.push_back( 2*(x^10) + 3*(x^9) + 2*(x^8) + 4*(x^7) + 1*(x^6) + 3*(x^5) + 2*(x^3) + 3*(x^2) + 2*x + 2 ); - random_p.push_back( 3*(x^10) + 1*(x^9) + 4*(x^8) + 2*(x^7) + 1*(x^6) + 4*(x^5) + 1*(x^4) + 3*(x^3) + 1*x + 2 ); - random_p.push_back( 4*(x^10) + 2*(x^9) + 1*(x^8) + 1*(x^6) + 1*(x^5) + 3*(x^4) + 4*(x^3) + 1*(x^2) + 1*x + 0 ); - random_p.push_back( 4*(x^10) + 2*(x^7) + 4*(x^6) + 2*(x^3) + 1*x + 0 ); - random_p.push_back( 4*(x^10) + 1*(x^9) + 1*(x^8) + 4*(x^7) + 4*(x^4) + 2*(x^2) + 1*x + 4 ); - random_p.push_back( 3*(x^10) + 2*(x^8) + 1*(x^7) + 1*(x^6) + 3*(x^4) + 3*(x^3) + 4*(x^2) + 3*x + 0 ); - random_p.push_back( 1*(x^10) + 2*(x^9) + 2*(x^6) + 4*(x^3) + 4*(x^2) + 0 ); - random_p.push_back( 1*(x^10) + 2*(x^9) + 2*(x^8) + 4*(x^7) + 4*(x^6) + 1*(x^5) + 1*(x^3) + 1*(x^2) + 3*x + 0 ); - - // factor all the randoms them for all the prime numbers - for (unsigned random_i = 0; random_i < random_p.size(); ++ random_i) { - for (unsigned prime_i = 0; prime_i < sizeof(primes)/sizeof(unsigned); ++ prime_i) { - unsigned prime = primes[prime_i]; - - upolynomial::zp_manager upm(nm); - upm.set_zp(prime); - - upolynomial::numeral_vector poly; - upm.to_numeral_vector(random_p[random_i], poly); - - cout << "Factoring "; upm.display(cout, poly); cout << " over Z_" << prime << endl; - cout << "Expecting " << swinnerton_dyer_factors[random_i][0][prime_i] << " distinct factors, " << random_polynomial[random_i][1][prime_i] << " total" << endl; - - upolynomial::zp_factors factors(upm); - upolynomial::zp_factor(upm, poly, factors); - - // check the result - unsigned distinct = factors.distinct_factors(); - unsigned total = factors.total_factors(); - - cout << "Got " << factors << endl; - cout << "Thats " << distinct << " distinct factors, " << total << " total" << endl; - - // SASSERT(random_polynomial[random_i][0][prime_i] == distinct); - // SASSERT(random_polynomial[random_i][1][prime_i] == total); - - upolynomial::numeral_vector multiplied; - factors.multiply(multiplied); - bool equal = upm.eq(poly, multiplied); - cout << (equal ? "equal" : "not equal") << endl; - SASSERT(equal); - upm.reset(multiplied); - - // remove the temp - upm.reset(poly); - } - } -} - -static void tst_factor_enumeration() { - polynomial::numeral_manager nm; - reslimit rl; polynomial::manager pm(rl, nm); - - polynomial_ref x(pm); - x = pm.mk_polynomial(pm.mk_var()); - - vector factors; - for (int i = 0; i < 5; ++ i) { - polynomial_ref factor(pm); - factor = x + i; - factors.push_back(factor); - } - - upolynomial::manager upm(nm); - - upolynomial::zp_manager upm_13(nm); - upm_13.set_zp(13); - upolynomial::zp_factors factors_13(upm_13); - - upolynomial::numeral constant; - nm.set(constant, 10); - factors_13.set_constant(constant); - - for (unsigned i = 0; i < 5; ++ i) { - upolynomial::numeral_vector ufactor; - upm_13.to_numeral_vector(factors[i], ufactor); - factors_13.push_back(ufactor, 1); - upm.reset(ufactor); - } - - cout << "All: " << factors_13 << endl; - - upolynomial::factorization_degree_set degrees(factors_13); - degrees.display(cout); cout << endl; - - scoped_mpz_vector left(nm), right(nm); - upolynomial::ufactorization_combination_iterator it(factors_13, degrees); - unsigned i = 0; - it.display(cout); - bool remove = false; - while (it.next(remove)) { - it.left(left); - it.right(right); - cout << "Left " << i << ": "; upm.display(cout, left); cout << endl; - cout << "Right " << i << ": "; upm.display(cout, right); cout << endl; - i ++; - if (i % 3 == 0) { - remove = true; - } else { - remove = false; - } - it.display(cout); - } - // SASSERT(i == 15); - - return; - - for (unsigned i = 0; i < 5; ++ i) { - factors_13.set_degree(i, factors_13.get_degree(i) + i); - } - cout << "Different: " << factors_13 << " of degree " << factors_13.get_degree() << endl; - upolynomial::factorization_degree_set degrees1(factors_13); - degrees1.display(cout); cout << endl; // [0, ..., 15] - - polynomial_ref tmp1 = (x^3) + 1; - polynomial_ref tmp2 = (x^5) + 2; - polynomial_ref tmp3 = (x^7) + 3; - upolynomial::numeral_vector up1, up2, up3; - upm_13.to_numeral_vector(tmp1, up1); - upm_13.to_numeral_vector(tmp2, up2); - upm_13.to_numeral_vector(tmp3, up3); - upolynomial::zp_factors tmp(upm_13); - tmp.push_back(up1, 1); - tmp.push_back(up2, 1); - tmp.push_back(up3, 1); - upm_13.reset(up1); - upm_13.reset(up2); - upm_13.reset(up3); - - cout << "Different: " << tmp << " of degree " << tmp.get_degree() << endl; - upolynomial::factorization_degree_set degrees2(tmp); - degrees2.display(cout); cout << endl; - - tmp1 = (x^2) + 1; - tmp2 = (x^10) + 2; - tmp3 = x + 3; - upm_13.to_numeral_vector(tmp1, up1); - upm_13.to_numeral_vector(tmp2, up2); - upm_13.to_numeral_vector(tmp3, up3); - tmp.clear(); - tmp.push_back(up1, 2); - tmp.push_back(up2, 1); - tmp.push_back(up3, 1); - cout << "Different: " << tmp << " of degree " << tmp.get_degree() << endl; - upm_13.reset(up1); - upm_13.reset(up2); - upm_13.reset(up3); - upolynomial::factorization_degree_set degrees3(tmp); - degrees3.display(cout); cout << endl; - degrees1.intersect(degrees3); - degrees1.display(cout); cout << endl; -} - -static void tst_factor_square_free_univariate_1(unsigned max_length) { - - polynomial::numeral_manager nm; - upolynomial::numeral test; - upolynomial::numeral p; - nm.set(test, -9); - nm.set(p, 5); - nm.mod(test, p, test); - - reslimit rl; polynomial::manager pm(rl, nm); - - polynomial_ref x(pm); - x = pm.mk_polynomial(pm.mk_var()); - - cout << "R. = QQ['x']" << endl; - - // let's start with \prod (p_i x^{p_{i+1} - p_{i+1}) - unsigned n_primes = sizeof(primes)/sizeof(unsigned); - max_length = std::min(max_length, n_primes); - for(unsigned length = 1; length < max_length; ++ length) { - - // starting from prime_i going for length - for(unsigned start_i = 0; start_i < n_primes; ++ start_i) { - - polynomial_ref f(pm); - - bool first = true; - for (unsigned prime_i = 0; prime_i < length; ++ prime_i) { - int p1 = primes[(start_i + prime_i) % n_primes]; - int p2 = primes[(start_i + prime_i + 1) % n_primes]; - if (first) { - f = (p1*(x^p2) - p2); - first = false; - } else { - f = f*(p1*(x^p2) - p2); - } - } - - upolynomial::manager upm(nm); - scoped_mpz_vector f_u(nm); - upm.to_numeral_vector(f, f_u); - - cout << "factoring "; upm.display(cout, f_u); cout << endl; - cout << "expecting " << length << " factors "; - upolynomial::factors factors(upm); - /* bool ok = */ upolynomial::factor_square_free(upm, f_u, factors); - cout << "got " << factors << endl; - - SASSERT(factors.distinct_factors() == length); - } - } -} - -static void tst_factor_square_free_univariate_2() { - polynomial::numeral_manager nm; - reslimit rl; polynomial::manager pm(rl, nm); - - polynomial_ref x(pm); - x = pm.mk_polynomial(pm.mk_var()); - - // Swinnerton-Dyer polynomials (irreducible, modular factors of degree at most 2) - polynomial_ref S1 = (x^2) - 2; - polynomial_ref S2 = (x^4) - 10*(x^2) + 1; - polynomial_ref S3 = (x^8) - 40*(x^6) + 352*(x^4) - 960*(x^2) + 576; - polynomial_ref S4 = (x^16) - 136*(x^14) + 6476*(x^12) - 141912*(x^10) + 1513334*(x^8) - 7453176*(x^6) + 13950764*(x^4) - 5596840*(x^2) + 46225; - - vector S; - S.push_back(S1); - S.push_back(S2); - S.push_back(S3); - S.push_back(S4); - - upolynomial::manager upm(nm); - - // factor all the S_i them for all the prime numbers - for (unsigned S_i = 0; S_i < S.size(); ++ S_i) { - upolynomial::numeral_vector S_i_u; - upm.to_numeral_vector(S[S_i], S_i_u); - - cout << "Factoring "; upm.display(cout, S_i_u); cout << " over Z " << endl; - upolynomial::factors factors(upm); - upolynomial::factor_square_free(upm, S_i_u, factors); - - // check the result - cout << "Got " << factors << endl; - - // remove the temp - upm.reset(S_i_u); - } -} - -static void tst_factor_square_free_univariate_3() { - polynomial::numeral_manager nm; - reslimit rl; polynomial::manager pm(rl, nm); - - polynomial_ref x(pm); - x = pm.mk_polynomial(pm.mk_var()); - - polynomial_ref deg70 = (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; - - upolynomial::manager upm(nm); - upolynomial::numeral_vector deg70_u; - - upm.to_numeral_vector(deg70, deg70_u); - - cout << "Factoring "; upm.display(cout, deg70_u); cout << " over Z " << endl; - upolynomial::factors factors(upm); - upolynomial::factor_square_free(upm, deg70_u, factors); - - cout << "Got " << factors << endl; - - upm.reset(deg70_u); -} -#endif - -void tst_factor_swinnerton_dyer_big(unsigned max) { - polynomial::numeral_manager nm; - reslimit rl; polynomial::manager pm(rl, nm); - - polynomial_ref x(pm); - x = pm.mk_polynomial(pm.mk_var()); - - vector roots; - vector vars; - - unsigned n = std::min(max, static_cast(sizeof(primes)/sizeof(unsigned))); - for(unsigned prime_i = 0; prime_i < n; ++ prime_i) { - - int prime = primes[prime_i]; - - cout << "Computing Swinnerton-Dyer[" << prime_i + 1 << "]" << endl; - - polynomial_ref y(pm); - vars.push_back(pm.mk_var()); - y = pm.mk_polynomial(vars.back()); - - polynomial_ref p(pm); - p = (y^2) - prime; - roots.push_back(p); - - polynomial_ref computation = x; - for (unsigned i = 0; i < roots.size(); ++ i) { - polynomial_ref var(pm); - var = pm.mk_polynomial(vars[i]); - computation = computation - var; - } - - { - timeit timer(true, "computing swinnerton-dyer"); - - for (unsigned i = 0; i < roots.size(); ++ i) { - polynomial_ref tmp(pm); - pm.resultant(computation, roots[i], vars[i], tmp); - computation = tmp; - } - } - - cout << "Computed Swinnerton-Dyer[" << prime_i + 1 << "], degree = " << pm.total_degree(computation) << ", size = " << pm.size(computation) << endl; - - cout << "Starting factoring " << endl; - - { - timeit timer(true, "factoring swinnerton-dyer"); - - reslimit rl; - upolynomial::manager upm(rl, nm); - scoped_mpz_vector sd_u(nm); - upm.to_numeral_vector(computation, sd_u); - upolynomial::factors factors(upm); - upolynomial::factor_square_free(upm, sd_u, factors); - cout << "Got " << factors.distinct_factors() << " factors" << endl; - } - - } -} - -static void tst_factor_square_free_multivariate_1(unsigned max_n) { -#if 0 - polynomial::numeral_manager nm; - upolynomial::numeral test; - upolynomial::numeral p; - nm.set(test, -9); - nm.set(p, 5); - nm.mod(test, p, test); - - reslimit rl; polynomial::manager pm(rl, nm); - - polynomial_ref x(pm); - x = pm.mk_polynomial(pm.mk_var()); - - polynomial_ref y(pm); - y = pm.mk_polynomial(pm.mk_var()); - - // lets start simple x^n - y^n - for (unsigned prime_i = 0; prime_i < sizeof(primes)/sizeof(unsigned); ++ prime_i) { - unsigned prime = primes[prime_i]; - - if (prime > max_n) { - break; - } - - polynomial_ref f = (x^prime) - (y^prime); - cout << "factoring: " << f << endl; - - // factor - polynomial::factors factors(pm); - polynomial::factor_square_free_primitive(f, factors); - - cout << "got: " << factors << endl; - } -#endif -} - - -void tst_polynomial_factorization() { - - enable_trace("polynomial::factorization"); - // enable_trace("polynomial::factorization::bughunt"); - enable_trace("polynomial::factorization::multivariate"); - // enable_trace("upolynomial"); - - // Z_p square-free factorization tests - // tst_square_free_finite_1(); - - // Z_p factorization tests - // tst_factor_finite_1(); - // tst_factor_finite_2(); - // tst_factor_finite_3(); - - // Z factorization - // tst_factor_enumeration(); - // tst_factor_square_free_univariate_1(3); - // tst_factor_square_free_univariate_2(); - // tst_factor_square_free_univariate_3(); - // tst_factor_swinnerton_dyer_big(3); - - // Multivariate factorization - tst_factor_square_free_multivariate_1(3); -} diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index f14ccf3e2..a73f7ed38 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -375,6 +375,9 @@ static void add_random_ineq( 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); @@ -382,8 +385,7 @@ static void add_random_ineq( 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); - expr_ref bound(m); + model mdl(m); arith_util a(m); for (unsigned i = 0; i < num_vars; ++i) { app_ref var(m); @@ -391,7 +393,8 @@ static void test_maximize(opt::model_based_opt& mbo, ast_manager& m, unsigned nu rational val = mbo.get_value(i); mdl.register_decl(var->get_decl(), a.mk_numeral(val, false)); } - opt::inf_eps value1 = plugin.maximize(fmls, mdl, t, bound); + 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); @@ -438,10 +441,158 @@ static void check_random_ineqs() { } } +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"); diff --git a/src/test/sorting_network.cpp b/src/test/sorting_network.cpp index 4802e2bec..81340651a 100644 --- a/src/test/sorting_network.cpp +++ b/src/test/sorting_network.cpp @@ -22,7 +22,7 @@ struct ast_ext { ast_ext(ast_manager& m):m(m) {} typedef expr* T; typedef expr_ref_vector vector; - T mk_ite(T a, T b, T c) { + T mk_ite(T a, T b, T c) { return m.mk_ite(a, b, c); } T mk_le(T a, T b) { @@ -34,7 +34,7 @@ struct ast_ext { } T mk_default() { return m.mk_false(); - } + } }; @@ -164,17 +164,17 @@ struct ast_ext2 { literal mk_false() { return m.mk_false(); } literal mk_true() { return m.mk_true(); } - literal mk_max(literal a, literal b) { - return trail(m.mk_or(a, b)); + literal mk_max(literal a, literal b) { + return trail(m.mk_or(a, b)); } literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } - literal mk_not(literal a) { if (m.is_not(a,a)) return a; - return trail(m.mk_not(a)); + literal mk_not(literal a) { if (m.is_not(a,a)) return a; + return trail(m.mk_not(a)); } std::ostream& pp(std::ostream& out, literal lit) { return out << mk_pp(lit, m); } - literal fresh() { + literal fresh() { return trail(m.mk_fresh_const("x", m.mk_bool_sort())); } void mk_clause(unsigned n, literal const* lits) { @@ -200,7 +200,7 @@ static void test_sorting_eq(unsigned n, unsigned k) { // equality: std::cout << "eq " << k << "\n"; solver.push(); - result = sn.eq(k, in.size(), in.c_ptr()); + result = sn.eq(true, k, in.size(), in.c_ptr()); solver.assert_expr(result); for (unsigned i = 0; i < ext.m_clauses.size(); ++i) { solver.assert_expr(ext.m_clauses[i].get()); @@ -210,7 +210,7 @@ static void test_sorting_eq(unsigned n, unsigned k) { solver.push(); for (unsigned i = 0; i < k; ++i) { - solver.assert_expr(in[i].get()); + solver.assert_expr(in[i].get()); } res = solver.check(); SASSERT(res == l_true); @@ -256,7 +256,7 @@ static void test_sorting_le(unsigned n, unsigned k) { SASSERT(res == l_true); for (unsigned i = 0; i < k; ++i) { - solver.assert_expr(in[i].get()); + solver.assert_expr(in[i].get()); } res = solver.check(); SASSERT(res == l_true); @@ -304,7 +304,7 @@ void test_sorting_ge(unsigned n, unsigned k) { solver.push(); for (unsigned i = 0; i < n - k; ++i) { - solver.assert_expr(m.mk_not(in[i].get())); + solver.assert_expr(m.mk_not(in[i].get())); } res = solver.check(); SASSERT(res == l_true); @@ -332,7 +332,107 @@ void test_sorting5(unsigned n, unsigned k) { test_sorting_ge(n, k); } +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) { + 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); + 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"; + + smt_params fp; + smt::kernel solver(m, fp); + for (unsigned i = 0; i < ext.m_clauses.size(); ++i) { + solver.assert_expr(ext.m_clauses[i].get()); + } + lbool res; + if (full) { + solver.push(); + solver.assert_expr(m.mk_not(m.mk_eq(result1, result2))); + + std::cout << result1 << "\n"; + + res = solver.check(); + SASSERT(res == l_false); + + solver.pop(1); + } + + if (n >= 9) return; + for (unsigned i = 0; i < static_cast(1 << n); ++i) { + std::cout << "checking: " << n << ": " << i << "\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; + } + res = solver.check(); + SASSERT(res == l_true); + if (k > 1) { + solver.assert_expr(result1); + } + else if (!full) { + solver.pop(1); + continue; + } + else { + solver.assert_expr(m.mk_not(result1)); + } + res = solver.check(); + SASSERT(res == l_false); + solver.pop(1); + } +} + + +static void test_at_most1() { + 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); + expr_ref result(m); + result = sn.le(true, 1, in.size(), in.c_ptr()); + std::cout << result << "\n"; + std::cout << ext.m_clauses << "\n"; +} + void tst_sorting_network() { + for (unsigned i = 1; i < 17; ++i) { + test_at_most_1(i, true); + test_at_most_1(i, false); + } + test_at_most1(); + test_sorting_eq(11,7); for (unsigned n = 3; n < 20; n += 2) { for (unsigned k = 1; k < n; ++k) { diff --git a/src/test/uint_set.cpp b/src/test/uint_set.cpp index 582a1d42f..fcbbd5c48 100644 --- a/src/test/uint_set.cpp +++ b/src/test/uint_set.cpp @@ -131,6 +131,32 @@ static void tst4() { SASSERT(!s.contains(0)); } +#include "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); @@ -146,5 +172,6 @@ void tst_uint_set() { tst3(12); tst3(100); tst4(); + tst5(); } diff --git a/src/util/hashtable.h b/src/util/hashtable.h index 488157c00..44e05319d 100644 --- a/src/util/hashtable.h +++ b/src/util/hashtable.h @@ -556,6 +556,37 @@ public: out << "]"; } + core_hashtable& operator|=(core_hashtable const& other) { + if (this == &other) return *this; + iterator i = other.begin(), e = other.end(); + for (; i != e; ++i) { + insert(*i); + } + return *this; + } + + core_hashtable& operator&=(core_hashtable const& other) { + if (this == &other) return *this; + core_hashtable copy(*this); + iterator i = copy.begin(), e = copy.end(); + for (; i != e; ++i) { + if (!other.contains(*i)) { + remove(*i); + } + } + return *this; + } + + core_hashtable& operator=(core_hashtable const& other) { + if (this == &other) return *this; + reset(); + iterator i = other.begin(), e = other.end(); + for (; i != e; ++i) { + insert(*i); + } + return *this; + } + #ifdef Z3DEBUG bool check_invariant() { entry * curr = m_table; @@ -582,9 +613,6 @@ public: unsigned long long get_num_collision() const { return 0; } #endif - private: - - core_hashtable& operator=(core_hashtable const&); }; @@ -616,7 +644,6 @@ public: ptr_addr_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY): core_hashtable, ptr_hash, ptr_eq >(initial_capacity) {} - // Using iterators to traverse the elements of this kind of hashtable will produce non-determinism. iterator begin() const { UNREACHABLE(); } @@ -624,6 +651,9 @@ public: iterator end() const { UNREACHABLE(); } + + // NB. Using iterators to traverse the elements of this kind of hashtable will produce non-determinism. + }; /** @@ -640,4 +670,5 @@ public: core_hashtable, HashProc, EqProc>(initial_capacity, h, e) {} }; + #endif /* HASHTABLE_H_ */ diff --git a/src/util/max_cliques.h b/src/util/max_cliques.h new file mode 100644 index 000000000..ec888c84b --- /dev/null +++ b/src/util/max_cliques.h @@ -0,0 +1,137 @@ +/*++ +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 "vector.h" +#include "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) { + cliques.push_back(clique); + } + } + } + + +}; diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 0fcff0da0..e9d108cec 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -795,8 +795,9 @@ void mpf_manager::fma(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf co set(o, z); } else if (is_zero(x) || is_zero(y)) { - if (is_zero(z) && rm != MPF_ROUND_TOWARD_NEGATIVE) - mk_pzero(x.ebits, x.sbits, o); + bool xy_sgn = is_neg(x) ^ is_neg(y); + if (is_zero(z) && xy_sgn ^ is_neg(z)) + mk_zero(x.ebits, x.sbits, rm != MPF_ROUND_TOWARD_NEGATIVE, o); else set(o, z); } @@ -1224,7 +1225,7 @@ void mpf_manager::renormalize(unsigned ebits, unsigned sbits, mpf_exp_t & exp, m 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); @@ -1235,7 +1236,7 @@ void mpf_manager::renormalize(unsigned ebits, unsigned sbits, mpf_exp_t & exp, m 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); @@ -1252,7 +1253,7 @@ void mpf_manager::partial_remainder(mpf & x, mpf const & y, mpf_exp_t const & ex 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 + // 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); @@ -1271,7 +1272,7 @@ void mpf_manager::partial_remainder(mpf & x, mpf const & y, mpf_exp_t const & ex 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) { + 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); @@ -1290,10 +1291,11 @@ void mpf_manager::partial_remainder(mpf & x, mpf const & y, mpf_exp_t const & ex 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. @@ -1304,22 +1306,23 @@ void mpf_manager::partial_remainder(mpf & x, mpf const & y, mpf_exp_t const & ex // 3. Compute Y*Q / Y*QQ*2^{D-N} - bool YQ_sgn = y.sign ^ Q_sgn; + 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); + 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. + // `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 + // 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; @@ -1337,14 +1340,14 @@ void mpf_manager::partial_remainder(mpf & x, mpf const & y, mpf_exp_t const & ex 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); + sticky_rem.swap(subtrahend); else if (exp_delta > 0) - m_mpz_manager.machine_div_rem(subtrahend, m_powers2((unsigned)exp_delta), subtrahend, sticky_rem); + m_mpz_manager.machine_div_rem(subtrahend, m_powers2((unsigned)exp_delta), subtrahend, sticky_rem); else { SASSERT(exp_delta < 0); exp_delta = -exp_delta; @@ -1354,15 +1357,13 @@ void mpf_manager::partial_remainder(mpf & x, mpf const & y, mpf_exp_t const & ex 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 && !YQ_sgn && neg) || - (x.sign && YQ_sgn && !neg) || - (x.sign && !YQ_sgn)); + bool X_YQ_sgn = x.sign ^ neg; // 5. Rounding if (m_mpz_manager.is_zero(X_YQ_sig)) @@ -1374,7 +1375,7 @@ void mpf_manager::partial_remainder(mpf & x, mpf const & y, mpf_exp_t const & ex 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_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 @@ -1384,7 +1385,7 @@ void mpf_manager::partial_remainder(mpf & x, mpf const & y, mpf_exp_t const & ex 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.eq(rnd_bits, mpz(32))) { // tie. if (m_mpz_manager.is_odd(X_YQ_sig)) { m_mpz_manager.inc(X_YQ_sig); } @@ -1430,7 +1431,7 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { mpf_exp_t D; do { if (ST0.exponent() < ST1.exponent() - 1) { - D = 0; + D = 0; } else { D = ST0.exponent() - ST1.exponent(); @@ -1889,7 +1890,7 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { const mpz & p_m1 = m_powers2(o.sbits+2); const mpz & p_m2 = m_powers2(o.sbits+3); - (void)p_m1; + (void)p_m1; TRACE("mpf_dbg", tout << "p_m1 = " << m_mpz_manager.to_string(p_m1) << std::endl << "p_m2 = " << m_mpz_manager.to_string(p_m2) << std::endl;); diff --git a/src/util/mpff.h b/src/util/mpff.h index a4cab898c..eadfa0390 100644 --- a/src/util/mpff.h +++ b/src/util/mpff.h @@ -94,7 +94,7 @@ class mpff_manager { // // - All values of type int, unsigned, int64 and uint64 can be precisely represented as mpff numerals. // - // - Hardware float and double values (corresponding to rationals) can also be precisely represented as mpff numberals. + // - 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 diff --git a/src/util/mpq.cpp b/src/util/mpq.cpp index df4d207a6..feb051033 100644 --- a/src/util/mpq.cpp +++ b/src/util/mpq.cpp @@ -153,7 +153,7 @@ void mpq_manager::display_smt2(std::ostream & out, mpq const & a, bool de } template -void mpq_manager::display_decimal(std::ostream & out, mpq const & a, unsigned prec) { +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); @@ -177,7 +177,7 @@ void mpq_manager::display_decimal(std::ostream & out, mpq const & a, unsi if (is_zero(n1)) goto end; // number is precise } - out << "?"; + if (!truncate) out << "?"; end: del(ten); del(n1); del(d1); del(v1); } diff --git a/src/util/mpq.h b/src/util/mpq.h index 0a643c650..093cc0a44 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -265,7 +265,7 @@ public: void display_smt2(std::ostream & out, mpq const & a, bool decimal) const; - void display_decimal(std::ostream & out, mpq const & a, unsigned prec); + 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); } diff --git a/src/util/obj_hashtable.h b/src/util/obj_hashtable.h index 4e948d96d..383ecaeb3 100644 --- a/src/util/obj_hashtable.h +++ b/src/util/obj_hashtable.h @@ -51,6 +51,7 @@ class obj_hashtable : public core_hashtable, obj_ptr_hash, public: obj_hashtable(unsigned initial_capacity = DEFAULT_HASHTABLE_INITIAL_CAPACITY): core_hashtable, obj_ptr_hash, ptr_eq >(initial_capacity) {} + }; template diff --git a/src/util/rational.h b/src/util/rational.h index fc25837c6..ba447fca6 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -86,7 +86,7 @@ public: void display(std::ostream & out) const { return m().display(out, m_val); } - void display_decimal(std::ostream & out, unsigned prec) const { return m().display_decimal(out, m_val, prec); } + void display_decimal(std::ostream & out, unsigned prec, bool truncate = false) const { return m().display_decimal(out, m_val, prec, truncate); } bool is_uint64() const { return m().is_uint64(m_val); } diff --git a/src/util/rlimit.cpp b/src/util/rlimit.cpp index b3f055955..bda06157d 100644 --- a/src/util/rlimit.cpp +++ b/src/util/rlimit.cpp @@ -20,7 +20,7 @@ Revision History: #include "common_msgs.h" reslimit::reslimit(): - m_cancel(false), + m_cancel(0), m_count(0), m_limit(0) { } @@ -36,7 +36,7 @@ bool reslimit::inc() { bool reslimit::inc(unsigned offset) { m_count += offset; - return !m_cancel && (m_limit == 0 || m_count <= m_limit); + return m_cancel == 0 && (m_limit == 0 || m_count <= m_limit); } void reslimit::push(unsigned delta_limit) { @@ -45,7 +45,7 @@ void reslimit::push(unsigned delta_limit) { new_limit = 0; } m_limits.push_back(m_limit); - m_limit = m_limit==0?new_limit:std::min(new_limit, m_limit); + m_limit = m_limit==0 ? new_limit : std::min(new_limit, m_limit); m_cancel = 0; } @@ -70,14 +70,14 @@ char const* reslimit::get_cancel_msg() const { void reslimit::push_child(reslimit* r) { #pragma omp critical (reslimit_cancel) { - m_children.push_back(r); + m_children.push_back(r); } } void reslimit::pop_child() { #pragma omp critical (reslimit_cancel) { - m_children.pop_back(); + m_children.pop_back(); } } @@ -98,7 +98,7 @@ void reslimit::reset_cancel() { void reslimit::inc_cancel() { #pragma omp critical (reslimit_cancel) - { + { set_cancel(m_cancel+1); } } @@ -113,8 +113,8 @@ void reslimit::dec_cancel() { } } -void reslimit::set_cancel(unsigned f) { - m_cancel = f; +void reslimit::set_cancel(unsigned f) { + m_cancel = f; for (unsigned i = 0; i < m_children.size(); ++i) { m_children[i]->set_cancel(f); } diff --git a/src/util/rlimit.h b/src/util/rlimit.h index ac5db6136..283d7613d 100644 --- a/src/util/rlimit.h +++ b/src/util/rlimit.h @@ -29,8 +29,8 @@ class reslimit { ptr_vector m_children; void set_cancel(unsigned f); - -public: + +public: reslimit(); void push(unsigned delta_limit); void pop(); @@ -39,7 +39,7 @@ public: bool inc(); bool inc(unsigned offset); - uint64 count() const; + uint64 count() const; bool get_cancel_flag() const { return m_cancel > 0; } @@ -61,4 +61,13 @@ public: }; +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; } +}; + + #endif diff --git a/src/util/scoped_timer.cpp b/src/util/scoped_timer.cpp index bf3ca9b67..f6b0429d9 100644 --- a/src/util/scoped_timer.cpp +++ b/src/util/scoped_timer.cpp @@ -73,6 +73,7 @@ struct scoped_timer::imp { pthread_cond_t m_cond; unsigned m_ms; bool m_initialized; + bool m_signal_sent; #else // Other #endif @@ -119,10 +120,18 @@ struct scoped_timer::imp { pthread_mutex_lock(&st->m_mutex); st->m_initialized = true; - int e = pthread_cond_timedwait(&st->m_cond, &st->m_mutex, &end_time); - ENSURE(e == 0 || e == ETIMEDOUT); - + int e = 0; + // `pthread_cond_timedwait()` may spuriously wake even if the signal + // was not sent so we loop until a timeout occurs or the signal was + // **really** sent. + while (!(e == 0 && st->m_signal_sent)) { + e = pthread_cond_timedwait(&st->m_cond, &st->m_mutex, &end_time); + ENSURE(e == 0 || e == ETIMEDOUT); + if (e == ETIMEDOUT) + break; + } pthread_mutex_unlock(&st->m_mutex); + if (e == ETIMEDOUT) st->m_eh->operator()(); return 0; @@ -170,6 +179,7 @@ struct scoped_timer::imp { // Linux & FreeBSD m_ms = ms; m_initialized = false; + m_signal_sent = false; ENSURE(pthread_mutex_init(&m_mutex, NULL) == 0); ENSURE(pthread_cond_init(&m_cond, NULL) == 0); ENSURE(pthread_create(&m_thread_id, NULL, &thread_func, this) == 0); @@ -218,6 +228,10 @@ struct scoped_timer::imp { if (!init) sched_yield(); } + pthread_mutex_lock(&m_mutex); + m_signal_sent = true; + pthread_mutex_unlock(&m_mutex); + // Perform signal outside of lock to avoid waking timing thread twice. pthread_cond_signal(&m_cond); pthread_join(m_thread_id, NULL); @@ -231,7 +245,7 @@ struct scoped_timer::imp { }; scoped_timer::scoped_timer(unsigned ms, event_handler * eh) { - if (ms != UINT_MAX) + if (ms != UINT_MAX && ms != 0) m_imp = alloc(imp, ms, eh); else m_imp = 0; diff --git a/src/util/sorting_network.h b/src/util/sorting_network.h index b106117ea..b0efbd346 100644 --- a/src/util/sorting_network.h +++ b/src/util/sorting_network.h @@ -24,7 +24,6 @@ Notes: #ifndef SORTING_NETWORK_H_ #define SORTING_NETWORK_H_ - template class sorting_network { typedef typename Ext::vector vect; @@ -165,11 +164,27 @@ Notes: 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) {} literal ge(bool full, unsigned k, unsigned n, literal const* xs) { @@ -187,6 +202,7 @@ Notes: else { 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]; } @@ -201,35 +217,277 @@ Notes: 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); + return mk_at_most_1(full, n, xs, ors, false); + } else { SASSERT(2*k <= n); m_t = full?LE_FULL:LE; + // scoped_stats _ss(m_stats, k, n); card(k + 1, n, xs, out); return ctx.mk_not(out[k]); } } - literal eq(unsigned k, unsigned n, literal const* xs) { + 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(k, n, in.c_ptr()); + 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 { + // 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); - return ctx.mk_min(out[k-1], ctx.mk_not(out[k])); + if (k == 0) { + return ctx.mk_not(out[k]); + } + else { + return ctx.mk_min(out[k-1], ctx.mk_not(out[k])); + } } } private: + void add_implies_or(literal l, unsigned n, literal const* xs) { + literal_vector lits(n, xs); + lits.push_back(ctx.mk_not(l)); + add_clause(lits); + } + + void add_or_implies(literal l, unsigned n, literal const* xs) { + for (unsigned j = 0; j < n; ++j) { + add_clause(ctx.mk_not(xs[j]), l); + } + } + + literal mk_or(unsigned n, literal const* ors) { + if (n == 1) { + return ors[0]; + } + literal result = fresh(); + add_implies_or(result, n, ors); + add_or_implies(result, n, ors); + return result; + } + + literal mk_or(literal l1, literal l2) { + literal ors[2] = { l1, l2 }; + return mk_or(2, ors); + } + literal mk_or(literal_vector const& ors) { + return mk_or(ors.size(), ors.c_ptr()); + } + + void add_implies_and(literal l, literal_vector const& xs) { + for (unsigned j = 0; j < xs.size(); ++j) { + add_clause(ctx.mk_not(l), xs[j]); + } + } + + void add_and_implies(literal l, literal_vector const& xs) { + literal_vector lits; + for (unsigned j = 0; j < xs.size(); ++j) { + lits.push_back(ctx.mk_not(xs[j])); + } + lits.push_back(l); + add_clause(lits); + } + + 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_vector const& ands) { + if (ands.size() == 1) { + return ands[0]; + } + literal result = fresh(); + add_implies_and(result, ands); + add_and_implies(result, ands); + return result; + } + + literal mk_exactly_1(bool full, unsigned n, literal const* xs) { + literal_vector ors; + literal r1 = mk_at_most_1(full, n, xs, ors, true); + + 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", tout << (full?"full":"partial") << " "; + for (unsigned i = 0; i < n; ++i) tout << xs[i] << " "; + tout << "\n";); + + if (n >= 4 && false) { + return mk_at_most_1_bimander(full, n, xs, ors); + } + literal_vector in(n, xs); + literal result = fresh(); + 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(ctx.mk_not(result), ctx.mk_not(xs[i]), ctx.mk_not(xs[j])); + } + } + + // xs[0] + ... + xs[n-1] <= 1 => and_x + if (full) { + literal and_i = fresh(); + 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(ctx.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(); + } + + +#if 0 + literal result = fresh(); + + // result => xs[0] + ... + xs[n-1] <= 1 + for (unsigned i = 0; i < n; ++i) { + for (unsigned j = i + 1; j < n; ++j) { + add_clause(ctx.mk_not(result), ctx.mk_not(xs[i]), ctx.mk_not(xs[j])); + } + } + + // xs[0] + ... + xs[n-1] <= 1 => result + for (unsigned i = 0; i < n; ++i) { + literal_vector lits; + lits.push_back(result); + for (unsigned j = 0; j < n; ++j) { + if (j != i) lits.push_back(xs[j]); + } + add_clause(lits); + } + + return result; +#endif +#if 1 + // 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(ctx.mk_not(xs[i]), ctx.mk_not(xs[j]))); + } + } + return mk_and(ands); +#else + // r <=> or (and !x_{j != i}) + + literal_vector ors; + for (unsigned i = 0; i < n; ++i) { + literal_vector ands; + for (unsigned j = 0; j < n; ++j) { + if (j != i) { + ands.push_back(ctx.mk_not(xs[j])); + } + } + ors.push_back(mk_and(ands)); + } + return mk_or(ors); + +#endif + } + + + // + + literal mk_at_most_1_bimander(bool full, unsigned n, literal const* xs, literal_vector& ors) { + literal_vector in(n, xs); + literal result = fresh(); + 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()); + } + 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(ctx.mk_not(result), ctx.mk_not(ors[i]), bit_set ? bits[k] : ctx.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; @@ -293,9 +551,14 @@ Notes: 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) { m_stats.m_num_compiled_clauses++; + m_stats.m_num_clause_vars += n; literal_vector tmp(n, ls); + TRACE("pb", for (unsigned i = 0; i < n; ++i) tout << ls[i] << " "; tout << "\n";); ctx.mk_clause(n, tmp.c_ptr()); } @@ -332,7 +595,7 @@ Notes: } void card(unsigned k, unsigned n, literal const* xs, literal_vector& out) { - TRACE("pb", tout << "card k:" << k << " n: " << n << "\n";); + TRACE("pb", tout << "card k: " << k << " n: " << n << "\n";); if (n <= k) { psort_nw::sorting(n, xs, out); } @@ -346,7 +609,7 @@ Notes: card(k, n-l, xs + l, out2); smerge(k, out1.size(), out1.c_ptr(), out2.size(), out2.c_ptr(), out); } - TRACE("pb", tout << "card k:" << k << " n: " << n << "\n"; + TRACE("pb", tout << "card k: " << k << " n: " << n << "\n"; pp(tout << "in:", n, xs) << "\n"; pp(tout << "out:", out) << "\n";); @@ -476,6 +739,7 @@ Notes: return vc_cmp()*std::min(a-1,b); } + public: void sorting(unsigned n, literal const* xs, literal_vector& out) { TRACE("pb", tout << "sorting: " << n << "\n";); switch(n) { @@ -505,8 +769,9 @@ Notes: TRACE("pb", 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); @@ -692,7 +957,7 @@ Notes: if (j < b) { ls.push_back(as[i]); ls.push_back(bs[j]); - add_clause(ls.size(), ls.c_ptr()); + add_clause(ls); ls.pop_back(); ls.pop_back(); } @@ -753,7 +1018,7 @@ Notes: pp(tout, lits) << "\n";); SASSERT(k + offset <= n); if (k == 0) { - add_clause(lits.size(), lits.c_ptr()); + add_clause(lits); return; } for (unsigned i = offset; i < n - k + 1; ++i) { diff --git a/src/util/timer.h b/src/util/timer.h index cc082a9d8..b4a69f8bf 100644 --- a/src/util/timer.h +++ b/src/util/timer.h @@ -31,8 +31,8 @@ public: ~timer(); void start(); double get_seconds(); - bool timeout(unsigned secs) { return secs > 0 && get_seconds() > secs; } - bool ms_timeout(unsigned ms) { return ms > 0 && get_seconds() * 1000 > ms; } + bool timeout(unsigned secs) { return secs > 0 && secs != UINT_MAX && get_seconds() > secs; } + bool ms_timeout(unsigned ms) { return ms > 0 && ms != UINT_MAX && get_seconds() * 1000 > ms; } }; #endif /* TIMER_H_ */ diff --git a/src/util/uint_set.h b/src/util/uint_set.h index aca73a5c5..e78a4b0b6 100644 --- a/src/util/uint_set.h +++ b/src/util/uint_set.h @@ -163,10 +163,11 @@ public: class iterator { uint_set const* m_set; unsigned m_index; + unsigned m_last; - bool invariant() const { return m_index <= m_set->get_max_elem(); } + bool invariant() const { return m_index <= m_last; } - bool at_end() const { return m_index == m_set->get_max_elem(); } + bool at_end() const { return m_index == m_last; } void scan_idx() { SASSERT(invariant()); @@ -200,7 +201,7 @@ public: } public: iterator(uint_set const& s, bool at_end): - m_set(&s), m_index(at_end?s.get_max_elem():0) { + m_set(&s), m_index(at_end?s.get_max_elem():0), m_last(s.get_max_elem()) { scan(); SASSERT(invariant()); } @@ -212,12 +213,25 @@ public: 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) { diff --git a/src/util/version.h.in b/src/util/version.h.in index 05b619f2d..daf568a29 100644 --- a/src/util/version.h.in +++ b/src/util/version.h.in @@ -3,3 +3,5 @@ #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@ diff --git a/src/util/warning.cpp b/src/util/warning.cpp index 1105a12f7..ed4cd8725 100644 --- a/src/util/warning.cpp +++ b/src/util/warning.cpp @@ -167,4 +167,3 @@ void warning_msg(const char * msg, ...) { va_end(args); } } -
%s