mirror of
https://github.com/Z3Prover/z3
synced 2025-04-10 19:27:06 +00:00
Merge branch 'master' of https://github.com/Z3Prover/z3 into new-ml-api
This commit is contained in:
commit
86126e2c01
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ``$<TARGET_FILE_DIR:libz3>`` 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 ``$<TARGET_FILE_DIR:libz3>`` 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()
|
||||
|
|
236
contrib/cmake/src/api/java/CMakeLists.txt
Normal file
236
contrib/cmake/src/api/java/CMakeLists.txt
Normal file
|
@ -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()
|
|
@ -1,6 +1,7 @@
|
|||
z3_add_component(simplex
|
||||
SOURCES
|
||||
simplex.cpp
|
||||
model_based_opt.cpp
|
||||
COMPONENT_DEPENDENCIES
|
||||
util
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
###############################################################################
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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<char *>(m_string_buffer.c_str());
|
||||
}
|
||||
|
||||
|
|
|
@ -876,7 +876,7 @@ namespace z3 {
|
|||
unsigned lo() const { assert (is_app() && Z3_get_decl_num_parameters(ctx(), decl()) == 2); return static_cast<unsigned>(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<unsigned>(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<typename T> class cast_ast;
|
||||
|
||||
template<> class cast_ast<ast> {
|
||||
|
|
|
@ -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()); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical divsion operator (over reals) </summary>
|
||||
public static ArithExpr operator /(ArithExpr a, ArithExpr b) { return a.Context.MkDiv(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator /(ArithExpr a, int b) { return a / MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator /(ArithExpr a, double b) { return a / MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator /(int a, ArithExpr b) { return MkNum(b, a) / b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator /(double a, ArithExpr b) { return MkNum(b, a) / b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator -(ArithExpr a) { return a.Context.MkUnaryMinus(a); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator -(ArithExpr a, ArithExpr b) { return a.Context.MkSub(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator -(ArithExpr a, int b) { return a - MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator -(ArithExpr a, double b) { return a - MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator -(int a, ArithExpr b) { return MkNum(b, a) - b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator -(double a, ArithExpr b) { return MkNum(b, a) - b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator +(ArithExpr a, ArithExpr b) { return a.Context.MkAdd(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator +(ArithExpr a, int b) { return a + MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator +(ArithExpr a, double b) { return a + MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator +(int a, ArithExpr b) { return MkNum(b, a) + b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator +(double a, ArithExpr b) { return MkNum(b, a) + b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator *(ArithExpr a, ArithExpr b) { return a.Context.MkMul(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator *(ArithExpr a, int b) { return a * MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator *(ArithExpr a, double b) { return a * MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator *(int a, ArithExpr b) { return MkNum(b, a) * b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static ArithExpr operator *(double a, ArithExpr b) { return MkNum(b, a) * b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator <=(ArithExpr a, ArithExpr b) { return a.Context.MkLe(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator <=(ArithExpr a, int b) { return a <= MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator <=(ArithExpr a, double b) { return a <= MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator <=(int a, ArithExpr b) { return MkNum(b, a) <= b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator <=(double a, ArithExpr b) { return MkNum(b, a) <= b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator <(ArithExpr a, ArithExpr b) { return a.Context.MkLt(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator <(ArithExpr a, int b) { return a < MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator <(ArithExpr a, double b) { return a < MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator <(int a, ArithExpr b) { return MkNum(b, a) < b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator <(double a, ArithExpr b) { return MkNum(b, a) < b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator >(ArithExpr a, ArithExpr b) { return a.Context.MkGt(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator >(ArithExpr a, int b) { return a > MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator >(ArithExpr a, double b) { return a > MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator >(int a, ArithExpr b) { return MkNum(b, a) > b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator >(double a, ArithExpr b) { return MkNum(b, a) > b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator >=(ArithExpr a, ArithExpr b) { return a.Context.MkGe(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator >=(ArithExpr a, int b) { return a >= MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator >=(ArithExpr a, double b) { return a >= MkNum(a, b); }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator >=(int a, ArithExpr b) { return MkNum(b, a) >= b; }
|
||||
|
||||
/// <summary> Operator overloading for arithmetical operator </summary>
|
||||
public static BoolExpr operator >=(double a, ArithExpr b) { return MkNum(b, a) >= b; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,5 +34,20 @@ namespace Microsoft.Z3
|
|||
/// <summary> Constructor for BoolExpr </summary>
|
||||
internal BoolExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); }
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
/// <summary> Disjunction of Boolean expressions </summary>
|
||||
public static BoolExpr operator|(BoolExpr a, BoolExpr b) { return a.Context.MkOr(a, b); }
|
||||
|
||||
/// <summary> Conjunction of Boolean expressions </summary>
|
||||
public static BoolExpr operator &(BoolExpr a, BoolExpr b) { return a.Context.MkAnd(a, b); }
|
||||
|
||||
/// <summary> Xor of Boolean expressions </summary>
|
||||
public static BoolExpr operator ^(BoolExpr a, BoolExpr b) { return a.Context.MkXor(a, b); }
|
||||
|
||||
/// <summary> Negation </summary>
|
||||
public static BoolExpr operator !(BoolExpr a) { return a.Context.MkNot(a); }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new function application.
|
||||
/// </summary>
|
||||
public Expr MkApp(FuncDecl f, IEnumerable<Expr> args)
|
||||
{
|
||||
Contract.Requires(f != null);
|
||||
Contract.Requires(args == null || Contract.ForAll(args, a => a != null));
|
||||
Contract.Ensures(Contract.Result<Expr>() != null);
|
||||
|
||||
CheckContextMatch(f);
|
||||
CheckContextMatch(args);
|
||||
return Expr.Create(this, f, args.ToArray());
|
||||
}
|
||||
|
||||
#region Propositional
|
||||
/// <summary>
|
||||
/// 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)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an expression representing <c>t[0] and t[1] and ...</c>.
|
||||
/// </summary>
|
||||
public BoolExpr MkAnd(IEnumerable<BoolExpr> t)
|
||||
{
|
||||
Contract.Requires(t != null);
|
||||
Contract.Requires(Contract.ForAll(t, a => a != null));
|
||||
Contract.Ensures(Contract.Result<BoolExpr>() != null);
|
||||
CheckContextMatch(t);
|
||||
return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Count(), AST.EnumToNative(t)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an expression representing <c>t[0] or t[1] or ...</c>.
|
||||
/// </summary>
|
||||
|
@ -973,6 +1000,19 @@ namespace Microsoft.Z3
|
|||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Create an expression representing <c>t[0] or t[1] or ...</c>.
|
||||
/// </summary>
|
||||
public BoolExpr MkOr(IEnumerable<BoolExpr> t)
|
||||
{
|
||||
Contract.Requires(t != null);
|
||||
Contract.Requires(Contract.ForAll(t, a => a != null));
|
||||
Contract.Ensures(Contract.Result<BoolExpr>() != 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)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an expression representing <c>t[0] + t[1] + ...</c>.
|
||||
/// </summary>
|
||||
public ArithExpr MkAdd(IEnumerable<ArithExpr> t)
|
||||
{
|
||||
Contract.Requires(t != null);
|
||||
Contract.Requires(Contract.ForAll(t, a => a != null));
|
||||
Contract.Ensures(Contract.Result<ArithExpr>() != null);
|
||||
|
||||
CheckContextMatch(t);
|
||||
return (ArithExpr)Expr.Create(this, Native.Z3_mk_add(nCtx, (uint)t.Count(), AST.EnumToNative(t)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an expression representing <c>t[0] * t[1] * ...</c>.
|
||||
/// </summary>
|
||||
|
@ -4743,6 +4796,21 @@ namespace Microsoft.Z3
|
|||
}
|
||||
}
|
||||
|
||||
[Pure]
|
||||
internal void CheckContextMatch(IEnumerable<Z3Object> 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()
|
||||
{
|
||||
|
|
|
@ -66,6 +66,38 @@ namespace Microsoft.Z3
|
|||
/// Assert a constraint (or multiple) into the optimize solver.
|
||||
/// </summary>
|
||||
public void Assert(params BoolExpr[] constraints)
|
||||
{
|
||||
AddConstraints(constraints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert a constraint (or multiple) into the optimize solver.
|
||||
/// </summary>
|
||||
public void Assert(IEnumerable<BoolExpr> constraints)
|
||||
{
|
||||
AddConstraints(constraints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alias for Assert.
|
||||
/// </summary>
|
||||
public void Add(params BoolExpr[] constraints)
|
||||
{
|
||||
AddConstraints(constraints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alias for Assert.
|
||||
/// </summary>
|
||||
public void Add(IEnumerable<BoolExpr> constraints)
|
||||
{
|
||||
AddConstraints(constraints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert a constraint (or multiple) into the optimize solver.
|
||||
/// </summary>
|
||||
private void AddConstraints(IEnumerable<BoolExpr> 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alias for Assert.
|
||||
/// </summary>
|
||||
public void Add(params BoolExpr[] constraints)
|
||||
{
|
||||
Assert(constraints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle to objectives returned by objective functions.
|
||||
/// </summary>
|
||||
|
|
|
@ -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<Z3Object> a)
|
||||
{
|
||||
Contract.Ensures(a == null || Contract.Result<IntPtr[]>() != null);
|
||||
Contract.Ensures(a == null || Contract.Result<IntPtr[]>().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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ Notes:
|
|||
#include"symbol.h"
|
||||
#include"trace.h"
|
||||
#include<sstream>
|
||||
#include<vector>
|
||||
|
||||
void register_z3_replayer_cmds(z3_replayer & in);
|
||||
|
||||
|
@ -46,7 +47,7 @@ struct z3_replayer::imp {
|
|||
size_t m_ptr;
|
||||
size_t_map<void *> m_heap;
|
||||
svector<z3_replayer_cmd> m_cmds;
|
||||
vector<std::string> m_cmds_names;
|
||||
std::vector<std::string> 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<unsigned>(m_cmds_names.size()) <= id+1) {
|
||||
m_cmds_names.push_back("");
|
||||
}
|
||||
m_cmds[id] = cmd;
|
||||
m_cmds_names[id] = name;
|
||||
}
|
||||
|
|
384
src/ast/ast.cpp
384
src/ast/ast.cpp
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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); }
|
||||
|
||||
|
|
|
@ -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<std::string>::const_iterator it = m_assertion_strings.begin();
|
||||
vector<std::string>::const_iterator end = m_assertion_strings.end();
|
||||
std::vector<std::string>::const_iterator it = m_assertion_strings.begin();
|
||||
std::vector<std::string>::const_iterator end = m_assertion_strings.end();
|
||||
regular_stream() << "(";
|
||||
for (bool first = true; it != end; ++it) {
|
||||
std::string const & s = *it;
|
||||
|
|
|
@ -22,6 +22,7 @@ Notes:
|
|||
#define CMD_CONTEXT_H_
|
||||
|
||||
#include<sstream>
|
||||
#include<vector>
|
||||
#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<pdecl> m_aux_pdecls;
|
||||
ptr_vector<expr> m_assertions;
|
||||
vector<std::string> m_assertion_strings;
|
||||
std::vector<std::string> m_assertion_strings;
|
||||
ptr_vector<expr> 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; }
|
||||
|
|
452
src/math/simplex/model_based_opt.cpp
Normal file
452
src/math/simplex/model_based_opt.cpp
Normal file
|
@ -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<var> 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<var> 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<var> 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<var> 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<var> 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<var> const& coeffs, rational const& c) {
|
||||
set_row(m_objective_id, coeffs, c, t_le);
|
||||
}
|
||||
|
||||
}
|
||||
|
119
src/math/simplex/model_based_opt.h
Normal file
119
src/math/simplex/model_based_opt.h
Normal file
|
@ -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_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<var> 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<row> m_rows;
|
||||
unsigned m_objective_id;
|
||||
vector<unsigned_vector> m_var2row_ids;
|
||||
vector<rational> m_var2value;
|
||||
vector<var> 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<var> 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<var> const& coeffs, rational const& c, ineq_type r);
|
||||
|
||||
// Set the objective function (linear).
|
||||
void set_objective(vector<var> 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
|
|
@ -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<expr> & 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<expr> 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<func_entry>::const_iterator it = m_entries.begin();
|
||||
ptr_vector<func_entry>::const_iterator end = m_entries.end();
|
||||
for (; it != end; ++it) {
|
||||
func_entry * curr = *it;
|
||||
func_entry * curr = *it;
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned i=0; i<m_arity; i++)
|
||||
new_args.push_back(translator(curr->get_arg(i)));
|
||||
|
|
|
@ -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<func_entry> 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<expr> & args);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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<expr_ref_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) {
|
||||
|
|
|
@ -19,6 +19,7 @@ Revision History:
|
|||
#ifndef DL_UTIL_H_
|
||||
#define DL_UTIL_H_
|
||||
|
||||
#include<vector>
|
||||
#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<var *> varidx2var_map;
|
||||
typedef obj_hashtable<func_decl> func_decl_set; //!< Rule dependencies.
|
||||
typedef vector<std::string> string_vector;
|
||||
typedef std::vector<std::string> string_vector;
|
||||
|
||||
bool contains_var(expr * trm, unsigned var_idx);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -61,7 +61,9 @@ namespace opt {
|
|||
void get_model(model_ref& mdl, svector<symbol>& 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();
|
||||
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -298,7 +298,7 @@ namespace smt2 {
|
|||
}
|
||||
|
||||
unsigned m_cache_end;
|
||||
vector<std::string> m_cached_strings;
|
||||
std::vector<std::string> 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()) {
|
||||
|
|
|
@ -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<expr, unsigned> m_expr2var;
|
||||
ptr_vector<expr> 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<rational> m_ineq_coeffs;
|
||||
svector<ineq_type> m_ineq_types;
|
||||
svector<opt::ineq_type> m_ineq_types;
|
||||
expr_ref_vector m_div_terms;
|
||||
vector<rational> 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<expr, rational>& 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<expr, unsigned>& tids) {
|
||||
obj_map<expr, rational> 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<expr, rational>& 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<var> 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<expr, rational> ts;
|
||||
obj_map<expr, unsigned> 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<expr> vars;
|
||||
obj_map<expr, unsigned>::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<expr, rational> const& ts, obj_map<expr, unsigned>& tids, vars& coeffs) {
|
||||
coeffs.reset();
|
||||
obj_map<expr, rational>::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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -107,6 +107,7 @@ void project_plugin::push_back(expr_ref_vector& lits, expr* e) {
|
|||
class mbp::impl {
|
||||
ast_manager& m;
|
||||
ptr_vector<project_plugin> 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<expr> 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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
275
src/qe/qsat.cpp
275
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<app_ref_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<expr_replacer> 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<expr> 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<expr,expr*> result;
|
||||
expr_ref_vector trail(m);
|
||||
ptr_vector<expr> 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<bool> 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
|
||||
|
|
|
@ -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<bool> 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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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<qinfo> q(qi);
|
||||
ptr_vector<qinfo>::iterator it = m_qinfo_vect.begin();
|
||||
ptr_vector<qinfo>::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";);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<theory_seq, expr>(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<theory_seq, expr_ref_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<theory_seq, rational_set, rational>(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));
|
||||
|
|
|
@ -287,7 +287,10 @@ namespace smt {
|
|||
unsigned m_extensionality;
|
||||
unsigned m_fixed_length;
|
||||
unsigned m_propagate_contains;
|
||||
unsigned m_int_string;
|
||||
};
|
||||
typedef hashtable<rational, rational::hash_proc, rational::eq_proc> rational_set;
|
||||
|
||||
ast_manager& m;
|
||||
dependency_manager m_dm;
|
||||
solution_map m_rep; // unification representative.
|
||||
|
@ -303,6 +306,8 @@ namespace smt {
|
|||
obj_hashtable<expr> 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<expr> m_length; // is length applied
|
||||
scoped_ptr_vector<apply> 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<rational>& 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;
|
||||
|
|
|
@ -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";);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<expr, func_decl*>::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<func_decl*>(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<expr> & 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<expr> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<expr> & args);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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 *, bool> expr_pol;
|
||||
sbuffer<expr_pol, 64> 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<expr> & 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
227
src/test/model_based_opt.cpp
Normal file
227
src/test/model_based_opt.cpp
Normal file
|
@ -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<var> 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<var> 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<var> 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<int> const& values,
|
||||
unsigned max_vars,
|
||||
unsigned max_coeff) {
|
||||
unsigned num_vars = values.size();
|
||||
uint_set used_vars;
|
||||
vector<var> 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<int> 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<var> 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<var> 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<var> 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<var> 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<var> 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();
|
||||
}
|
|
@ -26,12 +26,14 @@ Revision History:
|
|||
*/
|
||||
template<typename T>
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -23,6 +23,7 @@ Revision History:
|
|||
#include"debug.h"
|
||||
#include"vector.h"
|
||||
#include"rational.h"
|
||||
#include"inf_rational.h"
|
||||
|
||||
template<typename Numeral>
|
||||
class inf_eps_rational {
|
||||
|
|
109
src/util/mpf.cpp
109
src/util/mpf.cpp
|
@ -17,6 +17,7 @@ Revision History:
|
|||
|
||||
--*/
|
||||
#include<sstream>
|
||||
#include<iomanip>
|
||||
#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 = " << less_than_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<const unsigned long long *>(&(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);
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<uint64> m_limits;
|
||||
ptr_vector<reslimit> 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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -457,5 +457,15 @@ template<typename Hash>
|
|||
struct svector_hash : public vector_hash_tpl<Hash, svector<typename Hash::data> > {};
|
||||
|
||||
|
||||
// Specialize vector<std::string> to be inaccessible.
|
||||
// This will catch any regression of issue #564 and #420.
|
||||
// Use std::vector<std::string> instead.
|
||||
template <>
|
||||
class vector<std::string, true, unsigned> {
|
||||
private:
|
||||
vector<std::string, true, unsigned>();
|
||||
};
|
||||
|
||||
|
||||
#endif /* VECTOR_H_ */
|
||||
|
||||
|
|
Loading…
Reference in a new issue