diff --git a/README-CMake.md b/README-CMake.md index 2cec1653a..0943e7a7d 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -279,6 +279,10 @@ The following useful options can be passed to CMake whilst configuring. * ``INSTALL_DOTNET_BINDINGS`` - BOOL. If set to ``TRUE`` and ``BUILD_DOTNET_BINDINGS`` is ``TRUE`` then running the ``install`` target will install Z3's .NET bindings. * ``DOTNET_CSC_EXECUTABLE`` - STRING. The path to the C# compiler to use. Only relevant if ``BUILD_DOTNET_BINDINGS`` is set to ``TRUE``. * ``DOTNET_GACUTIL_EXECUTABLE`` - STRING. The path to the gacutil program to use. Only relevant if ``BUILD_DOTNET_BINDINGS`` is set to ``TRUE``. +* ``BUILD_JAVA_BINDINGS`` - BOOL. If set to ``TRUE`` then Z3's Java bindings will be built. +* ``INSTALL_JAVA_BINDINGS`` - BOOL. If set to ``TRUE`` and ``BUILD_JAVA_BINDINGS`` is ``TRUE`` then running the ``install`` target will install Z3's Java bindings. +* ``Z3_JAVA_JAR_INSTALLDIR`` - STRING. The path to directory to install the Z3 Java ``.jar`` file. This path should be relative to ``CMAKE_INSTALL_PREFIX``. +* ``Z3_JAVA_JNI_LIB_INSTALLDIRR`` - STRING. The path to directory to install the Z3 Java JNI bridge library. This path should be relative to ``CMAKE_INSTALL_PREFIX``. On the command line these can be passed to ``cmake`` using the ``-D`` option. In ``ccmake`` and ``cmake-gui`` these can be set in the user interface. @@ -286,8 +290,29 @@ Example ``` cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_TRACING=FALSE ../ + ``` +## Z3 API Bindings + +Z3 exposes various language bindings for its API. Below are some notes on building +and/or installing these bindings when building Z3 with CMake. + +### Java bindings + +The CMake build uses the ``FindJava`` and ``FindJNI`` cmake modules to detect the +installation of Java. If CMake fails to find your installation of Java set the +``JAVA_HOME`` environment variable when invoking CMake so that it points at the +correct location. For example + +``` +JAVA_HOME=/usr/lib/jvm/default cmake -DBUILD_JAVA_BINDINGS=ON ../ +``` +Note that the built ``.jar`` file is named ``com.microsoft.z3-VERSION.jar`` +where ``VERSION`` is the Z3 version. Under non Windows systems a +symbolic link named ``com.microsoft.z3.jar`` is provided. This symbolic +link is not created when building under Windows. + ## Developer/packager notes These notes are help developers and packagers of Z3. diff --git a/contrib/cmake/src/CMakeLists.txt b/contrib/cmake/src/CMakeLists.txt index 13403afb1..cba30c46f 100644 --- a/contrib/cmake/src/CMakeLists.txt +++ b/contrib/cmake/src/CMakeLists.txt @@ -166,7 +166,8 @@ endforeach() install(TARGETS libz3 LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" # On Windows this installs ``libz3.lib`` which CMake calls the "corresponding import library". Do we want this installed? + RUNTIME DESTINATION "${CMAKE_INSTALL_LIBDIR}" # For Windows. DLLs are runtime targets for CMake PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" ) @@ -234,4 +235,16 @@ if (BUILD_DOTNET_BINDINGS) add_subdirectory(api/dotnet) endif() +################################################################################ +# Java bindings +################################################################################ +option(BUILD_JAVA_BINDINGS "Build Java bindings for Z3" OFF) +if (BUILD_JAVA_BINDINGS) + if (NOT BUILD_LIBZ3_SHARED) + message(FATAL_ERROR "The Java bindings will not work with a static libz3. " + "You either need to disable BUILD_JAVA_BINDINGS or enable BUILD_LIBZ3_SHARED") + endif() + add_subdirectory(api/java) +endif() + # TODO: Implement support for other bindigns diff --git a/contrib/cmake/src/api/dotnet/CMakeLists.txt b/contrib/cmake/src/api/dotnet/CMakeLists.txt index f077b57ce..7440f021b 100644 --- a/contrib/cmake/src/api/dotnet/CMakeLists.txt +++ b/contrib/cmake/src/api/dotnet/CMakeLists.txt @@ -203,12 +203,24 @@ if (DOTNET_TOOLCHAIN_IS_WINDOWS) endif() endif() -# FIXME: The get_property() command only works correctly for single configuration generators -# so we can't use it. We also can't use ``$`` because the ``OUTPUT`` -# argument to ``add_custom_commands()`` won't accept it. For now just output file to the -# current binary directory. -# get_property(Z3_DOTNET_ASSEMBLY_OUTPUT_DIR TARGET libz3 PROPERTY LIBRARY_OUTPUT_DIRECTORY) -set(Z3_DOTNET_ASSEMBLY_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") +# FIXME: Ideally we should emit files into a configuration specific directory +# when using multi-configuration generators so that the files generated by each +# configuration don't clobber each other. Unfortunately the ``get_property()`` +# command only works correctly for single configuration generators so we can't +# use it. We also can't use ``$`` because the ``OUTPUT`` +# argument to ``add_custom_commands()`` won't accept it. +# See http://public.kitware.com/pipermail/cmake/2016-March/063101.html +# +# For now just output file to the root binary directory like the Python build +# system does and emit a warning when appropriate. +if (DEFINED CMAKE_CONFIGURATION_TYPES) + # Multi-configuration build (e.g. Visual Studio and Xcode). + message(WARNING "You are using a multi-configuration generator. The build rules for" + " the \".NET\" bindings currently do not emit files per configuration so previously" + " generated files for other configurations will be overwritten.") +endif() + +set(Z3_DOTNET_ASSEMBLY_OUTPUT_DIR "${CMAKE_BINARY_DIR}") set(Z3_DOTNET_ASSEMBLY_NAME "Microsoft.Z3.dll") set(Z3_DOTNET_ASSEMBLY_DLL "${Z3_DOTNET_ASSEMBLY_OUTPUT_DIR}/${Z3_DOTNET_ASSEMBLY_NAME}") # csc.exe doesn't work with UNIX style paths so convert to native path @@ -252,7 +264,9 @@ if (DOTNET_TOOLCHAIN_IS_MONO) # to find the assembly install(FILES "${Z3_DOTNET_PKGCONFIG_FILE}" DESTINATION "${CMAKE_INSTALL_PKGCONFIGDIR}") - # Configure the install and uninstall scripts + # Configure the install and uninstall scripts. + # Note: If multi-configuration generator support is ever fixed then these + # scripts will be broken. configure_file(cmake_install_gac.cmake.in cmake_install_gac.cmake @ONLY) configure_file(cmake_uninstall_gac.cmake.in cmake_uninstall_gac.cmake @ONLY) @@ -268,9 +282,10 @@ if (DOTNET_TOOLCHAIN_IS_MONO) add_dependencies(uninstall remove_dotnet_dll_from_gac) elseif(DOTNET_TOOLCHAIN_IS_WINDOWS) - # FIXME: This isn't implemented because I'm not sure how/if the assembly should - # be installed to the GAC. - message(WARNING "Install of .NET bindings is not implemented for Windows") + # Don't install Z3_DOTNET_ASSEMBLY_DLL into the gac. Instead just copy into + # installation directory. + install(FILES "${Z3_DOTNET_ASSEMBLY_DLL}" DESTINATION "${CMAKE_INSTALL_LIBDIR}") + install(FILES "${Z3_DOTNET_ASSEMBLY_DLL_DOC}" DESTINATION "${CMAKE_INSTALL_LIBDIR}") else() message(FATAL_ERROR "Unknown .NET toolchain") endif() diff --git a/contrib/cmake/src/api/java/CMakeLists.txt b/contrib/cmake/src/api/java/CMakeLists.txt new file mode 100644 index 000000000..6e16ddb74 --- /dev/null +++ b/contrib/cmake/src/api/java/CMakeLists.txt @@ -0,0 +1,236 @@ +find_package(Java REQUIRED) +find_package(JNI REQUIRED) +include(UseJava) + +# Sanity check for dirty source tree +foreach (file_name "enumerations" "Native.cpp" "Native.java") + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${file_name}") + message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/${file_name}\"" + ${z3_polluted_tree_msg}) + endif() +endforeach() + +set(Z3_JAVA_PACKAGE_NAME "com.microsoft.z3") + +# Rule to generate ``Native.java`` and ``Native.cpp`` +set(Z3_JAVA_NATIVE_JAVA "${CMAKE_CURRENT_BINARY_DIR}/Native.java") +set(Z3_JAVA_NATIVE_CPP "${CMAKE_CURRENT_BINARY_DIR}/Native.cpp") +add_custom_command(OUTPUT "${Z3_JAVA_NATIVE_JAVA}" "${Z3_JAVA_NATIVE_CPP}" + COMMAND "${PYTHON_EXECUTABLE}" + "${CMAKE_SOURCE_DIR}/scripts/update_api.py" + ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} + "--java-output-dir" + "${CMAKE_CURRENT_BINARY_DIR}" + "--java-package-name" + ${Z3_JAVA_PACKAGE_NAME} + DEPENDS + ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} + "${CMAKE_SOURCE_DIR}/scripts/update_api.py" + ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} + # FIXME: When update_api.py no longer uses ``mk_util`` drop this dependency + "${CMAKE_SOURCE_DIR}/scripts/mk_util.py" + COMMENT "Generating \"${Z3_JAVA_NATIVE_JAVA}\" and \"${Z3_JAVA_NATIVE_CPP}\"" + ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} +) + +# Add rule to build native code that provides a bridge between +# ``Native.java`` and libz3's interfac3. +add_library(z3java SHARED ${Z3_JAVA_NATIVE_CPP}) +target_link_libraries(z3java PRIVATE libz3) +# FIXME: +# Not sure if using all the flags used by the Z3 components is really necessary +# here. At the bare minimum setting _AMD64_ depending on the target is +# necessary but seeing as the Python build system uses all the flags used for building +# Z3's components to build ``Native.cpp`` lets do the same for now. +target_compile_options(z3java PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) +target_compile_definitions(z3java PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) +z3_append_linker_flag_list_to_target(z3java ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) +target_include_directories(z3java PRIVATE + "${CMAKE_SOURCE_DIR}/src/api" + "${CMAKE_BINARY_DIR}/src/api" + ${JNI_INCLUDE_DIRS} +) +# FIXME: Should this library have SONAME and VERSION set? + +# This prevents CMake from automatically defining ``z3java_EXPORTS`` +set_property(TARGET z3java PROPERTY DEFINE_SYMBOL "") + +# Rule to generate the ``com.microsoft.z3.enumerations`` package +# FIXME: This list of files is fragile +set(Z3_JAVA_ENUMERATION_PACKAGE_FILES + Z3_ast_kind.java + Z3_ast_print_mode.java + Z3_decl_kind.java + Z3_error_code.java + Z3_goal_prec.java + Z3_lbool.java + Z3_param_kind.java + Z3_parameter_kind.java + Z3_sort_kind.java + Z3_symbol_kind.java +) +set(Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH "") +foreach (enum_file ${Z3_JAVA_ENUMERATION_PACKAGE_FILES}) + list(APPEND Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH + "${CMAKE_CURRENT_BINARY_DIR}/enumerations/${enum_file}" + ) +endforeach() +add_custom_command(OUTPUT ${Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH} + COMMAND "${PYTHON_EXECUTABLE}" + "${CMAKE_SOURCE_DIR}/scripts/mk_consts_files.py" + ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} + "--java-output-dir" + "${CMAKE_CURRENT_BINARY_DIR}" + "--java-package-name" + ${Z3_JAVA_PACKAGE_NAME} + DEPENDS + ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} + "${CMAKE_SOURCE_DIR}/scripts/mk_consts_files.py" + ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} + COMMENT "Generating ${Z3_JAVA_PACKAGE_NAME}.enumerations package" + ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} +) + +set(Z3_JAVA_JAR_SOURCE_FILES + AlgebraicNum.java + ApplyResultDecRefQueue.java + ApplyResult.java + ArithExpr.java + ArithSort.java + ArrayExpr.java + ArraySort.java + ASTDecRefQueue.java + AST.java + AstMapDecRefQueue.java + ASTMap.java + AstVectorDecRefQueue.java + ASTVector.java + BitVecExpr.java + BitVecNum.java + BitVecSort.java + BoolExpr.java + BoolSort.java + Constructor.java + ConstructorList.java + Context.java + DatatypeExpr.java + DatatypeSort.java + EnumSort.java + Expr.java + FiniteDomainExpr.java + FiniteDomainNum.java + FiniteDomainSort.java + FixedpointDecRefQueue.java + Fixedpoint.java + FPExpr.java + FPNum.java + FPRMExpr.java + FPRMNum.java + FPRMSort.java + FPSort.java + FuncDecl.java + FuncInterpDecRefQueue.java + FuncInterpEntryDecRefQueue.java + FuncInterp.java + Global.java + GoalDecRefQueue.java + Goal.java + IDecRefQueue.java + IDisposable.java + InterpolationContext.java + IntExpr.java + IntNum.java + IntSort.java + IntSymbol.java + ListSort.java + Log.java + ModelDecRefQueue.java + Model.java + OptimizeDecRefQueue.java + Optimize.java + ParamDescrsDecRefQueue.java + ParamDescrs.java + ParamsDecRefQueue.java + Params.java + Pattern.java + ProbeDecRefQueue.java + Probe.java + Quantifier.java + RatNum.java + RealExpr.java + RealSort.java + ReExpr.java + RelationSort.java + ReSort.java + SeqExpr.java + SeqSort.java + SetSort.java + SolverDecRefQueue.java + Solver.java + Sort.java + StatisticsDecRefQueue.java + Statistics.java + Status.java + StringSymbol.java + Symbol.java + TacticDecRefQueue.java + Tactic.java + TupleSort.java + UninterpretedSort.java + Version.java + Z3Exception.java + Z3Object.java +) +set(Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH "") +foreach (java_src_file ${Z3_JAVA_JAR_SOURCE_FILES}) + list(APPEND Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${java_src_file}") +endforeach() +# Add generated files to list +list(APPEND Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH + ${Z3_JAVA_NATIVE_JAVA} + ${Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH} +) + +# Convenient top-level target +add_custom_target(build_z3_java_bindings + ALL + DEPENDS + z3java + z3JavaJar +) + +# Rule to build ``com.microsoft.z3.jar`` +# TODO: Should we set ``CMAKE_JNI_TARGET`` to ``TRUE``? +add_jar(z3JavaJar + SOURCES ${Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH} + OUTPUT_NAME ${Z3_JAVA_PACKAGE_NAME} + OUTPUT_DIR "${CMAKE_BINARY_DIR}" + VERSION "${Z3_VERSION}" +) + +############################################################################### +# Install +############################################################################### +option(INSTALL_JAVA_BINDINGS "Install Java bindings when invoking install target" ON) +if (INSTALL_JAVA_BINDINGS) + # Provide cache variables for the install locations that the user can change. + # This defaults to ``/usr/local/java`` which seems to be the location for ``.jar`` + # files on Linux distributions + set(Z3_JAVA_JAR_INSTALLDIR + "${CMAKE_INSTALL_DATAROOTDIR}/java" + CACHE + PATH + "Directory to install Z3 Java jar file relative to install prefix" + ) + # FIXME: I don't think this the right installation location + set(Z3_JAVA_JNI_LIB_INSTALLDIR + "${CMAKE_INSTALL_LIBDIR}" + CACHE + PATH + "Directory to install Z3 Java JNI bridge library relative to install prefix" + ) + install(TARGETS z3java DESTINATION "${Z3_JAVA_JNI_LIB_INSTALLDIR}") + # Note: Don't use ``DESTINATION`` here as the version of ``UseJava.cmake`` shipped + # with CMake 2.8.12.2 handles that incorrectly. + install_jar(z3JavaJar "${Z3_JAVA_JAR_INSTALLDIR}") +endif() diff --git a/contrib/cmake/src/math/simplex/CMakeLists.txt b/contrib/cmake/src/math/simplex/CMakeLists.txt index 6f965919d..de55f1634 100644 --- a/contrib/cmake/src/math/simplex/CMakeLists.txt +++ b/contrib/cmake/src/math/simplex/CMakeLists.txt @@ -1,6 +1,7 @@ z3_add_component(simplex SOURCES simplex.cpp + model_based_opt.cpp COMPONENT_DEPENDENCIES util ) diff --git a/contrib/cmake/src/test/CMakeLists.txt b/contrib/cmake/src/test/CMakeLists.txt index f504760e0..427cedcdb 100644 --- a/contrib/cmake/src/test/CMakeLists.txt +++ b/contrib/cmake/src/test/CMakeLists.txt @@ -61,6 +61,7 @@ add_executable(test-z3 "${CMAKE_CURRENT_BINARY_DIR}/mem_initializer.cpp" memory.cpp model2expr.cpp + model_based_opt.cpp model_evaluator.cpp model_retrieval.cpp mpbq.cpp diff --git a/scripts/mk_consts_files.py b/scripts/mk_consts_files.py index 482b13c94..e582d8468 100755 --- a/scripts/mk_consts_files.py +++ b/scripts/mk_consts_files.py @@ -17,6 +17,11 @@ def main(args): parser.add_argument("api_files", nargs="+") parser.add_argument("--z3py-output-dir", dest="z3py_output_dir", default=None) parser.add_argument("--dotnet-output-dir", dest="dotnet_output_dir", default=None) + parser.add_argument("--java-output-dir", dest="java_output_dir", default=None) + parser.add_argument("--java-package-name", + dest="java_package_name", + default=None, + help="Name to give the Java package (e.g. ``com.microsoft.z3``).") pargs = parser.parse_args(args) if not mk_genfile_common.check_files_exist(pargs.api_files): @@ -41,6 +46,20 @@ def main(args): logging.info('Generated "{}"'.format(output)) count += 1 + if pargs.java_output_dir: + if pargs.java_package_name == None: + logging.error('Java package name must be specified') + return 1 + if not mk_genfile_common.check_dir_exists(pargs.java_output_dir): + return 1 + outputs = mk_genfile_common.mk_z3consts_java_internal( + pargs.api_files, + pargs.java_package_name, + pargs.java_output_dir) + for generated_file in outputs: + logging.info('Generated "{}"'.format(generated_file)) + count += 1 + if 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 0734d52b0..b8d6ac5e1 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -255,6 +255,116 @@ def mk_z3consts_dotnet_internal(api_files, output_dir): z3consts.close() return z3consts_output_path + +def mk_z3consts_java_internal(api_files, package_name, output_dir): + """ + Generate "com.microsoft.z3.enumerations" package from the list of API + header files in ``api_files`` and write the package directory into + the ``output_dir`` directory + + Returns a list of the generated java source files. + """ + blank_pat = re.compile("^ *$") + comment_pat = re.compile("^ *//.*$") + typedef_pat = re.compile("typedef enum *") + typedef2_pat = re.compile("typedef enum { *") + openbrace_pat = re.compile("{ *") + closebrace_pat = re.compile("}.*;") + + DeprecatedEnums = [ 'Z3_search_failure' ] + gendir = os.path.join(output_dir, "enumerations") + if not os.path.exists(gendir): + os.mkdir(gendir) + + generated_enumeration_files = [] + for api_file in api_files: + api = open(api_file, 'r') + + SEARCHING = 0 + FOUND_ENUM = 1 + IN_ENUM = 2 + + mode = SEARCHING + decls = {} + idx = 0 + + linenum = 1 + for line in api: + m1 = blank_pat.match(line) + m2 = comment_pat.match(line) + if m1 or m2: + # skip blank lines and comments + linenum = linenum + 1 + elif mode == SEARCHING: + m = typedef_pat.match(line) + if m: + mode = FOUND_ENUM + m = typedef2_pat.match(line) + if m: + mode = IN_ENUM + decls = {} + idx = 0 + elif mode == FOUND_ENUM: + m = openbrace_pat.match(line) + if m: + mode = IN_ENUM + decls = {} + idx = 0 + else: + assert False, "Invalid %s, line: %s" % (api_file, linenum) + else: + assert mode == IN_ENUM + words = re.split('[^\-a-zA-Z0-9_]+', line) + m = closebrace_pat.match(line) + if m: + name = words[1] + if name not in DeprecatedEnums: + efile = open('%s.java' % os.path.join(gendir, name), 'w') + generated_enumeration_files.append(efile.name) + efile.write('/**\n * Automatically generated file\n **/\n\n') + efile.write('package %s.enumerations;\n\n' % package_name) + + efile.write('/**\n') + efile.write(' * %s\n' % name) + efile.write(' **/\n') + efile.write('public enum %s {\n' % name) + efile.write + first = True + # Iterate over key-value pairs ordered by value + for k, v in sorted(decls.items(), key=lambda pair: pair[1]): + if first: + first = False + else: + efile.write(',\n') + efile.write(' %s (%s)' % (k, v)) + efile.write(";\n") + efile.write('\n private final int intValue;\n\n') + efile.write(' %s(int v) {\n' % name) + efile.write(' this.intValue = v;\n') + efile.write(' }\n\n') + efile.write(' 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(' }\n\n') + efile.write(' public final int toInt() { return this.intValue; }\n') + # efile.write(';\n %s(int v) {}\n' % name) + efile.write('}\n\n') + efile.close() + mode = SEARCHING + else: + if words[2] != '': + if len(words[2]) > 1 and words[2][1] == 'x': + idx = int(words[2], 16) + else: + idx = int(words[2]) + decls[words[1]] = idx + idx = idx + 1 + linenum = linenum + 1 + api.close() + return generated_enumeration_files + + ############################################################################### # Functions for generating a "module definition file" for MSVC ############################################################################### diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 282d40590..a73a0e59c 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2776,109 +2776,19 @@ def mk_z3consts_dotnet(api_files): # Extract enumeration types from z3_api.h, and add Java definitions def mk_z3consts_java(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("}.*;") - java = get_component(JAVA_COMPONENT) - - DeprecatedEnums = [ 'Z3_search_failure' ] - gendir = os.path.join(java.src_dir, "enumerations") - if not os.path.exists(gendir): - os.mkdir(gendir) - + full_path_api_files = [] for api_file in api_files: api_file_c = java.find_file(api_file, java.name) api_file = os.path.join(api_file_c.src_dir, api_file) - - api = open(api_file, 'r') - - SEARCHING = 0 - FOUND_ENUM = 1 - IN_ENUM = 2 - - mode = SEARCHING - decls = {} - idx = 0 - - linenum = 1 - for line in api: - m1 = blank_pat.match(line) - m2 = comment_pat.match(line) - if m1 or m2: - # skip blank lines and comments - linenum = linenum + 1 - elif mode == SEARCHING: - m = typedef_pat.match(line) - if m: - mode = FOUND_ENUM - m = typedef2_pat.match(line) - if m: - mode = IN_ENUM - decls = {} - idx = 0 - elif mode == FOUND_ENUM: - m = openbrace_pat.match(line) - if m: - mode = IN_ENUM - decls = {} - idx = 0 - else: - assert False, "Invalid %s, line: %s" % (api_file, linenum) - else: - assert mode == IN_ENUM - words = re.split('[^\-a-zA-Z0-9_]+', line) - m = closebrace_pat.match(line) - if m: - name = words[1] - if name not in DeprecatedEnums: - efile = open('%s.java' % os.path.join(gendir, name), 'w') - efile.write('/**\n * Automatically generated file\n **/\n\n') - efile.write('package %s.enumerations;\n\n' % java.package_name) - - efile.write('/**\n') - efile.write(' * %s\n' % name) - efile.write(' **/\n') - efile.write('public enum %s {\n' % name) - efile.write - first = True - for k in decls: - i = decls[k] - if first: - first = False - else: - efile.write(',\n') - efile.write(' %s (%s)' % (k, i)) - efile.write(";\n") - efile.write('\n private final int intValue;\n\n') - efile.write(' %s(int v) {\n' % name) - efile.write(' this.intValue = v;\n') - efile.write(' }\n\n') - efile.write(' 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(' }\n\n') - efile.write(' public final int toInt() { return this.intValue; }\n') - # efile.write(';\n %s(int v) {}\n' % name) - efile.write('}\n\n') - efile.close() - mode = SEARCHING - else: - if words[2] != '': - if len(words[2]) > 1 and words[2][1] == 'x': - idx = int(words[2], 16) - else: - idx = int(words[2]) - decls[words[1]] = idx - idx = idx + 1 - linenum = linenum + 1 - api.close() + full_path_api_files.append(api_file) + generated_files = mk_genfile_common.mk_z3consts_java_internal( + full_path_api_files, + java.package_name, + java.src_dir) if VERBOSE: - print("Generated '%s'" % ('%s' % gendir)) + for generated_file in generated_files: + print("Generated '{}'".format(generated_file)) # Extract enumeration types from z3_api.h, and add ML definitions def mk_z3consts_ml(api_files): diff --git a/src/ackermannization/ackr_helper.h b/src/ackermannization/ackr_helper.h index 5c572907e..327763da4 100644 --- a/src/ackermannization/ackr_helper.h +++ b/src/ackermannization/ackr_helper.h @@ -33,19 +33,12 @@ class ackr_helper { which are not marked as uninterpreted but effectively are. */ inline bool should_ackermannize(app const * a) const { - if (a->get_family_id() == m_bvutil.get_family_id()) { - switch (a->get_decl_kind()) { - case OP_BSDIV0: - case OP_BUDIV0: - case OP_BSREM0: - case OP_BUREM0: - case OP_BSMOD0: - return true; - default: - return is_uninterp(a); - } + if (is_uninterp(a)) + return true; + else { + decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id()); + return p->is_considered_uninterpreted(a->get_decl()); } - return (is_uninterp(a)); } inline bv_util& bvutil() { return m_bvutil; } diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 47a25f313..bc48874a7 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -131,7 +131,7 @@ namespace api { } char * context::mk_external_string(char const * str) { - m_string_buffer = str; + m_string_buffer = str?str:""; return const_cast(m_string_buffer.c_str()); } diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index fad4ca7a2..6e8544770 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -876,7 +876,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 @@ -1275,6 +1275,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)); } + template class cast_ast; template<> class cast_ast { diff --git a/src/api/dotnet/ArithExpr.cs b/src/api/dotnet/ArithExpr.cs index 7858ff3e1..b6beaef0c 100644 --- a/src/api/dotnet/ArithExpr.cs +++ b/src/api/dotnet/ArithExpr.cs @@ -38,5 +38,136 @@ namespace Microsoft.Z3 Contract.Requires(ctx != null); } #endregion + + #region Operators + + private static ArithExpr MkNum(ArithExpr e, int i) { return (ArithExpr)e.Context.MkNumeral(i, e.Context.MkIntSort()); } + + private static ArithExpr MkNum(ArithExpr e, double d) { return (ArithExpr)e.Context.MkNumeral(d.ToString(), e.Context.MkRealSort()); } + + /// Operator overloading for arithmetical divsion operator (over reals) + public static ArithExpr operator /(ArithExpr a, ArithExpr b) { return a.Context.MkDiv(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator /(ArithExpr a, int b) { return a / MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator /(ArithExpr a, double b) { return a / MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator /(int a, ArithExpr b) { return MkNum(b, a) / b; } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator /(double a, ArithExpr b) { return MkNum(b, a) / b; } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(ArithExpr a) { return a.Context.MkUnaryMinus(a); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(ArithExpr a, ArithExpr b) { return a.Context.MkSub(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(ArithExpr a, int b) { return a - MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(ArithExpr a, double b) { return a - MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(int a, ArithExpr b) { return MkNum(b, a) - b; } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(double a, ArithExpr b) { return MkNum(b, a) - b; } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator +(ArithExpr a, ArithExpr b) { return a.Context.MkAdd(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator +(ArithExpr a, int b) { return a + MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator +(ArithExpr a, double b) { return a + MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator +(int a, ArithExpr b) { return MkNum(b, a) + b; } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator +(double a, ArithExpr b) { return MkNum(b, a) + b; } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator *(ArithExpr a, ArithExpr b) { return a.Context.MkMul(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator *(ArithExpr a, int b) { return a * MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator *(ArithExpr a, double b) { return a * MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator *(int a, ArithExpr b) { return MkNum(b, a) * b; } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator *(double a, ArithExpr b) { return MkNum(b, a) * b; } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <=(ArithExpr a, ArithExpr b) { return a.Context.MkLe(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <=(ArithExpr a, int b) { return a <= MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <=(ArithExpr a, double b) { return a <= MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <=(int a, ArithExpr b) { return MkNum(b, a) <= b; } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <=(double a, ArithExpr b) { return MkNum(b, a) <= b; } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <(ArithExpr a, ArithExpr b) { return a.Context.MkLt(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <(ArithExpr a, int b) { return a < MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <(ArithExpr a, double b) { return a < MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <(int a, ArithExpr b) { return MkNum(b, a) < b; } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <(double a, ArithExpr b) { return MkNum(b, a) < b; } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >(ArithExpr a, ArithExpr b) { return a.Context.MkGt(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >(ArithExpr a, int b) { return a > MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >(ArithExpr a, double b) { return a > MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >(int a, ArithExpr b) { return MkNum(b, a) > b; } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >(double a, ArithExpr b) { return MkNum(b, a) > b; } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(ArithExpr a, ArithExpr b) { return a.Context.MkGe(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(ArithExpr a, int b) { return a >= MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(ArithExpr a, double b) { return a >= MkNum(a, b); } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(int a, ArithExpr b) { return MkNum(b, a) >= b; } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(double a, ArithExpr b) { return MkNum(b, a) >= b; } + + #endregion } } diff --git a/src/api/dotnet/BoolExpr.cs b/src/api/dotnet/BoolExpr.cs index a9a15e4dc..c52109352 100644 --- a/src/api/dotnet/BoolExpr.cs +++ b/src/api/dotnet/BoolExpr.cs @@ -34,5 +34,20 @@ namespace Microsoft.Z3 /// Constructor for BoolExpr internal BoolExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion + + #region Operators + /// Disjunction of Boolean expressions + public static BoolExpr operator|(BoolExpr a, BoolExpr b) { return a.Context.MkOr(a, b); } + + /// Conjunction of Boolean expressions + public static BoolExpr operator &(BoolExpr a, BoolExpr b) { return a.Context.MkAnd(a, b); } + + /// Xor of Boolean expressions + public static BoolExpr operator ^(BoolExpr a, BoolExpr b) { return a.Context.MkXor(a, b); } + + /// Negation + public static BoolExpr operator !(BoolExpr a) { return a.Context.MkNot(a); } + + #endregion } } diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index a97036897..5bff8960a 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; +using System.Linq; namespace Microsoft.Z3 { @@ -814,6 +815,20 @@ namespace Microsoft.Z3 return Expr.Create(this, f, args); } + /// + /// Create a new function application. + /// + public Expr MkApp(FuncDecl f, IEnumerable args) + { + Contract.Requires(f != null); + Contract.Requires(args == null || Contract.ForAll(args, a => a != null)); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(f); + CheckContextMatch(args); + return Expr.Create(this, f, args.ToArray()); + } + #region Propositional /// /// The true Term. @@ -959,6 +974,18 @@ namespace Microsoft.Z3 return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } + /// + /// Create an expression representing t[0] and t[1] and .... + /// + public BoolExpr MkAnd(IEnumerable t) + { + Contract.Requires(t != null); + Contract.Requires(Contract.ForAll(t, a => a != null)); + Contract.Ensures(Contract.Result() != null); + CheckContextMatch(t); + return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Count(), AST.EnumToNative(t))); + } + /// /// Create an expression representing t[0] or t[1] or .... /// @@ -973,6 +1000,19 @@ namespace Microsoft.Z3 } + /// + /// Create an expression representing t[0] or t[1] or .... + /// + public BoolExpr MkOr(IEnumerable t) + { + Contract.Requires(t != null); + Contract.Requires(Contract.ForAll(t, a => a != null)); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(t); + return new BoolExpr(this, Native.Z3_mk_or(nCtx, (uint)t.Count(), AST.EnumToNative(t))); + } + #endregion #region Arithmetic @@ -989,6 +1029,19 @@ namespace Microsoft.Z3 return (ArithExpr)Expr.Create(this, Native.Z3_mk_add(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } + /// + /// Create an expression representing t[0] + t[1] + .... + /// + public ArithExpr MkAdd(IEnumerable t) + { + 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_add(nCtx, (uint)t.Count(), AST.EnumToNative(t))); + } + /// /// Create an expression representing t[0] * t[1] * .... /// @@ -4743,6 +4796,21 @@ namespace Microsoft.Z3 } } + [Pure] + internal void CheckContextMatch(IEnumerable arr) + { + Contract.Requires(arr == null || Contract.ForAll(arr, a => a != null)); + + if (arr != null) + { + foreach (Z3Object a in arr) + { + Contract.Assert(a != null); // It was an assume, now we added the precondition, and we made it into an assert + CheckContextMatch(a); + } + } + } + [ContractInvariantMethod] private void ObjectInvariant() { diff --git a/src/api/dotnet/Optimize.cs b/src/api/dotnet/Optimize.cs index 501603668..304e3e86f 100644 --- a/src/api/dotnet/Optimize.cs +++ b/src/api/dotnet/Optimize.cs @@ -66,6 +66,38 @@ namespace Microsoft.Z3 /// Assert a constraint (or multiple) into the optimize solver. /// public void Assert(params BoolExpr[] constraints) + { + AddConstraints(constraints); + } + + /// + /// Assert a constraint (or multiple) into the optimize solver. + /// + public void Assert(IEnumerable constraints) + { + AddConstraints(constraints); + } + + /// + /// Alias for Assert. + /// + public void Add(params BoolExpr[] constraints) + { + AddConstraints(constraints); + } + + /// + /// Alias for Assert. + /// + public void Add(IEnumerable constraints) + { + AddConstraints(constraints); + } + + /// + /// Assert a constraint (or multiple) into the optimize solver. + /// + private void AddConstraints(IEnumerable constraints) { Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); @@ -76,15 +108,6 @@ namespace Microsoft.Z3 Native.Z3_optimize_assert(Context.nCtx, NativeObject, a.NativeObject); } } - - /// - /// Alias for Assert. - /// - public void Add(params BoolExpr[] constraints) - { - Assert(constraints); - } - /// /// Handle to objectives returned by objective functions. /// diff --git a/src/api/dotnet/Z3Object.cs b/src/api/dotnet/Z3Object.cs index 8e474041a..cd6803341 100644 --- a/src/api/dotnet/Z3Object.cs +++ b/src/api/dotnet/Z3Object.cs @@ -20,6 +20,8 @@ Notes: using System; using System.Diagnostics.Contracts; using System.Threading; +using System.Collections.Generic; +using System.Linq; namespace Microsoft.Z3 { @@ -135,6 +137,23 @@ namespace Microsoft.Z3 return an; } + [Pure] + internal static IntPtr[] EnumToNative(IEnumerable a) + { + Contract.Ensures(a == null || Contract.Result() != null); + Contract.Ensures(a == null || Contract.Result().Length == a.Count()); + + if (a == null) return null; + IntPtr[] an = new IntPtr[a.Count()]; + int i = 0; + foreach (var ai in a) + { + if (ai != null) an[i] = ai.NativeObject; + ++i; + } + return an; + } + [Pure] internal static uint ArrayLength(Z3Object[] a) { diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 211b6777e..848883ba3 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -52,7 +52,7 @@ import math if sys.version < '3': def _is_int(v): - return isinstance(v, int) or isinstance(v, long) + return isinstance(v, (int, long)) else: def _is_int(v): return isinstance(v, int) @@ -95,7 +95,7 @@ def append_log(s): def to_symbol(s, ctx=None): """Convert an integer or string into a Z3 symbol.""" - if isinstance(s, int): + if _is_int(s): return Z3_mk_int_symbol(_get_ctx(ctx).ref(), s) else: return Z3_mk_string_symbol(_get_ctx(ctx).ref(), s) @@ -3627,7 +3627,7 @@ def Extract(high, low, a): return SeqRef(Z3_mk_seq_extract(s.ctx_ref(), s.as_ast(), offset.as_ast(), length.as_ast()), s.ctx) if __debug__: _z3_assert(low <= high, "First argument must be greater than or equal to second argument") - _z3_assert(isinstance(high, int) and high >= 0 and isinstance(low, int) and low >= 0, "First and second arguments must be non negative integers") + _z3_assert(_is_int(high) and high >= 0 and _is_int(low) and low >= 0, "First and second arguments must be non negative integers") _z3_assert(is_bv(a), "Third argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_extract(a.ctx_ref(), high, low, a.as_ast()), a.ctx) @@ -3849,7 +3849,7 @@ def SignExt(n, a): fe """ if __debug__: - _z3_assert(isinstance(n, int), "First argument must be an integer") + _z3_assert(_is_int(n), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_sign_ext(a.ctx_ref(), n, a.as_ast()), a.ctx) @@ -3876,7 +3876,7 @@ def ZeroExt(n, a): 8 """ if __debug__: - _z3_assert(isinstance(n, int), "First argument must be an integer") + _z3_assert(_is_int(n), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_zero_ext(a.ctx_ref(), n, a.as_ast()), a.ctx) @@ -3899,7 +3899,7 @@ def RepeatBitVec(n, a): aaaa """ if __debug__: - _z3_assert(isinstance(n, int), "First argument must be an integer") + _z3_assert(_is_int(n), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_repeat(a.ctx_ref(), n, a.as_ast()), a.ctx) @@ -4580,7 +4580,7 @@ class ParamsRef: name_sym = to_symbol(name, self.ctx) if isinstance(val, bool): Z3_params_set_bool(self.ctx.ref(), self.params, name_sym, val) - elif isinstance(val, int): + elif _is_int(val): Z3_params_set_uint(self.ctx.ref(), self.params, name_sym, val) elif isinstance(val, float): Z3_params_set_double(self.ctx.ref(), self.params, name_sym, val) @@ -5627,7 +5627,7 @@ class ModelRef(Z3PPObject): x -> 1 f -> [1 -> 0, else -> 0] """ - if isinstance(idx, int): + if _is_int(idx): if idx >= len(self): raise IndexError num_consts = Z3_model_get_num_consts(self.ctx.ref(), self.model) @@ -9310,7 +9310,7 @@ def IndexOf(s, substr, offset): ctx = _get_ctx2(s, substr, ctx) s = _coerce_seq(s, ctx) substr = _coerce_seq(substr, ctx) - if isinstance(offset, int): + if _is_int(offset): offset = IntVal(offset, ctx) return SeqRef(Z3_mk_seq_index(s.ctx_ref(), s.as_ast(), substr.as_ast(), offset.as_ast()), s.ctx) diff --git a/src/api/z3_replayer.cpp b/src/api/z3_replayer.cpp index 8ce81ec31..b1baa6de2 100644 --- a/src/api/z3_replayer.cpp +++ b/src/api/z3_replayer.cpp @@ -23,6 +23,7 @@ Notes: #include"symbol.h" #include"trace.h" #include +#include void register_z3_replayer_cmds(z3_replayer & in); @@ -46,7 +47,7 @@ struct z3_replayer::imp { size_t m_ptr; size_t_map m_heap; svector m_cmds; - vector m_cmds_names; + std::vector m_cmds_names; enum value_kind { INT64, UINT64, DOUBLE, STRING, SYMBOL, OBJECT, UINT_ARRAY, INT_ARRAY, SYMBOL_ARRAY, OBJECT_ARRAY, FLOAT }; @@ -676,7 +677,9 @@ struct z3_replayer::imp { void register_cmd(unsigned id, z3_replayer_cmd cmd, char const* name) { m_cmds.reserve(id+1, 0); - m_cmds_names.reserve(id+1, ""); + while (static_cast(m_cmds_names.size()) <= id+1) { + m_cmds_names.push_back(""); + } m_cmds[id] = cmd; m_cmds_names[id] = name; } diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 21a06a71b..0365a267e 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -82,7 +82,7 @@ void parameter::del_eh(ast_manager & m, family_id fid) { } } -bool parameter::operator==(parameter const & p) const { +bool parameter::operator==(parameter const & p) const { if (m_kind != p.m_kind) return false; switch(m_kind) { case PARAM_INT: return m_int == p.m_int; @@ -138,7 +138,7 @@ void display_parameters(std::ostream & out, unsigned n, parameter const * p) { // ----------------------------------- family_id family_manager::mk_family_id(symbol const & s) { - family_id r; + family_id r; if (m_families.find(s, r)) { return r; } @@ -150,7 +150,7 @@ family_id family_manager::mk_family_id(symbol const & s) { } family_id family_manager::get_family_id(symbol const & s) const { - family_id r; + family_id r; if (m_families.find(s, r)) return r; else @@ -167,7 +167,7 @@ bool family_manager::has_family(symbol const & s) const { // // ----------------------------------- -decl_info::decl_info(family_id family_id, decl_kind k, unsigned num_parameters, +decl_info::decl_info(family_id family_id, decl_kind k, unsigned num_parameters, parameter const * parameters, bool private_params): m_family_id(family_id), m_kind(k), @@ -179,7 +179,7 @@ decl_info::decl_info(decl_info const& other) : m_family_id(other.m_family_id), m_kind(other.m_kind), m_parameters(other.m_parameters.size(), other.m_parameters.c_ptr()), - m_private_parameters(other.m_private_parameters) { + m_private_parameters(other.m_private_parameters) { } @@ -212,7 +212,7 @@ unsigned decl_info::hash() const { } bool decl_info::operator==(decl_info const & info) const { - return m_family_id == info.m_family_id && m_kind == info.m_kind && + return m_family_id == info.m_family_id && m_kind == info.m_kind && compare_arrays(m_parameters.begin(), info.m_parameters.begin(), m_parameters.size()); } @@ -269,13 +269,13 @@ func_decl_info::func_decl_info(family_id family_id, decl_kind k, unsigned num_pa } bool func_decl_info::operator==(func_decl_info const & info) const { - return decl_info::operator==(info) && - m_left_assoc == info.m_left_assoc && - m_right_assoc == info.m_right_assoc && + return decl_info::operator==(info) && + m_left_assoc == info.m_left_assoc && + m_right_assoc == info.m_right_assoc && m_flat_associative == info.m_flat_associative && - m_commutative == info.m_commutative && + m_commutative == info.m_commutative && m_chainable == info.m_chainable && - m_pairwise == info.m_pairwise && + m_pairwise == info.m_pairwise && m_injective == info.m_injective && m_skolem == info.m_skolem; } @@ -394,7 +394,7 @@ quantifier::quantifier(bool forall, unsigned num_decls, sort * const * decl_sort sort * get_sort(expr const * n) { while (true) { switch(n->get_kind()) { - case AST_APP: + case AST_APP: return to_app(n)->get_decl()->get_range(); case AST_VAR: return to_var(n)->get_sort(); @@ -426,7 +426,7 @@ unsigned get_node_size(ast const * n) { default: UNREACHABLE(); } return 0; -} +} bool compare_nodes(ast const * n1, ast const * n2) { if (n1->get_kind() != n2->get_kind()) { @@ -452,32 +452,32 @@ bool compare_nodes(ast const * n1, ast const * n2) { to_func_decl(n1)->get_name() == to_func_decl(n2)->get_name() && to_func_decl(n1)->get_arity() == to_func_decl(n2)->get_arity() && to_func_decl(n1)->get_range() == to_func_decl(n2)->get_range() && - compare_arrays(to_func_decl(n1)->get_domain(), + compare_arrays(to_func_decl(n1)->get_domain(), to_func_decl(n2)->get_domain(), to_func_decl(n1)->get_arity()); case AST_APP: - return + return to_app(n1)->get_decl() == to_app(n2)->get_decl() && to_app(n1)->get_num_args() == to_app(n2)->get_num_args() && compare_arrays(to_app(n1)->get_args(), to_app(n2)->get_args(), to_app(n1)->get_num_args()); case AST_VAR: - return + return to_var(n1)->get_idx() == to_var(n2)->get_idx() && to_var(n1)->get_sort() == to_var(n2)->get_sort(); case AST_QUANTIFIER: - return + return to_quantifier(n1)->is_forall() == to_quantifier(n2)->is_forall() && to_quantifier(n1)->get_num_decls() == to_quantifier(n2)->get_num_decls() && compare_arrays(to_quantifier(n1)->get_decl_sorts(), to_quantifier(n2)->get_decl_sorts(), to_quantifier(n1)->get_num_decls()) && - to_quantifier(n1)->get_expr() == to_quantifier(n2)->get_expr() && - to_quantifier(n1)->get_weight() == to_quantifier(n2)->get_weight() && + to_quantifier(n1)->get_expr() == to_quantifier(n2)->get_expr() && + to_quantifier(n1)->get_weight() == to_quantifier(n2)->get_weight() && to_quantifier(n1)->get_num_patterns() == to_quantifier(n2)->get_num_patterns() && - compare_arrays(to_quantifier(n1)->get_patterns(), + compare_arrays(to_quantifier(n1)->get_patterns(), to_quantifier(n2)->get_patterns(), to_quantifier(n1)->get_num_patterns()) && - to_quantifier(n1)->get_num_no_patterns() == to_quantifier(n2)->get_num_no_patterns() && + to_quantifier(n1)->get_num_no_patterns() == to_quantifier(n2)->get_num_no_patterns() && compare_arrays(to_quantifier(n1)->get_no_patterns(), to_quantifier(n2)->get_no_patterns(), to_quantifier(n1)->get_num_no_patterns()); @@ -503,7 +503,7 @@ inline unsigned ast_array_hash(T * const * array, unsigned size, unsigned init_v default: { unsigned a, b, c; a = b = 0x9e3779b9; - c = init_value; + c = init_value; while (size >= 3) { size--; a += array[size]->hash(); @@ -529,7 +529,7 @@ unsigned get_asts_hash(unsigned sz, ast * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_apps_hash(unsigned sz, app * const* ns, unsigned init) { - return ast_array_hash(ns, sz, init); + return ast_array_hash(ns, sz, init); } unsigned get_exprs_hash(unsigned sz, expr * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); @@ -543,19 +543,19 @@ unsigned get_decl_hash(unsigned sz, func_decl* const* ns, unsigned init) { unsigned get_node_hash(ast const * n) { unsigned a, b, c; - + switch (n->get_kind()) { case AST_SORT: - if (to_sort(n)->get_info() == 0) + if (to_sort(n)->get_info() == 0) return to_sort(n)->get_name().hash(); else return combine_hash(to_sort(n)->get_name().hash(), to_sort(n)->get_info()->hash()); case AST_FUNC_DECL: - return ast_array_hash(to_func_decl(n)->get_domain(), to_func_decl(n)->get_arity(), - to_func_decl(n)->get_info() == 0 ? + return ast_array_hash(to_func_decl(n)->get_domain(), to_func_decl(n)->get_arity(), + to_func_decl(n)->get_info() == 0 ? to_func_decl(n)->get_name().hash() : combine_hash(to_func_decl(n)->get_name().hash(), to_func_decl(n)->get_info()->hash())); case AST_APP: - return ast_array_hash(to_app(n)->get_args(), + return ast_array_hash(to_app(n)->get_args(), to_app(n)->get_num_args(), to_app(n)->get_decl()->hash()); case AST_VAR: @@ -645,7 +645,7 @@ basic_decl_plugin::basic_decl_plugin(): m_not_decl(0), m_interp_decl(0), m_implies_decl(0), - + m_proof_sort(0), m_undef_decl(0), m_true_pr_decl(0), @@ -683,10 +683,10 @@ basic_decl_plugin::basic_decl_plugin(): bool basic_decl_plugin::check_proof_sorts(basic_op_kind k, unsigned arity, sort * const * domain) const { if (k == PR_UNDEF) return arity == 0; - if (arity == 0) + if (arity == 0) return false; else { - for (unsigned i = 0; i < arity - 1; i++) + for (unsigned i = 0; i < arity - 1; i++) if (domain[i] != m_proof_sort) return false; return domain[arity-1] == m_bool_sort || domain[arity-1] == m_proof_sort; @@ -696,14 +696,14 @@ bool basic_decl_plugin::check_proof_sorts(basic_op_kind k, unsigned arity, sort bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, expr * const * args) const { if (k == PR_UNDEF) return num_args == 0; - if (num_args == 0) + if (num_args == 0) return false; else { - for (unsigned i = 0; i < num_args - 1; i++) + for (unsigned i = 0; i < num_args - 1; i++) if (m_manager->get_sort(args[i]) != m_proof_sort) return false; - return - m_manager->get_sort(args[num_args - 1]) == m_bool_sort || + return + m_manager->get_sort(args[num_args - 1]) == m_bool_sort || m_manager->get_sort(args[num_args - 1]) == m_proof_sort; } } @@ -711,7 +711,7 @@ bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, exp func_decl * basic_decl_plugin::mk_bool_op_decl(char const * name, basic_op_kind k, unsigned num_args, bool assoc, bool comm, bool idempotent, bool flat_associative, bool chainable) { ptr_buffer domain; - for (unsigned i = 0; i < num_args; i++) + for (unsigned i = 0; i < num_args; i++) domain.push_back(m_bool_sort); func_decl_info info(m_family_id, k); info.set_associative(assoc); @@ -734,10 +734,10 @@ func_decl * basic_decl_plugin::mk_implies_decl() { } func_decl * basic_decl_plugin::mk_proof_decl( - char const * name, basic_op_kind k, + char const * name, basic_op_kind k, unsigned num_parameters, parameter const* params, unsigned num_parents) { ptr_buffer domain; - for (unsigned i = 0; i < num_parents; i++) + for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); domain.push_back(m_bool_sort); func_decl_info info(m_family_id, k, num_parameters, params); @@ -746,7 +746,7 @@ func_decl * basic_decl_plugin::mk_proof_decl( func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) { ptr_buffer domain; - for (unsigned i = 0; i < num_parents; i++) + for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); domain.push_back(m_bool_sort); func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents+1, domain.c_ptr(), m_proof_sort, func_decl_info(m_family_id, k)); @@ -756,7 +756,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, func_decl * basic_decl_plugin::mk_compressed_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) { ptr_buffer domain; - for (unsigned i = 0; i < num_parents; i++) + for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents, domain.c_ptr(), m_proof_sort, func_decl_info(m_family_id, k)); m_manager->inc_ref(d); @@ -793,7 +793,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_param #define MK_DECL(_decl_,_mk_decl_) if (!_decl_) _decl_ = _mk_decl_; return _decl_; - + func_decl * basic_decl_plugin::mk_proof_decl(char const* name, basic_op_kind k, unsigned num_parents, func_decl*& fn) { if (!fn) { fn = mk_proof_decl(name, k, num_parents); @@ -805,11 +805,11 @@ func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_paren SASSERT(k == PR_UNDEF || m_manager->proofs_enabled()); switch (static_cast(k)) { // - // A description of the semantics of the proof + // A description of the semantics of the proof // declarations is provided in z3_api.h // case PR_UNDEF: return m_undef_decl; - case PR_TRUE: return mk_proof_decl("true-axiom", k, 0, m_true_pr_decl); + case PR_TRUE: return mk_proof_decl("true-axiom", k, 0, m_true_pr_decl); case PR_ASSERTED: return mk_proof_decl("asserted", k, 0, m_asserted_decl); case PR_GOAL: return mk_proof_decl("goal", k, 2, m_goal_decl); case PR_MODUS_PONENS: return mk_proof_decl("mp", k, 2, m_modus_ponens_decl); @@ -869,11 +869,11 @@ void basic_decl_plugin::set_manager(ast_manager * m, family_id id) { m_not_decl = mk_bool_op_decl("not", OP_NOT, 1); m_interp_decl = mk_bool_op_decl("interp", OP_INTERP, 1); m_implies_decl = mk_implies_decl(); - + m_proof_sort = m->mk_sort(symbol("Proof"), sort_info(id, PROOF_SORT)); m->inc_ref(m_proof_sort); - - m_undef_decl = mk_compressed_proof_decl("undef", PR_UNDEF, 0); + + m_undef_decl = mk_compressed_proof_decl("undef", PR_UNDEF, 0); } void basic_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { @@ -884,7 +884,7 @@ void basic_decl_plugin::get_sort_names(svector & sort_names, symbo void basic_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { op_names.push_back(builtin_name("true", OP_TRUE)); - op_names.push_back(builtin_name("false", OP_FALSE)); + op_names.push_back(builtin_name("false", OP_FALSE)); op_names.push_back(builtin_name("=", OP_EQ)); op_names.push_back(builtin_name("distinct", OP_DISTINCT)); op_names.push_back(builtin_name("ite", OP_ITE)); @@ -931,7 +931,7 @@ void basic_decl_plugin::finalize() { DEC_REF(m_implies_decl); DEC_ARRAY_REF(m_eq_decls); DEC_ARRAY_REF(m_ite_decls); - + DEC_ARRAY_REF(m_oeq_decls); DEC_REF(m_proof_sort); DEC_REF(m_undef_decl); @@ -1072,7 +1072,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_DISTINCT: { func_decl_info info(m_family_id, OP_DISTINCT); info.set_pairwise(); - for (unsigned i = 1; i < arity; i++) { + for (unsigned i = 1; i < arity; i++) { if (domain[i] != domain[0]) { std::ostringstream buffer; buffer << "Sort mismatch between first argument and argument " << (i+1); @@ -1086,7 +1086,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters } SASSERT(is_proof(k)); - + if (!check_proof_sorts(static_cast(k), arity, domain)) m_manager->raise_exception("Invalid proof object."); @@ -1120,7 +1120,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters SASSERT(is_proof(k)); - if (!check_proof_args(static_cast(k), num_args, args)) + if (!check_proof_args(static_cast(k), num_args, args)) m_manager->raise_exception("Invalid proof object."); if (num_parameters == 0) { @@ -1135,19 +1135,19 @@ expr * basic_decl_plugin::get_some_value(sort * s) { return 0; } -bool basic_recognizers::is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const { - if (is_ite(n)) { - t1 = to_app(n)->get_arg(0); - t2 = to_app(n)->get_arg(1); +bool basic_recognizers::is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const { + if (is_ite(n)) { + t1 = to_app(n)->get_arg(0); + t2 = to_app(n)->get_arg(1); t3 = to_app(n)->get_arg(2); - return true; - } - return false; + return true; + } + return false; } // ----------------------------------- // -// label_decl_plugin +// label_decl_plugin // // ----------------------------------- @@ -1169,7 +1169,7 @@ sort * label_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete return 0; } -func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (k == OP_LABEL) { if (arity != 1 || num_parameters < 2 || !parameters[0].is_int() || !parameters[1].is_symbol() || !m_manager->is_bool(domain[0])) { @@ -1182,7 +1182,7 @@ func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters return 0; } } - return m_manager->mk_func_decl(parameters[0].get_int() ? m_lblpos : m_lblneg, arity, domain, domain[0], + return m_manager->mk_func_decl(parameters[0].get_int() ? m_lblpos : m_lblneg, arity, domain, domain[0], func_decl_info(m_family_id, OP_LABEL, num_parameters, parameters)); } else { @@ -1204,7 +1204,7 @@ func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters // ----------------------------------- // -// pattern_decl_plugin +// pattern_decl_plugin // // ----------------------------------- @@ -1213,16 +1213,16 @@ sort * pattern_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parame return 0; } -func_decl * pattern_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * pattern_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { - return m_manager->mk_func_decl(symbol("pattern"), arity, domain, + return m_manager->mk_func_decl(symbol("pattern"), arity, domain, m_manager->mk_bool_sort(), // the range can be an arbitrary sort. func_decl_info(m_family_id, OP_PATTERN)); } // ----------------------------------- // -// model_value_decl_plugin +// model_value_decl_plugin // // ----------------------------------- @@ -1231,7 +1231,7 @@ sort * model_value_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, pa return 0; } -func_decl * model_value_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * model_value_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(k == OP_MODEL_VALUE); if (arity != 0 || num_parameters != 2 || !parameters[0].is_int() || !parameters[1].is_ast() || !is_sort(parameters[1].get_ast())) { @@ -1269,7 +1269,7 @@ sort * user_sort_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter return m_manager->mk_sort(m_sort_names[k], si); } -func_decl * user_sort_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * user_sort_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { UNREACHABLE(); return 0; @@ -1289,7 +1289,7 @@ decl_plugin * user_sort_plugin::mk_fresh() { user_sort_plugin * p = alloc(user_sort_plugin); svector::iterator it = m_sort_names.begin(); svector::iterator end = m_sort_names.end(); - for (; it != end; ++it) + for (; it != end; ++it) p->register_name(*it); return p; } @@ -1318,7 +1318,7 @@ ast_manager::ast_manager(proof_gen_mode m, char const * trace_file, bool is_form if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true); - else + else m_format_manager = 0; init(); } @@ -1335,7 +1335,7 @@ ast_manager::ast_manager(proof_gen_mode m, std::fstream * trace_stream, bool is_ if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, trace_stream, true); - else + else m_format_manager = 0; init(); } @@ -1406,7 +1406,7 @@ ast_manager::~ast_manager() { dealloc(*it); } DEBUG_CODE({ - if (!m_ast_table.empty()) + if (!m_ast_table.empty()) std::cout << "ast_manager LEAKED: " << m_ast_table.size() << std::endl; }); #if 1 @@ -1440,14 +1440,14 @@ void ast_manager::compact_memory() { m_alloc.consolidate(); unsigned capacity = m_ast_table.capacity(); if (capacity > 4*m_ast_table.size()) { - ast_table new_ast_table; + ast_table new_ast_table; ast_table::iterator it = m_ast_table.begin(); ast_table::iterator end = m_ast_table.end(); for (; it != end; ++it) { new_ast_table.insert(*it); } m_ast_table.swap(new_ast_table); - IF_VERBOSE(10, verbose_stream() << "(ast-table :prev-capacity " << capacity + IF_VERBOSE(10, verbose_stream() << "(ast-table :prev-capacity " << capacity << " :capacity " << m_ast_table.capacity() << " :size " << m_ast_table.size() << ")\n";); } else { @@ -1490,24 +1490,24 @@ void ast_manager::copy_families_plugins(ast_manager const & from) { SASSERT(from.is_builtin_family_id(fid) == is_builtin_family_id(fid)); SASSERT(!from.is_builtin_family_id(fid) || m_family_manager.has_family(fid)); symbol fid_name = from.get_family_name(fid); - TRACE("copy_families_plugins", tout << "copying: " << fid_name << ", src fid: " << fid + TRACE("copy_families_plugins", tout << "copying: " << fid_name << ", src fid: " << fid << ", target has_family: " << m_family_manager.has_family(fid) << "\n"; if (m_family_manager.has_family(fid)) tout << get_family_id(fid_name) << "\n";); if (!m_family_manager.has_family(fid)) { - family_id new_fid = mk_family_id(fid_name); + family_id new_fid = mk_family_id(fid_name); TRACE("copy_families_plugins", tout << "new target fid created: " << new_fid << " fid_name: " << fid_name << "\n";); } TRACE("copy_families_plugins", tout << "target fid: " << get_family_id(fid_name) << "\n";); SASSERT(fid == get_family_id(fid_name)); if (from.has_plugin(fid) && !has_plugin(fid)) { - decl_plugin * new_p = from.get_plugin(fid)->mk_fresh(); + decl_plugin * new_p = from.get_plugin(fid)->mk_fresh(); register_plugin(fid, new_p); SASSERT(new_p->get_family_id() == fid); SASSERT(has_plugin(fid)); } SASSERT(from.m_family_manager.has_family(fid) == m_family_manager.has_family(fid)); SASSERT(from.get_family_id(fid_name) == get_family_id(fid_name)); - SASSERT(!from.has_plugin(fid) || has_plugin(fid)); + SASSERT(!from.has_plugin(fid) || has_plugin(fid)); } } @@ -1524,7 +1524,7 @@ void ast_manager::set_next_expr_id(unsigned id) { if (it == end) return; // id is in use, move to the next one. - id++; + id++; } } @@ -1604,7 +1604,7 @@ bool ast_manager::slow_not_contains(ast const * n) { for (; it != end; ++it) { ast * curr = *it; if (compare_nodes(curr, n)) { - TRACE("nondet_bug", + TRACE("nondet_bug", tout << "id1: " << curr->get_id() << ", id2: " << n->get_id() << "\n"; tout << "hash1: " << get_node_hash(curr) << ", hash2: " << get_node_hash(n) << "\n";); return false; @@ -1621,7 +1621,7 @@ bool ast_manager::slow_not_contains(ast const * n) { #endif ast * ast_manager::register_node_core(ast * n) { - unsigned h = get_node_hash(n); + unsigned h = get_node_hash(n); n->m_hash = h; #ifdef Z3DEBUG bool contains = m_ast_table.contains(n); @@ -1662,7 +1662,7 @@ ast * ast_manager::register_node_core(ast * n) { n->m_id = is_decl(n) ? m_decl_id_gen.mk() : m_expr_id_gen.mk(); - + TRACE("ast", tout << "Object " << n->m_id << " was created.\n";); TRACE("mk_var_bug", tout << "mk_ast: " << n->m_id << "\n";); // increment reference counters @@ -1722,7 +1722,7 @@ ast * ast_manager::register_node_core(ast * n) { default: UNREACHABLE(); } - if (arg_depth > depth) + if (arg_depth > depth) depth = arg_depth; } depth++; @@ -1756,7 +1756,7 @@ void ast_manager::delete_node(ast * n) { while (!worklist.empty()) { n = worklist.back(); worklist.pop_back(); - + TRACE("ast", tout << "Deleting object " << n->m_id << " " << n << "\n";); CTRACE("del_quantifier", is_quantifier(n), tout << "deleting quantifier " << n->m_id << " " << n << "\n";); TRACE("mk_var_bug", tout << "del_ast: " << n->m_id << "\n";); @@ -1771,20 +1771,20 @@ void ast_manager::delete_node(ast * n) { if (!m_debug_ref_count) { if (is_decl(n)) m_decl_id_gen.recycle(n->m_id); - else + else m_expr_id_gen.recycle(n->m_id); } #endif switch (n->get_kind()) { case AST_SORT: - if (to_sort(n)->m_info != 0 && !m_debug_ref_count) { + if (to_sort(n)->m_info != 0 && !m_debug_ref_count) { sort_info * info = to_sort(n)->get_info(); info->del_eh(*this); - dealloc(info); + dealloc(info); } break; case AST_FUNC_DECL: - if (to_func_decl(n)->m_info != 0 && !m_debug_ref_count) { + if (to_func_decl(n)->m_info != 0 && !m_debug_ref_count) { func_decl_info * info = to_func_decl(n)->get_info(); info->del_eh(*this); dealloc(info); @@ -1821,7 +1821,7 @@ sort * ast_manager::mk_sort(family_id fid, decl_kind k, unsigned num_parameters, return p->mk_sort(k, num_parameters, parameters); return 0; } - + func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { decl_plugin * p = get_plugin(fid); @@ -1830,13 +1830,13 @@ func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_p return 0; } -func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { decl_plugin * p = get_plugin(fid); if (p) return p->mk_func_decl(k, num_parameters, parameters, num_args, args, range); return 0; -} +} app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { @@ -1867,7 +1867,7 @@ app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, sort * ast_manager::mk_sort(symbol const & name, sort_info * info) { unsigned sz = sort::get_obj_size(); void * mem = allocate_node(sz); - sort * new_node = new (mem) sort(name, info); + sort * new_node = new (mem) sort(name, info); return register_node(new_node); } @@ -1877,7 +1877,7 @@ sort * ast_manager::mk_uninterpreted_sort(symbol const & name, unsigned num_para return plugin->mk_sort(kind, num_parameters, parameters); } -func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, +func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, bool assoc, bool comm, bool inj) { func_decl_info info(null_family_id, null_decl_kind); info.set_associative(assoc); @@ -1931,8 +1931,8 @@ void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * c } /** - \brief Shallow sort checker. - Return true if success. + \brief Shallow sort checker. + Return true if success. If n == 0, then fail. If n is an application, checks whether the arguments of n match the expected types. */ @@ -1940,18 +1940,18 @@ void ast_manager::check_sorts_core(ast const * n) const { if (!n) { throw ast_exception("expression is null"); } - if (n->get_kind() != AST_APP) + if (n->get_kind() != AST_APP) return; // nothing else to check... app const * a = to_app(n); func_decl* d = a->get_decl(); check_sort(d, a->get_num_args(), a->get_args()); if (a->get_num_args() == 2 && - !d->is_flat_associative() && + !d->is_flat_associative() && d->is_right_associative()) { check_sorts_core(a->get_arg(1)); } if (a->get_num_args() == 2 && - !d->is_flat_associative() && + !d->is_flat_associative() && d->is_left_associative()) { check_sorts_core(a->get_arg(0)); } @@ -1991,7 +1991,7 @@ bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * co if (decl->get_arity() != num_args) { // Invalid input: unexpected number of arguments for non-associative operator. // So, there is no point in coercing the input arguments. - return false; + return false; } for (unsigned i = 0; i < num_args; i++) { sort * d = decl->get_domain(i); @@ -2008,7 +2008,7 @@ app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const unsigned sz = app::get_obj_size(num_args); void * mem = allocate_node(sz); - try { + try { if (m_int_real_coercions && coercion_needed(decl, num_args, args)) { expr_ref_buffer new_args(*this); if (decl->is_associative()) { @@ -2082,11 +2082,11 @@ void ast_manager::check_args(func_decl* f, unsigned n, expr* const* es) { sort * expected_sort = f->is_associative() ? f->get_domain(0) : f->get_domain(i); if (expected_sort != actual_sort) { std::ostringstream buffer; - buffer << "Sort mismatch at argument #" << (i+1) - << " for function " << mk_pp(f,*this) - << " supplied sort is " + buffer << "Sort mismatch at argument #" << (i+1) + << " for function " << mk_pp(f,*this) + << " supplied sort is " << mk_pp(actual_sort, *this); - throw ast_exception(buffer.str().c_str()); + throw ast_exception(buffer.str().c_str()); } } } @@ -2099,17 +2099,17 @@ inline app * ast_manager::mk_app_core(func_decl * decl, expr * arg1, expr * arg2 app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * args) { - bool type_error = - decl->get_arity() != num_args && !decl->is_right_associative() && + bool type_error = + decl->get_arity() != num_args && !decl->is_right_associative() && !decl->is_left_associative() && !decl->is_chainable(); - type_error |= (decl->get_arity() != num_args && num_args < 2 && + type_error |= (decl->get_arity() != num_args && num_args < 2 && decl->get_family_id() == m_basic_family_id && !decl->is_associative()); if (type_error) { std::ostringstream buffer; - buffer << "Wrong number of arguments (" << num_args - << ") passed to function " << mk_pp(decl, *this); + buffer << "Wrong number of arguments (" << num_args + << ") passed to function " << mk_pp(decl, *this); throw ast_exception(buffer.str().c_str()); } app * r = 0; @@ -2148,7 +2148,7 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar -func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity, +func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, sort * range) { func_decl_info info(null_family_id, null_decl_kind); info.m_skolem = true; @@ -2200,7 +2200,7 @@ app * ast_manager::mk_label(bool pos, unsigned num_names, symbol const * names, SASSERT(get_sort(n) == m_bool_sort); buffer p; p.push_back(parameter(static_cast(pos))); - for (unsigned i = 0; i < num_names; i++) + for (unsigned i = 0; i < num_names; i++) p.push_back(parameter(names[i])); return mk_app(m_label_family_id, OP_LABEL, p.size(), p.c_ptr(), 1, &n); } @@ -2215,7 +2215,7 @@ bool ast_manager::is_label(expr const * n, bool & pos, buffer & names) c } func_decl const * decl = to_app(n)->get_decl(); pos = decl->get_parameter(0).get_int() != 0; - for (unsigned i = 1; i < decl->get_num_parameters(); i++) + for (unsigned i = 1; i < decl->get_num_parameters(); i++) names.push_back(decl->get_parameter(i).get_symbol()); return true; } @@ -2223,7 +2223,7 @@ bool ast_manager::is_label(expr const * n, bool & pos, buffer & names) c app * ast_manager::mk_label_lit(unsigned num_names, symbol const * names) { SASSERT(num_names > 0); buffer p; - for (unsigned i = 0; i < num_names; i++) + for (unsigned i = 0; i < num_names; i++) p.push_back(parameter(names[i])); return mk_app(m_label_family_id, OP_LABEL_LIT, p.size(), p.c_ptr(), 0, 0); } @@ -2237,7 +2237,7 @@ bool ast_manager::is_label_lit(expr const * n, buffer & names) const { return false; } func_decl const * decl = to_app(n)->get_decl(); - for (unsigned i = 0; i < decl->get_num_parameters(); i++) + for (unsigned i = 0; i < decl->get_num_parameters(); i++) names.push_back(decl->get_parameter(i).get_symbol()); return true; } @@ -2262,9 +2262,9 @@ bool ast_manager::is_pattern(expr const * n) const { return true; } -quantifier * ast_manager::mk_quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, +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, + unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns) { SASSERT(body); SASSERT(num_patterns == 0 || num_no_patterns == 0); @@ -2279,14 +2279,14 @@ quantifier * ast_manager::mk_quantifier(bool forall, unsigned num_decls, sort * weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); quantifier * r = register_node(new_node); - + if (m_trace_stream && r == new_node) { *m_trace_stream << "[mk-quant] #" << r->get_id() << " " << qid; for (unsigned i = 0; i < num_patterns; ++i) { *m_trace_stream << " #" << patterns[i]->get_id(); } *m_trace_stream << " #" << body->get_id() << "\n"; - + } return r; @@ -2447,7 +2447,7 @@ expr_dependency * ast_manager::mk_leaf(expr * t) { if (t == 0) return 0; else - return m_expr_dependency_manager.mk_leaf(t); + return m_expr_dependency_manager.mk_leaf(t); } expr_dependency * ast_manager::mk_join(unsigned n, expr * const * ts) { @@ -2514,7 +2514,7 @@ bool ast_manager::is_fully_interp(sort const * s) const { // ----------------------------------- proof * ast_manager::mk_proof(family_id fid, decl_kind k, unsigned num_args, expr * const * args) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(fid, k, num_args, args); } @@ -2538,23 +2538,23 @@ proof * ast_manager::mk_true_proof() { return mk_proof(m_basic_family_id, PR_TRUE, f); } -proof * ast_manager::mk_asserted(expr * f) { +proof * ast_manager::mk_asserted(expr * f) { CTRACE("mk_asserted_bug", !is_bool(f), tout << mk_ismt2_pp(f, *this) << "\nsort: " << mk_ismt2_pp(get_sort(f), *this) << "\n";); SASSERT(is_bool(f)); - return mk_proof(m_basic_family_id, PR_ASSERTED, f); + return mk_proof(m_basic_family_id, PR_ASSERTED, f); } -proof * ast_manager::mk_goal(expr * f) { +proof * ast_manager::mk_goal(expr * f) { SASSERT(is_bool(f)); - return mk_proof(m_basic_family_id, PR_GOAL, f); + return mk_proof(m_basic_family_id, PR_GOAL, f); } proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p1)); SASSERT(has_fact(p2)); - CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))), + CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))), tout << mk_ll_pp(p1, *this) << "\n"; tout << mk_ll_pp(p2, *this) << "\n";); SASSERT(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))); @@ -2564,21 +2564,21 @@ proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) { if (is_reflexivity(p2)) return p1; expr * f = to_app(get_fact(p2))->get_arg(1); - if (is_oeq(get_fact(p2))) + if (is_oeq(get_fact(p2))) return mk_app(m_basic_family_id, PR_MODUS_PONENS_OEQ, p1, p2, f); return mk_app(m_basic_family_id, PR_MODUS_PONENS, p1, p2, f); } proof * ast_manager::mk_reflexivity(expr * e) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; - return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_eq(e, e)); + return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_eq(e, e)); } -proof * ast_manager::mk_oeq_reflexivity(expr * e) { - if (m_proof_mode == PGM_DISABLED) +proof * ast_manager::mk_oeq_reflexivity(expr * e) { + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; - return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_oeq(e, e)); + return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_oeq(e, e)); } proof * ast_manager::mk_commutativity(app * f) { @@ -2591,7 +2591,7 @@ proof * ast_manager::mk_commutativity(app * f) { \brief Given a proof of p, return a proof of (p <=> true) */ proof * ast_manager::mk_iff_true(proof * pr) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(pr)); SASSERT(is_bool(get_fact(pr))); @@ -2602,7 +2602,7 @@ proof * ast_manager::mk_iff_true(proof * pr) { \brief Given a proof of (not p), return a proof of (p <=> false) */ proof * ast_manager::mk_iff_false(proof * pr) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(pr)); SASSERT(is_not(get_fact(pr))); @@ -2611,7 +2611,7 @@ proof * ast_manager::mk_iff_false(proof * pr) { } proof * ast_manager::mk_symmetry(proof * p) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p) return p; @@ -2622,12 +2622,12 @@ proof * ast_manager::mk_symmetry(proof * p) { SASSERT(has_fact(p)); SASSERT(is_app(get_fact(p))); SASSERT(to_app(get_fact(p))->get_num_args() == 2); - return mk_app(m_basic_family_id, PR_SYMMETRY, p, + return mk_app(m_basic_family_id, PR_SYMMETRY, p, mk_app(to_app(get_fact(p))->get_decl(), to_app(get_fact(p))->get_arg(1), to_app(get_fact(p))->get_arg(0))); } proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p1) return p2; @@ -2644,11 +2644,11 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { tout << mk_pp(to_app(get_fact(p1))->get_decl(), *this) << "\n"; tout << mk_pp(to_app(get_fact(p2))->get_decl(), *this) << "\n";); SASSERT(to_app(get_fact(p1))->get_decl() == to_app(get_fact(p2))->get_decl() || - ((is_iff(get_fact(p1)) || is_eq(get_fact(p1))) && + ((is_iff(get_fact(p1)) || is_eq(get_fact(p1))) && (is_iff(get_fact(p2)) || is_eq(get_fact(p2)))) || ( (is_eq(get_fact(p1)) || is_oeq(get_fact(p1))) && (is_eq(get_fact(p2)) || is_oeq(get_fact(p2))))); - CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0), + CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0), tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n"; tout << mk_bounded_pp(p1, *this, 5) << "\n\n"; tout << mk_bounded_pp(p2, *this, 5) << "\n\n"; @@ -2660,7 +2660,7 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { return p1; // OEQ is compatible with EQ for transitivity. func_decl* f = to_app(get_fact(p1))->get_decl(); - if (is_oeq(get_fact(p2))) f = to_app(get_fact(p2))->get_decl(); + if (is_oeq(get_fact(p2))) f = to_app(get_fact(p2))->get_decl(); return mk_app(m_basic_family_id, PR_TRANSITIVITY, p1, p2, mk_app(f, to_app(get_fact(p1))->get_arg(0), to_app(get_fact(p2))->get_arg(1))); } @@ -2673,19 +2673,19 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2, proof * p3, proof * } proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(num_proofs > 0); proof * r = proofs[0]; - for (unsigned i = 1; i < num_proofs; i++) + for (unsigned i = 1; i < num_proofs; i++) r = mk_transitivity(r, proofs[i]); return r; } proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs, expr * n1, expr * n2) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; - if (fine_grain_proofs()) + if (fine_grain_proofs()) return mk_transitivity(num_proofs, proofs); SASSERT(num_proofs > 0); if (num_proofs == 1) @@ -2703,7 +2703,7 @@ proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs } proof * ast_manager::mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(f1->get_num_args() == f2->get_num_args()); SASSERT(f1->get_decl() == f2->get_decl()); @@ -2714,7 +2714,7 @@ proof * ast_manager::mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned } proof * ast_manager::mk_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); @@ -2723,7 +2723,7 @@ proof * ast_manager::mk_congruence(app * f1, app * f2, unsigned num_proofs, proo } proof * ast_manager::mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); @@ -2732,11 +2732,11 @@ proof * ast_manager::mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, } proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p) { return 0; - } + } SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); SASSERT(is_iff(get_fact(p))); @@ -2744,7 +2744,7 @@ proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) } proof * ast_manager::mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof * p) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); @@ -2753,25 +2753,25 @@ proof * ast_manager::mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof } proof * ast_manager::mk_distributivity(expr * s, expr * r) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_DISTRIBUTIVITY, mk_eq(s, r)); } proof * ast_manager::mk_rewrite(expr * s, expr * t) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_REWRITE, mk_eq(s, t)); } proof * ast_manager::mk_oeq_rewrite(expr * s, expr * t) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_REWRITE, mk_oeq(s, t)); } proof * ast_manager::mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -2780,37 +2780,37 @@ proof * ast_manager::mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, pr } proof * ast_manager::mk_pull_quant(expr * e, quantifier * q) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_PULL_QUANT, mk_iff(e, q)); } proof * ast_manager::mk_pull_quant_star(expr * e, quantifier * q) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_PULL_QUANT_STAR, mk_iff(e, q)); } proof * ast_manager::mk_push_quant(quantifier * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_PUSH_QUANT, mk_iff(q, e)); } proof * ast_manager::mk_elim_unused_vars(quantifier * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_ELIM_UNUSED_VARS, mk_iff(q, e)); } proof * ast_manager::mk_der(quantifier * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_DER, mk_iff(q, e)); } proof * ast_manager::mk_quant_inst(expr * not_q_or_i, unsigned num_bind, expr* const* binding) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; vector params; for (unsigned i = 0; i < num_bind; ++i) { @@ -2845,7 +2845,7 @@ bool ast_manager::is_rewrite(expr const* e, expr*& r1, expr*& r2) const { } proof * ast_manager::mk_def_axiom(expr * ax) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_DEF_AXIOM, ax); } @@ -2879,7 +2879,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro expr const * _fact = get_fact(proofs[j]); if (is_complement(lit, _fact)) { found_complement = true; - DEBUG_CODE(found.setx(j, true, false); continue;); + DEBUG_CODE(found.setx(j, true, false); continue;); break; } } @@ -2888,9 +2888,9 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro } DEBUG_CODE({ for (unsigned i = 1; m_proof_mode == PGM_FINE && i < num_proofs; i++) { - CTRACE("mk_unit_resolution_bug", !found.get(i, false), + CTRACE("mk_unit_resolution_bug", !found.get(i, false), for (unsigned j = 0; j < num_proofs; j++) { - if (j == i) tout << "Index " << i << " was not found:\n"; + if (j == i) tout << "Index " << i << " was not found:\n"; tout << mk_ll_pp(get_fact(proofs[j]), *this); }); SASSERT(found.get(i, false)); @@ -2915,7 +2915,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro } proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs, expr * new_fact) { - TRACE("unit_bug", + TRACE("unit_bug", for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n"; tout << "===>\n"; tout << mk_pp(new_fact, *this) << "\n";); @@ -2956,20 +2956,20 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro SASSERT(num_matches == cls_sz || num_matches == cls_sz - 1); SASSERT(num_matches != cls_sz || is_false(new_fact)); } -#endif +#endif proof * pr = mk_app(m_basic_family_id, PR_UNIT_RESOLUTION, args.size(), args.c_ptr()); TRACE("unit_resolution", tout << "unit_resolution using fact\n" << mk_ll_pp(pr, *this);); return pr; } proof * ast_manager::mk_hypothesis(expr * h) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_HYPOTHESIS, h); } proof * ast_manager::mk_lemma(proof * p, expr * lemma) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p)); CTRACE("mk_lemma", !is_false(get_fact(p)), tout << mk_ll_pp(p, *this) << "\n";); @@ -2979,11 +2979,11 @@ proof * ast_manager::mk_lemma(proof * p, expr * lemma) { proof * ast_manager::mk_def_intro(expr * new_def) { SASSERT(is_bool(new_def)); - return mk_proof(m_basic_family_id, PR_DEF_INTRO, new_def); + return mk_proof(m_basic_family_id, PR_DEF_INTRO, new_def); } proof * ast_manager::mk_apply_defs(expr * n, expr * def, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -2992,11 +2992,11 @@ proof * ast_manager::mk_apply_defs(expr * n, expr * def, unsigned num_proofs, pr } proof * ast_manager::mk_iff_oeq(proof * p) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p) return p; - + SASSERT(has_fact(p)); SASSERT(is_iff(get_fact(p)) || is_oeq(get_fact(p))); if (is_oeq(get_fact(p))) @@ -3019,7 +3019,7 @@ bool ast_manager::check_nnf_proof_parents(unsigned num_proofs, proof * const * p } proof * ast_manager::mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; check_nnf_proof_parents(num_proofs, proofs); ptr_buffer args; @@ -3029,7 +3029,7 @@ proof * ast_manager::mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * } proof * ast_manager::mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; check_nnf_proof_parents(num_proofs, proofs); ptr_buffer args; @@ -3039,7 +3039,7 @@ proof * ast_manager::mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * } proof * ast_manager::mk_nnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -3048,7 +3048,7 @@ proof * ast_manager::mk_nnf_star(expr * s, expr * t, unsigned num_proofs, proof } proof * ast_manager::mk_skolemization(expr * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(is_bool(q)); SASSERT(is_bool(e)); @@ -3056,7 +3056,7 @@ proof * ast_manager::mk_skolemization(expr * q, expr * e) { } proof * ast_manager::mk_cnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -3065,7 +3065,7 @@ proof * ast_manager::mk_cnf_star(expr * s, expr * t, unsigned num_proofs, proof } proof * ast_manager::mk_and_elim(proof * p, unsigned i) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p)); SASSERT(is_and(get_fact(p))); @@ -3076,7 +3076,7 @@ proof * ast_manager::mk_and_elim(proof * p, unsigned i) { } proof * ast_manager::mk_not_or_elim(proof * p, unsigned i) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p)); SASSERT(is_not(get_fact(p))); @@ -3094,14 +3094,14 @@ proof * ast_manager::mk_not_or_elim(proof * p, unsigned i) { proof * ast_manager::mk_th_lemma( - family_id tid, + family_id tid, expr * fact, unsigned num_proofs, proof * const * proofs, unsigned num_params, parameter const* params - ) + ) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; - + ptr_buffer args; vector parameters; parameters.push_back(parameter(get_family_name(tid))); @@ -3121,7 +3121,7 @@ proof* ast_manager::mk_hyper_resolve(unsigned num_premises, proof* const* premis for (unsigned i = 0; i < num_premises; ++i) { TRACE("hyper_res", tout << mk_pp(premises[i], *this) << "\n";); fmls.push_back(get_fact(premises[i])); - } + } SASSERT(is_bool(concl)); vector params; for (unsigned i = 0; i < substs.size(); ++i) { @@ -3134,7 +3134,7 @@ proof* ast_manager::mk_hyper_resolve(unsigned num_premises, proof* const* premis params.push_back(parameter(positions[i].second)); } } - TRACE("hyper_res", + TRACE("hyper_res", for (unsigned i = 0; i < params.size(); ++i) { params[i].display(tout); tout << "\n"; }); @@ -3153,7 +3153,7 @@ proof* ast_manager::mk_hyper_resolve(unsigned num_premises, proof* const* premis } bool ast_manager::is_hyper_resolve( - proof* p, + proof* p, proof_ref_vector& premises, expr_ref& conclusion, svector > & positions, @@ -3170,7 +3170,7 @@ bool ast_manager::is_hyper_resolve( func_decl* d = p->get_decl(); unsigned num_p = d->get_num_parameters(); parameter const* params = d->get_parameters(); - + substs.push_back(expr_ref_vector(*this)); for (unsigned i = 0; i < num_p; ++i) { if (params[i].is_int()) { @@ -3186,10 +3186,10 @@ bool ast_manager::is_hyper_resolve( SASSERT(params[i].is_ast()); ast* a = params[i].get_ast(); SASSERT(is_expr(a)); - substs.back().push_back(to_expr(a)); + substs.back().push_back(to_expr(a)); } } - + return true; } @@ -3201,7 +3201,7 @@ bool ast_manager::is_hyper_resolve( // ----------------------------------- bool ast_mark::is_marked(ast * n) const { - if (is_decl(n)) + if (is_decl(n)) return m_decl_marks.is_marked(to_decl(n)); else return m_expr_marks.is_marked(to_expr(n)); @@ -3242,7 +3242,7 @@ void scoped_mark::reset() { m_stack.reset(); m_lim.reset(); } - + void scoped_mark::push_scope() { m_lim.push_back(m_stack.size()); } @@ -3251,7 +3251,7 @@ void scoped_mark::pop_scope() { unsigned new_size = m_stack.size(); unsigned old_size = m_lim.back(); for (unsigned i = old_size; i < new_size; ++i) { - ast_mark::mark(m_stack[i].get(), false); + ast_mark::mark(m_stack[i].get(), false); } m_lim.pop_back(); m_stack.resize(old_size); @@ -3262,7 +3262,7 @@ void scoped_mark::pop_scope(unsigned num_scopes) { pop_scope(); } } - + // Added by KLM for use in GDB // show an expr_ref on stdout diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index c958fb67f..aefa6294a 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -3,15 +3,15 @@ Copyright (c) 2012 Microsoft Corporation Module Name: - fpa2bv_converter.cpp +fpa2bv_converter.cpp Abstract: - Conversion routines for Floating Point -> Bit-Vector +Conversion routines for Floating Point -> Bit-Vector Author: - Christoph (cwinter) 2012-02-09 +Christoph (cwinter) 2012-02-09 Notes: @@ -50,7 +50,7 @@ void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { SASSERT(is_app_of(b, m_plugin->get_family_id(), OP_FPA_FP)); TRACE("fpa2bv", tout << "mk_eq a=" << mk_ismt2_pp(a, m) << std::endl; - tout << "mk_eq b=" << mk_ismt2_pp(b, m) << std::endl;); + tout << "mk_eq b=" << mk_ismt2_pp(b, m) << std::endl;); expr_ref eq_sgn(m), eq_exp(m), eq_sig(m); m_simp.mk_eq(to_app(a)->get_arg(0), to_app(b)->get_arg(0), eq_sgn); @@ -81,7 +81,7 @@ void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { SASSERT(is_app_of(b, m_plugin->get_family_id(), OP_FPA_INTERNAL_RM)); TRACE("fpa2bv", tout << "mk_eq_rm a=" << mk_ismt2_pp(a, m) << std::endl; - tout << "mk_eq_rm b=" << mk_ismt2_pp(b, m) << std::endl;); + tout << "mk_eq_rm b=" << mk_ismt2_pp(b, m) << std::endl;); m_simp.mk_eq(to_app(a)->get_arg(0), to_app(b)->get_arg(0), result); } @@ -146,7 +146,7 @@ void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * ar } else { expr_ref bv_sgn(m), bv_sig(m), e(m), biased_exp(m); - bv_sgn = m_bv_util.mk_numeral( (sign) ? 1 : 0, 1); + bv_sgn = m_bv_util.mk_numeral((sign) ? 1 : 0, 1); bv_sig = m_bv_util.mk_numeral(rational(sig), sbits-1); e = m_bv_util.mk_numeral(exp, ebits); @@ -154,7 +154,7 @@ void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * ar mk_fp(bv_sgn, biased_exp, bv_sig, result); TRACE("fpa2bv_dbg", tout << "value of [" << sign << " " << m_mpz_manager.to_string(sig) << " " << exp << "] is " - << mk_ismt2_pp(result, m) << std::endl;); + << mk_ismt2_pp(result, m) << std::endl;); } } @@ -183,8 +183,8 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { std::string name = f->get_name().str(); sgn = mk_fresh_const((p + "_sgn_" + name).c_str(), 1); - s = mk_fresh_const((p + "_sig_" + name).c_str(), sbits - 1); e = mk_fresh_const((p + "_exp_" + name).c_str(), ebits); + s = mk_fresh_const((p + "_sig_" + name).c_str(), sbits-1); #else app_ref bv(m); unsigned bv_sz = 1 + ebits + (sbits - 1); @@ -215,8 +215,8 @@ void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) expr_ref sgn(m), s(m), e(m); sgn = m.mk_var(base_inx, m_bv_util.mk_sort(1)); - s = m.mk_var(base_inx + 1, m_bv_util.mk_sort(sbits-1)); - e = m.mk_var(base_inx + 2, m_bv_util.mk_sort(ebits)); + s = m.mk_var(base_inx+1, m_bv_util.mk_sort(sbits-1)); + e = m.mk_var(base_inx+2, m_bv_util.mk_sort(ebits)); mk_fp(sgn, e, s, result); } @@ -230,9 +230,9 @@ void fpa2bv_converter::mk_uninterpreted_output(sort * rng, func_decl * fbv, expr app_ref na(m); na = m.mk_app(fbv, new_args.size(), new_args.c_ptr()); mk_fp(m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, na), - m_bv_util.mk_extract(bv_sz - 2, sbits - 1, na), - m_bv_util.mk_extract(sbits - 2, 0, na), - result); + m_bv_util.mk_extract(bv_sz - 2, sbits - 1, na), + m_bv_util.mk_extract(sbits - 2, 0, na), + result); } else if (m_util.is_rm(rng)) { app_ref na(m); @@ -316,11 +316,11 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { expr_ref bv3(m); bv3 = m.mk_fresh_const( - #ifdef Z3DEBUG +#ifdef Z3DEBUG "fpa2bv_rm" - #else +#else 0 - #endif +#endif , m_bv_util.mk_sort(3)); mk_rm(bv3, result); @@ -335,79 +335,105 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { } void fpa2bv_converter::mk_pinf(func_decl * f, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); + mk_pinf(f->get_range(), result); +} + +void fpa2bv_converter::mk_pinf(sort * s, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(0, 1), - top_exp, - m_bv_util.mk_numeral(0, sbits-1), - result); + top_exp, + m_bv_util.mk_numeral(0, sbits-1), + result); } void fpa2bv_converter::mk_ninf(func_decl * f, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); + mk_ninf(f->get_range(), result); +} + +void fpa2bv_converter::mk_ninf(sort * s, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(1, 1), - top_exp, - m_bv_util.mk_numeral(0, sbits-1), - result); + top_exp, + m_bv_util.mk_numeral(0, sbits-1), + result); } void fpa2bv_converter::mk_nan(func_decl * f, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); + mk_nan(f->get_range(), result); +} + +void fpa2bv_converter::mk_nan(sort * s, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(0, 1), - top_exp, - m_bv_util.mk_numeral(1, sbits-1), - result); + top_exp, + m_bv_util.mk_numeral(1, sbits - 1), + result); } -void fpa2bv_converter::mk_nzero(func_decl *f, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); +void fpa2bv_converter::mk_nzero(func_decl * f, expr_ref & result) { + mk_nzero(f->get_range(), result); +} + +void fpa2bv_converter::mk_nzero(sort * s, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); expr_ref bot_exp(m); mk_bot_exp(ebits, bot_exp); mk_fp(m_bv_util.mk_numeral(1, 1), - bot_exp, - m_bv_util.mk_numeral(0, sbits - 1), - result); + bot_exp, + m_bv_util.mk_numeral(0, sbits - 1), + result); } -void fpa2bv_converter::mk_pzero(func_decl *f, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); +void fpa2bv_converter::mk_pzero(func_decl * f, expr_ref & result) { + mk_pzero(f->get_range(), result); +} + +void fpa2bv_converter::mk_pzero(sort * s, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); expr_ref bot_exp(m); mk_bot_exp(ebits, bot_exp); mk_fp(m_bv_util.mk_numeral(0, 1), - bot_exp, - m_bv_util.mk_numeral(0, sbits-1), - result); + bot_exp, + m_bv_util.mk_numeral(0, sbits-1), + result); } -void fpa2bv_converter::mk_one(func_decl *f, expr_ref sign, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); +void fpa2bv_converter::mk_zero(sort * s, expr_ref & sgn, expr_ref & result) { + expr_ref is_pos(m), pzero(m), nzero(m); + is_pos = m.mk_eq(sgn, m_bv_util.mk_numeral(0, 1)); + mk_pzero(s, pzero); + mk_nzero(s, nzero); + mk_ite(is_pos, pzero, nzero, result); +} + +void fpa2bv_converter::mk_one(func_decl * f, expr_ref & sign, expr_ref & result) { + mk_one(f->get_range(), sign, result); +} + +void fpa2bv_converter::mk_one(sort * s, expr_ref & sign, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); mk_fp(sign, - m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits), - m_bv_util.mk_numeral(0, sbits-1), - result); + m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits), + m_bv_util.mk_numeral(0, sbits-1), + result); } void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits, @@ -524,15 +550,18 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, SASSERT(num == 3); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); - expr_ref bv_rm(m), x(m), y(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m), y(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; + mk_add(f->get_range(), rm, x, y, result); +} +void fpa2bv_converter::mk_add(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref nan(m), nzero(m), pzero(m); - mk_nan(f, nan); - mk_nzero(f, nzero); - mk_pzero(f, pzero); + mk_nan(s, nan); + mk_nzero(s, nzero); + mk_pzero(s, pzero); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); @@ -582,7 +611,7 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, m_simp.mk_and(x_is_zero, y_is_zero, c4); m_simp.mk_and(x_is_neg, y_is_neg, signs_and); m_simp.mk_xor(x_is_neg, y_is_neg, signs_xor); - mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); m_simp.mk_and(rm_is_to_neg, signs_xor, rm_and_xor); m_simp.mk_or(signs_and, rm_and_xor, neg_cond); mk_ite(neg_cond, nzero, pzero, v4); @@ -595,9 +624,9 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, c6 = y_is_zero; v6 = x; - //// Actual addition. - unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); + // Actual addition. + unsigned ebits = m_util.get_ebits(s); + unsigned sbits = m_util.get_sbits(s); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, false); @@ -623,8 +652,8 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, expr_ref res_sgn(m), res_sig(m), res_exp(m); add_core(sbits, ebits, - c_sgn, c_sig, c_exp, d_sgn, d_sig, d_exp, - res_sgn, res_sig, res_exp); + c_sgn, c_sig, c_exp, d_sgn, d_sig, d_exp, + res_sgn, res_sig, res_exp); expr_ref is_zero_sig(m), nil_sbit4(m); nil_sbit4 = m_bv_util.mk_numeral(0, sbits+4); @@ -638,7 +667,7 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, mk_ite(rm_is_to_neg, nzero, pzero, zero_case); expr_ref rounded(m); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, rounded); + round(s, rm, res_sgn, res_sig, res_exp, rounded); mk_ite(is_zero_sig, zero_case, rounded, v7); @@ -656,39 +685,55 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_sub(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); + expr_ref rm(m), x(m), y(m); + rm = args[0]; + x = args[1]; + y = args[2]; + mk_sub(f->get_range(), rm, x, y, result); +} + +void fpa2bv_converter::mk_sub(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref t(m); - mk_neg(f, 1, &args[2], t); - expr * nargs[3] = { args[0], args[1], t }; - mk_add(f, 3, nargs, result); + mk_neg(s, y, t); + mk_add(s, rm, x, t, result); } void fpa2bv_converter::mk_neg(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); - expr * sgn, * s, * e; - split_fp(args[0], sgn, e, s); + expr_ref x(m); + x = args[0]; + mk_neg(f->get_range(), x, result); +} + +void fpa2bv_converter::mk_neg(sort * srt, expr_ref & x, expr_ref & result) { + expr * sgn, *sig, *e; + split_fp(x, sgn, e, sig); expr_ref c(m), nsgn(m); - mk_is_nan(args[0], c); + mk_is_nan(x, c); nsgn = m_bv_util.mk_bv_not(sgn); expr_ref r_sgn(m); m_simp.mk_ite(c, sgn, nsgn, r_sgn); - mk_fp(r_sgn, e, s, result); + mk_fp(r_sgn, e, sig, result); } void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); - expr_ref bv_rm(m), x(m), y(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m), y(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; + mk_mul(f->get_range(), rm, x, y, result); +} +void fpa2bv_converter::mk_mul(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); - mk_nan(f, nan); - mk_nzero(f, nzero); - mk_pzero(f, pzero); - mk_ninf(f, ninf); - mk_pinf(f, pinf); + mk_nan(s, nan); + mk_nzero(s, nzero); + mk_pzero(s, pzero); + mk_ninf(s, ninf); + mk_pinf(s, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); @@ -748,7 +793,7 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, mk_ite(sign_xor, nzero, pzero, v6); // else comes the actual multiplication. - unsigned sbits = m_util.get_sbits(f->get_range()); + unsigned sbits = m_util.get_sbits(s); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); @@ -781,8 +826,8 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, dbg_decouple("fpa2bv_mul_res_sgn", res_sgn); res_exp = m_bv_util.mk_bv_add( - m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), - m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); + m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref product(m); product = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); @@ -806,7 +851,7 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, SASSERT(m_bv_util.get_bv_size(rbits) == 4); res_sig = m_bv_util.mk_concat(h_p, rbits); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, v7); + round(s, rm, res_sgn, res_sig, res_exp, v7); // And finally, we tie them together. mk_ite(c6, v6, v7, result); @@ -824,18 +869,20 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); - - expr_ref bv_rm(m), x(m), y(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m), y(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; + mk_div(f->get_range(), rm, x, y, result); +} +void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); - mk_nan(f, nan); - mk_nzero(f, nzero); - mk_pzero(f, pzero); - mk_ninf(f, ninf); - mk_pinf(f, pinf); + mk_nan(s, nan); + mk_nzero(s, nzero); + mk_pzero(s, pzero); + mk_ninf(s, ninf); + mk_pinf(s, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); @@ -899,8 +946,8 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, mk_ite(signs_xor, nzero, pzero, v7); // else comes the actual division. - unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); + unsigned ebits = m_util.get_ebits(s); + unsigned sbits = m_util.get_sbits(s); SASSERT(ebits <= sbits); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); @@ -925,8 +972,8 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); res_exp = m_bv_util.mk_bv_sub( - m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), - m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); + m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref quotient(m); // b_sig_ext can't be 0 here, so it's safe to use OP_BUDIV_I @@ -956,7 +1003,7 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, m_simp.mk_ite(shift_cond, res_sig, res_sig_shifted, res_sig); m_simp.mk_ite(shift_cond, res_exp, res_exp_shifted, res_exp); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, v8); + round(s, rm, res_sgn, res_sig, res_exp, v8); // And finally, we tie them together. mk_ite(c7, v7, v8, result); @@ -974,21 +1021,22 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); - - // Remainder is always exact, so there is no rounding mode. expr_ref x(m), y(m); x = args[0]; y = args[1]; + mk_rem(f->get_range(), x, y, result); +} +void fpa2bv_converter::mk_rem(sort * s, expr_ref & x, expr_ref & y, expr_ref & result) { TRACE("fpa2bv_rem", tout << "X = " << mk_ismt2_pp(x, m) << std::endl; tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); - mk_nan(f, nan); - mk_nzero(f, nzero); - mk_pzero(f, pzero); - mk_ninf(f, ninf); - mk_pinf(f, pinf); + mk_nan(s, nan); + mk_nzero(s, nzero); + mk_pzero(s, pzero); + mk_ninf(s, ninf); + mk_pinf(s, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); @@ -1033,71 +1081,32 @@ void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, c5 = x_is_zero; v5 = pzero; - // else the actual remainder. - unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); - - expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); - expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); - unpack(x, a_sgn, a_sig, a_exp, a_lz, true); - unpack(y, b_sgn, b_sig, b_exp, b_lz, true); - - dbg_decouple("fpa2bv_rem_a_sgn", a_sgn); - dbg_decouple("fpa2bv_rem_a_sig", a_sig); - dbg_decouple("fpa2bv_rem_a_exp", a_exp); - dbg_decouple("fpa2bv_rem_a_lz", a_lz); - dbg_decouple("fpa2bv_rem_b_sgn", b_sgn); - dbg_decouple("fpa2bv_rem_b_sig", b_sig); - dbg_decouple("fpa2bv_rem_b_exp", b_exp); - dbg_decouple("fpa2bv_rem_b_lz", b_lz); - - BVSLT(a_exp, b_exp, c6); + // exp(x) < exp(y) -> x + 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); v6 = x; - // 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); + // 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); - unsigned int max_exp_diff_ui = (unsigned int)m_mpz_manager.get_uint64(max_exp_diff); - m_mpz_manager.del(max_exp_diff); + 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); - 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); - 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)); - 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. - expr_ref huge_sig(m), shifted_sig(m), huge_rem(m); - huge_sig = m_bv_util.mk_zero_extend(max_exp_diff_ui, a_sig); - shifted_sig = m_bv_util.mk_bv_shl(huge_sig, m_bv_util.mk_zero_extend(max_exp_diff_ui + sbits - ebits - 2, exp_diff)); - huge_rem = m_bv_util.mk_bv_urem(shifted_sig, m_bv_util.mk_zero_extend(max_exp_diff_ui, b_sig)); - dbg_decouple("fpa2bv_rem_huge_rem", huge_rem); - - expr_ref res_sgn(m), res_sig(m), res_exp(m); - res_sgn = a_sgn; - res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits, 0, huge_rem), - m_bv_util.mk_numeral(0, 3)); - - res_exp = m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext); - - // CMW: Actual rounding is not necessary here, this is - // just convenience to get rid of the extra bits. - expr_ref bv_rm(m); - bv_rm = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, v7); + 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); // And finally, we tie them together. mk_ite(c6, v6, v7, result); @@ -1136,7 +1145,9 @@ void fpa2bv_converter::mk_min(func_decl * f, unsigned num, expr * const * args, v = m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MIN_UNSPECIFIED, x, y); // Note: This requires BR_REWRITE_FULL afterwards. - result = m.mk_ite(c, v, m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MIN_I, x, y)); + expr_ref min_i(m); + min_i = m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MIN_I, x, y); + m_simp.mk_ite(c, v, min_i, result); } void fpa2bv_converter::mk_min_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -1163,8 +1174,7 @@ void fpa2bv_converter::mk_min_i(func_decl * f, unsigned num, expr * const * args expr_ref lt(m); mk_float_lt(f, num, args, lt); - result = y; - mk_ite(lt, x, result, result); + mk_ite(lt, x, y, result); mk_ite(both_zero, y, result, result); mk_ite(y_is_nan, x, result, result); mk_ite(x_is_nan, y, result, result); @@ -1192,13 +1202,13 @@ expr_ref fpa2bv_converter::mk_min_unspecified(func_decl * f, expr * x, expr * y) expr_ref pn(m), np(m); mk_fp(decls.first, - m_bv_util.mk_numeral(0, ebits), - m_bv_util.mk_numeral(0, sbits - 1), - pn); + m_bv_util.mk_numeral(0, ebits), + m_bv_util.mk_numeral(0, sbits - 1), + pn); mk_fp(decls.second, - m_bv_util.mk_numeral(0, ebits), - m_bv_util.mk_numeral(0, sbits - 1), - np); + m_bv_util.mk_numeral(0, ebits), + m_bv_util.mk_numeral(0, sbits - 1), + np); expr_ref x_is_pzero(m), x_is_nzero(m), xyzero(m); mk_is_pzero(x, x_is_pzero); @@ -1221,7 +1231,9 @@ void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, v = m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MAX_UNSPECIFIED, x, y); // Note: This requires BR_REWRITE_FULL afterwards. - result = m.mk_ite(c, v, m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MAX_I, x, y)); + expr_ref max_i(m); + max_i = m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MAX_I, x, y); + m_simp.mk_ite(c, v, max_i, result); } void fpa2bv_converter::mk_max_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -1242,14 +1254,14 @@ void fpa2bv_converter::mk_max_i(func_decl * f, unsigned num, expr * const * args mk_is_nan(y, y_is_nan); mk_pzero(f, pzero); - expr_ref sgn_diff(m); - sgn_diff = m.mk_not(m.mk_eq(x_sgn, y_sgn)); + expr_ref sgn_diff(m), sgn_eq(m); + sgn_eq = m.mk_eq(x_sgn, y_sgn); + sgn_diff = m.mk_not(sgn_eq); expr_ref gt(m); mk_float_gt(f, num, args, gt); - result = y; - mk_ite(gt, x, result, result); + mk_ite(gt, x, y, result); mk_ite(both_zero, y, result, result); mk_ite(y_is_nan, x, result, result); mk_ite(x_is_nan, y, result, result); @@ -1277,13 +1289,13 @@ expr_ref fpa2bv_converter::mk_max_unspecified(func_decl * f, expr * x, expr * y) expr_ref pn(m), np(m); mk_fp(decls.first, - m_bv_util.mk_numeral(0, ebits), - m_bv_util.mk_numeral(0, sbits - 1), - pn); + m_bv_util.mk_numeral(0, ebits), + m_bv_util.mk_numeral(0, sbits - 1), + pn); mk_fp(decls.second, - m_bv_util.mk_numeral(0, ebits), - m_bv_util.mk_numeral(0, sbits - 1), - np); + m_bv_util.mk_numeral(0, ebits), + m_bv_util.mk_numeral(0, sbits - 1), + np); expr_ref x_is_pzero(m), x_is_nzero(m), xyzero(m); mk_is_pzero(x, x_is_pzero); @@ -1299,8 +1311,8 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); // fusedma means (x * y) + z - expr_ref bv_rm(m), x(m), y(m), z(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m), y(m), z(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; z = args[3]; @@ -1332,7 +1344,7 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, mk_is_inf(z, z_is_inf); expr_ref rm_is_to_neg(m); - mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); dbg_decouple("fpa2bv_fma_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_fma_x_is_zero", x_is_zero); @@ -1440,7 +1452,7 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, dbg_decouple("fpa2bv_fma_mul_sgn", mul_sgn); mul_exp = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), - m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); dbg_decouple("fpa2bv_fma_mul_exp", mul_exp); mul_sig = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); @@ -1501,8 +1513,8 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, // Alignment shift with sticky bit computation. expr_ref shifted_big(m), shifted_f_sig(m), sticky_raw(m); shifted_big = m_bv_util.mk_bv_lshr( - m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits)), - m_bv_util.mk_zero_extend((3*sbits)-(ebits+2), exp_delta)); + m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits)), + m_bv_util.mk_zero_extend((3*sbits)-(ebits+2), exp_delta)); shifted_f_sig = m_bv_util.mk_extract(3*sbits-1, sbits, shifted_big); sticky_raw = m_bv_util.mk_extract(sbits-1, 0, shifted_big); SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2 * sbits); @@ -1535,7 +1547,7 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, expr_ref sum(m), e_plus_f(m), e_minus_f(m); e_plus_f = m_bv_util.mk_bv_add(e_sig, shifted_f_sig); e_minus_f = m_bv_util.mk_bv_sub(e_sig, shifted_f_sig), - m_simp.mk_ite(eq_sgn, e_plus_f, e_minus_f, sum); + m_simp.mk_ite(eq_sgn, e_plus_f, e_minus_f, sum); SASSERT(is_well_sorted(m, sum)); @@ -1594,7 +1606,7 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, mk_ite(rm_is_to_neg, nzero, pzero, zero_case); expr_ref rounded(m); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, rounded); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); mk_ite(is_zero_sig, zero_case, rounded, v8); @@ -1616,8 +1628,8 @@ void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, SASSERT(num == 2); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); - expr_ref bv_rm(m), x(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); @@ -1721,7 +1733,7 @@ void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, expr * or_args[2] = { Q, S }; expr_ref Q_or_S(m), R_shftd(m), T_lsds4(m); - Q_or_S = m_bv_util.mk_bv_or(2, or_args); + Q_or_S = m_bv_util.mk_bv_or(2, or_args); m_simp.mk_ite(t_lt_0, Q, Q_or_S, Q); R_shftd = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits + 3, 0, R), zero1); T_lsds4 = m_bv_util.mk_extract(sbits + 4, 0, T); @@ -1749,7 +1761,7 @@ void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); expr_ref rounded(m); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, rounded); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); v5 = rounded; // And finally, we tie them together. @@ -1765,21 +1777,24 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * SASSERT(num == 2); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); - expr_ref bv_rm(m), x(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; + mk_round_to_integral(f->get_range(), rm, x, result); +} +void fpa2bv_converter::mk_round_to_integral(sort * s, expr_ref & rm, expr_ref & x, expr_ref & result) { expr_ref rm_is_rta(m), rm_is_rte(m), rm_is_rtp(m), rm_is_rtn(m), rm_is_rtz(m); - mk_is_rm(bv_rm, BV_RM_TIES_TO_AWAY, rm_is_rta); - mk_is_rm(bv_rm, BV_RM_TIES_TO_EVEN, rm_is_rte); - mk_is_rm(bv_rm, BV_RM_TO_POSITIVE, rm_is_rtp); - mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_is_rtn); - mk_is_rm(bv_rm, BV_RM_TO_ZERO, rm_is_rtz); + mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_is_rta); + mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_is_rte); + mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_rtp); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_rtn); + mk_is_rm(rm, BV_RM_TO_ZERO, rm_is_rtz); expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); - mk_nan(f, nan); - mk_nzero(f, nzero); - mk_pzero(f, pzero); + mk_nan(s, nan); + mk_nzero(s, nzero); + mk_pzero(s, pzero); expr_ref x_is_zero(m), x_is_pos(m), x_is_neg(m); mk_is_zero(x, x_is_zero); @@ -1809,17 +1824,20 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * one_1 = m_bv_util.mk_numeral(1, 1); zero_1 = m_bv_util.mk_numeral(0, 1); - unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); + unsigned ebits = m_util.get_ebits(s); + unsigned sbits = m_util.get_sbits(s); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); - dbg_decouple("fpa2bv_r2i_unpacked_sig", a_sig); + dbg_decouple("fpa2bv_r2i_unpacked_sgn", a_sgn); dbg_decouple("fpa2bv_r2i_unpacked_exp", a_exp); + dbg_decouple("fpa2bv_r2i_unpacked_sig", a_sig); + dbg_decouple("fpa2bv_r2i_unpacked_lz", a_lz); - expr_ref xzero(m); - mk_ite(m.mk_eq(a_sgn, one_1), nzero, pzero, xzero); + expr_ref xzero(m), sgn_eq_1(m); + sgn_eq_1 = m.mk_eq(a_sgn, one_1); + mk_ite(sgn_eq_1, nzero, pzero, xzero); // exponent < 0 -> 0/1 expr_ref exp_lt_zero(m), exp_h(m); @@ -1829,9 +1847,9 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * c4 = exp_lt_zero; expr_ref pone(m), none(m), xone(m), c421(m), c422(m), c423(m), t1(m), t2(m), tie(m), v42(m), exp_lt_m1(m); - mk_one(f, zero_1, pone); - mk_one(f, one_1, none); - mk_ite(m.mk_eq(a_sgn, one_1), none, pone, xone); + mk_one(s, zero_1, pone); + mk_one(s, one_1, none); + mk_ite(sgn_eq_1, none, pone, xone); expr_ref pow_2_sbitsm1(m), m1(m); pow_2_sbitsm1 = m_bv_util.mk_numeral(fu().fm().m_powers2(sbits - 1), sbits); @@ -1878,49 +1896,53 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * res_sgn = a_sgn; res_exp = a_exp; - expr_ref shift(m), rshift(m), div(m), rem(m); - shift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits - 1, sbits + 1), - m_bv_util.mk_sign_extend(sbits - ebits + 1, a_exp)); - rshift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits, sbits + 1), shift); - div = m_bv_util.mk_bv_lshr(m_bv_util.mk_zero_extend(1, a_sig), shift); - rem = m_bv_util.mk_bv_lshr(m_bv_util.mk_bv_shl(m_bv_util.mk_zero_extend(1, a_sig), rshift), rshift); + SASSERT(m_bv_util.get_bv_size(a_sig) == sbits); + SASSERT(m_bv_util.get_bv_size(a_exp) == ebits); + + expr_ref zero_s(m); + zero_s = m_bv_util.mk_numeral(0, sbits); + + expr_ref shift(m), shifted_sig(m), div(m), rem(m); + shift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits - 1, sbits), + m_bv_util.mk_zero_extend(sbits-ebits, a_exp)); + shifted_sig = m_bv_util.mk_bv_lshr(m_bv_util.mk_concat(a_sig, zero_s), + m_bv_util.mk_concat(zero_s, shift)); + div = m_bv_util.mk_extract(2*sbits-1, sbits, shifted_sig); + rem = m_bv_util.mk_extract(sbits-1, 0, shifted_sig); SASSERT(is_well_sorted(m, div)); SASSERT(is_well_sorted(m, rem)); - SASSERT(m_bv_util.get_bv_size(div) == sbits + 1); - SASSERT(m_bv_util.get_bv_size(rem) == sbits + 1); + SASSERT(m_bv_util.get_bv_size(shift) == sbits); + SASSERT(m_bv_util.get_bv_size(div) == sbits); + SASSERT(m_bv_util.get_bv_size(rem) == sbits); + dbg_decouple("fpa2bv_r2i_shifted_sig", shifted_sig); dbg_decouple("fpa2bv_r2i_shift", shift); - dbg_decouple("fpa2bv_r2i_rshift", rshift); dbg_decouple("fpa2bv_r2i_div", div); dbg_decouple("fpa2bv_r2i_rem", rem); expr_ref div_p1(m); - div_p1 = m_bv_util.mk_bv_add(div, m_bv_util.mk_numeral(1, sbits+1)); + div_p1 = m_bv_util.mk_bv_add(div, m_bv_util.mk_numeral(1, sbits)); - expr_ref tie2(m), tie2_c(m), div_last(m), v51(m), rem_shl(m); - rem_shl = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits - 1, 0, rem), zero_1); - m_simp.mk_eq(rem_shl, - m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits+1), shift), - tie2); + expr_ref tie_pttrn(m), tie2(m), tie2_c(m), div_last(m), v51(m); + tie_pttrn = m_bv_util.mk_concat(one_1, m_bv_util.mk_numeral(0, sbits-1)); + m_simp.mk_eq(rem, tie_pttrn, tie2); div_last = m_bv_util.mk_extract(0, 0, div); - tie2_c = m.mk_or(m.mk_and(tie2, - m.mk_or(m.mk_and(rm_is_rte, m.mk_eq(div_last, one_1)), - m.mk_and(rm_is_rta, m.mk_eq(div_last, zero_1)))), - m.mk_xor(m.mk_eq(a_sgn, one_1), - m_bv_util.mk_sle(m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits + 1), shift), - rem_shl))); + tie2_c = m.mk_ite(tie2, m.mk_or(m.mk_and(rm_is_rte, m.mk_eq(div_last, one_1)), + m.mk_and(rm_is_rta, m.mk_eq(div_last, zero_1))), + m_bv_util.mk_ule(tie_pttrn, rem)); m_simp.mk_ite(tie2_c, div_p1, div, v51); dbg_decouple("fpa2bv_r2i_v51", v51); dbg_decouple("fpa2bv_r2i_tie2", tie2); + dbg_decouple("fpa2bv_r2i_tie2_c", tie2_c); SASSERT(is_well_sorted(m, tie2)); SASSERT(is_well_sorted(m, tie2_c)); SASSERT(is_well_sorted(m, v51)); expr_ref c521(m), v52(m), rem_eq_0(m), sgn_eq_zero(m); - rem_eq_0 = m.mk_eq(rem, m_bv_util.mk_numeral(0, sbits + 1)); + rem_eq_0 = m.mk_eq(rem, m_bv_util.mk_numeral(0, sbits)); sgn_eq_zero = m.mk_eq(res_sgn, zero_1); m_simp.mk_not(rem_eq_0, c521); m_simp.mk_and(c521, sgn_eq_zero, c521); @@ -1941,14 +1963,14 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * m_simp.mk_ite(c53, v53, res_sig, res_sig); m_simp.mk_ite(c52, v52, res_sig, res_sig); m_simp.mk_ite(c51, v51, res_sig, res_sig); - res_sig = m_bv_util.mk_concat(res_sig, m_bv_util.mk_numeral(0, 3)); // rounding bits are all 0. + res_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_concat(res_sig, m_bv_util.mk_numeral(0, 3))); // rounding bits are all 0. SASSERT(m_bv_util.get_bv_size(res_exp) == ebits); - SASSERT(m_bv_util.get_bv_size(shift) == sbits + 1); + SASSERT(m_bv_util.get_bv_size(shift) == sbits); expr_ref e_shift(m); e_shift = (ebits + 2 <= sbits + 1) ? m_bv_util.mk_extract(ebits + 1, 0, shift) : - m_bv_util.mk_sign_extend((ebits + 2) - (sbits + 1), shift); + m_bv_util.mk_sign_extend((ebits + 2) - (sbits), shift); SASSERT(m_bv_util.get_bv_size(e_shift) == ebits + 2); res_exp = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(2, res_exp), e_shift); @@ -1957,7 +1979,7 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * SASSERT(m_bv_util.get_bv_size(res_exp) == ebits + 2); // CMW: We use the rounder for normalization. - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, v6); + round(s, rm, res_sgn, res_sig, res_exp, v6); // And finally, we tie them together. mk_ite(c5, v5, v6, result); @@ -1977,7 +1999,7 @@ void fpa2bv_converter::mk_float_eq(func_decl * f, unsigned num, expr * const * a expr * x = args[0], * y = args[1]; 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); @@ -2004,7 +2026,6 @@ void fpa2bv_converter::mk_float_eq(func_decl * f, unsigned num, expr * const * a expr_ref c3t4(m), c2else(m); m_simp.mk_ite(c3, m.mk_false(), t4, c3t4); m_simp.mk_ite(c2, m.mk_true(), c3t4, c2else); - m_simp.mk_ite(c1, m.mk_false(), c2else, result); TRACE("fpa2bv_float_eq", tout << "FLOAT_EQ = " << mk_ismt2_pp(result, m) << std::endl; ); @@ -2154,7 +2175,7 @@ void fpa2bv_converter::mk_is_positive(func_decl * f, unsigned num, expr * const void fpa2bv_converter::mk_to_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp", for (unsigned i=0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl; ); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl; ); if (num == 1 && m_bv_util.is_bv(args[0])) { @@ -2167,33 +2188,33 @@ void fpa2bv_converter::mk_to_fp(func_decl * f, unsigned num, expr * const * args SASSERT((unsigned)sz == to_sbits + to_ebits); mk_fp(m_bv_util.mk_extract(sz - 1, sz - 1, bv), - m_bv_util.mk_extract(sz - 2, sz - to_ebits - 1, bv), - m_bv_util.mk_extract(sz - to_ebits - 2, 0, bv), - result); + m_bv_util.mk_extract(sz - 2, sz - to_ebits - 1, bv), + m_bv_util.mk_extract(sz - to_ebits - 2, 0, bv), + result); } else if (num == 2 && - m_util.is_rm(args[0]) && - m_util.is_float(m.get_sort(args[1]))) { + m_util.is_rm(args[0]) && + m_util.is_float(m.get_sort(args[1]))) { // rm + float -> float mk_to_fp_float(f, f->get_range(), args[0], args[1], result); } else if (num == 2 && - m_util.is_rm(args[0]) && - (m_arith_util.is_int(args[1]) || - m_arith_util.is_real(args[1]))) { + m_util.is_rm(args[0]) && + (m_arith_util.is_int(args[1]) || + m_arith_util.is_real(args[1]))) { // rm + real -> float mk_to_fp_real(f, f->get_range(), args[0], args[1], result); } else if (num == 2 && - m_util.is_rm(args[0]) && - m_bv_util.is_bv(args[1])) { + m_util.is_rm(args[0]) && + m_bv_util.is_bv(args[1])) { // rm + signed bv -> float mk_to_fp_signed(f, num, args, result); } else if (num == 3 && - m_bv_util.is_bv(args[0]) && - m_bv_util.is_bv(args[1]) && - m_bv_util.is_bv(args[2])) { + m_bv_util.is_bv(args[0]) && + m_bv_util.is_bv(args[1]) && + m_bv_util.is_bv(args[2])) { // 3 BV -> float SASSERT(m_bv_util.get_bv_size(args[0]) == 1); SASSERT(m_util.get_ebits(f->get_range()) == m_bv_util.get_bv_size(args[1])); @@ -2201,9 +2222,9 @@ void fpa2bv_converter::mk_to_fp(func_decl * f, unsigned num, expr * const * args mk_fp(args[0], args[1], args[2], result); } else if (num == 3 && - m_util.is_rm(args[0]) && - m_arith_util.is_numeral(args[1]) && - m_arith_util.is_numeral(args[2])) + m_util.is_rm(args[0]) && + m_arith_util.is_numeral(args[1]) && + m_arith_util.is_numeral(args[2])) { // rm + real + int -> float mk_to_fp_real_int(f, num, args, result); @@ -2309,15 +2330,15 @@ void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); } else if (from_ebits > (to_ebits + 2)) { - unsigned ebits_diff = from_ebits - (to_ebits + 2); + unsigned ebits_diff = from_ebits - (to_ebits + 2); // subtract lz for subnormal numbers. expr_ref exp_sub_lz(m); exp_sub_lz = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(2, exp), m_bv_util.mk_sign_extend(2, lz)); dbg_decouple("fpa2bv_to_float_exp_sub_lz", exp_sub_lz); - // check whether exponent is within roundable (to_ebits+2) range. - expr_ref max_exp(m), min_exp(m), exp_in_range(m); + // check whether exponent is within roundable (to_ebits+2) range. + expr_ref max_exp(m), min_exp(m), exp_in_range(m); const mpz & z = m_mpf_manager.m_powers2(to_ebits + 1, true); max_exp = m_bv_util.mk_concat( m_bv_util.mk_numeral(m_mpf_manager.m_powers2.m1(to_ebits, false), to_ebits + 1), @@ -2330,23 +2351,23 @@ void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * const mpz & ovft = m_mpf_manager.m_powers2.m1(to_ebits+1, false); first_ovf_exp = m_bv_util.mk_numeral(ovft, from_ebits+2); first_udf_exp = m_bv_util.mk_concat( - m_bv_util.mk_numeral(-1, ebits_diff + 3), - m_bv_util.mk_numeral(1, to_ebits + 1)); + m_bv_util.mk_numeral(-1, ebits_diff + 3), + m_bv_util.mk_numeral(1, to_ebits + 1)); dbg_decouple("fpa2bv_to_float_first_ovf_exp", first_ovf_exp); dbg_decouple("fpa2bv_to_float_first_udf_exp", first_udf_exp); - exp_in_range = m_bv_util.mk_extract(to_ebits + 1, 0, exp_sub_lz); - SASSERT(m_bv_util.get_bv_size(exp_in_range) == to_ebits + 2); + exp_in_range = m_bv_util.mk_extract(to_ebits + 1, 0, exp_sub_lz); + SASSERT(m_bv_util.get_bv_size(exp_in_range) == to_ebits + 2); - expr_ref ovf_cond(m), udf_cond(m); - ovf_cond = m_bv_util.mk_sle(first_ovf_exp, exp_sub_lz); - udf_cond = m_bv_util.mk_sle(exp_sub_lz, first_udf_exp); + expr_ref ovf_cond(m), udf_cond(m); + ovf_cond = m_bv_util.mk_sle(first_ovf_exp, exp_sub_lz); + udf_cond = m_bv_util.mk_sle(exp_sub_lz, first_udf_exp); dbg_decouple("fpa2bv_to_float_exp_ovf", ovf_cond); dbg_decouple("fpa2bv_to_float_exp_udf", udf_cond); - res_exp = exp_in_range; - res_exp = m.mk_ite(ovf_cond, max_exp, res_exp); - res_exp = m.mk_ite(udf_cond, min_exp, res_exp); + res_exp = exp_in_range; + res_exp = m.mk_ite(ovf_cond, max_exp, res_exp); + res_exp = m.mk_ite(udf_cond, min_exp, res_exp); } else { // from_ebits == (to_ebits + 2) res_exp = m_bv_util.mk_bv_sub(exp, lz); @@ -2381,7 +2402,7 @@ void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * x, expr_ref & result) { TRACE("fpa2bv_to_fp_real", tout << "rm: " << mk_ismt2_pp(rm, m) << std::endl << - "x: " << mk_ismt2_pp(x, m) << std::endl;); + "x: " << mk_ismt2_pp(x, m) << std::endl;); SASSERT(m_util.is_float(s)); SASSERT(au().is_real(x) || au().is_int(x)); SASSERT(is_app_of(rm, m_util.get_family_id(), OP_FPA_INTERNAL_RM)); @@ -2572,7 +2593,7 @@ void fpa2bv_converter::mk_to_fp_real_int(func_decl * f, unsigned num, expr * con void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_real", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); SASSERT(num == 1); SASSERT(f->get_num_parameters() == 0); SASSERT(is_app_of(args[0], m_plugin->get_family_id(), OP_FPA_FP)); @@ -2596,7 +2617,7 @@ void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * ar SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits); - expr_ref rsig(m), bit(m), zero(m), one(m), two(m), bv0(m), bv1(m); + expr_ref rsig(m), bit(m), bit_eq_1(m), rsig_mul_2(m), zero(m), one(m), two(m), bv0(m), bv1(m); zero = m_arith_util.mk_numeral(rational(0), rs); one = m_arith_util.mk_numeral(rational(1), rs); two = m_arith_util.mk_numeral(rational(2), rs); @@ -2605,8 +2626,10 @@ void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * ar rsig = one; for (unsigned i = sbits - 2; i != (unsigned)-1; i--) { bit = m_bv_util.mk_extract(i, i, sig); - rsig = m_arith_util.mk_add(m_arith_util.mk_mul(rsig, two), - m.mk_ite(m.mk_eq(bit, bv1), one, zero)); + bit_eq_1 = m.mk_eq(bit, bv1); + rsig_mul_2 = m_arith_util.mk_mul(rsig, two); + rsig = m_arith_util.mk_add(rsig_mul_2, + m.mk_ite(bit_eq_1, one, zero)); } const mpz & p2 = fu().fm().m_powers2(sbits - 1); @@ -2625,37 +2648,44 @@ void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * ar dbg_decouple("fpa2bv_to_real_exp_abs", exp); SASSERT(m_bv_util.get_bv_size(exp_abs) == ebits + 1); - expr_ref exp2(m), prev_bit(m); + expr_ref exp2(m), exp2_mul_2(m), prev_bit(m); exp2 = zero; for (unsigned i = ebits; i != (unsigned)-1; i--) { bit = m_bv_util.mk_extract(i, i, exp_abs); - exp2 = m_arith_util.mk_add(m_arith_util.mk_mul(exp2, two), - m.mk_ite(m.mk_eq(bit, bv1), one, zero)); + bit_eq_1 = m.mk_eq(bit, bv1); + exp2_mul_2 = m_arith_util.mk_mul(exp2, two); + exp2 = m_arith_util.mk_add(exp2_mul_2, + m.mk_ite(bit_eq_1, one, zero)); prev_bit = bit; } - exp2 = m.mk_ite(exp_is_neg, m_arith_util.mk_div(one, exp2), exp2); + expr_ref one_div_exp2(m); + one_div_exp2 = m_arith_util.mk_div(one, exp2); + exp2 = m.mk_ite(exp_is_neg, one_div_exp2, exp2); dbg_decouple("fpa2bv_to_real_exp2", exp2); - expr_ref res(m), two_exp2(m); + expr_ref res(m), two_exp2(m), minus_res(m); two_exp2 = m_arith_util.mk_power(two, exp2); res = m_arith_util.mk_mul(rsig, two_exp2); - res = m.mk_ite(m.mk_eq(sgn, bv1), m_arith_util.mk_uminus(res), res); + minus_res = m_arith_util.mk_uminus(res); + res = m.mk_ite(m.mk_eq(sgn, bv1), minus_res, res); dbg_decouple("fpa2bv_to_real_sig_times_exp2", res); TRACE("fpa2bv_to_real", tout << "rsig = " << mk_ismt2_pp(rsig, m) << std::endl; tout << "exp2 = " << mk_ismt2_pp(exp2, m) << std::endl;); + expr_ref unspec(m); + unspec = mk_to_real_unspecified(ebits, sbits); result = m.mk_ite(x_is_zero, zero, res); - result = m.mk_ite(x_is_inf, mk_to_real_unspecified(ebits, sbits), result); - result = m.mk_ite(x_is_nan, mk_to_real_unspecified(ebits, sbits), result); + result = m.mk_ite(x_is_inf, unspec, result); + result = m.mk_ite(x_is_nan, unspec, result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp_signed", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); // This is a conversion from signed bitvector to float: // ; from signed machine integer, represented as a 2's complement bit vector @@ -2671,8 +2701,8 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); SASSERT(m_bv_util.is_bv(args[1])); - expr_ref bv_rm(m), x(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; dbg_decouple("fpa2bv_to_fp_signed_x", x); @@ -2680,7 +2710,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); unsigned bv_sz = m_bv_util.get_bv_size(x); - SASSERT(m_bv_util.get_bv_size(bv_rm) == 3); + SASSERT(m_bv_util.get_bv_size(rm) == 3); expr_ref bv0_1(m), bv1_1(m), bv0_sz(m), bv1_sz(m); bv0_1 = m_bv_util.mk_numeral(0, 1); @@ -2702,10 +2732,11 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const // Special case: x != 0 expr_ref is_neg_bit(m), exp_too_large(m), sig_4(m), exp_2(m); - expr_ref is_neg(m), x_abs(m); + expr_ref is_neg(m), x_abs(m), neg_x(m); is_neg_bit = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, x); is_neg = m.mk_eq(is_neg_bit, bv1_1); - x_abs = m.mk_ite(is_neg, m_bv_util.mk_bv_neg(x), x); + neg_x = m_bv_util.mk_bv_neg(x); + x_abs = m.mk_ite(is_neg, neg_x, x); dbg_decouple("fpa2bv_to_fp_signed_is_neg", is_neg); // x_abs has an extra bit in the front. // x_abs is [bv_sz-1, bv_sz-2] . [bv_sz-3 ... 0] * 2^(bv_sz-2) @@ -2736,7 +2767,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const extra_zeros = m_bv_util.mk_numeral(0, extra_bits); sig_4 = m_bv_util.mk_concat(shifted_sig, extra_zeros); lz = m_bv_util.mk_bv_add(m_bv_util.mk_concat(extra_zeros, lz), - m_bv_util.mk_numeral(extra_bits, sig_sz)); + m_bv_util.mk_numeral(extra_bits, sig_sz)); bv_sz = bv_sz + extra_bits; SASSERT(is_well_sorted(m, lz)); } @@ -2762,15 +2793,16 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const // exp_sz < exp_worst_case_sz and exp >= 0. // Take the maximum legal exponent; this // allows us to keep the most precision. - expr_ref max_exp(m), max_exp_bvsz(m); + expr_ref max_exp(m), max_exp_bvsz(m), zero_sig_sz(m); mk_max_exp(exp_sz, max_exp); max_exp_bvsz = m_bv_util.mk_zero_extend(bv_sz - exp_sz, max_exp); exp_too_large = m_bv_util.mk_ule(m_bv_util.mk_bv_add( - max_exp_bvsz, - m_bv_util.mk_numeral(1, bv_sz)), - s_exp); - sig_4 = m.mk_ite(exp_too_large, m_bv_util.mk_numeral(0, sig_sz), sig_4); + max_exp_bvsz, + m_bv_util.mk_numeral(1, bv_sz)), + s_exp); + zero_sig_sz = m_bv_util.mk_numeral(0, sig_sz); + sig_4 = m.mk_ite(exp_too_large, zero_sig_sz, sig_4); exp_2 = m.mk_ite(exp_too_large, max_exp, exp_2); } dbg_decouple("fpa2bv_to_fp_signed_exp_too_large", exp_too_large); @@ -2788,14 +2820,14 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); expr_ref v2(m); - round(f->get_range(), bv_rm, sgn, sig, exp, v2); + round(f->get_range(), rm, sgn, sig, exp, v2); mk_ite(c1, v1, v2, result); } void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp_unsigned", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); // This is a conversion from unsigned bitvector to float: // ((_ to_fp_unsigned eb sb) RoundingMode (_ BitVec m) (_ FloatingPoint eb sb)) @@ -2811,8 +2843,8 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); SASSERT(m_bv_util.is_bv(args[1])); - expr_ref bv_rm(m), x(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; dbg_decouple("fpa2bv_to_fp_unsigned_x", x); @@ -2820,7 +2852,7 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); unsigned bv_sz = m_bv_util.get_bv_size(x); - SASSERT(m_bv_util.get_bv_size(bv_rm) == 3); + SASSERT(m_bv_util.get_bv_size(rm) == 3); expr_ref bv0_1(m), bv1_1(m), bv0_sz(m), bv1_sz(m); bv0_1 = m_bv_util.mk_numeral(0, 1); @@ -2870,7 +2902,7 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con extra_zeros = m_bv_util.mk_numeral(0, extra_bits); sig_4 = m_bv_util.mk_concat(shifted_sig, extra_zeros); lz = m_bv_util.mk_bv_add(m_bv_util.mk_concat(extra_zeros, lz), - m_bv_util.mk_numeral(extra_bits, sig_sz)); + m_bv_util.mk_numeral(extra_bits, sig_sz)); bv_sz = bv_sz + extra_bits; SASSERT(is_well_sorted(m, lz)); } @@ -2886,15 +2918,15 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con // the remaining bits are 0 if ebits is large enough. exp_too_large = m.mk_false(); // This is always in range. - // The exponent is at most bv_sz, i.e., we need ld(bv_sz)+1 ebits. - // exp < bv_sz (+sign bit which is [0]) + // The exponent is at most bv_sz, i.e., we need ld(bv_sz)+1 ebits. + // exp < bv_sz (+sign bit which is [0]) unsigned exp_worst_case_sz = (unsigned)((log((double)bv_sz) / log((double)2)) + 1.0); if (exp_sz < exp_worst_case_sz) { // exp_sz < exp_worst_case_sz and exp >= 0. // Take the maximum legal exponent; this // allows us to keep the most precision. - expr_ref max_exp(m), max_exp_bvsz(m); + expr_ref max_exp(m), max_exp_bvsz(m), zero_sig_sz(m); mk_max_exp(exp_sz, max_exp); max_exp_bvsz = m_bv_util.mk_zero_extend(bv_sz - exp_sz, max_exp); @@ -2902,7 +2934,8 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con max_exp_bvsz, m_bv_util.mk_numeral(1, bv_sz)), s_exp); - sig_4 = m.mk_ite(exp_too_large, m_bv_util.mk_numeral(0, sig_sz), sig_4); + zero_sig_sz = m_bv_util.mk_numeral(0, sig_sz); + sig_4 = m.mk_ite(exp_too_large, zero_sig_sz, sig_4); exp_2 = m.mk_ite(exp_too_large, max_exp, exp_2); } dbg_decouple("fpa2bv_to_fp_unsigned_exp_too_large", exp_too_large); @@ -2920,7 +2953,7 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); expr_ref v2(m); - round(f->get_range(), bv_rm, sgn, sig, exp, v2); + round(f->get_range(), rm, sgn, sig, exp, v2); mk_ite(c1, v1, v2, result); } @@ -2941,26 +2974,28 @@ void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * if (m_hi_fp_unspecified) // The "hardware interpretation" is 01...10...01. nanv = 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)))); + 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); - result = m.mk_ite(x_is_nan, nanv, m_bv_util.mk_concat(m_bv_util.mk_concat(sgn, e), s)); + 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); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result) { TRACE("fpa2bv_to_bv", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); SASSERT(num == 2); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); SASSERT(m_util.is_float(args[1])); - expr * bv_rm = to_app(args[0])->get_arg(0); + expr * rm = to_app(args[0])->get_arg(0); expr * x = args[1]; sort * xs = m.get_sort(x); sort * bv_srt = f->get_range(); @@ -3016,14 +3051,15 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args sig_sz = m_bv_util.get_bv_size(sig); SASSERT(sig_sz >= (bv_sz + 3)); - expr_ref exp_m_lz(m), e_m_lz_m_bv_sz(m), shift(m), bv0_e2(m), shift_abs(m); + expr_ref exp_m_lz(m), e_m_lz_m_bv_sz(m), shift(m), bv0_e2(m), shift_abs(m), shift_le_0(m); exp_m_lz = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(2, exp), - m_bv_util.mk_zero_extend(2, lz)); + m_bv_util.mk_zero_extend(2, lz)); e_m_lz_m_bv_sz = m_bv_util.mk_bv_sub(exp_m_lz, - m_bv_util.mk_numeral(bv_sz - 1, ebits + 2)); + m_bv_util.mk_numeral(bv_sz - 1, ebits + 2)); shift = m_bv_util.mk_bv_neg(e_m_lz_m_bv_sz); bv0_e2 = m_bv_util.mk_numeral(0, ebits + 2); - shift_abs = m.mk_ite(m_bv_util.mk_sle(shift, bv0_e2), e_m_lz_m_bv_sz, shift); + shift_le_0 = m_bv_util.mk_sle(shift, bv0_e2); + shift_abs = m.mk_ite(shift_le_0, e_m_lz_m_bv_sz, shift); SASSERT(m_bv_util.get_bv_size(shift) == ebits + 2); SASSERT(m_bv_util.get_bv_size(shift_abs) == ebits + 2); dbg_decouple("fpa2bv_to_bv_shift", shift); @@ -3041,15 +3077,15 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args c_in_limits = m_bv_util.mk_sle(bv0_e2, shift); else c_in_limits = m.mk_or(m_bv_util.mk_sle(m_bv_util.mk_numeral(1, ebits + 2), shift), - m.mk_and(m.mk_eq(m_bv_util.mk_numeral(0, ebits + 2), shift), - m.mk_eq(sig, m_bv_util.mk_concat(bv1, m_bv_util.mk_numeral(0, sig_sz-1))))); + m.mk_and(m.mk_eq(m_bv_util.mk_numeral(0, ebits + 2), shift), + m.mk_eq(sig, m_bv_util.mk_concat(bv1, m_bv_util.mk_numeral(0, sig_sz-1))))); dbg_decouple("fpa2bv_to_bv_in_limits", c_in_limits); expr_ref r_shifted_sig(m), l_shifted_sig(m); r_shifted_sig = m_bv_util.mk_bv_lshr(sig, shift_abs); l_shifted_sig = m_bv_util.mk_bv_shl(sig, m_bv_util.mk_bv_sub( - m_bv_util.mk_numeral(m_bv_util.get_bv_size(sig), m_bv_util.get_bv_size(sig)), - shift_abs)); + m_bv_util.mk_numeral(m_bv_util.get_bv_size(sig), m_bv_util.get_bv_size(sig)), + shift_abs)); dbg_decouple("fpa2bv_to_bv_r_shifted_sig", r_shifted_sig); dbg_decouple("fpa2bv_to_bv_l_shifted_sig", l_shifted_sig); @@ -3062,7 +3098,7 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args dbg_decouple("fpa2bv_to_bv_sticky", sticky); expr_ref rounding_decision(m); - rounding_decision = mk_rounding_decision(bv_rm, sgn, last, round, sticky); + rounding_decision = mk_rounding_decision(rm, sgn, last, round, sticky); SASSERT(m_bv_util.get_bv_size(rounding_decision) == 1); dbg_decouple("fpa2bv_to_bv_rounding_decision", rounding_decision); @@ -3079,13 +3115,19 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args rnd_has_overflown = m.mk_eq(rnd_overflow, bv1); dbg_decouple("fpa2bv_to_bv_rnd_has_overflown", rnd_has_overflown); - if (is_signed) - rnd = m.mk_ite(m.mk_eq(sgn, bv1), m_bv_util.mk_bv_neg(rnd), rnd); + if (is_signed) { + expr_ref sgn_eq_1(m), neg_rnd(m); + sgn_eq_1 = m.mk_eq(sgn, bv1); + neg_rnd = m_bv_util.mk_bv_neg(rnd); + m_simp.mk_ite(sgn_eq_1, neg_rnd, rnd, rnd); + } dbg_decouple("fpa2bv_to_bv_rnd", rnd); - result = m.mk_ite(rnd_has_overflown, mk_to_ubv_unspecified(ebits, sbits, bv_sz), rnd); - result = m.mk_ite(c_in_limits, result, mk_to_ubv_unspecified(ebits, sbits, bv_sz)); + expr_ref unspec(m); + unspec = 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); result = m.mk_ite(c1, v1, result); @@ -3094,13 +3136,13 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_ubv", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); mk_to_bv(f, num, args, false, result); } void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_sbv", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); mk_to_bv(f, num, args, true, result); } @@ -3358,7 +3400,7 @@ void fpa2bv_converter::mk_is_normal(expr * e, expr_ref & result) { m_simp.mk_not(or_ex, result); } -void fpa2bv_converter::mk_is_rm(expr * bv_rm, BV_RM_VAL rm, expr_ref & result) { +void fpa2bv_converter::mk_is_rm(expr * rme, BV_RM_VAL rm, expr_ref & result) { expr_ref rm_num(m); rm_num = m_bv_util.mk_numeral(rm, 3); @@ -3369,7 +3411,7 @@ void fpa2bv_converter::mk_is_rm(expr * bv_rm, BV_RM_VAL rm, expr_ref & result) { case BV_RM_TO_NEGATIVE: case BV_RM_TO_POSITIVE: case BV_RM_TO_ZERO: - return m_simp.mk_eq(bv_rm, rm_num, result); + return m_simp.mk_eq(rme, rm_num, result); default: UNREACHABLE(); } @@ -3571,18 +3613,37 @@ void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) } void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { - #ifdef Z3DEBUG +#ifdef Z3DEBUG return; // CMW: This works only for quantifier-free formulas. - expr_ref new_e(m); - new_e = m.mk_fresh_const(prefix, m.get_sort(e)); - m_extra_assertions.push_back(m.mk_eq(new_e, e)); - e = new_e; - #endif + if (is_app_of(e, m_plugin->get_family_id(), OP_FPA_FP)) { + expr_ref new_bv(m); + 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; + unsigned bv_sz = ebits + sbits; + new_bv = m.mk_fresh_const(prefix, m_bv_util.mk_sort(bv_sz)); + expr_ref bv_sgn(m), bv_exp(m), bv_sig(m); + bv_sgn = m_bv_util.mk_extract(bv_sz-1, bv_sz-1, new_bv); + bv_exp = m_bv_util.mk_extract(bv_sz-2, bv_sz-ebits-1, new_bv); + bv_sig = m_bv_util.mk_extract(sbits-2, 0, new_bv); + m_extra_assertions.push_back(m.mk_eq(e_sgn, bv_sgn)); + m_extra_assertions.push_back(m.mk_eq(e_exp, bv_exp)); + m_extra_assertions.push_back(m.mk_eq(e_sig, bv_sig)); + e = m_util.mk_fp(bv_sgn, bv_exp, bv_sig); + } + else { + expr_ref new_e(m); + new_e = m.mk_fresh_const(prefix, m.get_sort(e)); + m_extra_assertions.push_back(m.mk_eq(new_e, e)); + e = new_e; + } +#endif } -expr_ref fpa2bv_converter::mk_rounding_decision(expr * bv_rm, expr * sgn, expr * last, expr * round, expr * sticky) { - expr_ref rmr(bv_rm, m); +expr_ref fpa2bv_converter::mk_rounding_decision(expr * rm, expr * sgn, expr * last, expr * round, expr * sticky) { + expr_ref rmr(rm, m); expr_ref sgnr(sgn, m); expr_ref lastr(last, m); expr_ref roundr(round, m); @@ -3598,7 +3659,7 @@ expr_ref fpa2bv_converter::mk_rounding_decision(expr * bv_rm, expr * sgn, expr * expr * round_sticky[2] = { round, sticky }; last_or_sticky = m_bv_util.mk_bv_or(2, last_sticky); round_or_sticky = m_bv_util.mk_bv_or(2, round_sticky); - not_last= m_bv_util.mk_bv_not(last); + not_last = m_bv_util.mk_bv_not(last); not_round = m_bv_util.mk_bv_not(round); not_sticky = m_bv_util.mk_bv_not(sticky); not_lors = m_bv_util.mk_bv_not(last_or_sticky); @@ -3613,7 +3674,7 @@ expr_ref fpa2bv_converter::mk_rounding_decision(expr * bv_rm, expr * sgn, expr * expr_ref inc_teven(m), inc_taway(m), inc_pos(m), inc_neg(m); inc_teven = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, nround_lors)); expr *taway_args[2] = { m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, nl_r)), - m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(3, nl_nr_sn)) }; + m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(3, nl_nr_sn)) }; inc_taway = m_bv_util.mk_bv_or(2, taway_args); inc_pos = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, pos_args)); inc_neg = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, neg_args)); @@ -3621,10 +3682,10 @@ expr_ref fpa2bv_converter::mk_rounding_decision(expr * bv_rm, expr * sgn, expr * expr_ref res(m), inc_c2(m), inc_c3(m), inc_c4(m); expr_ref rm_is_to_neg(m), rm_is_to_pos(m), rm_is_away(m), rm_is_even(m), nil_1(m); nil_1 = m_bv_util.mk_numeral(0, 1); - mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); - mk_is_rm(bv_rm, BV_RM_TO_POSITIVE, rm_is_to_pos); - mk_is_rm(bv_rm, BV_RM_TIES_TO_AWAY, rm_is_away); - mk_is_rm(bv_rm, BV_RM_TIES_TO_EVEN, rm_is_even); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_to_pos); + mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_is_away); + mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_is_even); m_simp.mk_ite(rm_is_to_neg, inc_neg, nil_1, inc_c4); m_simp.mk_ite(rm_is_to_pos, inc_pos, inc_c4, inc_c3); m_simp.mk_ite(rm_is_away, inc_taway, inc_c3, inc_c2); @@ -3634,32 +3695,32 @@ expr_ref fpa2bv_converter::mk_rounding_decision(expr * bv_rm, expr * sgn, expr * return res; } -void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & result) { +void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & result) { unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); - dbg_decouple("fpa2bv_rnd_rm", bv_rm); + dbg_decouple("fpa2bv_rnd_rm", rm); dbg_decouple("fpa2bv_rnd_sgn", sgn); dbg_decouple("fpa2bv_rnd_sig", sig); dbg_decouple("fpa2bv_rnd_exp", exp); - SASSERT(is_well_sorted(m, bv_rm)); + SASSERT(is_well_sorted(m, rm)); SASSERT(is_well_sorted(m, sgn)); SASSERT(is_well_sorted(m, sig)); SASSERT(is_well_sorted(m, exp)); TRACE("fpa2bv_dbg", tout << "RND: " << std::endl << - "ebits = " << ebits << std::endl << - "sbits = " << sbits << std::endl << - "sgn = " << mk_ismt2_pp(sgn, m) << std::endl << - "sig = " << mk_ismt2_pp(sig, m) << std::endl << - "exp = " << mk_ismt2_pp(exp, m) << std::endl; ); + "ebits = " << ebits << std::endl << + "sbits = " << sbits << std::endl << + "sgn = " << mk_ismt2_pp(sgn, m) << std::endl << + "sig = " << mk_ismt2_pp(sig, m) << std::endl << + "exp = " << mk_ismt2_pp(exp, m) << std::endl; ); // Assumptions: sig is of the form f[-1:0] . f[1:sbits-1] [guard,round,sticky], // i.e., it has 2 + (sbits-1) + 3 = sbits + 4 bits, where the first one is in sgn. // Furthermore, note that sig is an unsigned bit-vector, while exp is signed. - SASSERT(m_bv_util.is_bv(bv_rm) && m_bv_util.get_bv_size(bv_rm) == 3); + SASSERT(m_bv_util.is_bv(rm) && m_bv_util.get_bv_size(rm) == 3); SASSERT(m_bv_util.is_bv(sgn) && m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.is_bv(sig) && m_bv_util.get_bv_size(sig) >= 5); SASSERT(m_bv_util.is_bv(exp) && m_bv_util.get_bv_size(exp) >= 4); @@ -3675,7 +3736,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re mk_max_exp(ebits, e_max); TRACE("fpa2bv_dbg", tout << "e_min = " << mk_ismt2_pp(e_min, m) << std::endl << - "e_max = " << mk_ismt2_pp(e_max, m) << std::endl;); + "e_max = " << mk_ismt2_pp(e_max, m) << std::endl;); expr_ref OVF1(m), e_top_three(m), sigm1(m), e_eq_emax_and_sigm1(m), e_eq_emax(m); expr_ref e3(m), ne3(m), e2(m), e1(m), e21(m), one_1(m), h_exp(m), sh_exp(m), th_exp(m); @@ -3724,7 +3785,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re expr_ref beta(m); beta = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(exp, lz), m_bv_util.mk_numeral(1, ebits+2)); - TRACE("fpa2bv_dbg", tout << "beta = " << mk_ismt2_pp(beta, m)<< std::endl; ); + TRACE("fpa2bv_dbg", tout << "beta = " << mk_ismt2_pp(beta, m) << std::endl; ); SASSERT(is_well_sorted(m, beta)); dbg_decouple("fpa2bv_rnd_beta", beta); @@ -3745,12 +3806,12 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re dbg_decouple("fpa2bv_rnd_sig_before_shift", sig); unsigned sig_size = m_bv_util.get_bv_size(sig); - SASSERT(sig_size == sbits+4); + SASSERT(sig_size == sbits + 4); SASSERT(m_bv_util.get_bv_size(sigma) == ebits+2); - unsigned sigma_size = ebits+2; + unsigned sigma_size = ebits + 2; expr_ref sigma_neg(m), sigma_cap(m), sigma_neg_capped(m), sigma_lt_zero(m), sig_ext(m), - rs_sig(m), ls_sig(m), big_sh_sig(m), sigma_le_cap(m); + rs_sig(m), ls_sig(m), big_sh_sig(m), sigma_le_cap(m); sigma_neg = m_bv_util.mk_bv_neg(sigma); sigma_cap = m_bv_util.mk_numeral(sbits+2, sigma_size); sigma_le_cap = m_bv_util.mk_ule(sigma_neg, sigma_cap); @@ -3809,13 +3870,13 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re sig = m_bv_util.mk_extract(sbits+1, 2, sig); expr_ref inc(m); - inc = mk_rounding_decision(bv_rm, sgn, last, round, sticky); + inc = mk_rounding_decision(rm, sgn, last, round, sticky); SASSERT(m_bv_util.get_bv_size(inc) == 1 && is_well_sorted(m, inc)); dbg_decouple("fpa2bv_rnd_inc", inc); sig = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(1, sig), - m_bv_util.mk_zero_extend(sbits, inc)); + m_bv_util.mk_zero_extend(sbits, inc)); SASSERT(is_well_sorted(m, sig)); dbg_decouple("fpa2bv_rnd_sig_plus_inc", sig); @@ -3882,9 +3943,9 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re nil_1 = m_bv_util.mk_numeral(0, 1); expr_ref rm_is_to_zero(m), rm_is_to_neg(m), rm_is_to_pos(m), rm_zero_or_neg(m), rm_zero_or_pos(m); - mk_is_rm(bv_rm, BV_RM_TO_ZERO, rm_is_to_zero); - mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); - mk_is_rm(bv_rm, BV_RM_TO_POSITIVE, rm_is_to_pos); + mk_is_rm(rm, BV_RM_TO_ZERO, rm_is_to_zero); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_to_pos); m_simp.mk_or(rm_is_to_zero, rm_is_to_neg, rm_zero_or_neg); m_simp.mk_or(rm_is_to_zero, rm_is_to_pos, rm_zero_or_pos); dbg_decouple("fpa2bv_rnd_rm_is_to_zero", rm_is_to_zero); @@ -3900,7 +3961,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re expr_ref max_sig(m), max_exp(m), inf_sig(m), inf_exp(m); max_sig = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(sbits-1, false), sbits-1); max_exp = m_bv_util.mk_concat(m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1, false), ebits-1), - m_bv_util.mk_numeral(0, 1)); + m_bv_util.mk_numeral(0, 1)); inf_sig = m_bv_util.mk_numeral(0, sbits-1); inf_exp = top_exp; @@ -3956,8 +4017,8 @@ void fpa2bv_converter::reset(void) { 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++) { + 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); diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index f69351cb2..b6a6bdbb3 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -145,7 +145,7 @@ public: expr_ref_vector m_extra_assertions; protected: - void mk_one(func_decl *f, expr_ref sign, expr_ref & result); + void mk_one(func_decl *f, expr_ref & sign, expr_ref & result); void mk_is_nan(expr * e, expr_ref & result); void mk_is_inf(expr * e, expr_ref & result); @@ -184,6 +184,23 @@ protected: void mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result); void mk_uninterpreted_output(sort * rng, func_decl * fbv, expr_ref_buffer & new_args, expr_ref & result); + +private: + void mk_nan(sort * s, expr_ref & result); + void mk_nzero(sort * s, expr_ref & result); + void mk_pzero(sort * s, expr_ref & result); + void mk_zero(sort * s, expr_ref & sgn, expr_ref & result); + void mk_ninf(sort * s, expr_ref & result); + void mk_pinf(sort * s, expr_ref & result); + + void mk_one(sort * s, expr_ref & sign, expr_ref & result); + void mk_neg(sort * s, expr_ref & x, expr_ref & result); + void mk_add(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); + void mk_sub(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); + void mk_mul(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); + void mk_div(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); + void mk_rem(sort * s, expr_ref & x, expr_ref & y, expr_ref & result); + void mk_round_to_integral(sort * s, expr_ref & rm, expr_ref & x, expr_ref & result); }; #endif diff --git a/src/ast/rewriter/bv_trailing.cpp b/src/ast/rewriter/bv_trailing.cpp index d3087e776..414854f3a 100644 --- a/src/ast/rewriter/bv_trailing.cpp +++ b/src/ast/rewriter/bv_trailing.cpp @@ -194,8 +194,7 @@ struct bv_trailing::imp { return 0; } - if (!i) {// all args eaten completely - SASSERT(new_last.get() == NULL); + if (!i && !new_last) {// all args eaten completely SASSERT(retv == m_util.get_bv_size(a)); result = NULL; return retv; @@ -204,7 +203,7 @@ struct bv_trailing::imp { expr_ref_vector new_args(m); for (unsigned j = 0; j < i; ++j) new_args.push_back(a->get_arg(j)); - if (new_last.get()) new_args.push_back(new_last); + if (new_last) new_args.push_back(new_last); result = new_args.size() == 1 ? new_args.get(0) : m_util.mk_concat(new_args.size(), new_args.c_ptr()); return retv; @@ -228,7 +227,7 @@ struct bv_trailing::imp { unsigned retv = 0; numeral e_val; if (m_util.is_numeral(e, e_val, sz)) { - retv = remove_trailing(n, e_val); + retv = remove_trailing(std::min(n, sz), e_val); const unsigned new_sz = sz - retv; result = new_sz ? (retv ? m_util.mk_numeral(e_val, new_sz) : e) : NULL; return retv; diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 22156afb8..78672182a 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -240,6 +240,8 @@ public: app* mk_index(expr* a, expr* b, expr* i) { expr* es[3] = { a, b, i}; return m.mk_app(m_fid, OP_SEQ_INDEX, 3, es); } app* mk_unit(expr* u) { return m.mk_app(m_fid, OP_SEQ_UNIT, 1, &u); } app* mk_char(zstring const& s, unsigned idx); + app* mk_itos(expr* i) { return m.mk_app(m_fid, OP_STRING_ITOS, 1, &i); } + app* mk_stoi(expr* s) { return m.mk_app(m_fid, OP_STRING_STOI, 1, &s); } bool is_string(expr const * n) const { return is_app_of(n, m_fid, OP_STRING_CONST); } diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 8f1718141..cfad0d394 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1322,7 +1322,7 @@ void cmd_context::restore_func_decls(unsigned old_sz) { sf_pair const & p = *it; erase_func_decl_core(p.first, p.second); } - m_func_decls_stack.shrink(old_sz); + m_func_decls_stack.resize(old_sz); } void cmd_context::restore_psort_decls(unsigned old_sz) { @@ -1389,7 +1389,7 @@ void cmd_context::restore_assertions(unsigned old_sz) { if (produce_unsat_cores()) restore(m(), m_assertion_names, old_sz); if (m_interactive_mode) - m_assertion_strings.shrink(old_sz); + m_assertion_strings.resize(old_sz); } void cmd_context::pop(unsigned n) { @@ -1724,8 +1724,8 @@ void cmd_context::display_statistics(bool show_total_time, double total_time) { void cmd_context::display_assertions() { if (!m_interactive_mode) throw cmd_exception("command is only available in interactive mode, use command (set-option :interactive-mode true)"); - vector::const_iterator it = m_assertion_strings.begin(); - vector::const_iterator end = m_assertion_strings.end(); + std::vector::const_iterator it = m_assertion_strings.begin(); + std::vector::const_iterator end = m_assertion_strings.end(); regular_stream() << "("; for (bool first = true; it != end; ++it) { std::string const & s = *it; diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 865999042..976fe946a 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -22,6 +22,7 @@ Notes: #define CMD_CONTEXT_H_ #include +#include #include"ast.h" #include"ast_printer.h" #include"pdecl.h" @@ -38,6 +39,7 @@ Notes: #include"scoped_ptr_vector.h" #include"context_params.h" + class func_decls { func_decl * m_decls; public: @@ -189,7 +191,7 @@ protected: // ptr_vector m_aux_pdecls; ptr_vector m_assertions; - vector m_assertion_strings; + std::vector m_assertion_strings; ptr_vector m_assertion_names; // named assertions are represented using boolean variables. struct scope { @@ -270,8 +272,6 @@ public: cmd_context(bool main_ctx = true, ast_manager * m = 0, symbol const & l = symbol::null); ~cmd_context(); void set_cancel(bool f); - void cancel() { set_cancel(true); } - void reset_cancel() { set_cancel(false); } context_params & params() { return m_params; } solver_factory &get_solver_factory() { return *m_solver_factory; } solver_factory &get_interpolating_solver_factory() { return *m_interpolating_solver_factory; } diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp new file mode 100644 index 000000000..e5db201a8 --- /dev/null +++ b/src/math/simplex/model_based_opt.cpp @@ -0,0 +1,452 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + model_based_opt.cpp + +Abstract: + + Model-based optimization for linear real arithmetic. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-27-4 + +Revision History: + + +--*/ + +#include "model_based_opt.h" +#include "uint_set.h" + +std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) { + switch (ie) { + case opt::t_eq: return out << " = "; + case opt::t_lt: return out << " < "; + case opt::t_le: return out << " <= "; + } + return out; +} + + +namespace opt { + + + model_based_opt::model_based_opt(): + m_objective_id(0) + { + 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; + } + } + return true; + } + + 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]; + } + 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()); + return true; + } + + // a1*x + obj + // a2*x + t2 <= 0 + // a3*x + t3 <= 0 + // a4*x + t4 <= 0 + // a1 > 0, a2 > 0, a3 > 0, a4 < 0 + // x <= -t2/a2 + // x <= -t2/a3 + // determine lub among these. + // then resolve lub with others + // e.g., -t2/a2 <= -t3/a3, then + // replace inequality a3*x + t3 <= 0 by -t2/a2 + t3/a3 <= 0 + // mark a4 as invalid. + // + + // a1 < 0, a2 < 0, a3 < 0, a4 > 0 + // x >= t2/a2 + // x >= t3/a3 + // determine glb among these + // the resolve glb with others. + // e.g. t2/a2 >= t3/a3 + // then replace a3*x + t3 by t3/a3 - t2/a2 <= 0 + // + inf_eps model_based_opt::maximize() { + SASSERT(invariant()); + unsigned_vector other; + unsigned_vector bound_trail, bound_vars; + 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())) { + SASSERT(!bound_coeff.is_zero()); + for (unsigned i = 0; i < other.size(); ++i) { + resolve(bound_row_index, bound_coeff, other[i], x); + } + // coeff*x + objective <= ub + // a2*x + t2 <= 0 + // => coeff*x <= -t2*coeff/a2 + // objective + t2*coeff/a2 <= ub + + mul_add(false, m_objective_id, - coeff/bound_coeff, bound_row_index); + m_rows[bound_row_index].m_alive = false; + bound_trail.push_back(bound_row_index); + bound_vars.push_back(x); + } + else { + return inf_eps::infinity(); + } + } + + // + // update the evaluation of variables to satisfy the bound. + // + + update_values(bound_vars, bound_trail); + + rational value = objective().m_value; + if (objective().m_type == t_lt) { + return inf_eps(inf_rational(value, rational(-1))); + } + else { + return inf_eps(inf_rational(value)); + } + } + + + void model_based_opt::update_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; + vector const& vars = r.m_vars; + for (unsigned j = 0; j < vars.size(); ++j) { + var const& v = vars[j]; + if (x == v.m_id) { + x_coeff = v.m_coeff; + } + else { + 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); + } + + SASSERT(!eps.is_zero()); + if (x_coeff.is_pos()) { + x_val -= eps; + } + // + // -ax + t < 0 + // <=> -ax < -t + // <=> -x < -t/a + // <=> x > t/a + // <=> x := t/a + epsilon + // + else if (x_coeff.is_neg()) { + x_val += eps; + } + } + m_var2value[x] = x_val; + r.m_value = (x_val * x_coeff) + val; + + TRACE("opt", display(tout << "v" << x << " val: " << val << " coeff_x: " + << x_coeff << " val_x: " << m_var2value[x] << " ", r); ); + SASSERT(invariant(bound_trail[i], r)); + } + } + + bool model_based_opt::find_bound(unsigned x, unsigned& bound_row_index, rational& bound_coeff, unsigned_vector& other, 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; + for (unsigned i = 0; i < row_ids.size(); ++i) { + unsigned row_id = row_ids[i]; + if (visited.contains(row_id)) { + continue; + } + visited.insert(row_id); + row& r = m_rows[row_id]; + if (r.m_alive) { + rational a = get_coefficient(row_id, x); + if (a.is_zero()) { + // skip + } + else if (a.is_pos() == is_pos || r.m_type == t_eq) { + rational value = x_val - (r.m_value/a); + if (bound_row_index == UINT_MAX) { + lub_val = value; + bound_row_index = row_id; + bound_coeff = a; + } + else if ((value == lub_val && r.m_type == opt::t_lt) || + (is_pos && value < lub_val) || + (!is_pos && value > lub_val)) { + other.push_back(bound_row_index); + lub_val = value; + bound_row_index = row_id; + bound_coeff = a; + } + else { + other.push_back(row_id); + } + } + else { + r.m_alive = false; + } + } + } + return bound_row_index != UINT_MAX; + } + + rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) { + row const& r = m_rows[row_id]; + if (r.m_vars.empty()) { + return rational::zero(); + } + unsigned lo = 0, hi = r.m_vars.size(); + while (lo < hi) { + unsigned mid = lo + (hi - lo)/2; + SASSERT(mid < hi); + unsigned id = r.m_vars[mid].m_id; + if (id == var_id) { + lo = mid; + break; + } + if (id < var_id) { + lo = mid + 1; + } + else { + hi = mid; + } + } + if (lo == r.m_vars.size()) { + return rational::zero(); + } + unsigned id = r.m_vars[lo].m_id; + if (id == var_id) { + return r.m_vars[lo].m_coeff; + } + else { + return rational::zero(); + } + } + + // + // Let + // row1: t1 + a1*x <= 0 + // row2: t2 + a2*x <= 0 + // + // assume a1, a2 have the same signs: + // (t2 + a2*x) <= (t1 + a1*x)*a2/a1 + // <=> t2*a1/a2 - t1 <= 0 + // <=> t2 - t1*a2/a1 <= 0 + // + // assume a1 > 0, -a2 < 0: + // t1 + a1*x <= 0, t2 - a2*x <= 0 + // t2/a2 <= -t1/a1 + // t2 + t1*a2/a1 <= 0 + // assume -a1 < 0, a2 > 0: + // t1 - a1*x <= 0, t2 + a2*x <= 0 + // t1/a1 <= -t2/a2 + // t2 + t1*a2/a1 <= 0 + // + // the resolvent is the same in all cases (simpler proof should exist) + // + + void model_based_opt::resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x) { + + SASSERT(a1 == get_coefficient(row_src, x)); + SASSERT(!a1.is_zero()); + SASSERT(row_src != row_dst); + + if (m_rows[row_dst].m_alive) { + rational a2 = get_coefficient(row_dst, x); + mul_add(row_dst != m_objective_id && a1.is_pos() == a2.is_pos(), row_dst, -a2/a1, row_src); + } + } + + // + // set row1 <- row1 + c*row2 + // + void model_based_opt::mul_add(bool same_sign, unsigned row_id1, rational const& c, unsigned row_id2) { + if (c.is_zero()) { + return; + } + m_new_vars.reset(); + row& r1 = m_rows[row_id1]; + row const& r2 = m_rows[row_id2]; + unsigned i = 0, j = 0; + for(; i < r1.m_vars.size() || j < r2.m_vars.size(); ) { + if (j == r2.m_vars.size()) { + m_new_vars.append(r1.m_vars.size() - i, r1.m_vars.c_ptr() + i); + break; + } + if (i == r1.m_vars.size()) { + for (; j < r2.m_vars.size(); ++j) { + m_new_vars.push_back(r2.m_vars[j]); + m_new_vars.back().m_coeff *= c; + if (row_id1 != m_objective_id) { + m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); + } + } + break; + } + + unsigned v1 = r1.m_vars[i].m_id; + unsigned v2 = r2.m_vars[j].m_id; + if (v1 == v2) { + m_new_vars.push_back(r1.m_vars[i]); + m_new_vars.back().m_coeff += c*r2.m_vars[j].m_coeff; + ++i; + ++j; + if (m_new_vars.back().m_coeff.is_zero()) { + m_new_vars.pop_back(); + } + } + else if (v1 < v2) { + m_new_vars.push_back(r1.m_vars[i]); + ++i; + } + else { + m_new_vars.push_back(r2.m_vars[j]); + m_new_vars.back().m_coeff *= c; + if (row_id1 != m_objective_id) { + m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); + } + ++j; + } + } + r1.m_coeff += c*r2.m_coeff; + r1.m_vars.swap(m_new_vars); + r1.m_value += c*r2.m_value; + + if (!same_sign && r2.m_type == t_lt) { + r1.m_type = t_lt; + } + else if (same_sign && r1.m_type == t_lt && r2.m_type == t_lt) { + r1.m_type = t_le; + } + SASSERT(invariant(row_id1, r1)); + } + + void model_based_opt::display(std::ostream& out) const { + for (unsigned i = 0; i < m_rows.size(); ++i) { + display(out, m_rows[i]); + } + for (unsigned i = 0; i < m_var2row_ids.size(); ++i) { + unsigned_vector const& rows = m_var2row_ids[i]; + out << i << ": "; + for (unsigned j = 0; j < rows.size(); ++j) { + out << rows[j] << " "; + } + out << "\n"; + } + } + + void model_based_opt::display(std::ostream& out, row const& r) const { + vector const& vars = r.m_vars; + out << (r.m_alive?"+":"-") << " "; + for (unsigned i = 0; i < vars.size(); ++i) { + if (i > 0 && vars[i].m_coeff.is_pos()) { + out << "+ "; + } + out << vars[i].m_coeff << "* v" << vars[i].m_id << " "; + } + if (r.m_coeff.is_pos()) { + out << " + " << r.m_coeff << " "; + } + else if (r.m_coeff.is_neg()) { + out << r.m_coeff << " "; + } + out << r.m_type << " 0; value: " << r.m_value << "\n"; + } + + unsigned model_based_opt::add_var(rational const& value) { + unsigned v = m_var2value.size(); + m_var2value.push_back(value); + m_var2row_ids.push_back(unsigned_vector()); + return v; + } + + rational model_based_opt::get_value(unsigned var) { + return m_var2value[var]; + } + + void model_based_opt::set_row(unsigned row_id, vector const& coeffs, rational const& c, 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()); + 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; + } + 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); + } + } + + void model_based_opt::set_objective(vector const& coeffs, rational const& c) { + set_row(m_objective_id, coeffs, c, t_le); + } + +} + diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h new file mode 100644 index 000000000..b48a96edb --- /dev/null +++ b/src/math/simplex/model_based_opt.h @@ -0,0 +1,119 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + model_based_opt.h + +Abstract: + + Model-based optimization for linear real arithmetic. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-27-4 + +Revision History: + + +--*/ + +#ifndef __MODEL_BASED_OPT_H__ +#define __MODEL_BASED_OPT_H__ + +#include "util.h" +#include "rational.h" +#include"inf_eps_rational.h" + +namespace opt { + + enum ineq_type { + t_eq, + t_lt, + t_le + }; + + + typedef inf_eps_rational inf_eps; + + class model_based_opt { + public: + struct var { + unsigned m_id; + rational m_coeff; + var(unsigned id, rational const& c): m_id(id), m_coeff(c) {} + struct compare { + bool operator()(var x, var y) { + return x.m_id < y.m_id; + } + }; + }; + private: + struct row { + row(): m_type(t_le), m_value(0), m_alive(false) {} + vector m_vars; // variables with coefficients + rational m_coeff; // constant in inequality + 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. + }; + + vector m_rows; + unsigned m_objective_id; + vector m_var2row_ids; + vector m_var2value; + vector m_new_vars; + + 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); + + rational get_coefficient(unsigned row_id, unsigned var_id); + + 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 update_values(unsigned_vector const& bound_vars, unsigned_vector const& bound_trail); + + public: + + model_based_opt(); + + // add a fresh variable with value 'value'. + unsigned add_var(rational const& value); + + // retrieve updated value of variable. + rational get_value(unsigned var_id); + + // add a constraint. We assume that the constraint is + // satisfied under the values provided to the variables. + void add_constraint(vector const& coeffs, rational const& c, ineq_type r); + + // Set the objective function (linear). + void set_objective(vector const& coeffs, rational const& c); + + // + // find a maximal value for the objective function over the current values. + // in other words, the returned maximal value may not be globally optimal, + // but the current evaluation of variables are used to select a local + // optimal. + // + inf_eps maximize(); + + void display(std::ostream& out) const; + void display(std::ostream& out, row const& r) const; + + }; + +} + +std::ostream& operator<<(std::ostream& out, opt::ineq_type ie); + + +#endif diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 90810f294..01e160eb0 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -104,9 +104,50 @@ void func_interp::reset_interp_cache() { m_manager.dec_ref(m_interp); m_interp = 0; } - + +bool func_interp::is_fi_entry_expr(expr * e, ptr_vector & args) { + args.reset(); + expr* c, *t, *f, *a0, *a1; + if (!m().is_ite(e, c, t, f)) { + return false; + } + + if ((m_arity == 0) || + (m_arity == 1 && !m().is_eq(c, a0, a1)) || + (m_arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != m_arity))) + return false; + + args.resize(m_arity, 0); + for (unsigned i = 0; i < m_arity; i++) { + expr * ci = (m_arity == 1 && i == 0) ? c : to_app(c)->get_arg(i); + + if (!m().is_eq(ci, a0, a1)) + return false; + + if (is_var(a0) && to_var(a0)->get_idx() == i) + args[i] = a1; + else if (is_var(a1) && to_var(a1)->get_idx() == i) + args[i] = a0; + else + return false; + } + + return true; +} + void func_interp::set_else(expr * e) { + if (e == m_else) + return; + reset_interp_cache(); + + ptr_vector args; + while (e && is_fi_entry_expr(e, args)) { + TRACE("func_interp", tout << "fi entry expr: " << mk_ismt2_pp(e, m()) << std::endl;); + insert_entry(args.c_ptr(), to_app(e)->get_arg(1)); + e = to_app(e)->get_arg(2); + } + m_manager.inc_ref(e); m_manager.dec_ref(m_else); m_else = e; @@ -148,7 +189,7 @@ func_entry * func_interp::get_entry(expr * const * args) const { void func_interp::insert_entry(expr * const * args, expr * r) { reset_interp_cache(); - func_entry * entry = get_entry(args); + func_entry * entry = get_entry(args); if (entry != 0) { entry->set_result(m_manager, r); return; @@ -201,7 +242,7 @@ expr * func_interp::get_max_occ_result() const { for (; it != end; ++it) { func_entry * curr = *it; expr * r = curr->get_result(); - unsigned occs = 0; + unsigned occs = 0; num_occs.find(r, occs); occs++; num_occs.insert(r, occs); @@ -283,13 +324,13 @@ expr * func_interp::get_interp() const { return r; } -func_interp * func_interp::translate(ast_translation & translator) const { +func_interp * func_interp::translate(ast_translation & translator) const { func_interp * new_fi = alloc(func_interp, translator.to(), m_arity); ptr_vector::const_iterator it = m_entries.begin(); ptr_vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { - func_entry * curr = *it; + func_entry * curr = *it; ptr_buffer new_args; for (unsigned i=0; iget_arg(i))); diff --git a/src/model/func_interp.h b/src/model/func_interp.h index b264f4f1d..d0d61546e 100644 --- a/src/model/func_interp.h +++ b/src/model/func_interp.h @@ -12,7 +12,7 @@ Abstract: modulo a model. Main goal: Remove some code duplication and make the evaluator more efficient. - + Example of code duplication that existed in Z3: - smt_model_generator was creating func_values that were essentially partial func_interps - smt_model_generator was creating if-then-else (lambda) exprs representing func_values @@ -39,17 +39,17 @@ class func_entry { bool m_args_are_values; //!< true if is_value(m_args[i]) is true for all i in [0, arity) // m_result and m_args[i] must be ground terms. - + expr * m_result; expr * m_args[]; static unsigned get_obj_size(unsigned arity) { return sizeof(func_entry) + arity * sizeof(expr*); } func_entry(ast_manager & m, unsigned arity, expr * const * args, expr * result); - + friend class func_interp; - + void set_result(ast_manager & m, expr * r); - + public: static func_entry * mk(ast_manager & m, unsigned arity, expr * const * args, expr * result); bool args_are_values() const { return m_args_are_values; } @@ -69,7 +69,7 @@ class func_interp { ptr_vector m_entries; expr * m_else; bool m_args_are_values; //!< true if forall e in m_entries e.args_are_values() == true - + expr * m_interp; //!< cache for representing the whole interpretation as a single expression (it uses ite terms). void reset_interp_cache(); @@ -83,7 +83,7 @@ public: ast_manager & m () const { return m_manager; } func_interp * copy() const; - + unsigned get_arity() const { return m_arity; } bool is_partial() const { return m_else == 0; } @@ -95,7 +95,7 @@ public: expr * get_else() const { return m_else; } void set_else(expr * e); - + void insert_entry(expr * const * args, expr * r); void insert_new_entry(expr * const * args, expr * r); func_entry * get_entry(expr * const * args) const; @@ -110,6 +110,9 @@ public: expr * get_interp() const; func_interp * translate(ast_translation & translator) const; + +private: + bool is_fi_entry_expr(expr * e, ptr_vector & args); }; #endif diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 3111f2b6c..e67e3cb5e 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -118,8 +118,8 @@ struct evaluator_cfg : public default_rewriter_cfg { expr * val = m_model.get_const_interp(f); if (val != 0) { result = val; + expand_value(result); return BR_DONE; -// return m().is_value(val)?BR_DONE:BR_REWRITE_FULL; } if (m_model_completion) { @@ -172,6 +172,10 @@ struct evaluator_cfg : public default_rewriter_cfg { st = m_f_rw.mk_app_core(f, num, args, result); else if (fid == m_seq_rw.get_fid()) st = m_seq_rw.mk_app_core(f, num, args, result); + else if (fid == m().get_label_family_id() && num == 1) { + result = args[0]; + st = BR_DONE; + } else if (evaluate(f, num, args, result)) { TRACE("model_evaluator", tout << "reduce_app " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n"; @@ -188,6 +192,21 @@ struct evaluator_cfg : public default_rewriter_cfg { return st; } + void expand_value(expr_ref& val) { + vector stores; + expr_ref else_case(m()); + if (m_ar.is_array(val) && extract_array_func_interp(val, stores, else_case)) { + sort* srt = m().get_sort(val); + val = m_ar.mk_const_array(srt, else_case); + for (unsigned i = stores.size(); i > 0; ) { + --i; + expr_ref_vector args(m()); + args.push_back(val); + args.append(stores[i].size(), stores[i].c_ptr()); + val = m_ar.mk_store(args.size(), args.c_ptr()); + } + } + } bool get_macro(func_decl * f, expr * & def, quantifier * & q, proof * & def_pr) { @@ -306,6 +325,21 @@ struct evaluator_cfg : public default_rewriter_cfg { TRACE("model_evaluator", tout << "non-ground else case " << mk_pp(a, m()) << "\n" << mk_pp(else_case, m()) << "\n";); return false; } + bool is_value = true; + for (unsigned i = stores.size(); is_value && i > 0; ) { + --i; + if (else_case == stores[i].back()) { + for (unsigned j = i + 1; j < stores.size(); ++j) { + stores[j-1].reset(); + stores[j-1].append(stores[j]); + } + stores.pop_back(); + continue; + } + for (unsigned j = 0; is_value && j + 1 < stores[i].size(); ++j) { + is_value = m().is_value(stores[i][j].get()); + } + } TRACE("model_evaluator", tout << "else case: " << mk_pp(else_case, m()) << "\n";); return true; } @@ -358,14 +392,10 @@ unsigned model_evaluator::get_num_steps() const { return m_imp->get_num_steps(); } - void model_evaluator::cleanup(params_ref const & p) { model_core & md = m_imp->cfg().m_model; - #pragma omp critical (model_evaluator) - { - dealloc(m_imp); - m_imp = alloc(imp, md, p); - } + dealloc(m_imp); + m_imp = alloc(imp, md, p); } void model_evaluator::reset(params_ref const & p) { diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index f20a77bda..78ce453ec 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -19,6 +19,7 @@ Revision History: #ifndef DL_UTIL_H_ #define DL_UTIL_H_ +#include #include"ast.h" #include"hashtable.h" #include"obj_hashtable.h" @@ -67,7 +68,7 @@ namespace datalog { typedef idx_set var_idx_set; typedef u_map varidx2var_map; typedef obj_hashtable func_decl_set; //!< Rule dependencies. - typedef vector string_vector; + typedef std::vector string_vector; bool contains_var(expr * trm, unsigned var_idx); diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index a28500568..4d65b9a25 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -42,6 +42,7 @@ Notes: #include "filter_model_converter.h" #include "ast_pp_util.h" #include "inc_sat_solver.h" +#include "qsat.h" namespace opt { @@ -237,6 +238,11 @@ namespace opt { import_scoped_state(); normalize(); internalize(); +#if 0 + if (is_qsat_opt()) { + return run_qsat_opt(); + } +#endif update_solver(); solver& s = get_solver(); s.assert_expr(m_hard_constraints); @@ -1205,7 +1211,7 @@ namespace opt { } inf_eps context::get_lower_as_num(unsigned idx) { - if (idx > m_objectives.size()) { + if (idx >= m_objectives.size()) { throw default_exception("index out of bounds"); } objective const& obj = m_objectives[idx]; @@ -1223,7 +1229,7 @@ namespace opt { } inf_eps context::get_upper_as_num(unsigned idx) { - if (idx > m_objectives.size()) { + if (idx >= m_objectives.size()) { throw default_exception("index out of bounds"); } objective const& obj = m_objectives[idx]; @@ -1439,4 +1445,39 @@ namespace opt { } } } + + bool context::is_qsat_opt() { + if (m_objectives.size() != 1) { + return false; + } + if (m_objectives[0].m_type != O_MAXIMIZE && + m_objectives[0].m_type != O_MINIMIZE) { + return false; + } + for (unsigned i = 0; i < m_hard_constraints.size(); ++i) { + if (has_quantifiers(m_hard_constraints[i].get())) { + return true; + } + } + return false; + } + + lbool context::run_qsat_opt() { + SASSERT(is_qsat_opt()); + objective const& obj = m_objectives[0]; + app_ref term(obj.m_term); + if (obj.m_type == O_MINIMIZE) { + term = m_arith.mk_uminus(term); + } + inf_eps value; + lbool result = qe::maximize(m_hard_constraints, term, value, m_model, m_params); + if (result != l_undef && obj.m_type == O_MINIMIZE) { + value.neg(); + } + if (result != l_undef) { + m_optsmt.update_lower(obj.m_index, value); + m_optsmt.update_upper(obj.m_index, value); + } + return result; + } } diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index e427a487f..b7dceb674 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -289,12 +289,15 @@ namespace opt { void display_benchmark(); - // pareto void yield(); expr_ref mk_ge(expr* t, expr* s); expr_ref mk_cmp(bool is_ge, model_ref& mdl, objective const& obj); + + // quantifiers + bool is_qsat_opt(); + lbool run_qsat_opt(); }; } diff --git a/src/opt/optsmt.h b/src/opt/optsmt.h index d11b84370..e01c58681 100644 --- a/src/opt/optsmt.h +++ b/src/opt/optsmt.h @@ -61,7 +61,9 @@ namespace opt { void get_model(model_ref& mdl, svector& labels); model* get_model(unsigned index) const { return m_models[index]; } + void update_lower(unsigned idx, inf_eps const& r); + void update_upper(unsigned idx, inf_eps const& r); void reset(); @@ -82,6 +84,7 @@ namespace opt { lbool update_upper(); + }; }; diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 91be93c0e..2e98a4072 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -298,7 +298,7 @@ namespace smt2 { } unsigned m_cache_end; - vector m_cached_strings; + std::vector m_cached_strings; int m_num_open_paren; @@ -403,7 +403,7 @@ namespace smt2 { void check_float(char const * msg) { if (!curr_is_float()) throw parser_exception(msg); } void error(unsigned line, unsigned pos, char const * msg) { - m_ctx.reset_cancel(); + m_ctx.set_cancel(false); if (use_vs_format()) { m_ctx.diagnostic_stream() << "Z3(" << line << ", " << pos << "): ERROR: " << msg; if (msg[strlen(msg)-1] != '\n') @@ -2197,7 +2197,7 @@ namespace smt2 { m_scanner.start_caching(); m_cache_end = 0; - m_cached_strings.reset(); + m_cached_strings.resize(0); check_lparen_next("invalid get-value command, '(' expected"); while (!curr_is_rparen()) { diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 2b00efba0..60111a473 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -20,6 +20,7 @@ Revision History: --*/ #include "qe_arith.h" +#include "qe_mbp.h" #include "ast_util.h" #include "arith_decl_plugin.h" #include "ast_pp.h" @@ -27,12 +28,12 @@ Revision History: #include "expr_functors.h" #include "model_v2_pp.h" #include "expr_safe_replace.h" +#include "model_based_opt.h" namespace qe { bool is_divides(arith_util& a, expr* e1, expr* e2, rational& k, expr_ref& p) { expr* t1, *t2; - ast_manager& m = a.get_manager(); if (a.is_mod(e2, t1, t2) && a.is_numeral(e1, k) && k.is_zero() && @@ -50,20 +51,22 @@ namespace qe { } return is_divides(a, e1, e2, k, t) || is_divides(a, e2, e1, k, t); } + + +#if 0 + obj_map m_expr2var; + ptr_vector m_var2expr; + +#endif struct arith_project_plugin::imp { - enum ineq_type { - t_eq, - t_lt, - t_le - }; ast_manager& m; arith_util a; th_rewriter m_rw; expr_ref_vector m_ineq_terms; vector m_ineq_coeffs; - svector m_ineq_types; + svector m_ineq_types; expr_ref_vector m_div_terms; vector m_div_divisors, m_div_coeffs; expr_ref_vector m_new_lits; @@ -85,6 +88,111 @@ namespace qe { } } + void insert_mul(expr* x, rational const& v, obj_map& ts) + { + rational w; + if (ts.find(x, w)) { + ts.insert(x, w + v); + } + else { + ts.insert(x, v); + } + } + + void linearize(model& model, opt::model_based_opt& mbo, expr* lit, 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; + 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)) { + if (is_not) mul.neg(); + linearize(model, mul, e1, c, ts); + linearize(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)) { + if (is_not) mul.neg(); + linearize(model, mul, e1, c, ts); + linearize(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)) { + linearize(model, mul, e1, c, ts); + linearize(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))) { + UNREACHABLE(); + } + else if (m.is_distinct(lit) && is_not && is_arith(to_app(lit)->get_arg(0))) { + UNREACHABLE(); + } + else if (m.is_eq(lit, e1, e2) && is_not && is_arith(e1)) { + UNREACHABLE(); + } + else { + return; + } + if (ty == opt::t_lt && is_int()) { + c += rational(1); + ty = opt::t_le; + } + vars coeffs; + extract_coefficients(ts, tids, coeffs); + mbo.add_constraint(coeffs, c, ty); + } + + void linearize(model& model, rational const& mul, expr* t, rational& c, obj_map& ts) { + expr* t1, *t2, *t3; + rational mul1; + expr_ref val(m); + if (a.is_mul(t, t1, t2) && is_numeral(model, t1, mul1)) { + linearize(model, mul* mul1, t2, c, ts); + } + else if (a.is_mul(t, t1, t2) && is_numeral(model, t2, mul1)) { + linearize(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) { + linearize(model, mul, ap->get_arg(i), c, ts); + } + } + else if (a.is_sub(t, t1, t2)) { + linearize(model, mul, t1, c, ts); + linearize(model, -mul, t2, c, ts); + } + else if (a.is_uminus(t, t1)) { + linearize(model, -mul, t1, c, ts); + } + else if (a.is_numeral(t, mul1)) { + c += mul*mul1; + } + else if (extract_mod(model, t, val)) { + insert_mul(val, mul, ts); + } + 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)) { + linearize(model, mul, t2, c, ts); + } + else { + linearize(model, mul, t3, c, ts); + } + } + else { + insert_mul(t, mul, ts); + } + } + void is_linear(model& model, rational const& mul, expr* t, rational& c, expr_ref_vector& ts) { expr* t1, *t2, *t3; rational mul1; @@ -117,9 +225,10 @@ namespace qe { else if (extract_mod(model, t, val)) { ts.push_back(mk_mul(mul, val)); } - else if (m.is_ite(t, t1, t2, t3)) { + 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); } @@ -140,7 +249,7 @@ namespace qe { bool is_linear(model& model, expr* lit, bool& found_eq) { rational c(0), mul(1); expr_ref t(m); - ineq_type ty = t_le; + opt::ineq_type ty = opt::t_le; expr* e1, *e2; expr_ref_vector ts(m); bool is_not = m.is_not(lit, lit); @@ -151,17 +260,17 @@ namespace qe { 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?t_lt:t_le; + 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?t_le:t_lt; + 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 = t_eq; + ty = opt::t_eq; } else if (m.is_distinct(lit) && !is_not && is_arith(to_app(lit)->get_arg(0))) { expr_ref val(m); @@ -180,7 +289,7 @@ namespace qe { 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, t_lt); + accumulate_linear(model, c, t, opt::t_lt); } t = mk_num(0); c.reset(); @@ -199,7 +308,7 @@ namespace qe { if (r1 < r2) { std::swap(e1, e2); } - ty = t_lt; + ty = opt::t_lt; is_linear(model, mul, e1, c, ts); is_linear(model, -mul, e2, c, ts); } @@ -207,24 +316,24 @@ namespace qe { TRACE("qe", tout << "can't project:" << mk_pp(lit, m) << "\n";); throw cant_project(); } - if (ty == t_lt && is_int()) { + if (ty == opt::t_lt && is_int()) { ts.push_back(mk_num(1)); - ty = t_le; + ty = opt::t_le; } t = add(ts); - if (ty == t_eq && c.is_neg()) { + if (ty == opt::t_eq && c.is_neg()) { t = mk_uminus(t); c.neg(); } - if (ty == t_eq && c > rational(1)) { + 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(t_le); + m_ineq_types.push_back(opt::t_le); m_num_neg++; - ty = t_le; + ty = opt::t_le; } accumulate_linear(model, c, t, ty); - found_eq = !c.is_zero() && ty == t_eq; + found_eq = !c.is_zero() && ty == opt::t_eq; return true; } @@ -275,16 +384,16 @@ namespace qe { } }; - void accumulate_linear(model& model, rational const& c, expr_ref& t, ineq_type ty) { + void accumulate_linear(model& model, rational const& c, expr_ref& t, opt::ineq_type ty) { if (c.is_zero()) { switch (ty) { - case t_eq: + case opt::t_eq: t = a.mk_eq(t, mk_num(0)); break; - case t_lt: + case opt::t_lt: t = a.mk_lt(t, mk_num(0)); break; - case t_le: + case opt::t_le: t = a.mk_le(t, mk_num(0)); break; } @@ -294,7 +403,7 @@ namespace qe { m_ineq_coeffs.push_back(c); m_ineq_terms.push_back(t); m_ineq_types.push_back(ty); - if (ty == t_eq) { + if (ty == opt::t_eq) { // skip } else if (c.is_pos()) { @@ -404,18 +513,18 @@ namespace qe { expr* ineq_term(unsigned i) const { return m_ineq_terms[i]; } rational const& ineq_coeff(unsigned i) const { return m_ineq_coeffs[i]; } - ineq_type ineq_ty(unsigned i) const { return m_ineq_types[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 t_lt: + case opt::t_lt: result = a.mk_lt(result, mk_num(0)); break; - case t_le: + case opt::t_le: result = a.mk_le(result, mk_num(0)); break; - case t_eq: + case opt::t_eq: result = m.mk_eq(result, mk_num(0)); break; } @@ -424,9 +533,9 @@ namespace qe { 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 t_eq: out << " = 0\n"; break; - case t_le: out << " <= 0\n"; break; - case t_lt: out << " < 0\n"; break; + 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(); } @@ -541,7 +650,7 @@ namespace qe { 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 || t_le == ineq_ty(i)); + SASSERT(!is_int || opt::t_le == ineq_ty(i)); // // ac*x + t < 0 @@ -555,7 +664,7 @@ namespace qe { new_max = new_max || (r > max_r) || - (r == max_r && t_lt == ineq_ty(i)) || + (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";); @@ -593,7 +702,7 @@ namespace qe { expr_ref ts = mk_add(bt, as); expr_ref z = mk_num(0); expr_ref fml(m); - if (t_lt == ineq_ty(i) || t_lt == ineq_ty(j)) { + if (opt::t_lt == ineq_ty(i) || opt::t_lt == ineq_ty(j)) { fml = a.mk_lt(ts, z); } else { @@ -610,7 +719,7 @@ namespace qe { rational ac = ineq_coeff(i); rational bc = ineq_coeff(j); expr_ref tmp(m); - SASSERT(t_le == ineq_ty(i) && t_le == ineq_ty(j)); + 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); @@ -689,7 +798,7 @@ namespace qe { expr* s = ineq_term(j); expr_ref bt = mk_mul(abs(bc), t); expr_ref as = mk_mul(abs(ac), s); - if (t_lt == ineq_ty(i) && t_le == ineq_ty(j)) { + if (opt::t_lt == ineq_ty(i) && opt::t_le == ineq_ty(j)) { return expr_ref(a.mk_lt(bt, as), m); } else { @@ -760,9 +869,9 @@ namespace qe { 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 t_lt: lhs = a.mk_lt(lhs, mk_num(0)); break; - case t_le: lhs = a.mk_le(lhs, mk_num(0)); break; - case t_eq: lhs = m.mk_eq(lhs, mk_num(0)); break; + 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)); @@ -853,6 +962,78 @@ namespace qe { } return true; } + + typedef opt::model_based_opt::var var; + typedef vector vars; + + + opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound) { + SASSERT(a.is_real(t)); + opt::model_based_opt mbo; + opt::inf_eps value; + obj_map ts; + obj_map tids; + + // extract objective function. + vars coeffs; + rational c(0), mul(1); + linearize(mdl, mul, t, c, ts); + extract_coefficients(ts, tids, coeffs); + mbo.set_objective(coeffs, c); + + // extract linear constraints + for (unsigned i = 0; i < fmls.size(); ++i) { + linearize(mdl, mbo, fmls[i], 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; + obj_map::iterator it = tids.begin(), end = tids.end(); + for (; it != end; ++it) { + expr* e = it->m_key; + if (is_uninterp_const(e)) { + unsigned id = it->m_value; + func_decl* f = to_app(e)->get_decl(); + expr_ref val(a.mk_numeral(mbo.get_value(id), false), m); + mdl.register_decl(f, val); + } + else { + TRACE("qe", tout << "omitting model update for non-uninterpreted constant " << mk_pp(e, m) << "\n";); + } + } + + // update the predicate 'bound' which forces larger values. + if (value.get_infinitesimal().is_neg()) { + bound = a.mk_le(val, t); + } + else { + bound = a.mk_lt(val, t); + } + return value; + } + + void extract_coefficients(obj_map const& ts, obj_map& tids, vars& coeffs) { + coeffs.reset(); + obj_map::iterator it = ts.begin(), end = ts.end(); + for (; it != end; ++it) { + unsigned id; + if (!tids.find(it->m_key, id)) { + id = tids.size(); + tids.insert(it->m_key, id); + } + coeffs.push_back(var(id, it->m_value)); + } + } + }; arith_project_plugin::arith_project_plugin(ast_manager& m) { @@ -875,6 +1056,10 @@ 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); + } + bool arith_project(model& model, app* var, expr_ref_vector& lits) { ast_manager& m = lits.get_manager(); arith_project_plugin ap(m); diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h index be5415a45..b89d16d04 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -22,13 +22,15 @@ namespace qe { class arith_project_plugin : public project_plugin { struct imp; - imp* m_imp; + imp* m_imp; public: arith_project_plugin(ast_manager& m); virtual ~arith_project_plugin(); virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); virtual family_id get_family_id(); + + opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound); }; bool arith_project(model& model, app* var, expr_ref_vector& lits); diff --git a/src/qe/qe_datatypes.cpp b/src/qe/qe_datatypes.cpp index 81001dea5..aa67d28a3 100644 --- a/src/qe/qe_datatypes.cpp +++ b/src/qe/qe_datatypes.cpp @@ -87,7 +87,6 @@ namespace qe { } void project_rec(model& model, app_ref_vector& vars, expr_ref_vector& lits) { - func_decl* f = m_val->get_decl(); expr_ref rhs(m); expr_ref_vector eqs(m); for (unsigned i = 0; i < lits.size(); ++i) { diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index cb8546b6c..7d5f3800e 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -107,6 +107,7 @@ void project_plugin::push_back(expr_ref_vector& lits, expr* e) { class mbp::impl { ast_manager& m; ptr_vector m_plugins; + expr_mark m_visited; void add_plugin(project_plugin* p) { family_id fid = p->get_family_id(); @@ -175,12 +176,53 @@ class mbp::impl { return false; } + + void extract_bools(model& model, expr_ref_vector& fmls, expr* fml) { + TRACE("qe", tout << "extract bools: " << mk_pp(fml, m) << "\n";); + ptr_vector todo; + if (is_app(fml)) { + todo.append(to_app(fml)->get_num_args(), to_app(fml)->get_args()); + } + while (!todo.empty()) { + expr* e = todo.back(); + todo.pop_back(); + if (m_visited.is_marked(e)) { + continue; + } + m_visited.mark(e); + if (m.is_bool(e)) { + expr_ref val(m); + VERIFY(model.eval(e, val)); + 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)); + } + } + else if (is_app(e)) { + todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); + } + else { + TRACE("qe", tout << "expression not handled " << mk_pp(e, m) << "\n";); + } + } + } + public: + + opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound) { + arith_project_plugin arith(m); + return arith.maximize(fmls, mdl, t, bound); + } + void extract_literals(model& model, expr_ref_vector& fmls) { expr_ref val(m); for (unsigned i = 0; i < fmls.size(); ++i) { expr* fml = fmls[i].get(), *nfml, *f1, *f2, *f3; + SASSERT(m.is_bool(fml)); if (m.is_not(fml, nfml) && m.is_distinct(nfml)) { fmls[i] = project_plugin::pick_equality(m, model, nfml); --i; @@ -205,26 +247,28 @@ public: f1 = mk_not(m, f1); f2 = mk_not(m, f2); } - project_plugin::push_back(fmls, f1); + fmls[i] = f1; project_plugin::push_back(fmls, f2); - project_plugin::erase(fmls, i); + --i; } else if (m.is_implies(fml, f1, f2)) { VERIFY (model.eval(f2, val)); if (m.is_true(val)) { - project_plugin::push_back(fmls, f2); + fmls[i] = f2; } else { - project_plugin::push_back(fmls, mk_not(m, f1)); + fmls[i] = mk_not(m, f1); } - project_plugin::erase(fmls, i); + --i; } else if (m.is_ite(fml, f1, f2, f3)) { VERIFY (model.eval(f1, val)); if (m.is_true(val)) { + project_plugin::push_back(fmls, f1); project_plugin::push_back(fmls, f2); } else { + project_plugin::push_back(fmls, mk_not(m, f1)); project_plugin::push_back(fmls, f3); } project_plugin::erase(fmls, i); @@ -269,17 +313,24 @@ public: else if (m.is_not(fml, nfml) && m.is_ite(nfml, f1, f2, f3)) { VERIFY (model.eval(f1, val)); if (m.is_true(val)) { + project_plugin::push_back(fmls, f1); project_plugin::push_back(fmls, mk_not(m, f2)); } else { + project_plugin::push_back(fmls, mk_not(m, f1)); project_plugin::push_back(fmls, mk_not(m, f3)); } project_plugin::erase(fmls, i); } + else if (m.is_not(fml, nfml)) { + extract_bools(model, fmls, nfml); + } else { + extract_bools(model, fmls, fml); // TBD other Boolean operations. } } + m_visited.reset(); } impl(ast_manager& m):m(m) { @@ -310,6 +361,7 @@ public: app_ref var(m); th_rewriter rw(m); bool progress = true; + TRACE("qe", tout << vars << " " << fmls << "\n";); while (progress && !vars.empty()) { preprocess_solve(model, vars, fmls); app_ref_vector new_vars(m); @@ -345,6 +397,7 @@ public: } vars.append(new_vars); } + TRACE("qe", tout << vars << " " << fmls << "\n";); } }; @@ -367,3 +420,7 @@ void mbp::solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { 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); +} diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index 5b4c59762..332659c0b 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -24,6 +24,7 @@ Revision History: #include "ast.h" #include "params.h" #include "model.h" +#include "model_based_opt.h" namespace qe { @@ -70,6 +71,12 @@ namespace qe { Extract literals from formulas based on model. */ void extract_literals(model& model, expr_ref_vector& lits); + + /** + \brief + Maximize objective t under current model for constraints in fmls. + */ + opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound); }; } diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 1b0555b22..74b1101fe 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -31,6 +31,8 @@ Notes: #include "expr_abstract.h" #include "qe.h" #include "label_rewriter.h" +#include "expr_replacer.h" +#include "th_rewriter.h" namespace qe { @@ -534,6 +536,13 @@ namespace qe { ); } }; + + enum qsat_mode { + qsat_qe, + qsat_qe_rec, + qsat_sat, + qsat_maximize + }; class qsat : public tactic { @@ -557,8 +566,7 @@ namespace qe { vector m_vars; // variables from alternating prefixes. unsigned m_level; model_ref m_model; - bool m_qelim; // perform quantifier elimination - bool m_force_elim; // force elimination of variables during projection. + qsat_mode m_mode; app_ref_vector m_avars; // variables to project app_ref_vector m_free_vars; @@ -582,12 +590,12 @@ namespace qe { SASSERT(validate_model(asms)); TRACE("qe", k.display(tout); display(tout << "\n", *m_model.get()); display(tout, asms); ); push(); - break; + break; case l_false: switch (m_level) { case 0: return l_false; case 1: - if (!m_qelim) return l_true; + if (m_mode == qsat_sat) return l_true; if (m_model.get()) { project_qe(asms); } @@ -670,20 +678,23 @@ namespace qe { m_pred_abs.get_free_vars(fml, vars); m_vars.push_back(vars); vars.reset(); - if (m_qelim) { + if (m_mode != qsat_sat) { is_forall = true; hoist.pull_quantifier(is_forall, fml, vars); m_vars.push_back(vars); + filter_vars(vars); } else { hoist.pull_quantifier(is_forall, fml, vars); m_vars.back().append(vars); + filter_vars(vars); } do { is_forall = !is_forall; vars.reset(); hoist.pull_quantifier(is_forall, fml, vars); m_vars.push_back(vars); + filter_vars(vars); } while (!vars.empty()); SASSERT(m_vars.back().empty()); @@ -691,6 +702,101 @@ namespace qe { TRACE("qe", tout << fml << "\n";); } + void filter_vars(app_ref_vector const& vars) { + for (unsigned i = 0; i < vars.size(); ++i) { + m_pred_abs.fmc()->insert(vars[i]->get_decl()); + } + } + +#if 0 + void hoist_ite(expr_ref& fml) { + app* ite; + scoped_ptr replace = mk_default_expr_replacer(m); + th_rewriter rewriter(m); + while (find_ite(fml, ite)) { + expr* cond, *th, *el; + VERIFY(m.is_ite(ite, cond, th, el)); + expr_ref tmp1(fml, m), tmp2(fml, m); + replace->apply_substitution(cond, m.mk_true(), tmp1); + replace->apply_substitution(cond, m.mk_false(), tmp2); + fml = m.mk_ite(cond, tmp1, tmp2); + rewriter(fml); + } + } + + bool find_ite(expr* e, app*& ite) { + ptr_vector todo; + todo.push_back(e); + ast_mark visited; + while(!todo.empty()) { + e = todo.back(); + todo.pop_back(); + if (visited.is_marked(e)) { + continue; + } + visited.mark(e, true); + if (m.is_ite(e) && !m.is_bool(e)) { + ite = to_app(e); + return true; + } + if (is_app(e)) { + app* a = to_app(e); + todo.append(a->get_num_args(), a->get_args()); + } + } + return false; + } + + // slower + void hoist_ite2(expr_ref& fml) { + obj_map result; + expr_ref_vector trail(m); + ptr_vector todo, args; + todo.push_back(fml); + + while (!todo.empty()) { + expr* e = todo.back(); + if (result.contains(e)) { + todo.pop_back(); + continue; + } + if (!is_app(e)) { + todo.pop_back(); + result.insert(e, e); + continue; + } + app* a = to_app(e); + expr* r; + unsigned sz = a->get_num_args(); + args.reset(); + for (unsigned i = 0; i < sz; ++i) { + if (result.find(a->get_arg(i), r)) { + args.push_back(r); + } + else { + todo.push_back(a->get_arg(i)); + } + } + if (sz == args.size()) { + r = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); + trail.push_back(r); + if (m.is_bool(e) && m.get_basic_family_id() != a->get_family_id()) { + expr_ref fml(r, m); + hoist_ite(fml); + trail.push_back(fml); + r = fml; + } + result.insert(e, r); + todo.pop_back(); + } + } + fml = result.find(fml); + } +#endif + + + + void initialize_levels() { // initialize levels. for (unsigned i = 0; i < m_vars.size(); ++i) { @@ -758,12 +864,18 @@ namespace qe { get_core(core, m_level); SASSERT(validate_core(core)); get_vars(m_level); - m_mbp(m_force_elim, m_avars, mdl, core); - fml = negate_core(core); - add_assumption(fml); - m_answer.push_back(fml); - m_free_vars.append(m_avars); - pop(1); + m_mbp(force_elim(), m_avars, mdl, core); + if (m_mode == qsat_maximize) { + maximize(core, mdl); + pop(1); + } + else { + fml = negate_core(core); + add_assumption(fml); + m_answer.push_back(fml); + m_free_vars.append(m_avars); + pop(1); + } } void project(expr_ref_vector& core) { @@ -778,7 +890,7 @@ namespace qe { get_vars(m_level-1); SASSERT(validate_project(mdl, core)); - m_mbp(m_force_elim, m_avars, mdl, core); + m_mbp(force_elim(), m_avars, mdl, core); m_free_vars.append(m_avars); fml = negate_core(core); unsigned num_scopes = 0; @@ -789,7 +901,7 @@ namespace qe { if (level.max() == UINT_MAX) { num_scopes = 2*(m_level/2); } - else if (m_qelim && !m_force_elim) { + else if (m_mode == qsat_qe_rec) { num_scopes = 2; } else { @@ -803,7 +915,7 @@ namespace qe { pop(num_scopes); TRACE("qe", tout << "backtrack: " << num_scopes << " new level: " << m_level << "\nproject:\n" << core << "\n|->\n" << fml << "\n";); - if (m_level == 0 && m_qelim) { + if (m_level == 0 && m_mode != qsat_sat) { add_assumption(fml); } else { @@ -819,9 +931,13 @@ namespace qe { } } - expr_ref negate_core(expr_ref_vector& core) { + expr_ref negate_core(expr_ref_vector const& core) { return ::push_not(::mk_and(core)); } + + bool force_elim() const { + return m_mode != qsat_qe_rec; + } expr_ref elim_rec(expr* fml) { expr_ref tmp(m); @@ -941,7 +1057,7 @@ namespace qe { expr_ref_vector fmls(m); fmls.append(core.size(), core.c_ptr()); fmls.append(k.size(), k.get_formulas()); - return check_fmls(fmls); + return check_fmls(fmls) || m.canceled(); } bool check_fmls(expr_ref_vector const& fmls) { @@ -953,7 +1069,7 @@ namespace qe { lbool is_sat = solver.check(); CTRACE("qe", is_sat != l_false, tout << fmls << "\nare not unsat\n";); - return (is_sat == l_false); + return (is_sat == l_false) || m.canceled(); } bool validate_model(expr_ref_vector const& asms) { @@ -967,7 +1083,7 @@ namespace qe { bool validate_model(model& mdl, unsigned sz, expr* const* fmls) { expr_ref val(m); for (unsigned i = 0; i < sz; ++i) { - if (!m_model->eval(fmls[i], val)) { + if (!m_model->eval(fmls[i], val) && !m.canceled()) { TRACE("qe", tout << "Formula does not evaluate in model: " << mk_pp(fmls[i], m) << "\n";); return false; } @@ -1001,6 +1117,9 @@ namespace qe { TRACE("qe", tout << "Projection is false in model\n";); return false; } + if (m.canceled()) { + return true; + } for (unsigned i = 0; i < m_avars.size(); ++i) { contains_app cont(m, m_avars[i].get()); if (cont(proj)) { @@ -1029,9 +1148,10 @@ namespace qe { return true; } + public: - qsat(ast_manager& m, params_ref const& p, bool qelim, bool force_elim): + qsat(ast_manager& m, params_ref const& p, qsat_mode mode): m(m), m_mbp(m), m_fa(m), @@ -1040,8 +1160,7 @@ namespace qe { m_answer(m), m_asms(m), m_level(0), - m_qelim(qelim), - m_force_elim(force_elim), + m_mode(mode), m_avars(m), m_free_vars(m) { @@ -1070,13 +1189,15 @@ namespace qe { expr_ref fml(m); mc = 0; pc = 0; core = 0; in->get_formulas(fmls); + + fml = mk_and(m, fmls.size(), fmls.c_ptr()); // for now: // fail if cores. (TBD) // fail if proofs. (TBD) - if (!m_force_elim) { + if (m_mode == qsat_qe_rec) { fml = elim_rec(fml); in->reset(); in->inc_depth(); @@ -1087,10 +1208,11 @@ namespace qe { reset(); TRACE("qe", tout << fml << "\n";); - if (m_qelim) { + if (m_mode != qsat_sat) { 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)); @@ -1104,11 +1226,12 @@ namespace qe { case l_false: in->reset(); in->inc_depth(); - if (m_qelim) { + if (m_mode == qsat_qe) { fml = ::mk_and(m_answer); in->assert_expr(fml); } else { + SASSERT(m_mode == qsat_sat); in->assert_expr(m.mk_false()); } result.push_back(in.get()); @@ -1155,12 +1278,92 @@ namespace qe { } tactic * translate(ast_manager & m) { - return alloc(qsat, m, m_params, m_qelim, m_force_elim); - } + return alloc(qsat, m, m_params, m_mode); + } + + app* m_objective; + opt::inf_eps m_value; + bool m_was_sat; + + lbool maximize(expr_ref_vector const& fmls, app* t, model_ref& mdl, opt::inf_eps& value) { + expr_ref_vector defs(m); + expr_ref fml = negate_core(fmls); + hoist(fml); + m_objective = t; + m_value = opt::inf_eps(); + m_was_sat = false; + m_pred_abs.abstract_atoms(fml, defs); + fml = m_pred_abs.mk_abstract(fml); + m_ex.assert_expr(mk_and(defs)); + m_fa.assert_expr(mk_and(defs)); + m_ex.assert_expr(fml); + m_fa.assert_expr(m.mk_not(fml)); + lbool is_sat = check_sat(); + mdl = m_model.get(); + switch (is_sat) { + case l_false: + if (!m_was_sat) { + return l_false; + } + break; + case l_true: + UNREACHABLE(); + break; + case l_undef: + std::string s = m_ex.k().last_failure_as_string(); + if (s == "ok") { + s = m_fa.k().last_failure_as_string(); + } + throw tactic_exception(s.c_str()); + } + value = m_value; + return l_true; + } + + void maximize(expr_ref_vector const& core, model& mdl) { + 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); + m_ex.assert_expr(bound); + } + }; - + lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, model_ref& mdl, params_ref const& p) { + ast_manager& m = fmls.get_manager(); + qsat qs(m, p, qsat_maximize); + return qs.maximize(fmls, t, mdl, value); + } +}; +tactic * mk_qsat_tactic(ast_manager& m, params_ref const& p) { + return alloc(qe::qsat, m, p, qe::qsat_sat); +} + +tactic * mk_qe2_tactic(ast_manager& m, params_ref const& p) { + return alloc(qe::qsat, m, p, qe::qsat_qe); +} + +tactic * mk_qe_rec_tactic(ast_manager& m, params_ref const& p) { + return alloc(qe::qsat, m, p, qe::qsat_qe_rec); +} + + + + +#if 0 + + class min_max_opt { + struct imp; + imp* m_imp; + public: + min_max_opt(ast_manager& m); + ~min_max_opt(); + void add(expr* e); + void add(expr_ref_vector const& fmls); + lbool check(svector const& is_max, app_ref_vector const& vars, app* t); + }; struct min_max_opt::imp { ast_manager& m; @@ -1239,20 +1442,4 @@ namespace qe { return m_imp->check(is_max, vars, t); } - - -}; - -tactic * mk_qsat_tactic(ast_manager& m, params_ref const& p) { - return alloc(qe::qsat, m, p, false, true); -} - -tactic * mk_qe2_tactic(ast_manager& m, params_ref const& p) { - return alloc(qe::qsat, m, p, true, true); -} - -tactic * mk_qe_rec_tactic(ast_manager& m, params_ref const& p) { - return alloc(qe::qsat, m, p, true, false); -} - - +#endif diff --git a/src/qe/qsat.h b/src/qe/qsat.h index 5c6b80a04..456711c4f 100644 --- a/src/qe/qsat.h +++ b/src/qe/qsat.h @@ -23,6 +23,7 @@ Revision History: #include "tactic.h" #include "filter_model_converter.h" +#include "qe_mbp.h" namespace qe { @@ -113,17 +114,7 @@ namespace qe { void collect_statistics(statistics& st) const; }; - class min_max_opt { - struct imp; - imp* m_imp; - public: - min_max_opt(ast_manager& m); - ~min_max_opt(); - void add(expr* e); - void add(expr_ref_vector const& fmls); - lbool check(svector const& is_max, app_ref_vector const& vars, app* t); - }; - + lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, model_ref& mdl, params_ref const& p); } diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 62c491b62..4d5542866 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -324,6 +324,7 @@ struct goal2sat::imp { } void process(expr * n) { + //SASSERT(m_result_stack.empty()); TRACE("goal2sat", tout << "converting: " << mk_ismt2_pp(n, m) << "\n";); if (visit(n, true, false)) { SASSERT(m_result_stack.empty()); @@ -342,8 +343,7 @@ struct goal2sat::imp { bool sign = fr.m_sign; TRACE("goal2sat_bug", tout << "result stack\n"; tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n"; - for (unsigned i = 0; i < m_result_stack.size(); i++) tout << m_result_stack[i] << " "; - tout << "\n";); + tout << m_result_stack << "\n";); if (fr.m_idx == 0 && process_cached(t, root, sign)) { m_frame_stack.pop_back(); continue; @@ -362,11 +362,11 @@ struct goal2sat::imp { } TRACE("goal2sat_bug", tout << "converting\n"; tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n"; - for (unsigned i = 0; i < m_result_stack.size(); i++) tout << m_result_stack[i] << " "; - tout << "\n";); + tout << m_result_stack << "\n";); convert(t, root, sign); m_frame_stack.pop_back(); } + CTRACE("goal2sat", !m_result_stack.empty(), tout << m_result_stack << "\n";); SASSERT(m_result_stack.empty()); } diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index f166125ba..1f8f6dabe 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -1756,16 +1756,16 @@ namespace smt { void insert_qinfo(qinfo * qi) { // I'm assuming the number of qinfo's per quantifier is small. So, the linear search is not a big deal. + scoped_ptr q(qi); ptr_vector::iterator it = m_qinfo_vect.begin(); ptr_vector::iterator end = m_qinfo_vect.end(); for (; it != end; ++it) { checkpoint(); if (qi->is_equal(*it)) { - dealloc(qi); return; } } - m_qinfo_vect.push_back(qi); + m_qinfo_vect.push_back(q.detach()); TRACE("model_finder", tout << "new quantifier qinfo: "; qi->display(tout); tout << "\n";); } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 7703f70aa..a8811b179 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -202,6 +202,7 @@ theory_seq::theory_seq(ast_manager& m): m_exclude(m), m_axioms(m), m_axioms_head(0), + m_int_string(m), m_mg(0), m_rewrite(m), m_seq_rewrite(m), @@ -257,6 +258,11 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>fixed_length\n";); return FC_CONTINUE; } + if (check_int_string()) { + ++m_stats.m_int_string; + TRACE("seq", tout << ">>int_string\n";); + return FC_CONTINUE; + } if (reduce_length_eq() || branch_unit_variable() || branch_binary_variable() || branch_variable_mb() || branch_variable()) { ++m_stats.m_branch_variable; TRACE("seq", tout << ">>branch_variable\n";); @@ -292,7 +298,6 @@ final_check_status theory_seq::final_check_eh() { bool theory_seq::reduce_length_eq() { context& ctx = get_context(); - unsigned sz = m_eqs.size(); int start = ctx.get_random_value(); for (unsigned i = 0; !ctx.inconsistent() && i < m_eqs.size(); ++i) { @@ -451,7 +456,6 @@ void theory_seq::branch_unit_variable(dependency* dep, expr* X, expr_ref_vector } bool theory_seq::branch_variable_mb() { - context& ctx = get_context(); bool change = false; for (unsigned i = 0; i < m_eqs.size(); ++i) { eq const& e = m_eqs[i]; @@ -2160,6 +2164,7 @@ void theory_seq::add_length(expr* e) { m_trail_stack.push(insert_obj_trail(m_length, e)); } + /* ensure that all elements in equivalence class occur under an applicatin of 'length' */ @@ -2177,6 +2182,48 @@ void theory_seq::enforce_length(enode* n) { while (n1 != n); } + +void theory_seq::add_int_string(expr* e) { + m_int_string.push_back(e); + m_trail_stack.push(push_back_vector(m_int_string)); +} + +bool theory_seq::check_int_string() { + bool change = false; + for (unsigned i = 0; i < m_int_string.size(); ++i) { + expr* e = m_int_string[i].get(), *n; + 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. + } + } + return change; +} + +bool theory_seq::add_itos_axiom(expr* e) { + rational val; + expr* n; + VERIFY(m_util.str.is_itos(e, n)); + if (get_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); + add_axiom(mk_eq(m_util.str.mk_itos(n1), e1, false)); + m_trail_stack.push(insert_map(m_itos_axioms, val)); + m_trail_stack.push(push_replay(alloc(replay_axiom, m, e))); + return true; + } + } + return false; +} + void theory_seq::apply_sort_cnstr(enode* n, sort* s) { mk_var(n); } @@ -2317,6 +2364,7 @@ void theory_seq::collect_statistics(::statistics & st) const { st.update("seq add axiom", m_stats.m_add_axiom); st.update("seq extensionality", m_stats.m_extensionality); st.update("seq fixed length", m_stats.m_fixed_length); + st.update("seq int.to.str", m_stats.m_int_string); } void theory_seq::init_model(expr_ref_vector const& es) { @@ -2627,6 +2675,9 @@ void theory_seq::deque_axiom(expr* n) { else if (m_util.str.is_string(n)) { add_elim_string_axiom(n); } + else if (m_util.str.is_itos(n)) { + add_itos_axiom(n); + } } @@ -2890,6 +2941,14 @@ static theory_mi_arith* get_th_arith(context& ctx, theory_id afid, expr* e) { } } +bool theory_seq::get_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(); +} + bool theory_seq::lower_bound(expr* _e, rational& lo) const { context& ctx = get_context(); expr_ref e(m_util.str.mk_length(_e), m); @@ -3525,6 +3584,11 @@ void theory_seq::relevant_eh(app* n) { enque_axiom(n); } + if (m_util.str.is_itos(n) || + m_util.str.is_stoi(n)) { + add_int_string(n); + } + expr* arg; if (m_util.str.is_length(n, arg) && !has_length(arg)) { enforce_length(get_context().get_enode(arg)); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index fc37a8f06..4107f3d05 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -287,7 +287,10 @@ namespace smt { unsigned m_extensionality; unsigned m_fixed_length; unsigned m_propagate_contains; + unsigned m_int_string; }; + typedef hashtable rational_set; + ast_manager& m; dependency_manager m_dm; solution_map m_rep; // unification representative. @@ -303,6 +306,8 @@ namespace smt { obj_hashtable m_axiom_set; unsigned m_axioms_head; // index of first axiom to add. bool m_incomplete; // is the solver (clearly) incomplete for the fragment. + expr_ref_vector m_int_string; + rational_set m_itos_axioms; obj_hashtable m_length; // is length applied scoped_ptr_vector m_replay; // set of actions to replay model_generator* m_mg; @@ -481,9 +486,14 @@ namespace smt { bool enforce_length(expr_ref_vector const& es, vector& len); void enforce_length_coherence(enode* n1, enode* n2); + // model-check the functions that convert integers to strings and the other way. + void add_int_string(expr* e); + bool check_int_string(); + void add_elim_string_axiom(expr* n); void add_at_axiom(expr* n); void add_in_re_axiom(expr* n); + bool add_itos_axiom(expr* n); literal mk_literal(expr* n); literal mk_eq_empty(expr* n, bool phase = true); literal mk_seq_eq(expr* a, expr* b); @@ -496,6 +506,7 @@ namespace smt { // arithmetic integration + bool get_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/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index e19d7a1e3..4e645116c 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -83,9 +83,14 @@ private: solver * m_solver; volatile bool m_canceled; aux_timeout_eh(solver * s):m_solver(s), m_canceled(false) {} + ~aux_timeout_eh() { + if (m_canceled) { + m_solver->get_manager().limit().dec_cancel(); + } + } virtual void operator()() { - m_solver->get_manager().limit().cancel(); m_canceled = true; + m_solver->get_manager().limit().inc_cancel(); } }; @@ -225,9 +230,6 @@ public: if ((r != l_undef || !use_solver1_when_undef()) && !eh.m_canceled) { return r; } - if (eh.m_canceled) { - m_solver1->get_manager().limit().reset_cancel(); - } } IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"solver 2 failed, trying solver1\")\n";); } diff --git a/src/tactic/arith/eq2bv_tactic.cpp b/src/tactic/arith/eq2bv_tactic.cpp index 7957edbc4..ac1ee9afd 100644 --- a/src/tactic/arith/eq2bv_tactic.cpp +++ b/src/tactic/arith/eq2bv_tactic.cpp @@ -191,15 +191,18 @@ public: expr* c = it->m_key; bool strict; rational r; - if (m_bounds.has_lower(c, r, strict)) { + expr_ref fml(m); + if (m_bounds.has_lower(c, r, strict) && !r.is_neg()) { SASSERT(!strict); expr* d = m_fd.find(c); - g->assert_expr(bv.mk_ule(bv.mk_numeral(r, m.get_sort(d)), d), m_bounds.lower_dep(c)); + fml = bv.mk_ule(bv.mk_numeral(r, m.get_sort(d)), d); + g->assert_expr(fml, m_bounds.lower_dep(c)); } - if (m_bounds.has_upper(c, r, strict)) { + if (m_bounds.has_upper(c, r, strict) && !r.is_neg()) { SASSERT(!strict); expr* d = m_fd.find(c); - g->assert_expr(bv.mk_ule(d, bv.mk_numeral(r, m.get_sort(d))), m_bounds.upper_dep(c)); + fml = bv.mk_ule(d, bv.mk_numeral(r, m.get_sort(d))); + g->assert_expr(fml, m_bounds.upper_dep(c)); } } g->inc_depth(); @@ -245,8 +248,9 @@ public: else { ++it->m_value; } - unsigned p = next_power_of_two(it->m_value); + unsigned p = next_power_of_two(it->m_value); if (p <= 1) p = 2; + if (it->m_value == p) p *= 2; unsigned n = log2(p); app* z = m.mk_fresh_const("z", bv.mk_sort(n)); m_trail.push_back(z); @@ -302,16 +306,16 @@ public: m_nonfd.mark(f, true); expr* e1, *e2; if (m.is_eq(f, e1, e2)) { - if (is_fd(e1, e2) || is_fd(e2, e1)) { + if (is_fd(e1, e2) || is_fd(e2, e1)) { continue; } } - if (is_app(f)) { - m_todo.append(to_app(f)->get_num_args(), to_app(f)->get_args()); - } - else if (is_quantifier(f)) { - m_todo.push_back(to_quantifier(f)->get_expr()); - } + if (is_app(f)) { + m_todo.append(to_app(f)->get_num_args(), to_app(f)->get_args()); + } + else if (is_quantifier(f)) { + m_todo.push_back(to_quantifier(f)->get_expr()); + } } } diff --git a/src/tactic/bv/bvarray2uf_rewriter.cpp b/src/tactic/bv/bvarray2uf_rewriter.cpp index 54c654d54..d4e738ed7 100644 --- a/src/tactic/bv/bvarray2uf_rewriter.cpp +++ b/src/tactic/bv/bvarray2uf_rewriter.cpp @@ -25,6 +25,7 @@ Notes: #include"ast_pp.h" #include"bvarray2uf_rewriter.h" #include"rewriter_def.h" +#include"ref_util.h" // [1] C. M. Wintersteiger, Y. Hamadi, and L. de Moura: Efficiently Solving // Quantified Bit-Vector Formulas, in Formal Methods in System Design, @@ -50,10 +51,7 @@ bvarray2uf_rewriter_cfg::bvarray2uf_rewriter_cfg(ast_manager & m, params_ref con } bvarray2uf_rewriter_cfg::~bvarray2uf_rewriter_cfg() { - for (obj_map::iterator it = m_arrays_fs.begin(); - it != m_arrays_fs.end(); - it++) - m_manager.dec_ref(it->m_value); + dec_ref_map_key_values(m_manager, m_arrays_fs); } void bvarray2uf_rewriter_cfg::reset() {} @@ -110,12 +108,12 @@ func_decl_ref bvarray2uf_rewriter_cfg::mk_uf_for_array(expr * e) { if (m_array_util.is_as_array(e)) return func_decl_ref(static_cast(to_app(e)->get_decl()->get_parameter(0).get_ast()), m_manager); else { - app * a = to_app(e); func_decl * bv_f = 0; if (!m_arrays_fs.find(e, bv_f)) { - sort * domain = get_index_sort(a); - sort * range = get_value_sort(a); + sort * domain = get_index_sort(e); + sort * range = get_value_sort(e); bv_f = m_manager.mk_fresh_func_decl("f_t", "", 1, &domain, range); + TRACE("bvarray2uf_rw", tout << "for " << mk_ismt2_pp(e, m_manager) << " new func_decl is " << mk_ismt2_pp(bv_f, m_manager) << std::endl; ); if (is_uninterp_const(e)) { if (m_emc) m_emc->insert(to_app(e)->get_decl(), @@ -124,8 +122,12 @@ func_decl_ref bvarray2uf_rewriter_cfg::mk_uf_for_array(expr * e) { else if (m_fmc) m_fmc->insert(bv_f); m_arrays_fs.insert(e, bv_f); + m_manager.inc_ref(e); m_manager.inc_ref(bv_f); } + else { + TRACE("bvarray2uf_rw", tout << "for " << mk_ismt2_pp(e, m_manager) << " found " << mk_ismt2_pp(bv_f, m_manager) << std::endl; ); + } return func_decl_ref(bv_f, m_manager); } @@ -138,18 +140,24 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr SASSERT(num == 2); // From [1]: Finally, we replace equations of the form t = s, // where t and s are arrays, with \forall x . f_t(x) = f_s(x). - func_decl_ref f_t(mk_uf_for_array(args[0]), m_manager); - func_decl_ref f_s(mk_uf_for_array(args[1]), m_manager); + if (m_manager.are_equal(args[0], args[1])) { + result = m_manager.mk_true(); + res = BR_DONE; + } + else { + func_decl_ref f_t(mk_uf_for_array(args[0]), m_manager); + func_decl_ref f_s(mk_uf_for_array(args[1]), m_manager); - sort * sorts[1] = { get_index_sort(m_manager.get_sort(args[0])) }; - symbol names[1] = { symbol("x") }; - var_ref x(m_manager.mk_var(0, sorts[0]), m_manager); + sort * sorts[1] = { get_index_sort(m_manager.get_sort(args[0])) }; + symbol names[1] = { symbol("x") }; + var_ref x(m_manager.mk_var(0, sorts[0]), m_manager); - expr_ref body(m_manager); - body = m_manager.mk_eq(m_manager.mk_app(f_t, x.get()), m_manager.mk_app(f_s, x.get())); + expr_ref body(m_manager); + body = m_manager.mk_eq(m_manager.mk_app(f_t, x.get()), m_manager.mk_app(f_s, x.get())); - result = m_manager.mk_forall(1, sorts, names, body); - res = BR_DONE; + result = m_manager.mk_forall(1, sorts, names, body); + res = BR_DONE; + } } else if (m_manager.is_distinct(f) && is_bv_array(f->get_domain()[0])) { result = m_manager.mk_distinct_expanded(num, args); @@ -310,7 +318,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr } } - CTRACE("bvarray2uf_rw", res==BR_DONE, tout << "result: " << mk_ismt2_pp(result, m()) << std::endl; ); + CTRACE("bvarray2uf_rw", res == BR_DONE, tout << "result: " << mk_ismt2_pp(result, m()) << std::endl; ); return res; } diff --git a/src/tactic/bv/bvarray2uf_tactic.cpp b/src/tactic/bv/bvarray2uf_tactic.cpp index 42ceaf78c..ecf1889a2 100644 --- a/src/tactic/bv/bvarray2uf_tactic.cpp +++ b/src/tactic/bv/bvarray2uf_tactic.cpp @@ -62,7 +62,6 @@ class bvarray2uf_tactic : public tactic { SASSERT(g->is_well_sorted()); tactic_report report("bvarray2uf", *g); mc = 0; pc = 0; core = 0; result.reset(); - fail_if_proof_generation("bvarray2uf", g); fail_if_unsat_core_generation("bvarray2uf", g); TRACE("bvarray2uf", tout << "Before: " << std::endl; g->display(tout); ); @@ -73,7 +72,6 @@ class bvarray2uf_tactic : public tactic { filter_model_converter * fmc = alloc(filter_model_converter, m_manager); mc = concat(emc, fmc); m_rw.set_mcs(emc, fmc); - } @@ -86,10 +84,10 @@ class bvarray2uf_tactic : public tactic { break; expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); - //if (m_produce_proofs) { - // proof * pr = g->pr(idx); - // new_pr = m.mk_modus_ponens(pr, new_pr); - //} + if (m_produce_proofs) { + proof * pr = g->pr(idx); + new_pr = m_manager.mk_modus_ponens(pr, new_pr); + } g->update(idx, new_curr, new_pr, g->dep(idx)); } @@ -143,7 +141,7 @@ public: virtual void cleanup() { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); - std::swap(d, m_imp); + std::swap(d, m_imp); dealloc(d); } diff --git a/src/tactic/extension_model_converter.cpp b/src/tactic/extension_model_converter.cpp index ebd530a58..cdd096455 100644 --- a/src/tactic/extension_model_converter.cpp +++ b/src/tactic/extension_model_converter.cpp @@ -43,41 +43,6 @@ static void display_decls_info(std::ostream & out, model_ref & md) { } } -bool extension_model_converter::is_fi_entry_expr(expr * e, unsigned arity, ptr_vector & args) { - args.reset(); - if (!is_app(e) || !m().is_ite(to_app(e))) - return false; - - app * a = to_app(e); - expr * c = a->get_arg(0); - expr * t = a->get_arg(1); - expr * f = a->get_arg(2); - - if ((arity == 0) || - (arity == 1 && (!m().is_eq(c) || to_app(c)->get_num_args() != 2)) || - (arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != arity))) - return false; - - args.resize(arity, 0); - for (unsigned i = 0; i < arity; i++) { - expr * ci = (arity == 1 && i == 0) ? to_app(c) : to_app(c)->get_arg(i); - - if (!m().is_eq(ci) || to_app(ci)->get_num_args() != 2) - return false; - - expr * a0 = to_app(ci)->get_arg(0); - expr * a1 = to_app(ci)->get_arg(1); - if (is_var(a0) && to_var(a0)->get_idx() == i) - args[i] = a1; - else if (is_var(a1) && to_var(a1)->get_idx() == i) - args[i] = a0; - else - return false; - } - - return true; -} - void extension_model_converter::operator()(model_ref & md, unsigned goal_idx) { SASSERT(goal_idx == 0); TRACE("extension_mc", model_v2_pp(tout, *md); display_decls_info(tout, md);); @@ -97,14 +62,7 @@ void extension_model_converter::operator()(model_ref & md, unsigned goal_idx) { } else { func_interp * new_fi = alloc(func_interp, m(), arity); - expr * e = val.get(); - ptr_vector args; - while (is_fi_entry_expr(e, arity, args)) { - TRACE("extension_mc", tout << "fi entry: " << mk_ismt2_pp(e, m()) << std::endl;); - new_fi->insert_entry(args.c_ptr(), to_app(e)->get_arg(1)); - e = to_app(e)->get_arg(2); - } - new_fi->set_else(e); + new_fi->set_else(val); md->register_decl(f, new_fi); } } diff --git a/src/tactic/extension_model_converter.h b/src/tactic/extension_model_converter.h index 9431906f3..46644eec2 100644 --- a/src/tactic/extension_model_converter.h +++ b/src/tactic/extension_model_converter.h @@ -43,9 +43,6 @@ public: void insert(func_decl * v, expr * def); virtual model_converter * translate(ast_translation & translator); - -private: - bool is_fi_entry_expr(expr * e, unsigned arity, ptr_vector & args); }; diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index 51d7fedb0..8897a83f8 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -22,7 +22,7 @@ Revision History: #include"for_each_expr.h" #include"well_sorted.h" -goal::precision goal::mk_union(precision p1, precision p2) { +goal::precision goal::mk_union(precision p1, precision p2) { if (p1 == PRECISE) return p2; if (p2 == PRECISE) return p1; if (p1 != p2) return UNDER_OVER; @@ -40,24 +40,24 @@ std::ostream & operator<<(std::ostream & out, goal::precision p) { } goal::goal(ast_manager & m, bool models_enabled, bool core_enabled): - m_manager(m), + m_manager(m), m_ref_count(0), - m_depth(0), + m_depth(0), m_models_enabled(models_enabled), - m_proofs_enabled(m.proofs_enabled()), - m_core_enabled(core_enabled), - m_inconsistent(false), + m_proofs_enabled(m.proofs_enabled()), + m_core_enabled(core_enabled), + m_inconsistent(false), m_precision(PRECISE) { } - + goal::goal(ast_manager & m, bool proofs_enabled, bool models_enabled, bool core_enabled): - m_manager(m), + m_manager(m), m_ref_count(0), - m_depth(0), + m_depth(0), m_models_enabled(models_enabled), - m_proofs_enabled(proofs_enabled), - m_core_enabled(core_enabled), - m_inconsistent(false), + m_proofs_enabled(proofs_enabled), + m_core_enabled(core_enabled), + m_inconsistent(false), m_precision(PRECISE) { SASSERT(!proofs_enabled || m.proofs_enabled()); } @@ -65,11 +65,11 @@ goal::goal(ast_manager & m, bool proofs_enabled, bool models_enabled, bool core_ goal::goal(goal const & src): m_manager(src.m()), m_ref_count(0), - m_depth(0), + m_depth(0), m_models_enabled(src.models_enabled()), - m_proofs_enabled(src.proofs_enabled()), - m_core_enabled(src.unsat_core_enabled()), - m_inconsistent(false), + m_proofs_enabled(src.proofs_enabled()), + m_core_enabled(src.unsat_core_enabled()), + m_inconsistent(false), m_precision(PRECISE) { copy_from(src); } @@ -79,16 +79,16 @@ goal::goal(goal const & src): goal::goal(goal const & src, bool): m_manager(src.m()), m_ref_count(0), - m_depth(src.m_depth), + m_depth(src.m_depth), m_models_enabled(src.models_enabled()), - m_proofs_enabled(src.proofs_enabled()), - m_core_enabled(src.unsat_core_enabled()), - m_inconsistent(false), + m_proofs_enabled(src.proofs_enabled()), + m_core_enabled(src.unsat_core_enabled()), + m_inconsistent(false), m_precision(src.m_precision) { } - -goal::~goal() { - reset_core(); + +goal::~goal() { + reset_core(); } void goal::copy_to(goal & target) const { @@ -136,22 +136,23 @@ void goal::push_back(expr * f, proof * pr, expr_dependency * d) { } } -void goal::quick_process(bool save_first, expr * & f, expr_dependency * d) { +void goal::quick_process(bool save_first, expr_ref& f, expr_dependency * d) { if (!m().is_and(f) && !(m().is_not(f) && m().is_or(to_app(f)->get_arg(0)))) { if (!save_first) { push_back(f, 0, d); } - return; + return; } typedef std::pair expr_pol; sbuffer todo; + expr_ref_vector tmp_exprs(m()); todo.push_back(expr_pol(f, true)); while (!todo.empty()) { if (m_inconsistent) return; - expr_pol p = todo.back(); + expr_pol p = todo.back(); expr * curr = p.first; - bool pol = p.second; + bool pol = p.second; todo.pop_back(); if (pol && m().is_and(curr)) { app * t = to_app(curr); @@ -173,10 +174,12 @@ void goal::quick_process(bool save_first, expr * & f, expr_dependency * d) { todo.push_back(expr_pol(to_app(curr)->get_arg(0), !pol)); } else { - if (!pol) - curr = m().mk_not(curr); + if (!pol) { + curr = m().mk_not(curr); + tmp_exprs.push_back(curr); + } if (save_first) { - f = curr; + f = curr; save_first = false; } else { @@ -214,9 +217,9 @@ void goal::process_not_or(bool save_first, app * f, proof * pr, expr_dependency } void goal::slow_process(bool save_first, expr * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr) { - if (m().is_and(f)) + if (m().is_and(f)) process_and(save_first, to_app(f), pr, d, out_f, out_pr); - else if (m().is_not(f) && m().is_or(to_app(f)->get_arg(0))) + else if (m().is_not(f) && m().is_or(to_app(f)->get_arg(0))) process_not_or(save_first, to_app(to_app(f)->get_arg(0)), pr, d, out_f, out_pr); else if (save_first) { out_f = f; @@ -239,8 +242,10 @@ void goal::assert_expr(expr * f, proof * pr, expr_dependency * d) { return; if (proofs_enabled()) slow_process(f, pr, d); - else - quick_process(false, f, d); + else { + expr_ref fr(f, m()); + quick_process(false, fr, d); + } } void goal::assert_expr(expr * f, expr_dependency * d) { @@ -255,7 +260,7 @@ void goal::get_formulas(ptr_vector & result) { } void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { - // KLM: don't know why this assertion is no longer true + // KLM: don't know why this assertion is no longer true // SASSERT(proofs_enabled() == (pr != 0 && !m().is_undef_proof(pr))); if (m_inconsistent) return; @@ -270,20 +275,21 @@ void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { else { m().set(m_forms, i, out_f); m().set(m_proofs, i, out_pr); - if (unsat_core_enabled()) + if (unsat_core_enabled()) m().set(m_dependencies, i, d); } } } else { - quick_process(true, f, d); + expr_ref fr(f, m()); + quick_process(true, fr, d); if (!m_inconsistent) { - if (m().is_false(f)) { + if (m().is_false(fr)) { push_back(f, 0, d); } else { - m().set(m_forms, i, f); - if (unsat_core_enabled()) + m().set(m_forms, i, fr); + if (unsat_core_enabled()) m().set(m_dependencies, i, d); } } @@ -303,9 +309,9 @@ void goal::reset_all() { m_precision = PRECISE; } -void goal::reset() { - reset_core(); - m_inconsistent = false; +void goal::reset() { + reset_core(); + m_inconsistent = false; } void goal::display(ast_printer & prn, std::ostream & out) const { @@ -573,7 +579,7 @@ void goal::elim_redundancies() { expr_dependency_ref d(m()); if (unsat_core_enabled()) d = m().mk_join(dep(get_idx(atom)), dep(i)); - push_back(m().mk_false(), p, d); + push_back(m().mk_false(), p, d); return; } neg_lits.mark(atom); @@ -627,10 +633,10 @@ goal * goal::translate(ast_translation & translator) const { ast_manager & m_to = translator.to(); goal * res = alloc(goal, m_to, m_to.proofs_enabled() && proofs_enabled(), models_enabled(), unsat_core_enabled()); - + unsigned sz = m().size(m_forms); for (unsigned i = 0; i < sz; i++) { - res->m().push_back(res->m_forms, translator(m().get(m_forms, i))); + res->m().push_back(res->m_forms, translator(m().get(m_forms, i))); if (res->proofs_enabled()) res->m().push_back(res->m_proofs, translator(m().get(m_proofs, i))); if (res->unsat_core_enabled()) @@ -645,15 +651,15 @@ goal * goal::translate(ast_translation & translator) const { } -bool goal::sat_preserved() const { - return prec() == PRECISE || prec() == UNDER; +bool goal::sat_preserved() const { + return prec() == PRECISE || prec() == UNDER; } bool goal::unsat_preserved() const { return prec() == PRECISE || prec() == OVER; } -bool goal::is_decided_sat() const { +bool goal::is_decided_sat() const { return size() == 0 && sat_preserved(); } @@ -661,7 +667,7 @@ bool goal::is_decided_unsat() const { return inconsistent() && unsat_preserved(); } -bool goal::is_decided() const { +bool goal::is_decided() const { return is_decided_sat() || is_decided_unsat(); } diff --git a/src/tactic/goal.h b/src/tactic/goal.h index 147c5b2e9..ea02dfa17 100644 --- a/src/tactic/goal.h +++ b/src/tactic/goal.h @@ -62,7 +62,7 @@ protected: unsigned m_precision:2; // PRECISE, UNDER, OVER. void push_back(expr * f, proof * pr, expr_dependency * d); - void quick_process(bool save_first, expr * & f, expr_dependency * d); + void quick_process(bool save_first, expr_ref & f, expr_dependency * d); void process_and(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); void process_not_or(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); void slow_process(bool save_first, expr * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); diff --git a/src/tactic/smtlogics/quant_tactics.cpp b/src/tactic/smtlogics/quant_tactics.cpp index f501ed75a..045b0ec87 100644 --- a/src/tactic/smtlogics/quant_tactics.cpp +++ b/src/tactic/smtlogics/quant_tactics.cpp @@ -27,6 +27,7 @@ Revision History: #include"nlqsat.h" #include"ctx_simplify_tactic.h" #include"smt_tactic.h" +#include"elim_term_ite_tactic.h" static tactic * mk_quant_preprocessor(ast_manager & m, bool disable_gaussian = false) { params_ref pull_ite_p; @@ -112,6 +113,7 @@ tactic * mk_lra_tactic(ast_manager & m, params_ref const & p) { )); #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 diff --git a/src/test/main.cpp b/src/test/main.cpp index 875f7bed0..8fc0a2de6 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -186,6 +186,7 @@ int main(int argc, char ** argv) { TST(smt_context); TST(theory_dl); TST(model_retrieval); + TST(model_based_opt); TST(factor_rewriter); TST(smt2print_parse); TST(substitution); diff --git a/src/test/model_based_opt.cpp b/src/test/model_based_opt.cpp new file mode 100644 index 000000000..8c4148638 --- /dev/null +++ b/src/test/model_based_opt.cpp @@ -0,0 +1,227 @@ +#include "model_based_opt.h" +#include "util.h" +#include "uint_set.h" + +typedef opt::model_based_opt::var var; + +static void add_ineq(opt::model_based_opt& mbo, unsigned x, int a, int k, opt::ineq_type rel) { + vector vars; + vars.push_back(var(x, rational(a))); + mbo.add_constraint(vars, rational(k), rel); +} + +static void add_ineq(opt::model_based_opt& mbo, + unsigned x, int a, + unsigned y, int b, int k, + opt::ineq_type rel) { + vector vars; + vars.push_back(var(x, rational(a))); + vars.push_back(var(y, rational(b))); + mbo.add_constraint(vars, rational(k), rel); +} + +static void add_ineq(opt::model_based_opt& mbo, + unsigned x, int a, + unsigned y, int b, + unsigned z, int c, int k, + opt::ineq_type rel) { + vector vars; + vars.push_back(var(x, rational(a))); + vars.push_back(var(y, rational(b))); + vars.push_back(var(z, rational(c))); + mbo.add_constraint(vars, rational(k), rel); +} + +static void add_random_ineq(opt::model_based_opt& mbo, + random_gen& r, + svector const& values, + unsigned max_vars, + unsigned max_coeff) { + unsigned num_vars = values.size(); + uint_set used_vars; + vector vars; + int value = 0; + for (unsigned i = 0; i < max_vars; ++i) { + unsigned x = r(num_vars); + if (used_vars.contains(x)) { + continue; + } + used_vars.insert(x); + int coeff = r(max_coeff + 1); + if (coeff == 0) { + continue; + } + unsigned sign = r(2); + coeff = sign == 0 ? coeff : -coeff; + vars.push_back(var(x, rational(coeff))); + value += coeff*values[x]; + } + unsigned abs_value = value < 0 ? - value : value; + // value + k <= 0 + // k <= - value + // range for k is 2*|value| + // k <= - value - range + opt::ineq_type rel = opt::t_le; + + int coeff = 0; + if (r(4) == 0) { + rel = opt::t_eq; + coeff = -value; + } + else { + if (abs_value > 0) { + coeff = -value - r(2*abs_value); + } + else { + coeff = 0; + } + if (coeff != -value && r(3) == 0) { + rel = opt::t_lt; + } + } + mbo.add_constraint(vars, rational(coeff), rel); +} + +static void check_random_ineqs(random_gen& r, unsigned num_vars, unsigned max_value, unsigned num_ineqs, unsigned max_vars, unsigned max_coeff) { + opt::model_based_opt mbo; + svector values; + for (unsigned i = 0; i < num_vars; ++i) { + values.push_back(r(max_value + 1)); + mbo.add_var(rational(values.back())); + } + for (unsigned i = 0; i < num_ineqs; ++i) { + add_random_ineq(mbo, r, values, max_vars, max_coeff); + } + + vector vars; + vars.reset(); + vars.push_back(var(0, rational(2))); + vars.push_back(var(1, rational(-2))); + mbo.set_objective(vars, rational(0)); + + mbo.display(std::cout); + opt::inf_eps value = mbo.maximize(); + std::cout << "optimal: " << value << "\n"; + mbo.display(std::cout); + for (unsigned i = 0; i < values.size(); ++i) { + std::cout << i << ": " << values[i] << " -> " << mbo.get_value(i) << "\n"; + } +} + +static void check_random_ineqs() { + random_gen r(1); + + for (unsigned i = 0; i < 1009; ++i) { + check_random_ineqs(r, 4, 5, 5, 3, 6); + } +} + +// test with upper bounds +static void test1() { + opt::model_based_opt mbo; + vector vars; + unsigned x = mbo.add_var(rational(2)); + unsigned y = mbo.add_var(rational(3)); + unsigned z = mbo.add_var(rational(4)); + unsigned u = mbo.add_var(rational(5)); + + add_ineq(mbo, x, 1, y, -1, 0, opt::t_le); + add_ineq(mbo, x, 1, z, -1, 0, opt::t_le); + add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); + add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); + add_ineq(mbo, u, 1, -6, opt::t_le); + + vars.reset(); + vars.push_back(var(x, rational(2))); + mbo.set_objective(vars, rational(0)); + + opt::inf_eps value = mbo.maximize(); + std::cout << value << "\n"; + std::cout << "x: " << mbo.get_value(x) << "\n"; + std::cout << "y: " << mbo.get_value(y) << "\n"; + std::cout << "z: " << mbo.get_value(z) << "\n"; + std::cout << "u: " << mbo.get_value(u) << "\n"; +} + +// test with lower bounds +static void test2() { + opt::model_based_opt mbo; + vector vars; + unsigned x = mbo.add_var(rational(5)); + unsigned y = mbo.add_var(rational(4)); + unsigned z = mbo.add_var(rational(3)); + unsigned u = mbo.add_var(rational(2)); + + add_ineq(mbo, x, -1, y, 1, 0, opt::t_le); + add_ineq(mbo, x, -1, z, 1, 0, opt::t_le); + add_ineq(mbo, y, -1, u, 1, 0, opt::t_le); + add_ineq(mbo, z, -1, u, 1, 1, opt::t_le); + add_ineq(mbo, u, -1, -6, opt::t_le); + + vars.reset(); + vars.push_back(var(x, rational(-2))); + mbo.set_objective(vars, rational(0)); + + opt::inf_eps value = mbo.maximize(); + std::cout << value << "\n"; +} + +// test unbounded +static void test3() { + opt::model_based_opt mbo; + vector vars; + unsigned x = mbo.add_var(rational(2)); + unsigned y = mbo.add_var(rational(3)); + unsigned z = mbo.add_var(rational(4)); + unsigned u = mbo.add_var(rational(5)); + + add_ineq(mbo, x, 1, y, -1, 0, opt::t_le); + add_ineq(mbo, x, 1, z, -1, 0, opt::t_le); + add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); + add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); + + vars.reset(); + vars.push_back(var(x, rational(2))); + mbo.set_objective(vars, rational(0)); + + opt::inf_eps value = mbo.maximize(); + std::cout << value << "\n"; + +} + +// test strict +static void test4() { + opt::model_based_opt mbo; + vector vars; + unsigned x = mbo.add_var(rational(2)); + unsigned y = mbo.add_var(rational(3)); + unsigned z = mbo.add_var(rational(4)); + unsigned u = mbo.add_var(rational(5)); + + add_ineq(mbo, x, 1, y, -1, 0, opt::t_lt); + add_ineq(mbo, x, 1, z, -1, 0, opt::t_lt); + add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); + add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); + add_ineq(mbo, u, 1, -6, opt::t_le); + + vars.reset(); + vars.push_back(var(x, rational(2))); + mbo.set_objective(vars, rational(0)); + + opt::inf_eps value = mbo.maximize(); + std::cout << value << "\n"; + std::cout << "x: " << mbo.get_value(x) << "\n"; + std::cout << "y: " << mbo.get_value(y) << "\n"; + std::cout << "z: " << mbo.get_value(z) << "\n"; + std::cout << "u: " << mbo.get_value(u) << "\n"; +} + +// test with mix of upper and lower bounds + +void tst_model_based_opt() { + check_random_ineqs(); + test1(); + test2(); + test3(); + test4(); +} diff --git a/src/util/cancel_eh.h b/src/util/cancel_eh.h index 6b8fc8351..d23e5f544 100644 --- a/src/util/cancel_eh.h +++ b/src/util/cancel_eh.h @@ -26,12 +26,14 @@ Revision History: */ template class cancel_eh : public event_handler { + bool m_canceled; T & m_obj; public: - cancel_eh(T & o):m_obj(o) {} - ~cancel_eh() { m_obj.reset_cancel(); } + cancel_eh(T & o): m_canceled(false), m_obj(o) {} + ~cancel_eh() { if (m_canceled) m_obj.dec_cancel(); } virtual void operator()() { - m_obj.cancel(); + m_canceled = true; + m_obj.inc_cancel(); } }; diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index 74a03b620..914d113b2 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -38,13 +38,13 @@ Revision History: // Note: // Which FPU will be used is determined by compiler settings. On x64 it's always SSE2, -// on x86 we have to chose SSE2 by enabling /arch:SSE2 (otherwise the x87 FPU will be used). +// on x86 we have to chose SSE2 by enabling /arch:SSE2 (otherwise the x87 FPU will be used). // Christoph has decided that we don't want to use the x87; this makes everything a lot easier. // For SSE2, it is best to use compiler intrinsics because this makes it completely // clear to the compiler what instructions should be used. E.g., for sqrt(), the Windows compiler selects -// the x87 FPU, even when /arch:SSE2 is on. +// the x87 FPU, even when /arch:SSE2 is on. // Luckily, these are kind of standardized, at least for Windows/Linux/OSX. #ifdef __clang__ #undef USE_INTRINSICS @@ -56,19 +56,19 @@ Revision History: hwf_manager::hwf_manager() : m_mpz_manager(m_mpq_manager) -{ +{ #ifdef _WINDOWS #if defined(_AMD64_) || defined(_M_IA64) - // Precision control is not supported on x64. + // Precision control is not supported on x64. // See: http://msdn.microsoft.com/en-us/library/e9b52ceh(VS.110).aspx - // CMW: I think this is okay though, the compiler will chose the right instructions + // CMW: I think this is okay though, the compiler will chose the right instructions // (the x64/SSE2 FPU has separate instructions for different precisions). #else // Setting the precision should only be required on the x87, but it won't hurt to do it anyways. // _PC_53 means double precision (53 significand bits). For extended precision use _PC_64. #ifndef USE_INTRINSICS - __control87_2(_PC_53, _MCW_PC, &x86_state, &sse2_state); + __control87_2(_PC_53, _MCW_PC, &x86_state, &sse2_state); #endif #endif #else @@ -78,7 +78,7 @@ hwf_manager::hwf_manager() : // We only set the precision of the FPU here in the constructor. At the moment, there are no // other parts of the code that could overwrite this, and Windows takes care of context switches. - // CMW: I'm not sure what happens on CPUs with hyper-threading (since the FPU is shared). + // CMW: I'm not sure what happens on CPUs with hyper-threading (since the FPU is shared). // I have yet to discover whether Linux and OSX save the FPU state when switching context. // As long as we stick to using the SSE2 FPU though, there shouldn't be any problems with respect // to the precision (not sure about the rounding modes though). @@ -104,7 +104,7 @@ void hwf_manager::set(hwf & o, double value) { o.value = value; } -void hwf_manager::set(hwf & o, float value) { +void hwf_manager::set(hwf & o, float value) { o.value = (double)value; } @@ -113,7 +113,7 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & value) { o.value = m_mpq_manager.get_double(value); } -void hwf_manager::set(hwf & o, mpf_rounding_mode rm, char const * value) { +void hwf_manager::set(hwf & o, mpf_rounding_mode rm, char const * value) { // We expect [i].[f]P[e], where P means that the exponent is interpreted as 2^e instead of 10^e. std::string v(value); @@ -123,17 +123,17 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, char const * value) { std::string f, e; f = (e_pos != std::string::npos) ? v.substr(0, e_pos) : v; - e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0"; + e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0"; - TRACE("mpf_dbg", tout << " f = " << f << " e = " << e << std::endl;); + TRACE("mpf_dbg", tout << " f = " << f << " e = " << e << std::endl;); - mpq q; + mpq q; m_mpq_manager.set(q, f.c_str()); mpz ex; m_mpz_manager.set(ex, e.c_str()); - set(o, rm, q, ex); + set(o, rm, q, ex); TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } @@ -142,7 +142,7 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mp // Assumption: this represents significand * 2^exponent. set_rounding_mode(rm); - mpq sig; + mpq sig; m_mpq_manager.set(sig, significand); int64 exp = m_mpz_manager.get_int64(exponent); @@ -150,7 +150,7 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mp o.value = 0.0; else { - while (m_mpq_manager.lt(sig, 1)) + while (m_mpq_manager.lt(sig, 1)) { m_mpq_manager.mul(sig, 2, sig); exp--; @@ -176,7 +176,7 @@ void hwf_manager::set(hwf & o, hwf const & x) { o.value = x.value; } -void hwf_manager::abs(hwf & o) { +void hwf_manager::abs(hwf & o) { o.value = fabs(o.value); } @@ -244,14 +244,14 @@ void hwf_manager::mul(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & // On the x86 FPU (x87), we use custom assembly routines because // the code generated for x*y and x/y suffers from the double // rounding on underflow problem. The scaling trick is described - // in Roger Golliver: `Efficiently producing default orthogonal IEEE + // in Roger Golliver: `Efficiently producing default orthogonal IEEE // double results using extended IEEE hardware', see // http://www.open-std.org/JTC1/SC22/JSG/docs/m3/docs/jsgn326.pdf // CMW: Tthis is not really needed if we use only the SSE2 FPU, // it shouldn't hurt the performance too much though. static const int const1 = -DBL_SCALE; - static const int const2 = +DBL_SCALE; + static const int const2 = +DBL_SCALE; double xv = x.value; double yv = y.value; double & ov = o.value; @@ -266,14 +266,14 @@ void hwf_manager::mul(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & fxch st(1); fscale; fstp ov; - } + } #endif } void hwf_manager::div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS - _mm_store_sd(&o.value, _mm_div_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); + _mm_store_sd(&o.value, _mm_div_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else o.value = x.value / y.value; #endif @@ -306,18 +306,18 @@ void hwf_manager::div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & #endif void hwf_manager::fma(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o) { - // CMW: fused_mul_add is not available on most CPUs. As of 2012, only Itanium, + // CMW: fused_mul_add is not available on most CPUs. As of 2012, only Itanium, // Intel Sandybridge and AMD Bulldozers support that (via AVX). set_rounding_mode(rm); #ifdef _M_IA64 - // IA64 (Itanium) will do it, if contractions are on. + // IA64 (Itanium) will do it, if contractions are on. o.value = x.value * y.value + z.value; #else -#if defined(_WINDOWS) +#if defined(_WINDOWS) #if _MSC_VER >= 1800 - o.value = ::fma(x.value, y.value, z.value); + o.value = ::fma(x.value, y.value, z.value); #else // Windows, older than VS 2013 #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_fmadd_sd(_mm_set_sd(x.value), _mm_set_sd(y.value), _mm_set_sd(z.value))); @@ -351,7 +351,7 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o // CMW: modf is not the right function here. // modf(x.value, &o.value); - // According to the Intel Architecture manual, the x87-instrunction FRNDINT is the + // According to the Intel Architecture manual, the x87-instrunction FRNDINT is the // same in 32-bit and 64-bit mode. The _mm_round_* intrinsics are SSE4 extensions. #ifdef _WINDOWS #ifdef USE_INTRINSICS @@ -383,17 +383,16 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o } void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) { - // The built-in fmod() works, except for the special numbers. +#if defined(_WINDOWS) && _MSC_VER <= 1700 + o.value = fmod(x.value, y.value); + if (o.value >= (y.value/2.0)) + o.value -= y.value; +#else + o.value = remainder(x.value, y.value); +#endif - if (is_inf(x) && is_inf(y)) - o.value = x.value/y.value; // NaN - else if (is_inf(y)) - o.value = x.value; - else - o.value = fmod(x.value, y.value); - - // Here is an x87 alternative if the above makes problems; this may also be faster. #if 0 + // Here is an x87 alternative if the above makes problems; this may also be faster. double xv = x.value; double yv = y.value; double & ov = o.value; @@ -423,7 +422,7 @@ void hwf_manager::maximum(hwf const & x, hwf const & y, hwf & o) { o.value = x.value; else if (lt(x, y)) o.value = y.value; - else + else o.value = x.value; #endif } @@ -439,12 +438,12 @@ void hwf_manager::minimum(hwf const & x, hwf const & y, hwf & o) { o.value = x.value; else if (lt(x, y)) o.value = x.value; - else + else o.value = y.value; #endif } -std::string hwf_manager::to_string(hwf const & x) { +std::string hwf_manager::to_string(hwf const & x) { std::stringstream ss(""); ss << std::scientific << x.value; return ss.str(); @@ -488,9 +487,9 @@ void hwf_manager::to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o) int e = exp(x); if (e >= 0) qm.mul2k(n, (unsigned)e); - else + else qm.mul2k(d, (unsigned)-e); - qm.set(o, n, d); + qm.set(o, n, d); } bool hwf_manager::is_zero(hwf const & x) { @@ -559,13 +558,13 @@ bool hwf_manager::is_denormal(hwf const & x) { (t & 0x000FFFFFFFFFFFFFull) != 0x0); } -bool hwf_manager::is_regular(hwf const & x) { +bool hwf_manager::is_regular(hwf const & x) { // Everything that doesn't have the top-exponent is considered regular. // Note that +-0.0 and denormal numbers have exponent==0; these are regular. - // All normal numbers are also regular. What remains is +-Inf and NaN, they are + // All normal numbers are also regular. What remains is +-Inf and NaN, they are // not regular and they are the only numbers that have exponent 7FF. uint64 e = RAW(x.value) & 0x7FF0000000000000ull; // the exponent - return (e != 0x7FF0000000000000ull); + return (e != 0x7FF0000000000000ull); } bool hwf_manager::is_int(hwf const & x) { @@ -596,8 +595,8 @@ void hwf_manager::mk_pzero(hwf & o) { } void hwf_manager::mk_zero(bool sign, hwf & o) { - if (sign) - mk_nzero(o); + if (sign) + mk_nzero(o); else mk_pzero(o); } @@ -627,7 +626,7 @@ void hwf_manager::mk_ninf(hwf & o) { #ifdef USE_INTRINSICS #define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) #else -#define SETRM(RM) _controlfp_s(&sse2_state, RM, _MCW_RC); +#define SETRM(RM) _controlfp_s(&sse2_state, RM, _MCW_RC); #endif #else #ifdef USE_INTRINSICS @@ -691,7 +690,7 @@ void hwf_manager::set_rounding_mode(mpf_rounding_mode rm) #endif #else // OSX/Linux switch (rm) { - case MPF_ROUND_NEAREST_TEVEN: + case MPF_ROUND_NEAREST_TEVEN: SETRM(FE_TONEAREST); break; case MPF_ROUND_TOWARD_POSITIVE: diff --git a/src/util/inf_eps_rational.h b/src/util/inf_eps_rational.h index 4327b47c0..048b6f383 100644 --- a/src/util/inf_eps_rational.h +++ b/src/util/inf_eps_rational.h @@ -23,6 +23,7 @@ Revision History: #include"debug.h" #include"vector.h" #include"rational.h" +#include"inf_rational.h" template class inf_eps_rational { diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 5de4c406a..1bf968cdd 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -17,6 +17,7 @@ Revision History: --*/ #include +#include #include"mpf.h" mpf::mpf() : @@ -1077,21 +1078,20 @@ void mpf_manager::round_to_integral(mpf_rounding_mode rm, mpf const & x, mpf & o unsigned shift = (o.sbits - 1) - ((unsigned)o.exponent); const mpz & shift_p = m_powers2(shift); + const mpz & shiftm1_p = m_powers2(shift-1); TRACE("mpf_dbg", tout << "shift=" << shift << std::endl;); + TRACE("mpf_dbg", tout << "shiftm1_p=" << m_mpz_manager.to_string(shiftm1_p) << std::endl;); scoped_mpz div(m_mpz_manager), rem(m_mpz_manager); m_mpz_manager.machine_div_rem(o.significand, shift_p, div, rem); TRACE("mpf_dbg", tout << "div=" << m_mpz_manager.to_string(div) << " rem=" << m_mpz_manager.to_string(rem) << std::endl;); - const mpz & shift_p1 = m_powers2(shift-1); - TRACE("mpf_dbg", tout << "shift_p1=" << m_mpz_manager.to_string(shift_p1) << std::endl;); - switch (rm) { case MPF_ROUND_NEAREST_TEVEN: case MPF_ROUND_NEAREST_TAWAY: { - bool tie = m_mpz_manager.eq(rem, shift_p1); - bool less_than_tie = m_mpz_manager.lt(rem, shift_p1); - bool more_than_tie = m_mpz_manager.gt(rem, shift_p1); + bool tie = m_mpz_manager.eq(rem, shiftm1_p); + bool less_than_tie = m_mpz_manager.lt(rem, shiftm1_p); + bool more_than_tie = m_mpz_manager.gt(rem, shiftm1_p); TRACE("mpf_dbg", tout << "tie= " << tie << "; tie = " << more_than_tie << std::endl;); if (tie) { if ((rm == MPF_ROUND_NEAREST_TEVEN && m_mpz_manager.is_odd(div)) || @@ -1231,43 +1231,56 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { else if (is_zero(x)) set(o, x); else { - o.ebits = x.ebits; - o.sbits = x.sbits; - o.sign = x.sign; + // This is a generalized version of the algorithm for FPREM1 in the `Intel + // 64 and IA-32 Architectures Software Developer’s Manual', + // Section 3-402 Vol. 2A `FPREM1-Partial Remainder'. + scoped_mpf ST0(*this), ST1(*this); + set(ST0, x); + set(ST1, y); - scoped_mpf a(*this), b(*this); - set(a, x); - set(b, y); - unpack(a, true); - unpack(b, true); + const mpf_exp_t B = x.sbits-1; // max bits per iteration. + mpf_exp_t D; + do { + D = ST0.exponent() - ST1.exponent(); + TRACE("mpf_dbg_rem", tout << "st0=" << to_string_hexfloat(ST0) << std::endl; + tout << "st1=" << to_string_hexfloat(ST1) << std::endl; + tout << "D=" << D << std::endl;); - TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); - TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); - - if (a.exponent() < b.exponent()) - set(o, x); - else { - mpf_exp_t exp_diff = a.exponent() - b.exponent(); - SASSERT(exp_diff >= 0); - TRACE("mpf_dbg", tout << "exp_diff = " << exp_diff << std::endl;); - - SASSERT(exp_diff < INT_MAX); - // CMW: This requires rather a lot of memory. There are algorithms that trade space for time by - // computing only a small chunk of the remainder bits at a time. - unsigned extra_bits = (unsigned) exp_diff; - m_mpz_manager.mul2k(a.significand(), extra_bits); - m_mpz_manager.rem(a.significand(), b.significand(), o.significand); - - TRACE("mpf_dbg", tout << "REM' = " << to_string(o) << std::endl;); - - if (m_mpz_manager.is_zero(o.significand)) - mk_zero(o.ebits, o.sbits, o.sign, o); - else { - o.exponent = b.exponent(); - m_mpz_manager.mul2k(o.significand, 3); // rounding bits - round(MPF_ROUND_NEAREST_TEVEN, o); + if (D < B) { + scoped_mpf ST0_DIV_ST1(*this), Q(*this), ST1_MUL_Q(*this), ST0p(*this); + div(MPF_ROUND_NEAREST_TEVEN, ST0, ST1, ST0_DIV_ST1); + round_to_integral(MPF_ROUND_NEAREST_TEVEN, ST0_DIV_ST1, Q); + mul(MPF_ROUND_NEAREST_TEVEN, ST1, Q, ST1_MUL_Q); + sub(MPF_ROUND_NEAREST_TEVEN, ST0, ST1_MUL_Q, ST0p); + TRACE("mpf_dbg_rem", tout << "ST0/ST1=" << to_string_hexfloat(ST0_DIV_ST1) << std::endl; + tout << "Q=" << to_string_hexfloat(Q) << std::endl; + tout << "ST1*Q=" << to_string_hexfloat(ST1_MUL_Q) << std::endl; + tout << "ST0'=" << to_string_hexfloat(ST0p) << std::endl;); + set(ST0, ST0p); } - } + else { + const mpf_exp_t N = B; + scoped_mpf ST0_DIV_ST1(*this), QQ(*this), ST1_MUL_QQ(*this), ST0p(*this); + div(MPF_ROUND_TOWARD_ZERO, ST0, ST1, ST0_DIV_ST1); + ST0_DIV_ST1.get().exponent -= D - N; + round_to_integral(MPF_ROUND_TOWARD_ZERO, ST0_DIV_ST1, QQ); + mul(MPF_ROUND_NEAREST_TEVEN, ST1, QQ, ST1_MUL_QQ); + ST1_MUL_QQ.get().exponent += D - N; + sub(MPF_ROUND_NEAREST_TEVEN, ST0, ST1_MUL_QQ, ST0p); + TRACE("mpf_dbg_rem", tout << "ST0/ST1/2^{D-N}=" << to_string_hexfloat(ST0_DIV_ST1) << std::endl; + tout << "QQ=" << to_string_hexfloat(QQ) << std::endl; + tout << "ST1*QQ*2^{D-N}=" << to_string_hexfloat(ST1_MUL_QQ) << std::endl; + tout << "ST0'=" << to_string_hexfloat(ST0p) << std::endl;); + SASSERT(!eq(ST0, ST0p)); + set(ST0, ST0p); + } + + SASSERT(ST0.exponent() - ST1.exponent() <= D); + } while (D >= B); + + set(o, ST0); + if (is_zero(o)) + o.sign = x.sign; } TRACE("mpf_dbg", tout << "REMAINDER = " << to_string(o) << std::endl;); @@ -1352,7 +1365,7 @@ std::string mpf_manager::to_string(mpf const & x) { } //DEBUG_CODE( - // res += " " + to_string_raw(x); + // res += " " + to_string_hexfloat(x); //); return res; @@ -1394,6 +1407,20 @@ std::string mpf_manager::to_string_raw(mpf const & x) { return res; } +std::string mpf_manager::to_string_hexfloat(mpf const & x) { + std::stringstream ss(""); + std::ios::fmtflags ff = ss.setf(std::ios_base::hex | std::ios_base::uppercase | + std::ios_base::showpoint | std::ios_base::showpos); + ss.setf(ff); + ss.precision(13); +#if defined(_WIN32) && _MSC_VER >= 1800 + ss << std::hexfloat << to_double(x); +#else + ss << std::hex << (*reinterpret_cast(&(x))); +#endif + return ss.str(); +} + void mpf_manager::to_rational(mpf const & x, unsynch_mpq_manager & qm, mpq & o) { scoped_mpf a(*this); scoped_mpz n(m_mpq_manager), d(m_mpq_manager); diff --git a/src/util/mpf.h b/src/util/mpf.h index 101491e3b..1bd0ac952 100644 --- a/src/util/mpf.h +++ b/src/util/mpf.h @@ -186,6 +186,7 @@ public: void mk_ninf(unsigned ebits, unsigned sbits, mpf & o); std::string to_string_raw(mpf const & a); + std::string to_string_hexfloat(mpf const & a); unsynch_mpz_manager & mpz_manager(void) { return m_mpz_manager; } unsynch_mpq_manager & mpq_manager(void) { return m_mpq_manager; } diff --git a/src/util/rlimit.cpp b/src/util/rlimit.cpp index fa34a9555..b3f055955 100644 --- a/src/util/rlimit.cpp +++ b/src/util/rlimit.cpp @@ -31,7 +31,7 @@ uint64 reslimit::count() const { bool reslimit::inc() { ++m_count; - return !m_cancel && (m_limit == 0 || m_count <= m_limit); + return m_cancel == 0 && (m_limit == 0 || m_count <= m_limit); } bool reslimit::inc(unsigned offset) { @@ -46,7 +46,7 @@ void reslimit::push(unsigned delta_limit) { } m_limits.push_back(m_limit); m_limit = m_limit==0?new_limit:std::min(new_limit, m_limit); - m_cancel = false; + m_cancel = 0; } void reslimit::pop() { @@ -55,11 +55,11 @@ void reslimit::pop() { } m_limit = m_limits.back(); m_limits.pop_back(); - m_cancel = false; + m_cancel = 0; } char const* reslimit::get_cancel_msg() const { - if (m_cancel) { + if (m_cancel > 0) { return Z3_CANCELED_MSG; } else { @@ -84,7 +84,7 @@ void reslimit::pop_child() { void reslimit::cancel() { #pragma omp critical (reslimit_cancel) { - set_cancel(true); + set_cancel(m_cancel+1); } } @@ -92,11 +92,28 @@ void reslimit::cancel() { void reslimit::reset_cancel() { #pragma omp critical (reslimit_cancel) { - set_cancel(false); + set_cancel(0); } } -void reslimit::set_cancel(bool f) { +void reslimit::inc_cancel() { + #pragma omp critical (reslimit_cancel) + { + set_cancel(m_cancel+1); + } +} + + +void reslimit::dec_cancel() { + #pragma omp critical (reslimit_cancel) + { + if (m_cancel > 0) { + set_cancel(m_cancel-1); + } + } +} + +void reslimit::set_cancel(unsigned f) { m_cancel = f; for (unsigned i = 0; i < m_children.size(); ++i) { m_children[i]->set_cancel(f); diff --git a/src/util/rlimit.h b/src/util/rlimit.h index c16a8d49b..ac5db6136 100644 --- a/src/util/rlimit.h +++ b/src/util/rlimit.h @@ -22,13 +22,13 @@ Revision History: #include "vector.h" class reslimit { - volatile bool m_cancel; + volatile unsigned m_cancel; uint64 m_count; uint64 m_limit; svector m_limits; ptr_vector m_children; - void set_cancel(bool f); + void set_cancel(unsigned f); public: reslimit(); @@ -42,10 +42,13 @@ public: uint64 count() const; - bool get_cancel_flag() const { return m_cancel; } + bool get_cancel_flag() const { return m_cancel > 0; } char const* get_cancel_msg() const; void cancel(); void reset_cancel(); + + void inc_cancel(); + void dec_cancel(); }; class scoped_rlimit { diff --git a/src/util/util.h b/src/util/util.h index 0dfa8ac8c..a040a79ae 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -226,9 +226,7 @@ public: } ~scoped_ptr() { - if (m_ptr) { - dealloc(m_ptr); - } + dealloc(m_ptr); } T * operator->() const { @@ -253,9 +251,7 @@ public: scoped_ptr & operator=(T * n) { if (m_ptr != n) { - if (m_ptr) { - dealloc(m_ptr); - } + dealloc(m_ptr); m_ptr = n; } return *this; diff --git a/src/util/vector.h b/src/util/vector.h index faab3b86c..03c1d6019 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -457,5 +457,15 @@ template struct svector_hash : public vector_hash_tpl > {}; +// Specialize vector to be inaccessible. +// This will catch any regression of issue #564 and #420. +// Use std::vector instead. +template <> +class vector { +private: + vector(); +}; + + #endif /* VECTOR_H_ */