diff --git a/README b/README index 42f6b1e58..13f3d4fa4 100644 --- a/README +++ b/README @@ -20,9 +20,10 @@ Execute: It will install z3 executable at /usr/bin, libraries at /usr/lib, and include files at /usr/include. You can change the installation p -Use the following commands to install in a different prefix (e.g., /home/leo). +Use the following commands to install in a different prefix (e.g., /home/leo), and the Z3 python +bindings in a different python package directory. - python scripts/mk_make.py --prefix=/home/leo + python scripts/mk_make.py --prefix=/home/leo --pydir=/home/leo/python cd build make sudo make install diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 4c74b41a5..7ac026673 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -3,6 +3,8 @@ RELEASE NOTES Version 4.3.2 ============= +- New parameter setting infrastructure. Now, it is possible to set parameter for Z3 internal modules. Several parameter names changed. Execute `z3 -p` for the new parameter list. + - Added get_version() and get_version_string() to Z3Py - Added support for FreeBSD. Z3 can be compiled on FreeBSD using g++. @@ -20,6 +22,10 @@ Version 4.3.2 - Fixed crash when parsing incorrect formulas. The crash was introduced when support for "arithmetic coercions" was added in Z3 4.3.0. +- Added new option to mk_make to allow users to specify where python bindings (Z3Py) will be installed. (Thanks to Dejan Jovanovic for reporting the problem). + +- Fixed crash reported at http://z3.codeplex.com/workitem/10 + Version 4.3.1 ============= diff --git a/doc/website.dox b/doc/website.dox index efbf71f56..6936b4f77 100644 --- a/doc/website.dox +++ b/doc/website.dox @@ -13,6 +13,7 @@ - \ref capi - \ref cppapi - .NET API + - Java API - Python API (also available in pydoc format). - Try Z3 online at RiSE4Fun using Python or SMT 2.0. */ diff --git a/doc/z3api.dox b/doc/z3api.dox index 07c7fe0dc..0233e6d83 100644 --- a/doc/z3api.dox +++ b/doc/z3api.dox @@ -687,6 +687,7 @@ WARN_LOGFILE = # with spaces. INPUT = ../src/api/dotnet \ + ../src/api/java \ ../src/api/c++ \ ./tmp @@ -740,7 +741,8 @@ FILE_PATTERNS = website.dox \ Util.cs \ Version.cs \ Z3Exception.cs \ - Z3Object.cs + Z3Object.cs \ + *.java # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 707d067e2..0ebcfe45d 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -58,11 +58,11 @@ def init_project_def(): add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('portfolio', ['smtlogic_tactics', 'ufbv_tactic', 'fpa', 'aig_tactic', 'muz_qe', 'sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('smtparser', ['portfolio'], 'parsers/smt') + API_files = ['z3_api.h', 'z3_algebraic.h', 'z3_polynomial.h'] add_lib('api', ['portfolio', 'user_plugin', 'smtparser'], - includes2install=['z3.h', 'z3_api.h', 'z3_v1.h', 'z3_macros.h']) + includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) add_exe('shell', ['api', 'sat', 'extra_cmds'], exe_name='z3') add_exe('test', ['api', 'fuzzing'], exe_name='test-z3', install=False) - API_files = ['z3_api.h'] add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll', reexports=['api'], dll_name='libz3', diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 98fae665a..381f92def 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -85,8 +85,9 @@ def is_linux(): def is_osx(): return IS_OSX -def unix_path2dos(path): - return string.join(path.split('/'), '\\') +def norm_path(p): + # We use '/' on mk_project for convenience + return os.path.join(*(p.split('/'))) def which(program): import os @@ -138,7 +139,9 @@ def exec_cmd(cmd): if e != "": se = e.split(' ') if len(se) > 1: - new_cmd.extend(se) + for e2 in se: + if e2 != "": + new_cmd.append(e2) else: new_cmd.append(e) cmd = new_cmd @@ -178,10 +181,18 @@ def test_c_compiler(cc): def test_gmp(cc): if is_verbose(): print "Testing GMP..." - t = TempFile('tst.cpp') + t = TempFile('tstgmp.cpp') t.add('#include\nint main() { mpz_t t; mpz_init(t); mpz_clear(t); return 0; }\n') t.commit() - return exec_compiler_cmd([cc, CPPFLAGS, CXXFLAGS, 'tst.cpp', LDFLAGS, '-lgmp']) == 0 + return exec_compiler_cmd([cc, CPPFLAGS, 'tstgmp.cpp', LDFLAGS, '-lgmp']) == 0 + +def test_openmp(cc): + if is_verbose(): + print "Testing OpenMP..." + t = TempFile('tstomp.cpp') + t.add('#include\nint main() { return omp_in_parallel() ? 1 : 0; }\n') + t.commit() + return exec_compiler_cmd([cc, CPPFLAGS, 'tstomp.cpp', LDFLAGS, '-fopenmp']) == 0 def check_java(): t = TempFile('Hello.java') @@ -200,12 +211,19 @@ def check_java(): raise MKException('Failed testing Java program. Set environment variable JAVA with the path to the Java virtual machine') find_java_home() +def find_jni_h(path): + for root, dirs, files in os.walk(path): + for f in files: + if f == 'jni.h': + return root + return False + def find_java_home(): global JAVA_HOME if JAVA_HOME != None: if is_verbose(): print "Checking jni.h..." - if os.path.exists('%s%sinclude%sjni.h' % (JAVA_HOME, os.sep, os.sep)): + if os.path.exists(os.path.join(JAVA_HOME, 'include', 'jni.h')): return if is_verbose(): print "Finding JAVA_HOME..." @@ -227,9 +245,12 @@ def find_java_home(): path = string.join(tmp[:len(tmp) - 3], os.sep) if is_verbose(): print "Checking jni.h..." - if not os.path.exists('%s%sinclude%sjni.h' % (path, os.sep, os.sep)): - raise MKException("Failed to detect jni.h at '%s%sinclude'" % (path, os.sep)) - JAVA_HOME = path + jni_dir = find_jni_h(path) + if not jni_dir: + raise MKException("Failed to detect jni.h at '%s'.Possible solution: set JAVA_HOME with the path to JDK." % os.path.join(path, 'include')) + JAVA_HOME = os.path.split(jni_dir)[0] + if is_verbose(): + print 'JAVA_HOME=%s' % JAVA_HOME return raise MKException('Failed to find JAVA_HOME') @@ -340,6 +361,7 @@ def display_help(exit_code): print " -s, --silent do not print verbose messages." if not IS_WINDOWS: print " -p , --prefix= installation prefix (default: %s)." % PREFIX + print " -y , --pydir= installation prefix for Z3 python bindings (default: %s)." % PYTHON_PACKAGE_DIR print " -b , --build= subdirectory where Z3 will be built (default: build)." print " -d, --debug compile Z3 in debug mode." print " -t, --trace enable tracing in release mode." @@ -370,52 +392,56 @@ def display_help(exit_code): # Parse configuration option for mk_make script def parse_options(): global VERBOSE, DEBUG_MODE, IS_WINDOWS, VS_X64, ONLY_MAKEFILES, SHOW_CPPS, VS_PROJ, TRACE - global DOTNET_ENABLED, JAVA_ENABLED, STATIC_LIB, PREFIX, GMP + global DOTNET_ENABLED, JAVA_ENABLED, STATIC_LIB, PREFIX, GMP, PYTHON_PACKAGE_DIR try: options, remainder = getopt.gnu_getopt(sys.argv[1:], - 'b:dsxhmcvtnp:gj', + 'b:dsxhmcvtnp:gjy:', ['build=', 'debug', 'silent', 'x64', 'help', 'makefiles', 'showcpp', 'vsproj', - 'trace', 'nodotnet', 'staticlib', 'prefix=', 'gmp', 'java']) - for opt, arg in options: - if opt in ('-b', '--build'): - if arg == 'src': - raise MKException('The src directory should not be used to host the Makefile') - set_build_dir(arg) - elif opt in ('-s', '--silent'): - VERBOSE = False - elif opt in ('-d', '--debug'): - DEBUG_MODE = True - elif opt in ('-x', '--x64'): - if not IS_WINDOWS: - raise MKException('x64 compilation mode can only be specified when using Visual Studio') - VS_X64 = True - elif opt in ('-h', '--help'): - display_help(0) - elif opt in ('-m', '--onlymakefiles'): - ONLY_MAKEFILES = True - elif opt in ('-c', '--showcpp'): - SHOW_CPPS = True - elif opt in ('-v', '--vsproj'): - VS_PROJ = True - elif opt in ('-t', '--trace'): - TRACE = True - elif opt in ('-n', '--nodotnet'): - DOTNET_ENABLED = False - elif opt in ('--staticlib'): - STATIC_LIB = True - elif opt in ('-p', '--prefix'): - PREFIX = arg - elif opt in ('-g', '--gmp'): - GMP = True - elif opt in ('-j', '--java'): - JAVA_ENABLED = True - else: - print "ERROR: Invalid command line option '%s'" % opt - display_help(1) + 'trace', 'nodotnet', 'staticlib', 'prefix=', 'gmp', 'java', 'pydir=']) except: print "ERROR: Invalid command line option" display_help(1) + for opt, arg in options: + if opt in ('-b', '--build'): + if arg == 'src': + raise MKException('The src directory should not be used to host the Makefile') + set_build_dir(arg) + elif opt in ('-s', '--silent'): + VERBOSE = False + elif opt in ('-d', '--debug'): + DEBUG_MODE = True + elif opt in ('-x', '--x64'): + if not IS_WINDOWS: + raise MKException('x64 compilation mode can only be specified when using Visual Studio') + VS_X64 = True + elif opt in ('-h', '--help'): + display_help(0) + elif opt in ('-m', '--onlymakefiles'): + ONLY_MAKEFILES = True + elif opt in ('-c', '--showcpp'): + SHOW_CPPS = True + elif opt in ('-v', '--vsproj'): + VS_PROJ = True + elif opt in ('-t', '--trace'): + TRACE = True + elif opt in ('-n', '--nodotnet'): + DOTNET_ENABLED = False + elif opt in ('--staticlib'): + STATIC_LIB = True + elif opt in ('-p', '--prefix'): + PREFIX = arg + elif opt in ('-y', '--pydir'): + PYTHON_PACKAGE_DIR = arg + mk_dir(PYTHON_PACKAGE_DIR) + elif opt in ('-g', '--gmp'): + GMP = True + elif opt in ('-j', '--java'): + JAVA_ENABLED = True + else: + print "ERROR: Invalid command line option '%s'" % opt + display_help(1) + # Return a list containing a file names included using '#include' in # the given C/C++ file named fname. def extract_c_includes(fname): @@ -440,11 +466,11 @@ def extract_c_includes(fname): # Given a path dir1/subdir2/subdir3 returns ../../.. def reverse_path(p): - l = p.split('/') + l = p.split(os.sep) n = len(l) r = '..' for i in range(1, n): - r = '%s/%s' % (r, '..') + r = os.path.join(r, '..') return r def mk_dir(d): @@ -453,12 +479,13 @@ def mk_dir(d): def set_build_dir(d): global BUILD_DIR, REV_BUILD_DIR - BUILD_DIR = d + BUILD_DIR = norm_path(d) REV_BUILD_DIR = reverse_path(d) def set_z3py_dir(p): global SRC_DIR, Z3PY_SRC_DIR - full = '%s/%s' % (SRC_DIR, p) + p = norm_path(p) + full = os.path.join(SRC_DIR, p) if not os.path.exists(full): raise MKException("Python bindings directory '%s' does not exist" % full) Z3PY_SRC_DIR = full @@ -556,22 +583,23 @@ class Component: if path == None: path = name self.name = name + path = norm_path(path) self.path = path self.deps = find_all_deps(name, deps) self.build_dir = path - self.src_dir = '%s/%s' % (SRC_DIR, path) - self.to_src_dir = '%s/%s' % (REV_BUILD_DIR, self.src_dir) + self.src_dir = os.path.join(SRC_DIR, path) + self.to_src_dir = os.path.join(REV_BUILD_DIR, self.src_dir) # Find fname in the include paths for the given component. # ownerfile is only used for creating error messages. # That is, we were looking for fname when processing ownerfile def find_file(self, fname, ownerfile): - full_fname = '%s/%s' % (self.src_dir, fname) + full_fname = os.path.join(self.src_dir, fname) if os.path.exists(full_fname): return self for dep in self.deps: c_dep = get_component(dep) - full_fname = '%s/%s' % (c_dep.src_dir, fname) + full_fname = os.path.join(c_dep.src_dir, fname) if os.path.exists(full_fname): return c_dep raise MKException("Failed to find include file '%s' for '%s' when processing '%s'." % (fname, ownerfile, self.name)) @@ -579,15 +607,15 @@ class Component: # Display all dependencies of file basename located in the given component directory. # The result is displayed at out def add_cpp_h_deps(self, out, basename): - includes = extract_c_includes('%s/%s' % (self.src_dir, basename)) - out.write('%s/%s' % (self.to_src_dir, basename)) + includes = extract_c_includes(os.path.join(self.src_dir, basename)) + out.write(os.path.join(self.to_src_dir, basename)) for include in includes: owner = self.find_file(include, basename) - out.write(' %s/%s.node' % (owner.build_dir, include)) + out.write(' %s.node' % os.path.join(owner.build_dir, include)) # Add a rule for each #include directive in the file basename located at the current component. def add_rule_for_each_include(self, out, basename): - fullname = '%s/%s' % (self.src_dir, basename) + fullname = os.path.join(self.src_dir, basename) includes = extract_c_includes(fullname) for include in includes: owner = self.find_file(include, fullname) @@ -599,12 +627,12 @@ class Component: # ast/ast_pp.h.node : ../src/util/ast_pp.h util/util.h.node ast/ast.h.node # @echo "done" > ast/ast_pp.h.node def add_h_rule(self, out, include): - include_src_path = '%s/%s' % (self.to_src_dir, include) + include_src_path = os.path.join(self.to_src_dir, include) if include_src_path in _Processed_Headers: return _Processed_Headers.add(include_src_path) self.add_rule_for_each_include(out, include) - include_node = '%s/%s.node' % (self.build_dir, include) + include_node = '%s.node' % os.path.join(self.build_dir, include) out.write('%s: ' % include_node) self.add_cpp_h_deps(out, include) out.write('\n') @@ -612,13 +640,13 @@ class Component: def add_cpp_rules(self, out, include_defs, cppfile): self.add_rule_for_each_include(out, cppfile) - objfile = '%s/%s$(OBJ_EXT)' % (self.build_dir, os.path.splitext(cppfile)[0]) - srcfile = '%s/%s' % (self.to_src_dir, cppfile) + objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) + srcfile = os.path.join(self.to_src_dir, cppfile) out.write('%s: ' % objfile) self.add_cpp_h_deps(out, cppfile) out.write('\n') if SHOW_CPPS: - out.write('\t@echo %s/%s\n' % (self.src_dir, cppfile)) + out.write('\t@echo %s\n' % os.path.join(self.src_dir, cppfile)) out.write('\t@$(CXX) $(CXXFLAGS) $(%s) $(CXX_OUT_FLAG)%s %s\n' % (include_defs, objfile, srcfile)) def mk_makefile(self, out): @@ -627,7 +655,7 @@ class Component: for dep in self.deps: out.write(' -I%s' % get_component(dep).to_src_dir) out.write('\n') - mk_dir('%s/%s' % (BUILD_DIR, self.build_dir)) + mk_dir(os.path.join(BUILD_DIR, self.build_dir)) for cppfile in get_cpp_files(self.src_dir): self.add_cpp_rules(out, include_defs, cppfile) @@ -676,10 +704,10 @@ class LibComponent(Component): # generate rule for lib objs = [] for cppfile in get_cpp_files(self.src_dir): - objfile = '%s/%s$(OBJ_EXT)' % (self.build_dir, os.path.splitext(cppfile)[0]) + objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) - libfile = '%s/%s$(LIB_EXT)' % (self.build_dir, self.name) + libfile = '%s$(LIB_EXT)' % os.path.join(self.build_dir, self.name) out.write('%s:' % libfile) for obj in objs: out.write(' ') @@ -694,17 +722,17 @@ class LibComponent(Component): def mk_install(self, out): for include in self.includes2install: - out.write('\t@cp %s/%s $(PREFIX)/include/%s\n' % (self.to_src_dir, include, include)) + out.write('\t@cp %s %s\n' % (os.path.join(self.to_src_dir, include), os.path.join('$(PREFIX)', 'include', include))) def mk_uninstall(self, out): for include in self.includes2install: - out.write('\t@rm -f $(PREFIX)/include/%s\n' % include) + out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'include', include)) def mk_win_dist(self, build_path, dist_path): - mk_dir('%s/include' % dist_path) + mk_dir(os.path.join(dist_path, 'include')) for include in self.includes2install: - shutil.copy('%s/%s' % (self.src_dir, include), - '%s/include/%s' % (dist_path, include)) + shutil.copy(os.path.join(self.src_dir, include), + os.path.join(dist_path, 'include', include)) # "Library" containing only .h files. This is just a placeholder for includes files to be installed. class HLibComponent(LibComponent): @@ -741,14 +769,14 @@ class ExeComponent(Component): deps = sort_components(self.deps) objs = [] for cppfile in get_cpp_files(self.src_dir): - objfile = '%s/%s$(OBJ_EXT)' % (self.build_dir, os.path.splitext(cppfile)[0]) + objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) for obj in objs: out.write(' ') out.write(obj) for dep in deps: c_dep = get_component(dep) - out.write(' %s/%s$(LIB_EXT)' % (c_dep.build_dir, c_dep.name)) + out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name)) out.write('\n') out.write('\t$(LINK) $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % exefile) for obj in objs: @@ -756,7 +784,7 @@ class ExeComponent(Component): out.write(obj) for dep in deps: c_dep = get_component(dep) - out.write(' %s/%s$(LIB_EXT)' % (c_dep.build_dir, c_dep.name)) + out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name)) out.write(' $(LINK_EXTRA_FLAGS)\n') out.write('%s: %s\n\n' % (self.name, exefile)) @@ -773,17 +801,17 @@ class ExeComponent(Component): def mk_install(self, out): if self.install: exefile = '%s$(EXE_EXT)' % self.exe_name - out.write('\t@cp %s $(PREFIX)/bin/%s\n' % (exefile, exefile)) + out.write('\t@cp %s %s\n' % (exefile, os.path.join('$(PREFIX)', 'bin', exefile))) def mk_uninstall(self, out): exefile = '%s$(EXE_EXT)' % self.exe_name - out.write('\t@rm -f $(PREFIX)/bin/%s\n' % exefile) + out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'bin', exefile)) def mk_win_dist(self, build_path, dist_path): if self.install: - mk_dir('%s/bin' % dist_path) - shutil.copy('%s/%s.exe' % (build_path, self.exe_name), - '%s/bin/%s.exe' % (dist_path, self.exe_name)) + mk_dir(os.path.join(dist_path, 'bin')) + shutil.copy('%s.exe' % os.path.join(build_path, self.exe_name), + '%s.exe' % os.path.join(dist_path, 'bin', self.exe_name)) class ExtraExeComponent(ExeComponent): @@ -812,13 +840,13 @@ class DLLComponent(Component): deps = sort_components(self.deps) objs = [] for cppfile in get_cpp_files(self.src_dir): - objfile = '%s/%s$(OBJ_EXT)' % (self.build_dir, os.path.splitext(cppfile)[0]) + objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) # Explicitly include obj files of reexport. This fixes problems with exported symbols on Linux and OSX. for reexport in self.reexports: reexport = get_component(reexport) for cppfile in get_cpp_files(reexport.src_dir): - objfile = '%s/%s$(OBJ_EXT)' % (reexport.build_dir, os.path.splitext(cppfile)[0]) + objfile = '%s$(OBJ_EXT)' % os.path.join(reexport.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) for obj in objs: out.write(' ') @@ -826,7 +854,7 @@ class DLLComponent(Component): for dep in deps: if not dep in self.reexports: c_dep = get_component(dep) - out.write(' %s/%s$(LIB_EXT)' % (c_dep.build_dir, c_dep.name)) + out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name)) out.write('\n') out.write('\t$(LINK) $(SLINK_OUT_FLAG)%s $(SLINK_FLAGS)' % dllfile) for obj in objs: @@ -835,10 +863,10 @@ class DLLComponent(Component): for dep in deps: if not dep in self.reexports: c_dep = get_component(dep) - out.write(' %s/%s$(LIB_EXT)' % (c_dep.build_dir, c_dep.name)) + out.write(' %s$(LIB_EXT)' % os.path.join(c_dep.build_dir, c_dep.name)) out.write(' $(SLINK_EXTRA_FLAGS)') if IS_WINDOWS: - out.write(' /DEF:%s/%s.def' % (self.to_src_dir, self.name)) + out.write(' /DEF:%s.def' % os.path.join(self.to_src_dir, self.name)) out.write('\n') if self.static: self.mk_static(out) @@ -851,13 +879,13 @@ class DLLComponent(Component): # generate rule for lib objs = [] for cppfile in get_cpp_files(self.src_dir): - objfile = '%s/%s$(OBJ_EXT)' % (self.build_dir, os.path.splitext(cppfile)[0]) + objfile = '%s$(OBJ_EXT)' % os.path.join(self.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) # we have to "reexport" all object files for dep in self.deps: dep = get_component(dep) for cppfile in get_cpp_files(dep.src_dir): - objfile = '%s/%s$(OBJ_EXT)' % (dep.build_dir, os.path.splitext(cppfile)[0]) + objfile = '%s$(OBJ_EXT)' % os.path.join(dep.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) libfile = '%s$(LIB_EXT)' % self.dll_name out.write('%s:' % libfile) @@ -886,29 +914,29 @@ class DLLComponent(Component): def mk_install(self, out): if self.install: dllfile = '%s$(SO_EXT)' % self.dll_name - out.write('\t@cp %s $(PREFIX)/lib/%s\n' % (dllfile, dllfile)) - out.write('\t@cp %s %s/%s\n' % (dllfile, PYTHON_PACKAGE_DIR, dllfile)) + out.write('\t@cp %s %s\n' % (dllfile, os.path.join('$(PREFIX)', 'lib', dllfile))) + out.write('\t@cp %s %s\n' % (dllfile, os.path.join(PYTHON_PACKAGE_DIR, dllfile))) if self.static: libfile = '%s$(LIB_EXT)' % self.dll_name - out.write('\t@cp %s $(PREFIX)/lib/%s\n' % (libfile, libfile)) + out.write('\t@cp %s %s\n' % (libfile, os.path.join('$(PREFIX)', 'lib', libfile))) def mk_uninstall(self, out): dllfile = '%s$(SO_EXT)' % self.dll_name - out.write('\t@rm -f $(PREFIX)/lib/%s\n' % dllfile) - out.write('\t@rm -f %s/%s\n' % (PYTHON_PACKAGE_DIR, dllfile)) + out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'lib', dllfile)) + out.write('\t@rm -f %s\n' % os.path.join(PYTHON_PACKAGE_DIR, dllfile)) libfile = '%s$(LIB_EXT)' % self.dll_name - out.write('\t@rm -f $(PREFIX)/lib/%s\n' % libfile) + out.write('\t@rm -f %s\n' % os.path.join('$(PREFIX)', 'lib', libfile)) def mk_win_dist(self, build_path, dist_path): if self.install: - mk_dir('%s/bin' % dist_path) - shutil.copy('%s/%s.dll' % (build_path, self.dll_name), - '%s/bin/%s.dll' % (dist_path, self.dll_name)) + mk_dir(os.path.join(dist_path, 'bin')) + shutil.copy('%s.dll' % os.path.join(build_path, self.dll_name), + '%s.dll' % os.path.join(dist_path, 'bin', self.dll_name)) if self.static: - mk_dir('%s/bin' % dist_path) - shutil.copy('%s/%s.lib' % (build_path, self.dll_name), - '%s/bin/%s.lib' % (dist_path, self.dll_name)) + mk_dir(os.path.join(dist_path, 'bin')) + shutil.copy('%s.lib' % os.path.join(build_path, self.dll_name), + '%s.lib' % os.path.join(dist_path, 'bin', self.dll_name)) class DotNetDLLComponent(Component): def __init__(self, name, dll_name, path, deps, assembly_info_dir): @@ -925,12 +953,12 @@ class DotNetDLLComponent(Component): cs_fp_files = [] cs_files = [] for cs_file in get_cs_files(self.src_dir): - cs_fp_files.append('%s/%s' % (self.to_src_dir, cs_file)) + cs_fp_files.append(os.path.join(self.to_src_dir, cs_file)) cs_files.append(cs_file) if self.assembly_info_dir != '.': - for cs_file in get_cs_files('%s/%s' % (self.src_dir, self.assembly_info_dir)): - cs_fp_files.append('%s/%s/%s' % (self.to_src_dir, self.assembly_info_dir, cs_file)) - cs_files.append('%s\%s' % (self.assembly_info_dir, cs_file)) + for cs_file in get_cs_files(os.path.join(self.src_dir, self.assembly_info_dir)): + cs_fp_files.append(os.path.join(self.to_src_dir, self.assembly_info_dir, cs_file)) + cs_files.append(os.path.join(self.assembly_info_dir, cs_file)) dllfile = '%s.dll' % self.dll_name out.write('%s:' % dllfile) for cs_file in cs_fp_files: @@ -944,8 +972,8 @@ class DotNetDLLComponent(Component): out.write('\n') # HACK win_to_src_dir = self.to_src_dir.replace('/', '\\') - out.write(' move %s\%s\n' % (win_to_src_dir, dllfile)) - out.write(' move %s\%s.pdb\n' % (win_to_src_dir, self.dll_name)) + out.write(' move %s\n' % os.path.join(win_to_src_dir, dllfile)) + out.write(' move %s.pdb\n' % os.path.join(win_to_src_dir, self.dll_name)) out.write('%s: %s\n\n' % (self.name, dllfile)) return @@ -958,9 +986,9 @@ class DotNetDLLComponent(Component): def mk_win_dist(self, build_path, dist_path): if DOTNET_ENABLED: # Assuming all DotNET dll should be in the distribution - mk_dir('%s/bin' % dist_path) - shutil.copy('%s/%s.dll' % (build_path, self.dll_name), - '%s/bin/%s.dll' % (dist_path, self.dll_name)) + mk_dir(os.path.join(dist_path, 'bin')) + shutil.copy('%s.dll' % os.path.join(build_path, self.dll_name), + '%s.dll' % os.path.join(dist_path, 'bin', self.dll_name)) class JavaDLLComponent(Component): @@ -974,9 +1002,9 @@ class JavaDLLComponent(Component): def mk_makefile(self, out): if is_java_enabled(): - mk_dir(BUILD_DIR+'/api/java/classes') + mk_dir(os.path.join(BUILD_DIR, 'api', 'java', 'classes')) dllfile = '%s$(SO_EXT)' % self.dll_name - out.write('libz3java$(SO_EXT): libz3$(SO_EXT) %s/Native.cpp\n' % self.to_src_dir) + out.write('libz3java$(SO_EXT): libz3$(SO_EXT) %s\n' % os.path.join(self.to_src_dir, 'Native.cpp')) t = '\t$(CXX) $(CXXFLAGS) $(CXX_OUT_FLAG)api/java/Native$(OBJ_EXT) -I"%s/include" -I"%s/include/PLATFORM" -I%s %s/Native.cpp\n' % (JAVA_HOME, JAVA_HOME, get_component('api').to_src_dir, self.to_src_dir) if IS_OSX: t = t.replace('PLATFORM', 'darwin') @@ -986,25 +1014,29 @@ class JavaDLLComponent(Component): t = t.replace('PLATFORM', 'win32') out.write(t) if IS_WINDOWS: # On Windows, CL creates a .lib file to link against. - out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) api/java/Native$(OBJ_EXT) libz3$(LIB_EXT)\n') + out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(LIB_EXT)\n' % + os.path.join('api', 'java', 'Native')) else: - out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) api/java/Native$(OBJ_EXT) libz3$(SO_EXT)\n') + out.write('\t$(SLINK) $(SLINK_OUT_FLAG)libz3java$(SO_EXT) $(SLINK_FLAGS) %s$(OBJ_EXT) libz3$(SO_EXT)\n' % + os.path.join('api', 'java', 'Native')) out.write('%s.jar: libz3java$(SO_EXT) ' % self.package_name) deps = '' for jfile in get_java_files(self.src_dir): - deps += ('%s/%s ' % (self.to_src_dir, jfile)) - for jfile in get_java_files((self.src_dir + "/enumerations")): - deps += ('%s/enumerations/%s ' % (self.to_src_dir, jfile)) - if IS_WINDOWS: deps = deps.replace('/', '\\') + deps += ('%s ' % os.path.join(self.to_src_dir, jfile)) + for jfile in get_java_files(os.path.join(self.src_dir, "enumerations")): + deps += '%s ' % os.path.join(self.to_src_dir, 'enumerations', jfile) out.write(deps) out.write('\n') - t = ('\t%s %s/enumerations/*.java -d api/java/classes\n' % (JAVAC, self.to_src_dir)) - if IS_WINDOWS: t = t.replace('/','\\') + t = ('\t%s %s.java -d %s\n' % (JAVAC, os.path.join(self.to_src_dir, 'enumerations', '*'), os.path.join('api', 'java', 'classes'))) out.write(t) - t = ('\t%s -cp api/java/classes %s/*.java -d api/java/classes\n' % (JAVAC, self.to_src_dir)) - if IS_WINDOWS: t = t.replace('/','\\') + t = ('\t%s -cp %s %s.java -d %s\n' % (JAVAC, + os.path.join('api', 'java', 'classes'), + os.path.join(self.to_src_dir, '*'), + os.path.join('api', 'java', 'classes'))) out.write(t) - out.write('\tjar cfm %s.jar %s/manifest -C api/java/classes .\n' % (self.package_name, self.to_src_dir)) + out.write('\tjar cfm %s.jar %s -C %s .\n' % (self.package_name, + os.path.join(self.to_src_dir, 'manifest'), + os.path.join('api', 'java', 'classes'))) out.write('java: %s.jar\n\n' % self.package_name) def main_component(self): @@ -1014,8 +1046,8 @@ class JavaDLLComponent(Component): class ExampleComponent(Component): def __init__(self, name, path): Component.__init__(self, name, path, []) - self.ex_dir = '%s/%s' % (EXAMPLE_DIR, self.path) - self.to_ex_dir = '%s/%s' % (REV_BUILD_DIR, self.ex_dir) + self.ex_dir = os.path.join(EXAMPLE_DIR, self.path) + self.to_ex_dir = os.path.join(REV_BUILD_DIR, self.ex_dir) def is_example(self): return True @@ -1038,7 +1070,7 @@ class CppExampleComponent(ExampleComponent): out.write('%s: %s' % (exefile, dll)) for cppfile in self.src_files(): out.write(' ') - out.write('%s/%s' % (self.to_ex_dir, cppfile)) + out.write(os.path.join(self.to_ex_dir, cppfile)) out.write('\n') out.write('\t%s $(LINK_OUT_FLAG)%s $(LINK_FLAGS)' % (self.compiler(), exefile)) # Add include dir components @@ -1046,7 +1078,7 @@ class CppExampleComponent(ExampleComponent): out.write(' -I%s' % get_component(CPP_COMPONENT).to_src_dir) for cppfile in self.src_files(): out.write(' ') - out.write('%s/%s' % (self.to_ex_dir, cppfile)) + out.write(os.path.join(self.to_ex_dir, cppfile)) out.write(' ') if IS_WINDOWS: out.write('%s.lib' % dll_name) @@ -1080,7 +1112,7 @@ class DotNetExampleComponent(ExampleComponent): out.write('%s: %s' % (exefile, dll)) for csfile in get_cs_files(self.ex_dir): out.write(' ') - out.write('%s/%s' % (self.to_ex_dir, csfile)) + out.write(os.path.join(self.to_ex_dir, csfile)) out.write('\n') out.write('\tcsc /out:%s /reference:%s /debug:full /reference:System.Numerics.dll' % (exefile, dll)) if VS_X64: @@ -1091,7 +1123,7 @@ class DotNetExampleComponent(ExampleComponent): out.write(' ') # HACK win_ex_dir = self.to_ex_dir.replace('/', '\\') - out.write('%s\\%s' % (win_ex_dir, csfile)) + out.write(os.path.join(win_ex_dir, csfile)) out.write('\n') out.write('_ex_%s: %s\n\n' % (self.name, exefile)) @@ -1108,7 +1140,7 @@ class JavaExampleComponent(ExampleComponent): out.write('_ex_%s: %s' % (self.name, pkg)) deps = '' for jfile in get_java_files(self.ex_dir): - out.write(' %s/%s' % (self.to_ex_dir, jfile)) + out.write(' %s' % os.path.join(self.to_ex_dir, jfile)) if IS_WINDOWS: deps = deps.replace('/', '\\') out.write('%s\n' % deps) @@ -1116,7 +1148,7 @@ class JavaExampleComponent(ExampleComponent): win_ex_dir = self.to_ex_dir for javafile in get_java_files(self.ex_dir): out.write(' ') - out.write('%s/%s' % (win_ex_dir, javafile)) + out.write(os.path.join(win_ex_dir, javafile)) out.write(' -d .\n\n') class PythonExampleComponent(ExampleComponent): @@ -1126,9 +1158,9 @@ class PythonExampleComponent(ExampleComponent): # Python examples are just placeholders, we just copy the *.py files when mk_makefile is invoked. # We don't need to include them in the :examples rule def mk_makefile(self, out): - full = '%s/%s' % (EXAMPLE_DIR, self.path) + full = os.path.join(EXAMPLE_DIR, self.path) for py in filter(lambda f: f.endswith('.py'), os.listdir(full)): - shutil.copyfile('%s/%s' % (full, py), '%s/%s' % (BUILD_DIR, py)) + shutil.copyfile(os.path.join(full, py), os.path.join(BUILD_DIR, py)) if is_verbose(): print "Copied Z3Py example '%s' to '%s'" % (py, BUILD_DIR) out.write('_ex_%s: \n\n' % self.name) @@ -1195,7 +1227,7 @@ def add_z3py_example(name, path=None): def mk_config(): if ONLY_MAKEFILES: return - config = open('%s/config.mk' % BUILD_DIR, 'w') + config = open(os.path.join(BUILD_DIR, 'config.mk'), 'w') if IS_WINDOWS: config.write( 'CC=cl\n' @@ -1268,22 +1300,26 @@ def mk_config(): else: CPPFLAGS = '%s -D_MP_INTERNAL' % CPPFLAGS CXXFLAGS = '%s -c' % CXXFLAGS - if is_CXX_gpp(): + HAS_OMP = test_openmp(CXX) + if HAS_OMP: CXXFLAGS = '%s -fopenmp -mfpmath=sse' % CXXFLAGS LDFLAGS = '%s -fopenmp' % LDFLAGS SLIBEXTRAFLAGS = '-fopenmp' else: - # CLang does not support OMP CXXFLAGS = '%s -D_NO_OMP_' % CXXFLAGS SLIBEXTRAFLAGS = '' + if DEBUG_MODE: + CXXFLAGS = '%s -g -Wall' % CXXFLAGS + else: + CXXFLAGS = '%s -O3 -D _EXTERNAL_RELEASE -fomit-frame-pointer' % CXXFLAGS + if is_CXX_clangpp(): + CXXFLAGS = '%s -Wno-unknown-pragmas -Wno-overloaded-virtual -Wno-unused-value' % CXXFLAGS sysname = os.uname()[0] if sysname == 'Darwin': SO_EXT = '.dylib' SLIBFLAGS = '-dynamiclib' elif sysname == 'Linux': CXXFLAGS = '%s -fno-strict-aliasing -D_LINUX_' % CXXFLAGS - if is_CXX_clangpp(): - CXXFLAGS = '%s -Wno-unknown-pragmas -Wno-overloaded-virtual -Wno-unused-value' % CXXFLAGS SO_EXT = '.so' LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' @@ -1309,10 +1345,6 @@ def mk_config(): CPPFLAGS = '%s -DZ3DEBUG' % CPPFLAGS if TRACE or DEBUG_MODE: CPPFLAGS = '%s -D_TRACE' % CPPFLAGS - if DEBUG_MODE: - CXXFLAGS = '%s -g -Wall' % CXXFLAGS - else: - CXXFLAGS = '%s -O3 -D _EXTERNAL_RELEASE -fomit-frame-pointer' % CXXFLAGS CXXFLAGS = '%s -msse -msse2' % CXXFLAGS config.write('PREFIX=%s\n' % PREFIX) config.write('CC=%s\n' % CC) @@ -1339,6 +1371,7 @@ def mk_config(): print 'C++ Compiler: %s' % CXX print 'C Compiler : %s' % CC print 'Arithmetic: %s' % ARITH + print 'OpenMP: %s' % HAS_OMP print 'Prefix: %s' % PREFIX print '64-bit: %s' % is64() if is_java_enabled(): @@ -1348,9 +1381,9 @@ def mk_config(): def mk_install(out): out.write('install:\n') - out.write('\t@mkdir -p $(PREFIX)/bin\n') - out.write('\t@mkdir -p $(PREFIX)/include\n') - out.write('\t@mkdir -p $(PREFIX)/lib\n') + out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'bin')) + out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'include')) + out.write('\t@mkdir -p %s\n' % os.path.join('$(PREFIX)', 'lib')) for c in get_components(): c.mk_install(out) out.write('\t@cp z3*.pyc %s\n' % PYTHON_PACKAGE_DIR) @@ -1361,7 +1394,7 @@ def mk_uninstall(out): out.write('uninstall:\n') for c in get_components(): c.mk_uninstall(out) - out.write('\t@rm -f %s/z3*.pyc\n' % PYTHON_PACKAGE_DIR) + out.write('\t@rm -f %s*.pyc\n' % os.path.join(PYTHON_PACKAGE_DIR, 'z3')) out.write('\t@echo Z3 was successfully uninstalled.\n') out.write('\n') @@ -1370,9 +1403,9 @@ def mk_makefile(): mk_dir(BUILD_DIR) mk_config() if VERBOSE: - print "Writing %s/Makefile" % BUILD_DIR - out = open('%s/Makefile' % BUILD_DIR, 'w') - out.write('# Automatically generated file. Generator: scripts/mk_make.py\n') + print "Writing %s" % os.path.join(BUILD_DIR, 'Makefile') + out = open(os.path.join(BUILD_DIR, 'Makefile'), 'w') + out.write('# Automatically generated file.\n') out.write('include config.mk\n') # Generate :all rule out.write('all:') @@ -1412,7 +1445,7 @@ def mk_makefile(): else: print " platform: x86" print "To build Z3, open a [Visual Studio Command Prompt], then" - print "type 'cd %s/%s && nmake'\n" % (os.getcwd(), BUILD_DIR) + print "type 'cd %s && nmake'\n" % os.path.join(os.getcwd(), BUILD_DIR) print 'Remark: to open a Visual Studio Command Prompt, go to: "Start > All Programs > Visual Studio > Visual Studio Tools"' else: print "Type 'cd %s; make' to build Z3" % BUILD_DIR @@ -1459,7 +1492,10 @@ def pyg_default_as_c_literal(p): return '"%s"' % p[2] elif p[1] == SYMBOL: return 'symbol("%s")' % p[2] - return p[2] + elif p[1] == UINT: + return '%su' % p[2] + else: + return p[2] def to_c_method(s): return s.replace('.', '_') @@ -1472,6 +1508,8 @@ def def_module_params(module_name, export, params, class_name=None, description= hpp = os.path.join(dirname, '%s.hpp' % class_name) out = open(hpp, 'w') out.write('// Automatically generated file\n') + out.write('#ifndef __%s_HPP_\n' % class_name.upper()) + out.write('#define __%s_HPP_\n' % class_name.upper()) out.write('#include"params.h"\n') if export: out.write('#include"gparams.h"\n') @@ -1479,7 +1517,7 @@ def def_module_params(module_name, export, params, class_name=None, description= out.write(' params_ref const & p;\n') if export: out.write(' params_ref g;\n') - out.write(' %s(params_ref const & _p = params_ref()):\n' % class_name) + out.write(' %s(params_ref const & _p = params_ref::get_empty()):\n' % class_name) out.write(' p(_p)') if export: out.write(', g(gparams::get_module("%s"))' % module_name) @@ -1503,6 +1541,7 @@ def def_module_params(module_name, export, params, class_name=None, description= out.write(' %s %s() const { return p.%s("%s", %s); }\n' % (TYPE2CTYPE[param[1]], to_c_method(param[0]), TYPE2GETTER[param[1]], param[0], pyg_default_as_c_literal(param))) out.write('};\n') + out.write('#endif\n') if is_verbose(): print "Generated '%s'" % hpp @@ -1532,14 +1571,14 @@ def exec_pyg_scripts(): # database.smt ==> database.h def mk_pat_db(): c = get_component(PATTERN_COMPONENT) - fin = open('%s/database.smt2' % c.src_dir, 'r') - fout = open('%s/database.h' % c.src_dir, 'w') + fin = open(os.path.join(c.src_dir, 'database.smt2'), 'r') + fout = open(os.path.join(c.src_dir, 'database.h'), 'w') fout.write('char const * g_pattern_database =\n') for line in fin: fout.write('"%s\\n"\n' % line.strip('\n')) fout.write(';\n') if VERBOSE: - print "Generated '%s/database.h'" % c.src_dir + print "Generated '%s'" % os.path.join(c.src_dir, 'database.h') # Update version numbers def update_version(): @@ -1557,32 +1596,32 @@ def update_version(): # Update files with the version number def mk_version_dot_h(major, minor, build, revision): c = get_component(UTIL_COMPONENT) - fout = open('%s/version.h' % c.src_dir, 'w') + fout = open(os.path.join(c.src_dir, 'version.h'), 'w') fout.write('// automatically generated file.\n') fout.write('#define Z3_MAJOR_VERSION %s\n' % major) fout.write('#define Z3_MINOR_VERSION %s\n' % minor) fout.write('#define Z3_BUILD_NUMBER %s\n' % build) fout.write('#define Z3_REVISION_NUMBER %s\n' % revision) if VERBOSE: - print "Generated '%s/version.h'" % c.src_dir + print "Generated '%s'" % os.path.join(c.src_dir, 'version.h') # Update version number in AssemblyInfo.cs files def update_all_assembly_infos(major, minor, build, revision): for c in get_components(): if c.has_assembly_info(): - assembly = '%s/%s/AssemblyInfo.cs' % (c.src_dir, c.assembly_info_dir) + assembly = os.path.join(c.src_dir, c.assembly_info_dir, 'AssemblyInfo.cs') if os.path.exists(assembly): # It is a CS file update_assembly_info_version(assembly, major, minor, build, revision, False) else: - assembly = '%s/%s/AssemblyInfo.cpp' % (c.src_dir, c.assembly_info_dir) + assembly = os.path.join(c.src_dir, c.assembly_info_dir, 'AssemblyInfo.cs') if os.path.exists(assembly): # It is a cpp file update_assembly_info_version(assembly, major, minor, build, revision, True) else: - raise MKException("Failed to find assembly info file at '%s/%s'" % (c.src_dir, c.assembly_info_dir)) + raise MKException("Failed to find assembly info file at '%s'" % os.path.join(c.src_dir, c.assembly_info_dir)) # Update version number in the given AssemblyInfo.cs files @@ -1641,7 +1680,7 @@ def mk_install_tactic_cpp(cnames, path): global ADD_TACTIC_DATA, ADD_PROBE_DATA ADD_TACTIC_DATA = [] ADD_PROBE_DATA = [] - fullname = '%s/install_tactic.cpp' % path + fullname = os.path.join(path, 'install_tactic.cpp') fout = open(fullname, 'w') fout.write('// Automatically generated file.\n') fout.write('#include"tactic.h"\n') @@ -1654,7 +1693,7 @@ def mk_install_tactic_cpp(cnames, path): h_files = filter(lambda f: f.endswith('.h') or f.endswith('.hpp'), os.listdir(c.src_dir)) for h_file in h_files: added_include = False - fin = open("%s/%s" % (c.src_dir, h_file), 'r') + fin = open(os.path.join(c.src_dir, h_file), 'r') for line in fin: if tactic_pat.match(line): if not added_include: @@ -1707,7 +1746,7 @@ def mk_all_install_tactic_cpps(): def mk_mem_initializer_cpp(cnames, path): initializer_cmds = [] finalizer_cmds = [] - fullname = '%s/mem_initializer.cpp' % path + fullname = os.path.join(path, 'mem_initializer.cpp') fout = open(fullname, 'w') fout.write('// Automatically generated file.\n') initializer_pat = re.compile('[ \t]*ADD_INITIALIZER\(\'([^\']*)\'\)') @@ -1719,7 +1758,7 @@ def mk_mem_initializer_cpp(cnames, path): h_files = filter(lambda f: f.endswith('.h') or f.endswith('.hpp'), os.listdir(c.src_dir)) for h_file in h_files: added_include = False - fin = open("%s/%s" % (c.src_dir, h_file), 'r') + fin = open(os.path.join(c.src_dir, h_file), 'r') for line in fin: m = initializer_pat.match(line) if m: @@ -1770,7 +1809,7 @@ def mk_gparams_register_modules(cnames, path): cmds = [] mod_cmds = [] mod_descrs = [] - fullname = '%s/gparams_register_modules.cpp' % path + fullname = os.path.join(path, 'gparams_register_modules.cpp') fout = open(fullname, 'w') fout.write('// Automatically generated file.\n') fout.write('#include"gparams.h"\n') @@ -1782,7 +1821,7 @@ def mk_gparams_register_modules(cnames, path): h_files = filter(lambda f: f.endswith('.h') or f.endswith('.hpp'), os.listdir(c.src_dir)) for h_file in h_files: added_include = False - fin = open("%s/%s" % (c.src_dir, h_file), 'r') + fin = open(os.path.join(c.src_dir, h_file), 'r') for line in fin: m = reg_pat.match(line) if m: @@ -1822,13 +1861,13 @@ def mk_all_gparams_register_modules(): # Generate a .def based on the files at c.export_files slot. def mk_def_file(c): pat1 = re.compile(".*Z3_API.*") - defname = '%s/%s.def' % (c.src_dir, c.name) + defname = '%s.def' % os.path.join(c.src_dir, c.name) fout = open(defname, 'w') fout.write('LIBRARY "%s"\nEXPORTS\n' % c.dll_name) num = 1 for dot_h in c.export_files: dot_h_c = c.find_file(dot_h, c.name) - api = open('%s/%s' % (dot_h_c.src_dir, dot_h), 'r') + api = open(os.path.join(dot_h_c.src_dir, dot_h), 'r') for line in api: m = pat1.match(line) if m: @@ -1855,11 +1894,11 @@ def cp_z3pyc_to_build(): raise MKException("failed to compile Z3Py sources") for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(Z3PY_SRC_DIR)): try: - os.remove('%s/%s' % (BUILD_DIR, pyc)) + os.remove(os.path.join(BUILD_DIR, pyc)) except: pass - shutil.copyfile('%s/%s' % (Z3PY_SRC_DIR, pyc), '%s/%s' % (BUILD_DIR, pyc)) - os.remove('%s/%s' % (Z3PY_SRC_DIR, pyc)) + shutil.copyfile(os.path.join(Z3PY_SRC_DIR, pyc), os.path.join(BUILD_DIR, pyc)) + os.remove(os.path.join(Z3PY_SRC_DIR, pyc)) if is_verbose(): print "Generated '%s'" % pyc @@ -1871,13 +1910,13 @@ def mk_bindings(api_files): api = get_component(API_COMPONENT) for api_file in api_files: api_file_path = api.find_file(api_file, api.name) - new_api_files.append('%s/%s' % (api_file_path.src_dir, api_file)) + new_api_files.append(os.path.join(api_file_path.src_dir, api_file)) g = {} g["API_FILES"] = new_api_files if is_java_enabled(): check_java() mk_z3consts_java(api_files) - execfile('scripts/update_api.py', g) # HACK + execfile(os.path.join('scripts', 'update_api.py'), g) # HACK cp_z3pyc_to_build() # Extract enumeration types from API files, and add python definitions. @@ -1892,14 +1931,14 @@ def mk_z3consts_py(api_files): openbrace_pat = re.compile("{ *") closebrace_pat = re.compile("}.*;") - z3consts = open('%s/z3consts.py' % Z3PY_SRC_DIR, 'w') + z3consts = open(os.path.join(Z3PY_SRC_DIR, 'z3consts.py'), 'w') z3consts.write('# Automatically generated file\n\n') api_dll = get_component(Z3_DLL_COMPONENT) for api_file in api_files: api_file_c = api_dll.find_file(api_file, api_dll.name) - api_file = '%s/%s' % (api_file_c.src_dir, api_file) + api_file = os.path.join(api_file_c.src_dir, api_file) api = open(api_file, 'r') SEARCHING = 0 @@ -1955,7 +1994,7 @@ def mk_z3consts_py(api_files): idx = idx + 1 linenum = linenum + 1 if VERBOSE: - print "Generated '%s'" % ('%s/z3consts.py' % Z3PY_SRC_DIR) + print "Generated '%s'" % os.path.join(Z3PY_SRC_DIR, 'z3consts.py') # Extract enumeration types from z3_api.h, and add .Net definitions @@ -1970,7 +2009,7 @@ def mk_z3consts_dotnet(api_files): dotnet = get_component(DOTNET_COMPONENT) DeprecatedEnums = [ 'Z3_search_failure' ] - z3consts = open('%s/Enumerations.cs' % dotnet.src_dir, 'w') + z3consts = open(os.path.join(dotnet.src_dir, 'Enumerations.cs'), 'w') z3consts.write('// Automatically generated file\n\n') z3consts.write('using System;\n\n' '#pragma warning disable 1591\n\n' @@ -1979,7 +2018,7 @@ def mk_z3consts_dotnet(api_files): for api_file in api_files: api_file_c = dotnet.find_file(api_file, dotnet.name) - api_file = '%s/%s' % (api_file_c.src_dir, api_file) + api_file = os.path.join(api_file_c.src_dir, api_file) api = open(api_file, 'r') @@ -2040,7 +2079,7 @@ def mk_z3consts_dotnet(api_files): linenum = linenum + 1 z3consts.write('}\n'); if VERBOSE: - print "Generated '%s'" % ('%s/Enumerations.cs' % dotnet.src_dir) + print "Generated '%s'" % os.path.join(dotnet.src_dir, 'Enumerations.cs') # Extract enumeration types from z3_api.h, and add Java definitions @@ -2055,13 +2094,13 @@ def mk_z3consts_java(api_files): java = get_component(JAVA_COMPONENT) DeprecatedEnums = [ 'Z3_search_failure' ] - gendir = java.src_dir + "/enumerations" + gendir = os.path.join(java.src_dir, "enumerations") if not os.path.exists(gendir): os.mkdir(gendir) for api_file in api_files: api_file_c = java.find_file(api_file, java.name) - api_file = '%s/%s' % (api_file_c.src_dir, api_file) + api_file = os.path.join(api_file_c.src_dir, api_file) api = open(api_file, 'r') @@ -2104,7 +2143,7 @@ def mk_z3consts_java(api_files): if m: name = words[1] if name not in DeprecatedEnums: - efile = open('%s/%s.java' % (gendir, name), 'w') + 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); @@ -2153,7 +2192,7 @@ def mk_gui_str(id): def mk_vs_proj(name, components): if not VS_PROJ: return - proj_name = '%s/%s.vcxproj' % (BUILD_DIR, name) + proj_name = '%s.vcxproj' % os.path.join(BUILD_DIR, name) modes=['Debug', 'Release'] PLATFORMS=['Win32'] f = open(proj_name, 'w') @@ -2225,7 +2264,7 @@ def mk_vs_proj(name, components): for dep in deps: dep = get_component(dep) for cpp in filter(lambda f: f.endswith('.cpp'), os.listdir(dep.src_dir)): - f.write(' \n' % (dep.to_src_dir, cpp)) + f.write(' \n' % os.path.join(dep.to_src_dir, cpp)) f.write(' \n') f.write(' \n') f.write(' \n') @@ -2239,8 +2278,8 @@ def mk_win_dist(build_path, dist_path): c.mk_win_dist(build_path, dist_path) # Add Z3Py to lib directory for pyc in filter(lambda f: f.endswith('.pyc'), os.listdir(build_path)): - shutil.copy('%s/%s' % (build_path, pyc), - '%s/bin/%s' % (dist_path, pyc)) + shutil.copy(os.path.join(build_path, pyc), + os.path.join(dist_path, 'bin', pyc)) if __name__ == '__main__': diff --git a/scripts/mk_win_dist.py b/scripts/mk_win_dist.py index 3cebce7f6..57ae66b03 100644 --- a/scripts/mk_win_dist.py +++ b/scripts/mk_win_dist.py @@ -100,7 +100,7 @@ def mk_build_dirs(): def check_vc_cmd_prompt(): try: DEVNULL = open(os.devnull, 'wb') - subprocess.call(['cl'], stdin=DEVNULL, stderr=DEVNULL) + subprocess.call(['cl'], stdout=DEVNULL, stderr=DEVNULL) except: raise MKException("You must execute the mk_win_dist.py script on a Visual Studio Command Prompt") diff --git a/scripts/update_api.py b/scripts/update_api.py index d7dbb7bcf..a636a1a02 100644 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -24,11 +24,11 @@ from mk_exception import * api_dir = get_component('api').src_dir dotnet_dir = get_component('dotnet').src_dir -log_h = open('%s/api_log_macros.h' % api_dir, 'w') -log_c = open('%s/api_log_macros.cpp' % api_dir, 'w') -exe_c = open('%s/api_commands.cpp' % api_dir, 'w') -core_py = open('%s/z3core.py' % get_z3py_dir(), 'w') -dotnet_fileout = '%s/Native.cs' % dotnet_dir +log_h = open(os.path.join(api_dir, 'api_log_macros.h'), 'w') +log_c = open(os.path.join(api_dir, 'api_log_macros.cpp'), 'w') +exe_c = open(os.path.join(api_dir, 'api_commands.cpp'), 'w') +core_py = open(os.path.join(get_z3py_dir(), 'z3core.py'), 'w') +dotnet_fileout = os.path.join(dotnet_dir, 'Native.cs') ## log_h.write('// Automatically generated file\n') log_h.write('#include\"z3.h\"\n') @@ -479,8 +479,8 @@ def mk_java(): if not is_java_enabled(): return java_dir = get_component('java').src_dir - java_nativef = '%s/Native.java' % java_dir - java_wrapperf = '%s/Native.cpp' % java_dir + java_nativef = os.path.join(java_dir, 'Native.java') + java_wrapperf = os.path.join(java_dir, 'Native.cpp') java_native = open(java_nativef, 'w') java_native.write('// Automatically generated file\n') java_native.write('package %s;\n' % get_component('java').package_name) @@ -981,8 +981,8 @@ exe_c.close() core_py.close() if is_verbose(): - print "Generated '%s'" % ('%s/api_log_macros.h' % api_dir) - print "Generated '%s'" % ('%s/api_log_macros.cpp' % api_dir) - print "Generated '%s'" % ('%s/api_commands.cpp' % api_dir) - print "Generated '%s'" % ('%s/z3core.py' % get_z3py_dir()) - print "Generated '%s'" % ('%s/Native.cs' % dotnet_dir) + print "Generated '%s'" % os.path.join(api_dir, 'api_log_macros.h') + print "Generated '%s'" % os.path.join(api_dir, 'api_log_macros.cpp') + print "Generated '%s'" % os.path.join(api_dir, 'api_commands.cpp') + print "Generated '%s'" % os.path.join(get_z3py_dir(), 'z3core.py') + print "Generated '%s'" % os.path.join(dotnet_dir, 'Native.cs') diff --git a/src/api/api_algebraic.cpp b/src/api/api_algebraic.cpp new file mode 100644 index 000000000..7716cbb59 --- /dev/null +++ b/src/api/api_algebraic.cpp @@ -0,0 +1,422 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + api_algebraic.cpp + +Abstract: + + Additional APIs for handling Z3 algebraic numbers encoded as + Z3_ASTs + +Author: + + Leonardo de Moura (leonardo) 2012-12-07 + +Notes: + +--*/ +#include +#include"z3.h" +#include"api_log_macros.h" +#include"api_context.h" +#include"api_ast_vector.h" +#include"algebraic_numbers.h" +#include"expr2polynomial.h" +#include"cancel_eh.h" +#include"scoped_timer.h" + + +#define CHECK_IS_ALGEBRAIC(ARG, RET) { \ + if (!Z3_algebraic_is_value_core(c, ARG)) { \ + SET_ERROR_CODE(Z3_INVALID_ARG); \ + return RET; \ + } \ +} + +#define CHECK_IS_ALGEBRAIC_X(ARG, RET) { \ + if (!Z3_algebraic_is_value_core(c, ARG)) { \ + SET_ERROR_CODE(Z3_INVALID_ARG); \ + RETURN_Z3(RET); \ + } \ +} + +static arith_util & au(Z3_context c) { + return mk_c(c)->autil(); +} + +static algebraic_numbers::manager & am(Z3_context c) { + return au(c).am(); +} + +static bool is_rational(Z3_context c, Z3_ast a) { + return au(c).is_numeral(to_expr(a)); +} + +static bool is_irrational(Z3_context c, Z3_ast a) { + return au(c).is_irrational_algebraic_numeral(to_expr(a)); +} + +static rational get_rational(Z3_context c, Z3_ast a) { + SASSERT(is_rational(c, a)); + rational r; + VERIFY(au(c).is_numeral(to_expr(a), r)); + return r; +} + +static algebraic_numbers::anum const & get_irrational(Z3_context c, Z3_ast a) { + SASSERT(is_irrational(c, a)); + return au(c).to_irrational_algebraic_numeral(to_expr(a)); +} + +extern "C" { + + bool Z3_algebraic_is_value_core(Z3_context c, Z3_ast a) { + api::context * _c = mk_c(c); + return + is_expr(a) && + (_c->autil().is_numeral(to_expr(a)) || + _c->autil().is_irrational_algebraic_numeral(to_expr(a))); + } + + Z3_bool Z3_API Z3_algebraic_is_value(Z3_context c, Z3_ast a) { + Z3_TRY; + LOG_Z3_algebraic_is_value(c, a); + RESET_ERROR_CODE(); + return Z3_algebraic_is_value_core(c, a) ? Z3_TRUE : Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_bool Z3_API Z3_algebraic_is_pos(Z3_context c, Z3_ast a) { + return Z3_algebraic_sign(c, a) > 0; + } + + Z3_bool Z3_API Z3_algebraic_is_neg(Z3_context c, Z3_ast a) { + return Z3_algebraic_sign(c, a) < 0; + } + + Z3_bool Z3_API Z3_algebraic_is_zero(Z3_context c, Z3_ast a) { + return Z3_algebraic_sign(c, a) == 0; + } + + int Z3_API Z3_algebraic_sign(Z3_context c, Z3_ast a) { + Z3_TRY; + LOG_Z3_algebraic_sign(c, a); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC(a, 0); + if (is_rational(c, a)) { + rational v = get_rational(c, a); + if (v.is_pos()) return 1; + else if (v.is_neg()) return -1; + else return 0; + } + else { + algebraic_numbers::anum const & v = get_irrational(c, a); + if (am(c).is_pos(v)) return 1; + else if (am(c).is_neg(v)) return -1; + else return 0; + } + Z3_CATCH_RETURN(0); + } + +#define BIN_OP(RAT_OP, IRAT_OP) \ + algebraic_numbers::manager & _am = am(c); \ + ast * r = 0; \ + if (is_rational(c, a)) { \ + rational av = get_rational(c, a); \ + if (is_rational(c, b)) { \ + rational bv = get_rational(c, b); \ + r = au(c).mk_numeral(av RAT_OP bv, false); \ + } \ + else { \ + algebraic_numbers::anum const & bv = get_irrational(c, b); \ + scoped_anum _av(_am); \ + _am.set(_av, av.to_mpq()); \ + scoped_anum _r(_am); \ + _am.IRAT_OP(_av, bv, _r); \ + r = au(c).mk_numeral(_r, false); \ + } \ + } \ + else { \ + algebraic_numbers::anum const & av = get_irrational(c, a); \ + if (is_rational(c, b)) { \ + rational bv = get_rational(c, b); \ + scoped_anum _bv(_am); \ + _am.set(_bv, bv.to_mpq()); \ + scoped_anum _r(_am); \ + _am.IRAT_OP(av, _bv, _r); \ + r = au(c).mk_numeral(_r, false); \ + } \ + else { \ + algebraic_numbers::anum const & bv = get_irrational(c, b); \ + scoped_anum _r(_am); \ + _am.IRAT_OP(av, bv, _r); \ + r = au(c).mk_numeral(_r, false); \ + } \ + } \ + mk_c(c)->save_ast_trail(r); \ + RETURN_Z3(of_ast(r)); + + + Z3_ast Z3_API Z3_algebraic_add(Z3_context c, Z3_ast a, Z3_ast b) { + Z3_TRY; + LOG_Z3_algebraic_add(c, a, b); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC_X(a, 0); + CHECK_IS_ALGEBRAIC_X(b, 0); + BIN_OP(+,add); + Z3_CATCH_RETURN(0); + } + + Z3_ast Z3_API Z3_algebraic_sub(Z3_context c, Z3_ast a, Z3_ast b) { + Z3_TRY; + LOG_Z3_algebraic_sub(c, a, b); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC_X(a, 0); + CHECK_IS_ALGEBRAIC_X(b, 0); + BIN_OP(-,sub); + Z3_CATCH_RETURN(0); + } + + Z3_ast Z3_API Z3_algebraic_mul(Z3_context c, Z3_ast a, Z3_ast b) { + Z3_TRY; + LOG_Z3_algebraic_mul(c, a, b); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC_X(a, 0); + CHECK_IS_ALGEBRAIC_X(b, 0); + BIN_OP(*,mul); + Z3_CATCH_RETURN(0); + } + + Z3_ast Z3_API Z3_algebraic_div(Z3_context c, Z3_ast a, Z3_ast b) { + Z3_TRY; + LOG_Z3_algebraic_div(c, a, b); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC_X(a, 0); + CHECK_IS_ALGEBRAIC_X(b, 0); + if ((is_rational(c, b) && get_rational(c, b).is_zero()) || + (!is_rational(c, b) && am(c).is_zero(get_irrational(c, b)))) { + SET_ERROR_CODE(Z3_INVALID_ARG); + RETURN_Z3(0); + } + BIN_OP(/,div); + Z3_CATCH_RETURN(0); + } + + Z3_ast Z3_API Z3_algebraic_root(Z3_context c, Z3_ast a, unsigned k) { + Z3_TRY; + LOG_Z3_algebraic_root(c, a, k); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC_X(a, 0); + if (k % 2 == 0) { + if ((is_rational(c, a) && get_rational(c, a).is_neg()) || + (!is_rational(c, a) && am(c).is_neg(get_irrational(c, a)))) { + SET_ERROR_CODE(Z3_INVALID_ARG); + RETURN_Z3(0); + } + } + algebraic_numbers::manager & _am = am(c); + scoped_anum _r(_am); + if (is_rational(c, a)) { + scoped_anum av(_am); + _am.set(av, get_rational(c, a).to_mpq()); + _am.root(av, k, _r); + } + else { + algebraic_numbers::anum const & av = get_irrational(c, a); + _am.root(av, k, _r); + } + expr * r = au(c).mk_numeral(_r, false); + mk_c(c)->save_ast_trail(r); + RETURN_Z3(of_ast(r)); + Z3_CATCH_RETURN(0); + } + + Z3_ast Z3_API Z3_algebraic_power(Z3_context c, Z3_ast a, unsigned k) { + Z3_TRY; + LOG_Z3_algebraic_power(c, a, k); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC_X(a, 0); + algebraic_numbers::manager & _am = am(c); + scoped_anum _r(_am); + if (is_rational(c, a)) { + scoped_anum av(_am); + _am.set(av, get_rational(c, a).to_mpq()); + _am.power(av, k, _r); + } + else { + algebraic_numbers::anum const & av = get_irrational(c, a); + _am.power(av, k, _r); + } + expr * r = au(c).mk_numeral(_r, false); + mk_c(c)->save_ast_trail(r); + RETURN_Z3(of_ast(r)); + Z3_CATCH_RETURN(0); + } + +#define BIN_PRED(RAT_PRED, IRAT_PRED) \ + algebraic_numbers::manager & _am = am(c); \ + bool r; \ + if (is_rational(c, a)) { \ + rational av = get_rational(c, a); \ + if (is_rational(c, b)) { \ + rational bv = get_rational(c, b); \ + r = av RAT_PRED bv; \ + } \ + else { \ + algebraic_numbers::anum const & bv = get_irrational(c, b); \ + scoped_anum _av(_am); \ + _am.set(_av, av.to_mpq()); \ + r = _am.IRAT_PRED(_av, bv); \ + } \ + } \ + else { \ + algebraic_numbers::anum const & av = get_irrational(c, a); \ + if (is_rational(c, b)) { \ + rational bv = get_rational(c, b); \ + scoped_anum _bv(_am); \ + _am.set(_bv, bv.to_mpq()); \ + r = _am.IRAT_PRED(av, _bv); \ + } \ + else { \ + algebraic_numbers::anum const & bv = get_irrational(c, b); \ + r = _am.IRAT_PRED(av, bv); \ + } \ + } \ + return r ? Z3_TRUE : Z3_FALSE; + + + Z3_bool Z3_API Z3_algebraic_lt(Z3_context c, Z3_ast a, Z3_ast b) { + Z3_TRY; + LOG_Z3_algebraic_lt(c, a, b); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC(a, 0); + CHECK_IS_ALGEBRAIC(b, 0); + BIN_PRED(<,lt); + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_bool Z3_API Z3_algebraic_gt(Z3_context c, Z3_ast a, Z3_ast b) { + return Z3_algebraic_lt(c, b, a); + } + + Z3_bool Z3_API Z3_algebraic_le(Z3_context c, Z3_ast a, Z3_ast b) { + return !Z3_algebraic_lt(c, b, a); + } + + Z3_bool Z3_API Z3_algebraic_ge(Z3_context c, Z3_ast a, Z3_ast b) { + return !Z3_algebraic_lt(c, a, b); + } + + Z3_bool Z3_API Z3_algebraic_eq(Z3_context c, Z3_ast a, Z3_ast b) { + Z3_TRY; + LOG_Z3_algebraic_eq(c, a, b); + RESET_ERROR_CODE(); + CHECK_IS_ALGEBRAIC(a, 0); + CHECK_IS_ALGEBRAIC(b, 0); + BIN_PRED(==,eq); + Z3_CATCH_RETURN(0); + } + + Z3_bool Z3_API Z3_algebraic_neq(Z3_context c, Z3_ast a, Z3_ast b) { + return !Z3_algebraic_eq(c, a, b); + } + + static bool to_anum_vector(Z3_context c, unsigned n, Z3_ast a[], scoped_anum_vector & as) { + algebraic_numbers::manager & _am = am(c); + scoped_anum tmp(_am); + for (unsigned i = 0; i < n; i++) { + if (is_rational(c, a[i])) { + _am.set(tmp, get_rational(c, a[i]).to_mpq()); + as.push_back(tmp); + } + else if (is_irrational(c, a[i])) { + as.push_back(get_irrational(c, a[i])); + } + else { + return false; + } + } + return true; + } + + class vector_var2anum : public polynomial::var2anum { + scoped_anum_vector const & m_as; + public: + vector_var2anum(scoped_anum_vector & as):m_as(as) {} + virtual ~vector_var2anum() {} + virtual algebraic_numbers::manager & m() const { return m_as.m(); } + virtual bool contains(polynomial::var x) const { return static_cast(x) < m_as.size(); } + virtual algebraic_numbers::anum const & operator()(polynomial::var x) const { return m_as.get(x); } + }; + + Z3_ast_vector Z3_API Z3_algebraic_roots(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]) { + Z3_TRY; + LOG_Z3_algebraic_roots(c, p, n, a); + RESET_ERROR_CODE(); + polynomial::manager & pm = mk_c(c)->pm(); + polynomial_ref _p(pm); + polynomial::scoped_numeral d(pm.m()); + expr2polynomial converter(mk_c(c)->m(), pm, 0, true); + if (!converter.to_polynomial(to_expr(p), _p, d) || + static_cast(max_var(_p)) >= n + 1) { + SET_ERROR_CODE(Z3_INVALID_ARG); + return 0; + } + algebraic_numbers::manager & _am = am(c); + scoped_anum_vector as(_am); + if (!to_anum_vector(c, n, a, as)) { + SET_ERROR_CODE(Z3_INVALID_ARG); + return 0; + } + scoped_anum_vector roots(_am); + { + cancel_eh eh(_am); + api::context::set_interruptable(*(mk_c(c)), eh); + scoped_timer timer(mk_c(c)->params().m_timeout, &eh); + vector_var2anum v2a(as); + _am.isolate_roots(_p, v2a, roots); + } + Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, mk_c(c)->m()); + mk_c(c)->save_object(result); + for (unsigned i = 0; i < roots.size(); i++) { + result->m_ast_vector.push_back(au(c).mk_numeral(roots.get(i), false)); + } + RETURN_Z3(of_ast_vector(result)); + Z3_CATCH_RETURN(0); + } + + int Z3_API Z3_algebraic_eval(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]) { + Z3_TRY; + LOG_Z3_algebraic_eval(c, p, n, a); + RESET_ERROR_CODE(); + polynomial::manager & pm = mk_c(c)->pm(); + polynomial_ref _p(pm); + polynomial::scoped_numeral d(pm.m()); + expr2polynomial converter(mk_c(c)->m(), pm, 0, true); + if (!converter.to_polynomial(to_expr(p), _p, d) || + static_cast(max_var(_p)) >= n) { + SET_ERROR_CODE(Z3_INVALID_ARG); + return 0; + } + algebraic_numbers::manager & _am = am(c); + scoped_anum_vector as(_am); + if (!to_anum_vector(c, n, a, as)) { + SET_ERROR_CODE(Z3_INVALID_ARG); + return 0; + } + { + cancel_eh eh(_am); + api::context::set_interruptable(*(mk_c(c)), eh); + scoped_timer timer(mk_c(c)->params().m_timeout, &eh); + vector_var2anum v2a(as); + int r = _am.eval_sign_at(_p, v2a); + if (r > 0) return 1; + else if (r < 0) return -1; + else return 0; + } + Z3_CATCH_RETURN(0); + } + +}; diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index c604927fd..404d9d9ac 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -324,7 +324,8 @@ extern "C" { switch (_a->get_kind()) { case AST_APP: { expr * e = to_expr(_a); - if (is_numeral_sort(c, of_sort(mk_c(c)->m().get_sort(e))) && mk_c(c)->m().is_value(e)) + // Real algebraic numbers are not considered Z3_NUMERAL_AST + if (is_numeral_sort(c, of_sort(mk_c(c)->m().get_sort(e))) && mk_c(c)->m().is_unique_value(e)) return Z3_NUMERAL_AST; return Z3_APP_AST; } diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 69b7ea999..7dc8d1a12 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -82,13 +82,13 @@ namespace api { context::context(context_params * p, bool user_ref_count): m_params(p != 0 ? *p : context_params()), m_user_ref_count(user_ref_count), - m_manager(m_params.m_proof ? PGM_FINE : PGM_DISABLED, m_params.m_trace ? m_params.m_trace_file_name.c_str() : 0), - m_plugins(m_manager), - m_arith_util(m_manager), - m_bv_util(m_manager), - m_datalog_util(m_manager), - m_last_result(m_manager), - m_ast_trail(m_manager), + m_manager(m_params.mk_ast_manager()), + m_plugins(m()), + m_arith_util(m()), + m_bv_util(m()), + m_datalog_util(m()), + m_last_result(m()), + m_ast_trail(m()), m_replay_stack() { m_solver = 0; @@ -102,22 +102,16 @@ namespace api { m_smtlib_parser_has_decls = false; z3_bound_num_procs(); - // - // Configuration parameter settings. - // - if (m_params.m_debug_ref_count) { - m_manager.debug_ref_count(); - } m_error_handler = &default_error_handler; - m_basic_fid = m_manager.get_basic_family_id(); - m_arith_fid = m_manager.get_family_id("arith"); - m_bv_fid = m_manager.get_family_id("bv"); - m_array_fid = m_manager.get_family_id("array"); - m_dt_fid = m_manager.get_family_id("datatype"); - m_datalog_fid = m_manager.get_family_id("datalog_relation"); - m_dt_plugin = static_cast(m_manager.get_plugin(m_dt_fid)); + m_basic_fid = m().get_basic_family_id(); + m_arith_fid = m().get_family_id("arith"); + m_bv_fid = m().get_family_id("bv"); + m_array_fid = m().get_family_id("array"); + m_dt_fid = m().get_family_id("datatype"); + m_datalog_fid = m().get_family_id("datalog_relation"); + m_dt_plugin = static_cast(m().get_plugin(m_dt_fid)); if (!m_user_ref_count) { m_replay_stack.push_back(0); @@ -143,6 +137,7 @@ namespace api { { if (m_interruptable) (*m_interruptable)(); + m().set_cancel(true); } } @@ -195,12 +190,12 @@ namespace api { expr * context::mk_and(unsigned num_exprs, expr * const * exprs) { switch(num_exprs) { case 0: - return m_manager.mk_true(); + return m().mk_true(); case 1: save_ast_trail(exprs[0]); return exprs[0]; default: { - expr * a = m_manager.mk_and(num_exprs, exprs); + expr * a = m().mk_and(num_exprs, exprs); save_ast_trail(a); return a; } } @@ -216,7 +211,7 @@ namespace api { SASSERT(m_replay_stack.size() > num_scopes); unsigned j = m_replay_stack.size() - num_scopes - 1; if (!m_replay_stack[j]) { - m_replay_stack[j] = alloc(ast_ref_vector, m_manager); + m_replay_stack[j] = alloc(ast_ref_vector, m()); } m_replay_stack[j]->push_back(n); } @@ -324,7 +319,7 @@ namespace api { smt::kernel & context::get_smt_kernel() { if (!m_solver) { m_fparams.updt_params(m_params); - m_solver = alloc(smt::kernel, m_manager, m_fparams); + m_solver = alloc(smt::kernel, m(), m_fparams); } return *m_solver; } diff --git a/src/api/api_context.h b/src/api/api_context.h index a126f0790..edb79b2d5 100644 --- a/src/api/api_context.h +++ b/src/api/api_context.h @@ -32,6 +32,7 @@ Revision History: #include"event_handler.h" #include"tactic_manager.h" #include"context_params.h" +#include"api_polynomial.h" namespace smtlib { class parser; @@ -45,7 +46,7 @@ namespace api { struct add_plugins { add_plugins(ast_manager & m); }; context_params m_params; bool m_user_ref_count; //!< if true, the user is responsible for managing referenc counters. - ast_manager m_manager; + scoped_ptr m_manager; add_plugins m_plugins; arith_util m_arith_util; @@ -81,6 +82,8 @@ namespace api { Z3_ast_print_mode m_print_mode; event_handler * m_interruptable; // Reference to an object that can be interrupted by Z3_interrupt + + pmanager m_pmanager; public: // Scoped obj for setting m_interruptable class set_interruptable { @@ -98,10 +101,10 @@ namespace api { context(context_params * p, bool user_ref_count = false); ~context(); - ast_manager & m() { return m_manager; } + ast_manager & m() const { return *(m_manager.get()); } context_params & params() { return m_params; } - bool produce_proofs() const { return m_manager.proofs_enabled(); } + bool produce_proofs() const { return m().proofs_enabled(); } bool produce_models() const { return m_params.m_model; } bool produce_unsat_cores() const { return m_params.m_unsat_core; } bool use_auto_config() const { return m_params.m_auto_config; } @@ -167,6 +170,13 @@ namespace api { void check_sorts(ast * n); + // ------------------------ + // + // Polynomial manager & caches + // + // ----------------------- + polynomial::manager & pm() { return m_pmanager.pm(); } + // ------------------------ // // Solver interface for backward compatibility diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index 69ca1fc81..8f6eb1125 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -252,42 +252,6 @@ extern "C" { // --------------- // Support for SMTLIB2 - class z3_context_solver : public solver_na2as { - api::context & m_ctx; - smt::kernel & ctx() const { return m_ctx.get_smt_kernel(); } - public: - virtual ~z3_context_solver() {} - z3_context_solver(api::context& c) : m_ctx(c) {} - virtual void init_core(ast_manager & m, symbol const & logic) {} - virtual void collect_statistics(statistics & st) const {} - virtual void reset_core() { ctx().reset(); } - virtual void assert_expr(expr * t) { ctx().assert_expr(t); } - virtual void push_core() { ctx().push(); } - virtual void pop_core(unsigned n) { ctx().pop(n); } - virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { - return ctx().check(num_assumptions, assumptions); - } - virtual void get_unsat_core(ptr_vector & r) { - unsigned sz = ctx().get_unsat_core_size(); - for (unsigned i = 0; i < sz; i++) - r.push_back(ctx().get_unsat_core_expr(i)); - } - virtual void get_model(model_ref & m) { ctx().get_model(m); } - virtual proof * get_proof() { return ctx().get_proof(); } - virtual std::string reason_unknown() const { return ctx().last_failure_as_string(); } - virtual void get_labels(svector & r) { - buffer tmp; - ctx().get_relevant_labels(0, tmp); - r.append(tmp.size(), tmp.c_ptr()); - } - - // These are controlled by the main API - virtual void set_cancel(bool f) { } - void cancel() { set_cancel(true); } - void reset_cancel() { set_cancel(false); } - virtual void set_progress_callback(progress_callback * callback) {} - }; - Z3_ast parse_smtlib2_stream(bool exec, Z3_context c, std::istream& is, unsigned num_sorts, Z3_symbol const sort_names[], @@ -298,9 +262,6 @@ extern "C" { Z3_TRY; cmd_context ctx(false, &(mk_c(c)->m())); ctx.set_ignore_check(true); - if (exec) { - ctx.set_solver(alloc(z3_context_solver, *mk_c(c))); - } for (unsigned i = 0; i < num_decls; ++i) { ctx.insert(to_symbol(decl_names[i]), to_func_decl(decls[i])); } @@ -353,39 +314,4 @@ extern "C" { RETURN_Z3(r); Z3_CATCH_RETURN(0); } - - Z3_ast Z3_API Z3_exec_smtlib2_string(Z3_context c, Z3_string str, - unsigned num_sorts, - Z3_symbol sort_names[], - Z3_sort sorts[], - unsigned num_decls, - Z3_symbol decl_names[], - Z3_func_decl decls[]) { - Z3_TRY; - cmd_context ctx(false, &(mk_c(c)->m())); - std::string s(str); - std::istringstream is(s); - // No logging for this one, since it private. - return parse_smtlib2_stream(true, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); - Z3_CATCH_RETURN(0); - } - - Z3_ast Z3_API Z3_exec_smtlib2_file(Z3_context c, Z3_string file_name, - unsigned num_sorts, - Z3_symbol sort_names[], - Z3_sort sorts[], - unsigned num_decls, - Z3_symbol decl_names[], - Z3_func_decl decls[]) { - Z3_TRY; - std::ifstream is(file_name); - if (!is) { - SET_ERROR_CODE(Z3_PARSER_ERROR); - return 0; - } - // No logging for this one, since it private. - return parse_smtlib2_stream(true, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); - Z3_CATCH_RETURN(0); - } - }; diff --git a/src/api/api_polynomial.cpp b/src/api/api_polynomial.cpp new file mode 100644 index 000000000..3148f972b --- /dev/null +++ b/src/api/api_polynomial.cpp @@ -0,0 +1,84 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + api_polynomial.cpp + +Abstract: + + Polynomial manager and caches for the external API. + +Author: + + Leonardo de Moura (leonardo) 2012-12-08 + +Notes: + +--*/ +#include +#include"z3.h" +#include"api_log_macros.h" +#include"api_context.h" +#include"api_polynomial.h" +#include"api_ast_vector.h" +#include"expr2polynomial.h" +#include"cancel_eh.h" +#include"scoped_timer.h" +#include"expr2var.h" + +namespace api { + + pmanager::pmanager(): + m_pm(m_nm) { + } + + pmanager::~pmanager() { + } + + void pmanager::set_cancel(bool f) { + m_pm.set_cancel(f); + } + +}; + +extern "C" { + + Z3_ast_vector Z3_API Z3_polynomial_subresultants(Z3_context c, Z3_ast p, Z3_ast q, Z3_ast x) { + Z3_TRY; + LOG_Z3_polynomial_subresultants(c, p, q, x); + RESET_ERROR_CODE(); + polynomial::manager & pm = mk_c(c)->pm(); + polynomial_ref _p(pm), _q(pm); + polynomial::scoped_numeral d(pm.m()); + default_expr2polynomial converter(mk_c(c)->m(), pm); + if (!converter.to_polynomial(to_expr(p), _p, d) || + !converter.to_polynomial(to_expr(q), _q, d)) { + SET_ERROR_CODE(Z3_INVALID_ARG); + return 0; + } + Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, mk_c(c)->m()); + mk_c(c)->save_object(result); + if (converter.is_var(to_expr(x))) { + expr2var const & mapping = converter.get_mapping(); + unsigned v_x = mapping.to_var(to_expr(x)); + polynomial_ref_vector rs(pm); + polynomial_ref r(pm); + expr_ref _r(mk_c(c)->m()); + { + cancel_eh eh(pm); + api::context::set_interruptable(*(mk_c(c)), eh); + scoped_timer timer(mk_c(c)->params().m_timeout, &eh); + pm.psc_chain(_p, _q, v_x, rs); + } + for (unsigned i = 0; i < rs.size(); i++) { + r = rs.get(i); + converter.to_expr(r, true, _r); + result->m_ast_vector.push_back(_r); + } + } + RETURN_Z3(of_ast_vector(result)); + Z3_CATCH_RETURN(0); + } + +}; diff --git a/src/api/api_polynomial.h b/src/api/api_polynomial.h new file mode 100644 index 000000000..27317a7dd --- /dev/null +++ b/src/api/api_polynomial.h @@ -0,0 +1,39 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + api_polynomial.h + +Abstract: + + Polynomial manager and caches for the external API. + +Author: + + Leonardo de Moura (leonardo) 2012-12-08 + +Notes: + +--*/ +#ifndef _API_POLYNOMIAL_H_ +#define _API_POLYNOMIAL_H_ + +#include"polynomial.h" + +namespace api { + + class pmanager { + unsynch_mpz_manager m_nm; + polynomial::manager m_pm; + // TODO: add support for caching expressions -> polynomial and back + public: + pmanager(); + virtual ~pmanager(); + polynomial::manager & pm() { return m_pm; } + void set_cancel(bool f); + }; + +}; + +#endif diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 67d083704..6bf184708 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -34,25 +34,15 @@ Revision History: extern "C" { static void init_solver_core(Z3_context c, Z3_solver _s) { - ast_manager & m = mk_c(c)->m(); Z3_solver_ref * s = to_solver(_s); - s->m_solver->set_produce_proofs(mk_c(c)->produce_proofs()); - s->m_solver->set_produce_unsat_cores(s->m_params.get_bool("unsat_core", mk_c(c)->produce_unsat_cores())); - s->m_solver->set_produce_models(s->m_params.get_bool("model", mk_c(c)->produce_models())); - if (!mk_c(c)->use_auto_config()) { - params_ref p = s->m_params; - p.set_bool("auto_config", false); - s->m_solver->updt_params(p); - } - else { - s->m_solver->updt_params(s->m_params); - } - s->m_solver->init(m, s->m_logic); - s->m_initialized = true; + bool proofs_enabled, models_enabled, unsat_core_enabled; + params_ref p = s->m_params; + mk_c(c)->params().get_solver_params(mk_c(c)->m(), p, proofs_enabled, models_enabled, unsat_core_enabled); + s->m_solver = (*(s->m_solver_factory))(mk_c(c)->m(), p, proofs_enabled, models_enabled, unsat_core_enabled, s->m_logic); } static void init_solver(Z3_context c, Z3_solver s) { - if (!to_solver(s)->m_initialized) + if (to_solver(s)->m_solver.get() == 0) init_solver_core(c, s); } @@ -60,8 +50,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_simple_solver(c); RESET_ERROR_CODE(); - Z3_solver_ref * s = alloc(Z3_solver_ref); - s->m_solver = mk_smt_solver(); + Z3_solver_ref * s = alloc(Z3_solver_ref, mk_smt_solver_factory()); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); @@ -72,8 +61,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_solver(c); RESET_ERROR_CODE(); - Z3_solver_ref * s = alloc(Z3_solver_ref); - s->m_solver = mk_smt_strategic_solver(); + Z3_solver_ref * s = alloc(Z3_solver_ref, mk_smt_strategic_solver_factory()); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); @@ -84,8 +72,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_solver_for_logic(c, logic); RESET_ERROR_CODE(); - Z3_solver_ref * s = alloc(Z3_solver_ref, to_symbol(logic)); - s->m_solver = mk_smt_strategic_solver(true /* force solver to use tactics even when auto_config is disabled */); + Z3_solver_ref * s = alloc(Z3_solver_ref, mk_smt_strategic_solver_factory(to_symbol(logic))); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); @@ -96,8 +83,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_solver_from_tactic(c, t); RESET_ERROR_CODE(); - Z3_solver_ref * s = alloc(Z3_solver_ref); - s->m_solver = alloc(tactic2solver, to_tactic_ref(t)); + Z3_solver_ref * s = alloc(Z3_solver_ref, mk_tactic2solver_factory(to_tactic_ref(t))); mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); @@ -110,7 +96,12 @@ extern "C" { RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; + bool initialized = to_solver(s)->m_solver.get() != 0; + if (!initialized) + init_solver(c, s); to_solver_ref(s)->collect_param_descrs(descrs); + if (!initialized) + to_solver(s)->m_solver = 0; descrs.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); @@ -122,7 +113,12 @@ extern "C" { RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref); mk_c(c)->save_object(d); + bool initialized = to_solver(s)->m_solver.get() != 0; + if (!initialized) + init_solver(c, s); to_solver_ref(s)->collect_param_descrs(d->m_descrs); + if (!initialized) + to_solver(s)->m_solver = 0; Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); Z3_CATCH_RETURN(0); @@ -132,7 +128,7 @@ extern "C" { Z3_TRY; LOG_Z3_solver_set_params(c, s, p); RESET_ERROR_CODE(); - if (to_solver(s)->m_initialized) { + if (to_solver(s)->m_solver) { bool old_model = to_solver(s)->m_params.get_bool("model", true); bool new_model = to_param_ref(p).get_bool("model", true); if (old_model != new_model) @@ -186,8 +182,7 @@ extern "C" { Z3_TRY; LOG_Z3_solver_reset(c, s); RESET_ERROR_CODE(); - to_solver_ref(s)->reset(); - to_solver(s)->m_initialized = false; + to_solver(s)->m_solver = 0; Z3_CATCH; } diff --git a/src/api/api_solver.h b/src/api/api_solver.h index 1569ef0da..f11c9bfd8 100644 --- a/src/api/api_solver.h +++ b/src/api/api_solver.h @@ -22,17 +22,16 @@ Revision History: #include"solver.h" struct Z3_solver_ref : public api::object { - solver * m_solver; - params_ref m_params; - bool m_initialized; - symbol m_logic; - Z3_solver_ref():m_solver(0), m_initialized(false), m_logic(symbol::null) {} - Z3_solver_ref(symbol const & logic):m_solver(0), m_initialized(false), m_logic(logic) {} - virtual ~Z3_solver_ref() { dealloc(m_solver); } + scoped_ptr m_solver_factory; + ref m_solver; + params_ref m_params; + symbol m_logic; + Z3_solver_ref(solver_factory * f):m_solver_factory(f), m_solver(0), m_logic(symbol::null) {} + virtual ~Z3_solver_ref() {} }; inline Z3_solver_ref * to_solver(Z3_solver s) { return reinterpret_cast(s); } inline Z3_solver of_solver(Z3_solver_ref * s) { return reinterpret_cast(s); } -inline solver * to_solver_ref(Z3_solver s) { return to_solver(s)->m_solver; } +inline solver * to_solver_ref(Z3_solver s) { return to_solver(s)->m_solver.get(); } #endif diff --git a/src/api/api_solver_old.cpp b/src/api/api_solver_old.cpp index cd4b797e3..d43e103c2 100644 --- a/src/api/api_solver_old.cpp +++ b/src/api/api_solver_old.cpp @@ -24,6 +24,7 @@ Revision History: #include"api_model.h" #include"smt_implied_equalities.h" #include"cancel_eh.h" +#include"api_solver.h" extern "C" { @@ -113,14 +114,16 @@ extern "C" { } Z3_lbool Z3_API Z3_get_implied_equalities(Z3_context c, + Z3_solver s, unsigned num_terms, Z3_ast const terms[], unsigned class_ids[]) { Z3_TRY; - LOG_Z3_get_implied_equalities(c, num_terms, terms, class_ids); + LOG_Z3_get_implied_equalities(c, s, num_terms, terms, class_ids); + ast_manager& m = mk_c(c)->m(); RESET_ERROR_CODE(); CHECK_SEARCHING(c); - lbool result = smt::implied_equalities(mk_c(c)->get_smt_kernel(), num_terms, to_exprs(terms), class_ids); + lbool result = smt::implied_equalities(m, *to_solver_ref(s), num_terms, to_exprs(terms), class_ids); return static_cast(result); Z3_CATCH_RETURN(Z3_L_UNDEF); } diff --git a/src/api/java/README b/src/api/java/README index 0591eec88..7afe098fb 100644 --- a/src/api/java/README +++ b/src/api/java/README @@ -1,4 +1,3 @@ Java bindings ------------- -This is currently "working in progress". \ No newline at end of file diff --git a/src/api/python/z3.py b/src/api/python/z3.py index e690a9bff..924ac0ea5 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -1126,6 +1126,27 @@ def Var(idx, s): _z3_assert(is_sort(s), "Z3 sort expected") return _to_expr_ref(Z3_mk_bound(s.ctx_ref(), idx, s.ast), s.ctx) +def RealVar(idx, ctx=None): + """ + Create a real free variable. Free variables are used to create quantified formulas. + They are also used to create polynomials. + + >>> RealVar(0) + Var(0) + """ + return Var(idx, RealSort(ctx)) + +def RealVarVector(n, ctx=None): + """ + Create a list of Real free variables. + The variables have ids: 0, 1, ..., n-1 + + >>> x0, x1, x2, x3 = RealVarVector(4) + >>> x2 + Var(2) + """ + return [ RealVar(i, ctx) for i in range(n) ] + ######################################### # # Booleans @@ -5787,8 +5808,15 @@ class Solver(Z3PPObject): >>> s.assert_and_track(x < 0, p3) >>> print s.check() unsat - >>> print s.unsat_core() - [p3, p1] + >>> c = s.unsat_core() + >>> len(c) + 2 + >>> Bool('p1') in c + True + >>> Bool('p2') in c + False + >>> p3 in c + True """ if isinstance(p, str): p = Bool(p, self.ctx) @@ -6985,9 +7013,9 @@ def solve(*args, **keywords): configure it using the options in `keywords`, adds the constraints in `args`, and invokes check. - >>> a, b = Ints('a b') - >>> solve(a + b == 3, Or(a == 0, a == 1), a != 0) - [b = 2, a = 1] + >>> a = Int('a') + >>> solve(a > 0, a < 2) + [a = 1] """ s = Solver() s.set(**keywords) diff --git a/src/api/python/z3num.py b/src/api/python/z3num.py new file mode 100644 index 000000000..581536e44 --- /dev/null +++ b/src/api/python/z3num.py @@ -0,0 +1,564 @@ +############################################ +# Copyright (c) 2012 Microsoft Corporation +# +# Z3 Python interface for Z3 numerals +# +# Author: Leonardo de Moura (leonardo) +############################################ +from z3 import * +from z3core import * +from z3printer import * + +def _to_numeral(num, ctx=None): + if isinstance(num, Numeral): + return num + else: + return Numeral(num, ctx) + +class Numeral: + """ + A Z3 numeral can be used to perform computations over arbitrary + precision integers, rationals and real algebraic numbers. + It also automatically converts python numeric values. + + >>> Numeral(2) + 2 + >>> Numeral("3/2") + 1 + 5/2 + >>> Numeral(Sqrt(2)) + 1.4142135623? + >>> Numeral(Sqrt(2)) + 2 + 3.4142135623? + >>> Numeral(Sqrt(2)) + Numeral(Sqrt(3)) + 3.1462643699? + + Z3 numerals can be used to perform computations with + values in a Z3 model. + + >>> s = Solver() + >>> x = Real('x') + >>> s.add(x*x == 2) + >>> s.add(x > 0) + >>> s.check() + sat + >>> m = s.model() + >>> m[x] + 1.4142135623? + >>> m[x] + 1 + 1.4142135623? + 1 + + The previous result is a Z3 expression. + + >>> (m[x] + 1).sexpr() + '(+ (root-obj (+ (^ x 2) (- 2)) 2) 1.0)' + + >>> Numeral(m[x]) + 1 + 2.4142135623? + >>> Numeral(m[x]).is_pos() + True + >>> Numeral(m[x])**2 + 2 + + We can also isolate the roots of polynomials. + + >>> x0, x1, x2 = RealVarVector(3) + >>> r0 = isolate_roots(x0**5 - x0 - 1) + >>> r0 + [1.1673039782?] + + In the following example, we are isolating the roots + of a univariate polynomial (on x1) obtained after substituting + x0 -> r0[0] + + >>> r1 = isolate_roots(x1**2 - x0 + 1, [ r0[0] ]) + >>> r1 + [-0.4090280898?, 0.4090280898?] + + Similarly, in the next example we isolate the roots of + a univariate polynomial (on x2) obtained after substituting + x0 -> r0[0] and x1 -> r1[0] + + >>> isolate_roots(x1*x2 + x0, [ r0[0], r1[0] ]) + [2.8538479564?] + + """ + def __init__(self, num, ctx=None): + if isinstance(num, Ast): + self.ast = num + self.ctx = z3._get_ctx(ctx) + elif isinstance(num, RatNumRef) or isinstance(num, AlgebraicNumRef): + self.ast = num.ast + self.ctx = num.ctx + elif isinstance(num, ArithRef): + r = simplify(num) + self.ast = r.ast + self.ctx = r.ctx + else: + v = RealVal(num, ctx) + self.ast = v.ast + self.ctx = v.ctx + Z3_inc_ref(self.ctx_ref(), self.as_ast()) + assert Z3_algebraic_is_value(self.ctx_ref(), self.ast) + + def __del__(self): + Z3_dec_ref(self.ctx_ref(), self.as_ast()) + + def is_integer(self): + """ Return True if the numeral is integer. + + >>> Numeral(2).is_integer() + True + >>> (Numeral(Sqrt(2)) * Numeral(Sqrt(2))).is_integer() + True + >>> Numeral(Sqrt(2)).is_integer() + False + >>> Numeral("2/3").is_integer() + False + """ + return self.is_rational() and self.denominator() == 1 + + def is_rational(self): + """ Return True if the numeral is rational. + + >>> Numeral(2).is_rational() + True + >>> Numeral("2/3").is_rational() + True + >>> Numeral(Sqrt(2)).is_rational() + False + + """ + return Z3_get_ast_kind(self.ctx_ref(), self.as_ast()) == Z3_NUMERAL_AST + + def denominator(self): + """ Return the denominator if `self` is rational. + + >>> Numeral("2/3").denominator() + 3 + """ + assert(self.is_rational()) + return Numeral(Z3_get_denominator(self.ctx_ref(), self.as_ast()), self.ctx) + + def numerator(self): + """ Return the numerator if `self` is rational. + + >>> Numeral("2/3").numerator() + 2 + """ + assert(self.is_rational()) + return Numeral(Z3_get_numerator(self.ctx_ref(), self.as_ast()), self.ctx) + + + def is_irrational(self): + """ Return True if the numeral is irrational. + + >>> Numeral(2).is_irrational() + False + >>> Numeral("2/3").is_irrational() + False + >>> Numeral(Sqrt(2)).is_irrational() + True + """ + return not self.is_rational() + + def as_long(self): + """ Return a numeral (that is an integer) as a Python long. + + >>> (Numeral(10)**20).as_long() + 100000000000000000000L + """ + assert(self.is_integer()) + return long(Z3_get_numeral_string(self.ctx_ref(), self.as_ast())) + + def approx(self, precision=10): + """Return a numeral that approximates the numeral `self`. + The result `r` is such that |r - self| <= 1/10^precision + + If `self` is rational, then the result is `self`. + + >>> x = Numeral(2).root(2) + >>> x.approx(20) + 6838717160008073720548335/4835703278458516698824704 + >>> x.approx(5) + 2965821/2097152 + >>> Numeral(2).approx(10) + 2 + """ + return self.upper(precision) + + def upper(self, precision=10): + """Return a upper bound that approximates the numeral `self`. + The result `r` is such that r - self <= 1/10^precision + + If `self` is rational, then the result is `self`. + + >>> x = Numeral(2).root(2) + >>> x.upper(20) + 6838717160008073720548335/4835703278458516698824704 + >>> x.upper(5) + 2965821/2097152 + >>> Numeral(2).upper(10) + 2 + """ + if self.is_rational(): + return self + else: + return Numeral(Z3_get_algebraic_number_upper(self.ctx_ref(), self.as_ast(), precision), self.ctx) + + def lower(self, precision=10): + """Return a lower bound that approximates the numeral `self`. + The result `r` is such that self - r <= 1/10^precision + + If `self` is rational, then the result is `self`. + + >>> x = Numeral(2).root(2) + >>> x.lower(20) + 1709679290002018430137083/1208925819614629174706176 + >>> Numeral("2/3").lower(10) + 2/3 + """ + if self.is_rational(): + return self + else: + return Numeral(Z3_get_algebraic_number_lower(self.ctx_ref(), self.as_ast(), precision), self.ctx) + + def sign(self): + """ Return the sign of the numeral. + + >>> Numeral(2).sign() + 1 + >>> Numeral(-3).sign() + -1 + >>> Numeral(0).sign() + 0 + """ + return Z3_algebraic_sign(self.ctx_ref(), self.ast) + + def is_pos(self): + """ Return True if the numeral is positive. + + >>> Numeral(2).is_pos() + True + >>> Numeral(-3).is_pos() + False + >>> Numeral(0).is_pos() + False + """ + return Z3_algebraic_is_pos(self.ctx_ref(), self.ast) + + def is_neg(self): + """ Return True if the numeral is negative. + + >>> Numeral(2).is_neg() + False + >>> Numeral(-3).is_neg() + True + >>> Numeral(0).is_neg() + False + """ + return Z3_algebraic_is_neg(self.ctx_ref(), self.ast) + + def is_zero(self): + """ Return True if the numeral is zero. + + >>> Numeral(2).is_zero() + False + >>> Numeral(-3).is_zero() + False + >>> Numeral(0).is_zero() + True + >>> sqrt2 = Numeral(2).root(2) + >>> sqrt2.is_zero() + False + >>> (sqrt2 - sqrt2).is_zero() + True + """ + return Z3_algebraic_is_zero(self.ctx_ref(), self.ast) + + def __add__(self, other): + """ Return the numeral `self + other`. + + >>> Numeral(2) + 3 + 5 + >>> Numeral(2) + Numeral(4) + 6 + >>> Numeral("2/3") + 1 + 5/3 + """ + return Numeral(Z3_algebraic_add(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) + + def __radd__(self, other): + """ Return the numeral `other + self`. + + >>> 3 + Numeral(2) + 5 + """ + return Numeral(Z3_algebraic_add(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) + + def __sub__(self, other): + """ Return the numeral `self - other`. + + >>> Numeral(2) - 3 + -1 + """ + return Numeral(Z3_algebraic_sub(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) + + def __rsub__(self, other): + """ Return the numeral `other - self`. + + >>> 3 - Numeral(2) + 1 + """ + return Numeral(Z3_algebraic_sub(self.ctx_ref(), _to_numeral(other, self.ctx).ast, self.ast), self.ctx) + + def __mul__(self, other): + """ Return the numeral `self * other`. + >>> Numeral(2) * 3 + 6 + """ + return Numeral(Z3_algebraic_mul(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) + + def __rmul__(self, other): + """ Return the numeral `other * mul`. + >>> 3 * Numeral(2) + 6 + """ + return Numeral(Z3_algebraic_mul(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) + + def __div__(self, other): + """ Return the numeral `self / other`. + >>> Numeral(2) / 3 + 2/3 + >>> Numeral(2).root(2) / 3 + 0.4714045207? + >>> Numeral(Sqrt(2)) / Numeral(Sqrt(3)) + 0.8164965809? + """ + return Numeral(Z3_algebraic_div(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast), self.ctx) + + def __rdiv__(self, other): + """ Return the numeral `other / self`. + >>> 3 / Numeral(2) + 3/2 + >>> 3 / Numeral(2).root(2) + 2.1213203435? + """ + return Numeral(Z3_algebraic_div(self.ctx_ref(), _to_numeral(other, self.ctx).ast, self.ast), self.ctx) + + def root(self, k): + """ Return the numeral `self^(1/k)`. + + >>> sqrt2 = Numeral(2).root(2) + >>> sqrt2 + 1.4142135623? + >>> sqrt2 * sqrt2 + 2 + >>> sqrt2 * 2 + 1 + 3.8284271247? + >>> (sqrt2 * 2 + 1).sexpr() + '(root-obj (+ (^ x 2) (* (- 2) x) (- 7)) 2)' + """ + return Numeral(Z3_algebraic_root(self.ctx_ref(), self.ast, k), self.ctx) + + def power(self, k): + """ Return the numeral `self^k`. + + >>> sqrt3 = Numeral(3).root(2) + >>> sqrt3 + 1.7320508075? + >>> sqrt3.power(2) + 3 + """ + return Numeral(Z3_algebraic_power(self.ctx_ref(), self.ast, k), self.ctx) + + def __pow__(self, k): + """ Return the numeral `self^k`. + + >>> sqrt3 = Numeral(3).root(2) + >>> sqrt3 + 1.7320508075? + >>> sqrt3**2 + 3 + """ + return self.power(k) + + def __lt__(self, other): + """ Return True if `self < other`. + + >>> Numeral(Sqrt(2)) < 2 + True + >>> Numeral(Sqrt(3)) < Numeral(Sqrt(2)) + False + >>> Numeral(Sqrt(2)) < Numeral(Sqrt(2)) + False + """ + return Z3_algebraic_lt(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) + + def __rlt__(self, other): + """ Return True if `other < self`. + + >>> 2 < Numeral(Sqrt(2)) + False + """ + return self > other + + def __gt__(self, other): + """ Return True if `self > other`. + + >>> Numeral(Sqrt(2)) > 2 + False + >>> Numeral(Sqrt(3)) > Numeral(Sqrt(2)) + True + >>> Numeral(Sqrt(2)) > Numeral(Sqrt(2)) + False + """ + return Z3_algebraic_gt(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) + + def __rgt__(self, other): + """ Return True if `other > self`. + + >>> 2 > Numeral(Sqrt(2)) + True + """ + return self < other + + + def __le__(self, other): + """ Return True if `self <= other`. + + >>> Numeral(Sqrt(2)) <= 2 + True + >>> Numeral(Sqrt(3)) <= Numeral(Sqrt(2)) + False + >>> Numeral(Sqrt(2)) <= Numeral(Sqrt(2)) + True + """ + return Z3_algebraic_le(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) + + def __rle__(self, other): + """ Return True if `other <= self`. + + >>> 2 <= Numeral(Sqrt(2)) + False + """ + return self >= other + + def __ge__(self, other): + """ Return True if `self >= other`. + + >>> Numeral(Sqrt(2)) >= 2 + False + >>> Numeral(Sqrt(3)) >= Numeral(Sqrt(2)) + True + >>> Numeral(Sqrt(2)) >= Numeral(Sqrt(2)) + True + """ + return Z3_algebraic_ge(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) + + def __rge__(self, other): + """ Return True if `other >= self`. + + >>> 2 >= Numeral(Sqrt(2)) + True + """ + return self <= other + + def __eq__(self, other): + """ Return True if `self == other`. + + >>> Numeral(Sqrt(2)) == 2 + False + >>> Numeral(Sqrt(3)) == Numeral(Sqrt(2)) + False + >>> Numeral(Sqrt(2)) == Numeral(Sqrt(2)) + True + """ + return Z3_algebraic_eq(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) + + def __ne__(self, other): + """ Return True if `self != other`. + + >>> Numeral(Sqrt(2)) != 2 + True + >>> Numeral(Sqrt(3)) != Numeral(Sqrt(2)) + True + >>> Numeral(Sqrt(2)) != Numeral(Sqrt(2)) + False + """ + return Z3_algebraic_neq(self.ctx_ref(), self.ast, _to_numeral(other, self.ctx).ast) + + def __str__(self): + if Z3_is_numeral_ast(self.ctx_ref(), self.ast): + return str(RatNumRef(self.ast, self.ctx)) + else: + return str(AlgebraicNumRef(self.ast, self.ctx)) + + def __repr__(self): + return self.__str__() + + def sexpr(self): + return Z3_ast_to_string(self.ctx_ref(), self.as_ast()) + + def as_ast(self): + return self.ast + + def ctx_ref(self): + return self.ctx.ref() + +def eval_sign_at(p, vs): + """ + Evaluate the sign of the polynomial `p` at `vs`. `p` is a Z3 + Expression containing arithmetic operators: +, -, *, ^k where k is + an integer; and free variables x that is_var(x) is True. Moreover, + all variables must be real. + + The result is 1 if the polynomial is positive at the given point, + -1 if negative, and 0 if zero. + + >>> x0, x1, x2 = RealVarVector(3) + >>> eval_sign_at(x0**2 + x1*x2 + 1, (Numeral(0), Numeral(1), Numeral(2))) + 1 + >>> eval_sign_at(x0**2 - 2, [ Numeral(Sqrt(2)) ]) + 0 + >>> eval_sign_at((x0 + x1)*(x0 + x2), (Numeral(0), Numeral(Sqrt(2)), Numeral(Sqrt(3)))) + 1 + """ + num = len(vs) + _vs = (Ast * num)() + for i in range(num): + _vs[i] = vs[i].ast + return Z3_algebraic_eval(p.ctx_ref(), p.as_ast(), num, _vs) + +def isolate_roots(p, vs=[]): + """ + Given a multivariate polynomial p(x_0, ..., x_{n-1}, x_n), returns the + roots of the univariate polynomial p(vs[0], ..., vs[len(vs)-1], x_n). + + Remarks: + * p is a Z3 expression that contains only arithmetic terms and free variables. + * forall i in [0, n) vs is a numeral. + + The result is a list of numerals + + >>> x0 = RealVar(0) + >>> isolate_roots(x0**5 - x0 - 1) + [1.1673039782?] + >>> x1 = RealVar(1) + >>> isolate_roots(x0**2 - x1**4 - 1, [ Numeral(Sqrt(3)) ]) + [-1.1892071150?, 1.1892071150?] + >>> x2 = RealVar(2) + >>> isolate_roots(x2**2 + x0 - x1, [ Numeral(Sqrt(3)), Numeral(Sqrt(2)) ]) + [] + """ + num = len(vs) + _vs = (Ast * num)() + for i in range(num): + _vs[i] = vs[i].ast + _roots = AstVector(Z3_algebraic_roots(p.ctx_ref(), p.as_ast(), num, _vs), p.ctx) + return [ Numeral(r) for r in _roots ] + +if __name__ == "__main__": + import doctest + if doctest.testmod().failed: + exit(1) + diff --git a/src/api/python/z3poly.py b/src/api/python/z3poly.py new file mode 100644 index 000000000..0b8bf9457 --- /dev/null +++ b/src/api/python/z3poly.py @@ -0,0 +1,37 @@ +############################################ +# Copyright (c) 2012 Microsoft Corporation +# +# Z3 Python interface for Z3 polynomials +# +# Author: Leonardo de Moura (leonardo) +############################################ +from z3 import * + +def subresultants(p, q, x): + """ + Return the non-constant subresultants of 'p' and 'q' with respect to the "variable" 'x'. + + 'p', 'q' and 'x' are Z3 expressions where 'p' and 'q' are arithmetic terms. + Note that, any subterm that cannot be viewed as a polynomial is assumed to be a variable. + Example: f(a) is a considered to be a variable b in the polynomial + + f(a)*f(a) + 2*f(a) + 1 + + >>> x, y = Reals('x y') + >>> subresultants(2*x + y, 3*x - 2*y + 2, x) + [-7*y + 4] + >>> r = subresultants(3*y*x**2 + y**3 + 1, 2*x**3 + y + 3, x) + >>> r[0] + 4*y**9 + 12*y**6 + 27*y**5 + 162*y**4 + 255*y**3 + 4 + >>> r[1] + -6*y**4 + -6*y + """ + return AstVector(Z3_polynomial_subresultants(p.ctx_ref(), p.as_ast(), q.as_ast(), x.as_ast()), p.ctx) + +if __name__ == "__main__": + import doctest + if doctest.testmod().failed: + exit(1) + + + diff --git a/src/api/z3.h b/src/api/z3.h index 8df894f40..c3e38e3f1 100644 --- a/src/api/z3.h +++ b/src/api/z3.h @@ -24,6 +24,8 @@ Notes: #include #include"z3_macros.h" #include"z3_api.h" +#include"z3_algebraic.h" +#include"z3_polynomial.h" #undef __in #undef __out diff --git a/src/api/z3_algebraic.h b/src/api/z3_algebraic.h new file mode 100644 index 000000000..eef791170 --- /dev/null +++ b/src/api/z3_algebraic.h @@ -0,0 +1,226 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + z3_algebraic.h + +Abstract: + + Additional APIs for handling Z3 algebraic numbers encoded as + Z3_ASTs + +Author: + + Leonardo de Moura (leonardo) 2012-12-07 + +Notes: + +--*/ + +#ifndef _Z3_ALGEBRAIC_H_ +#define _Z3_ALGEBRAIC_H_ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + /** + \brief Return Z3_TRUE if \c can be used as value in the Z3 real algebraic + number package. + + def_API('Z3_algebraic_is_value', BOOL, (_in(CONTEXT), _in(AST))) + */ + Z3_bool Z3_API Z3_algebraic_is_value(__in Z3_context c, __in Z3_ast a); + + /** + \brief Return the Z3_TRUE if \c a is positive, and Z3_FALSE otherwise. + + \pre Z3_algebraic_is_value(c, a) + + def_API('Z3_algebraic_is_pos', BOOL, (_in(CONTEXT), _in(AST))) + */ + Z3_bool Z3_API Z3_algebraic_is_pos(__in Z3_context c, __in Z3_ast a); + + /** + \brief Return the Z3_TRUE if \c a is negative, and Z3_FALSE otherwise. + + \pre Z3_algebraic_is_value(c, a) + + def_API('Z3_algebraic_is_neg', BOOL, (_in(CONTEXT), _in(AST))) + */ + Z3_bool Z3_API Z3_algebraic_is_neg(__in Z3_context c, __in Z3_ast a); + + /** + \brief Return the Z3_TRUE if \c a is zero, and Z3_FALSE otherwise. + + \pre Z3_algebraic_is_value(c, a) + + def_API('Z3_algebraic_is_zero', BOOL, (_in(CONTEXT), _in(AST))) + */ + Z3_bool Z3_API Z3_algebraic_is_zero(__in Z3_context c, __in Z3_ast a); + + /** + \brief Return 1 if \c a is positive, 0 if \c a is zero, and -1 if \c a is negative. + + \pre Z3_algebraic_is_value(c, a) + + def_API('Z3_algebraic_sign', INT, (_in(CONTEXT), _in(AST))) + */ + int Z3_API Z3_algebraic_sign(__in Z3_context c, __in Z3_ast a); + + /** + \brief Return the value a + b. + + \pre Z3_algebraic_is_value(c, a) + \pre Z3_algebraic_is_value(c, b) + \post Z3_algebraic_is_value(c, result) + + def_API('Z3_algebraic_add', AST, (_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_algebraic_add(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + + /** + \brief Return the value a - b. + + \pre Z3_algebraic_is_value(c, a) + \pre Z3_algebraic_is_value(c, b) + \post Z3_algebraic_is_value(c, result) + + def_API('Z3_algebraic_sub', AST, (_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_algebraic_sub(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + + /** + \brief Return the value a * b. + + \pre Z3_algebraic_is_value(c, a) + \pre Z3_algebraic_is_value(c, b) + \post Z3_algebraic_is_value(c, result) + + def_API('Z3_algebraic_mul', AST, (_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_algebraic_mul(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + + /** + \brief Return the value a / b. + + \pre Z3_algebraic_is_value(c, a) + \pre Z3_algebraic_is_value(c, b) + \pre !Z3_algebraic_is_zero(c, b) + \post Z3_algebraic_is_value(c, result) + + def_API('Z3_algebraic_div', AST, (_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_ast Z3_API Z3_algebraic_div(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + + /** + \brief Return the a^(1/k) + + \pre Z3_algebraic_is_value(c, a) + \pre k is even => !Z3_algebraic_is_neg(c, a) + \post Z3_algebraic_is_value(c, result) + + def_API('Z3_algebraic_root', AST, (_in(CONTEXT), _in(AST), _in(UINT))) + */ + Z3_ast Z3_API Z3_algebraic_root(__in Z3_context c, __in Z3_ast a, __in unsigned k); + + /** + \brief Return the a^k + + \pre Z3_algebraic_is_value(c, a) + \post Z3_algebraic_is_value(c, result) + + def_API('Z3_algebraic_power', AST, (_in(CONTEXT), _in(AST), _in(UINT))) + */ + Z3_ast Z3_API Z3_algebraic_power(__in Z3_context c, __in Z3_ast a, __in unsigned k); + + /** + \brief Return Z3_TRUE if a < b, and Z3_FALSE otherwise. + + \pre Z3_algebraic_is_value(c, a) + \pre Z3_algebraic_is_value(c, b) + + def_API('Z3_algebraic_lt', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_bool Z3_API Z3_algebraic_lt(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + + /** + \brief Return Z3_TRUE if a > b, and Z3_FALSE otherwise. + + \pre Z3_algebraic_is_value(c, a) + \pre Z3_algebraic_is_value(c, b) + + def_API('Z3_algebraic_gt', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_bool Z3_API Z3_algebraic_gt(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + + /** + \brief Return Z3_TRUE if a <= b, and Z3_FALSE otherwise. + + \pre Z3_algebraic_is_value(c, a) + \pre Z3_algebraic_is_value(c, b) + + def_API('Z3_algebraic_le', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_bool Z3_API Z3_algebraic_le(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + + /** + \brief Return Z3_TRUE if a >= b, and Z3_FALSE otherwise. + + \pre Z3_algebraic_is_value(c, a) + \pre Z3_algebraic_is_value(c, b) + + def_API('Z3_algebraic_ge', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_bool Z3_API Z3_algebraic_ge(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + + /** + \brief Return Z3_TRUE if a == b, and Z3_FALSE otherwise. + + \pre Z3_algebraic_is_value(c, a) + \pre Z3_algebraic_is_value(c, b) + + def_API('Z3_algebraic_eq', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_bool Z3_API Z3_algebraic_eq(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + + /** + \brief Return Z3_TRUE if a != b, and Z3_FALSE otherwise. + + \pre Z3_algebraic_is_value(c, a) + \pre Z3_algebraic_is_value(c, b) + + def_API('Z3_algebraic_neq', BOOL, (_in(CONTEXT), _in(AST), _in(AST))) + */ + Z3_bool Z3_API Z3_algebraic_neq(__in Z3_context c, __in Z3_ast a, __in Z3_ast b); + + /** + \brief Given a multivariate polynomial p(x_0, ..., x_{n-1}, x_n), returns the + roots of the univariate polynomial p(a[0], ..., a[n-1], x_n). + + \pre p is a Z3 expression that contains only arithmetic terms and free variables. + \pre forall i in [0, n) Z3_algebraic_is_value(c, a[i]) + \post forall r in result Z3_algebraic_is_value(c, result) + + def_API('Z3_algebraic_roots', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) + */ + Z3_ast_vector Z3_API Z3_algebraic_roots(__in Z3_context c, __in Z3_ast p, __in unsigned n, __in Z3_ast a[]); + + /** + \brief Given a multivariate polynomial p(x_0, ..., x_{n-1}), return the + sign of p(a[0], ..., a[n-1]). + + \pre p is a Z3 expression that contains only arithmetic terms and free variables. + \pre forall i in [0, n) Z3_algebraic_is_value(c, a[i]) + + def_API('Z3_algebraic_eval', INT, (_in(CONTEXT), _in(AST), _in(UINT), _in_array(2, AST))) + */ + int Z3_API Z3_algebraic_eval(__in Z3_context c, __in Z3_ast p, __in unsigned n, __in Z3_ast a[]); + + +#ifdef __cplusplus +}; +#endif // __cplusplus + +#endif diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 49d59e125..e12ce3591 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -7123,18 +7123,19 @@ END_MLAPI_EXCLUDE This means that two terms map to the same class identifier if and only if the current context implies that they are equal. - A side-effect of the function is a satisfiability check. + A side-effect of the function is a satisfiability check on the assertions on the solver that is passed in. The function return Z3_L_FALSE if the current assertions are not satisfiable. \sa Z3_check_and_get_model \sa Z3_check - \deprecated Subsumed by Z3_solver API + \deprecated To be moved outside of API. - def_API('Z3_get_implied_equalities', UINT, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _out_array(1, UINT))) + def_API('Z3_get_implied_equalities', UINT, (_in(CONTEXT), _in(SOLVER), _in(UINT), _in_array(2, AST), _out_array(2, UINT))) */ Z3_lbool Z3_API Z3_get_implied_equalities( __in Z3_context c, + __in Z3_solver s, __in unsigned num_terms, __in_ecount(num_terms) Z3_ast const terms[], __out_ecount(num_terms) unsigned class_ids[] diff --git a/src/api/z3_polynomial.h b/src/api/z3_polynomial.h new file mode 100644 index 000000000..614e654f9 --- /dev/null +++ b/src/api/z3_polynomial.h @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + z3_polynomial.h + +Abstract: + + Additional APIs for polynomials. + +Author: + + Leonardo de Moura (leonardo) 2012-12-09 + +Notes: + +--*/ + +#ifndef _Z3_POLYNOMIAL_H_ +#define _Z3_POLYNOMIAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + /** + \brief Return the nonzero subresultants of \c p and \c q with respect to the "variable" \c x. + + \pre \c p, \c q and \c x are Z3 expressions where \c p and \c q are arithmetic terms. + Note that, any subterm that cannot be viewed as a polynomial is assumed to be a variable. + Example: f(a) is a considered to be a variable in the polynomial + + f(a)*f(a) + 2*f(a) + 1 + + def_API('Z3_polynomial_subresultants', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(AST))) + */ + Z3_ast_vector Z3_API Z3_polynomial_subresultants(__in Z3_context c, __in Z3_ast p, __in Z3_ast q, __in Z3_ast x); + + +#ifdef __cplusplus +}; +#endif // __cplusplus + +#endif diff --git a/src/api/z3_private.h b/src/api/z3_private.h index 17075a6c6..91becd158 100644 --- a/src/api/z3_private.h +++ b/src/api/z3_private.h @@ -36,38 +36,6 @@ extern "C" { Z3_bool Z3_API Z3_get_numeral_rational(__in Z3_context c, __in Z3_ast a, rational& r); - /** - \brief \mlh exec_smtlib2_string c str \endmlh - Parse the given string using the SMT-LIB2 parser and execute its commands. - - It returns a formula comprising of the conjunction of assertions in the scope - (up to push/pop) at the end of the string. - The returned formula is also asserted to the logical context on return. - */ - Z3_ast Z3_API Z3_exec_smtlib2_string(__in Z3_context c, - __in Z3_string str, - __in unsigned num_sorts, - __in_ecount(num_sorts) Z3_symbol sort_names[], - __in_ecount(num_sorts) Z3_sort sorts[], - __in unsigned num_decls, - __in_ecount(num_decls) Z3_symbol decl_names[], - __in_ecount(num_decls) Z3_func_decl decls[] - ); - - /** - \brief Similar to #Z3_exec_smtlib2_string, but reads the commands from a file. - */ - Z3_ast Z3_API Z3_exec_smtlib2_file(__in Z3_context c, - __in Z3_string file_name, - __in unsigned num_sorts, - __in_ecount(num_sorts) Z3_symbol sort_names[], - __in_ecount(num_sorts) Z3_sort sorts[], - __in unsigned num_decls, - __in_ecount(num_decls) Z3_symbol decl_names[], - __in_ecount(num_decls) Z3_func_decl decls[] - ); - - #ifndef CAMLIDL #ifdef __cplusplus }; diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index 4aadafae2..a89cc18d0 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -38,13 +38,6 @@ struct arith_decl_plugin::algebraic_numbers_wrapper { unsigned mk_id(algebraic_numbers::anum const & val) { SASSERT(!m_amanager.is_rational(val)); - // TODO: avoid linear scan. Use hashtable based on the floor of val - unsigned sz = m_nums.size(); - for (unsigned i = 0; i < sz; i++) { - algebraic_numbers::anum const & other = m_nums.get(i); - if (m_amanager.eq(val, other)) - return i; - } unsigned new_id = m_id_gen.mk(); m_nums.reserve(new_id+1); m_amanager.set(m_nums[new_id], val); @@ -71,13 +64,13 @@ struct arith_decl_plugin::algebraic_numbers_wrapper { }; -arith_decl_plugin::algebraic_numbers_wrapper & arith_decl_plugin::aw() { +arith_decl_plugin::algebraic_numbers_wrapper & arith_decl_plugin::aw() const { if (m_aw == 0) - m_aw = alloc(algebraic_numbers_wrapper); + const_cast(this)->m_aw = alloc(algebraic_numbers_wrapper); return *m_aw; } -algebraic_numbers::manager & arith_decl_plugin::am() { +algebraic_numbers::manager & arith_decl_plugin::am() const { return aw().m_amanager; } @@ -509,16 +502,43 @@ void arith_decl_plugin::get_op_names(svector& op_names, symbol con } } -bool arith_decl_plugin::is_value(app* e) const { - return is_app_of(e, m_family_id, OP_NUM); +bool arith_decl_plugin::is_value(app * e) const { + return + is_app_of(e, m_family_id, OP_NUM) || + is_app_of(e, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM) || + is_app_of(e, m_family_id, OP_PI) || + is_app_of(e, m_family_id, OP_E); } -bool arith_decl_plugin::are_distinct(app* a, app* b) const { +bool arith_decl_plugin::is_unique_value(app * e) const { + return + is_app_of(e, m_family_id, OP_NUM) || + is_app_of(e, m_family_id, OP_PI) || + is_app_of(e, m_family_id, OP_E); +} + +bool arith_decl_plugin::are_equal(app * a, app * b) const { + if (decl_plugin::are_equal(a, b)) { + return true; + } + + if (is_app_of(a, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM) && is_app_of(b, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM)) { + return am().eq(aw().to_anum(a->get_decl()), aw().to_anum(b->get_decl())); + } + + return false; +} + +bool arith_decl_plugin::are_distinct(app * a, app * b) const { TRACE("are_distinct_bug", tout << mk_ismt2_pp(a, *m_manager) << "\n" << mk_ismt2_pp(b, *m_manager) << "\n";); if (decl_plugin::are_distinct(a,b)) { return true; } + if (is_app_of(a, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM) && is_app_of(b, m_family_id, OP_IRRATIONAL_ALGEBRAIC_NUM)) { + return am().neq(aw().to_anum(a->get_decl()), aw().to_anum(b->get_decl())); + } + #define is_non_zero(e) is_app_of(e,m_family_id, OP_NUM) && !to_app(e)->get_decl()->get_parameter(0).get_rational().is_zero() if (is_app_of(a, m_family_id, OP_ADD) && diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 6a377c577..c39768d4c 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -141,8 +141,8 @@ public: virtual ~arith_decl_plugin(); virtual void finalize(); - algebraic_numbers::manager & am(); - algebraic_numbers_wrapper & aw(); + algebraic_numbers::manager & am() const; + algebraic_numbers_wrapper & aw() const; virtual void del(parameter const & p); virtual parameter translate(parameter const & p, decl_plugin & target); @@ -159,9 +159,13 @@ public: virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range); - virtual bool is_value(app* e) const; + virtual bool is_value(app * e) const; - virtual bool are_distinct(app* a, app* b) const; + virtual bool is_unique_value(app * e) const; + + virtual bool are_equal(app * a, app * b) const; + + virtual bool are_distinct(app * a, app * b) const; virtual void get_op_names(svector & op_names, symbol const & logic); @@ -180,7 +184,7 @@ public: virtual expr * get_some_value(sort * s); - void set_cancel(bool f); + virtual void set_cancel(bool f); }; class arith_util { diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index ae82dcadf..3fb422a2e 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -872,6 +872,10 @@ bool basic_decl_plugin::is_value(app* a) const { return a->get_decl() == m_true_decl || a->get_decl() == m_false_decl; } +bool basic_decl_plugin::is_unique_value(app* a) const { + return is_value(a); +} + void basic_decl_plugin::finalize() { #define DEC_REF(FIELD) if (FIELD) { m_manager->dec_ref(FIELD); } #define DEC_ARRAY_REF(FIELD) m_manager->dec_array_ref(FIELD.size(), FIELD.begin()) @@ -1151,6 +1155,10 @@ bool model_value_decl_plugin::is_value(app* n) const { return is_app_of(n, m_family_id, OP_MODEL_VALUE); } +bool model_value_decl_plugin::is_unique_value(app* n) const { + return is_value(n); +} + // ----------------------------------- // // user_sort_plugin @@ -1328,6 +1336,12 @@ ast_manager::~ast_manager() { } } +void ast_manager::set_cancel(bool f) { + for (unsigned i = 0; i < m_plugins.size(); i++) { + m_plugins[i]->set_cancel(f); + } +} + void ast_manager::compact_memory() { m_alloc.consolidate(); unsigned capacity = m_ast_table.capacity(); @@ -1442,6 +1456,27 @@ bool ast_manager::is_value(expr* e) const { return false; } +bool ast_manager::is_unique_value(expr* e) const { + decl_plugin const * p = 0; + if (is_app(e)) { + p = get_plugin(to_app(e)->get_family_id()); + return p && p->is_unique_value(to_app(e)); + } + return false; +} + +bool ast_manager::are_equal(expr * a, expr * b) const { + if (is_app(a) && is_app(b)) { + app* ap = to_app(a), *bp = to_app(b); + decl_plugin const * p = get_plugin(ap->get_family_id()); + if (!p) { + p = get_plugin(bp->get_family_id()); + } + return p && p->are_equal(ap, bp); + } + return false; +} + bool ast_manager::are_distinct(expr* a, expr* b) const { if (is_app(a) && is_app(b)) { app* ap = to_app(a), *bp = to_app(b); diff --git a/src/ast/ast.h b/src/ast/ast.h index 2a0872721..e9da41377 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -921,6 +921,8 @@ public: virtual ~decl_plugin() {} virtual void finalize() {} + virtual void set_cancel(bool f) {} + virtual decl_plugin * mk_fresh() = 0; family_id get_family_id() const { return m_family_id; } @@ -933,9 +935,39 @@ public: virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const* parameters, unsigned num_args, expr * const * args, sort * range); - virtual bool is_value(app*) const { return false; } + /** + \brief Return true if the plugin can decide whether two + interpreted constants are equal or not. + + For all a, b: + If is_value(a) and is_value(b) + Then, + are_equal(a, b) != are_distinct(a, b) + + The may be much more expensive than checking a pointer. - virtual bool are_distinct(app* a, app* b) const { return a != b && is_value(a) && is_value(b); } + We need this because some plugin values are too expensive too canonize. + */ + virtual bool is_value(app * a) const { return false; } + + /** + \brief Return true if \c a is a unique plugin value. + The following property should hold for unique theory values: + + For all a, b: + If is_unique_value(a) and is_unique_value(b) + Then, + a == b (pointer equality) + IFF + the interpretations of these theory terms are equal. + + \remark This is a stronger version of is_value. + */ + virtual bool is_unique_value(app * a) const { return false; } + + virtual bool are_equal(app * a, app * b) const { return a == b && is_unique_value(a) && is_unique_value(b); } + + virtual bool are_distinct(app * a, app * b) const { return a != b && is_unique_value(a) && is_unique_value(b); } virtual void get_op_names(svector & op_names, symbol const & logic = symbol()) {} @@ -1080,6 +1112,8 @@ public: virtual void get_sort_names(svector & sort_names, symbol const & logic); virtual bool is_value(app* a) const; + + virtual bool is_unique_value(app* a) const; sort * mk_bool_sort() const { return m_bool_sort; } sort * mk_proof_sort() const { return m_proof_sort; } @@ -1116,7 +1150,6 @@ public: virtual decl_plugin * mk_fresh() { return alloc(label_decl_plugin); } - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); /** @@ -1198,6 +1231,8 @@ public: unsigned arity, sort * const * domain, sort * range); virtual bool is_value(app* n) const; + + virtual bool is_unique_value(app* a) const; }; // ----------------------------------- @@ -1367,6 +1402,9 @@ public: ast_manager(ast_manager const & src, bool disable_proofs = false); ~ast_manager(); + // propagate cancellation signal to decl_plugins + void set_cancel(bool f); + bool has_trace_stream() const { return m_trace_stream != 0; } std::ostream & trace_stream() { SASSERT(has_trace_stream()); return *m_trace_stream; } @@ -1442,9 +1480,13 @@ public: */ void set_next_expr_id(unsigned id); - bool is_value(expr* e) const; + bool is_value(expr * e) const; + + bool is_unique_value(expr * e) const; - bool are_distinct(expr* a, expr* b) const; + bool are_equal(expr * a, expr * b) const; + + bool are_distinct(expr * a, expr * b) const; bool contains(ast * a) const { return m_ast_table.contains(a); } diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index a98f39323..34f2baf6b 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -260,8 +260,10 @@ public: virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range); - virtual bool is_value(app* e) const; + virtual bool is_value(app * e) const; + virtual bool is_unique_value(app * e) const { return is_value(e); } + virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h index a8bd28d8e..96b678fb2 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.h @@ -146,6 +146,9 @@ public: virtual bool is_fully_interp(sort const * s) const; virtual bool is_value(app* e) const; + + virtual bool is_unique_value(app * e) const { return is_value(e); } + private: bool is_value_visit(expr * arg, ptr_buffer & todo) const; }; diff --git a/src/ast/dl_decl_plugin.h b/src/ast/dl_decl_plugin.h index 32e3c6da9..559aff7bd 100644 --- a/src/ast/dl_decl_plugin.h +++ b/src/ast/dl_decl_plugin.h @@ -130,7 +130,8 @@ namespace datalog { virtual void get_sort_names(svector & sort_names, symbol const & logic); - virtual bool is_value(app* e) const { return is_app_of(e, m_family_id, OP_DL_CONSTANT); } + virtual bool is_value(app * e) const { return is_app_of(e, m_family_id, OP_DL_CONSTANT); } + virtual bool is_unique_value(app * e) const { return is_value(e); } }; diff --git a/src/ast/expr2polynomial.cpp b/src/ast/expr2polynomial.cpp index 72bd72aa7..fbabcb150 100644 --- a/src/ast/expr2polynomial.cpp +++ b/src/ast/expr2polynomial.cpp @@ -49,21 +49,24 @@ struct expr2polynomial::imp { polynomial::polynomial_ref_vector m_presult_stack; polynomial::scoped_numeral_vector m_dresult_stack; + bool m_use_var_idxs; + volatile bool m_cancel; - imp(expr2polynomial & w, ast_manager & am, polynomial::manager & pm, expr2var * e2v): + imp(expr2polynomial & w, ast_manager & am, polynomial::manager & pm, expr2var * e2v, bool use_var_idxs): m_wrapper(w), m_am(am), m_autil(am), m_pm(pm), - m_expr2var(e2v == 0 ? alloc(expr2var, am) : e2v), - m_expr2var_owner(e2v == 0), + m_expr2var(e2v == 0 && !use_var_idxs ? alloc(expr2var, am) : e2v), + m_expr2var_owner(e2v == 0 && !use_var_idxs), m_var2expr(am), m_cached_domain(am), m_cached_polynomials(pm), m_cached_denominators(pm.m()), m_presult_stack(pm), m_dresult_stack(pm.m()), + m_use_var_idxs(use_var_idxs), m_cancel(false) { } @@ -95,6 +98,14 @@ struct expr2polynomial::imp { cooperate("expr2polynomial"); } + void throw_not_polynomial() { + throw default_exception("the given expression is not a polynomial"); + } + + void throw_no_int_var() { + throw default_exception("integer variables are not allowed in the given polynomial"); + } + void push_frame(app * t) { m_frame_stack.push_back(frame(t)); } @@ -127,14 +138,26 @@ struct expr2polynomial::imp { } void store_var_poly(expr * t) { - polynomial::var x = m_expr2var->to_var(t); - if (x == UINT_MAX) { - bool is_int = m_autil.is_int(t); - x = m_wrapper.mk_var(is_int); - m_expr2var->insert(t, x); - if (x >= m_var2expr.size()) - m_var2expr.resize(x+1, 0); - m_var2expr.set(x, t); + polynomial::var x; + if (m_use_var_idxs) { + SASSERT(::is_var(t)); + if (m_autil.is_int(t)) + throw_no_int_var(); + unsigned idx = to_var(t)->get_idx(); + while (idx >= m_pm.num_vars()) + m_pm.mk_var(); + x = static_cast(idx); + } + else { + x = m_expr2var->to_var(t); + if (x == UINT_MAX) { + bool is_int = m_autil.is_int(t); + x = m_wrapper.mk_var(is_int); + m_expr2var->insert(t, x); + if (x >= m_var2expr.size()) + m_var2expr.resize(x+1, 0); + m_var2expr.set(x, t); + } } polynomial::numeral one(1); store_result(t, pm().mk_polynomial(x), one); @@ -160,7 +183,10 @@ struct expr2polynomial::imp { rational k; SASSERT(t->get_num_args() == 2); if (!m_autil.is_numeral(t->get_arg(1), k) || !k.is_int() || !k.is_unsigned()) { - store_var_poly(t); + if (m_use_var_idxs) + throw_not_polynomial(); + else + store_var_poly(t); return true; } push_frame(t); @@ -168,6 +194,8 @@ struct expr2polynomial::imp { } default: // can't handle operator + if (m_use_var_idxs) + throw_not_polynomial(); store_var_poly(t); return true; } @@ -190,6 +218,8 @@ struct expr2polynomial::imp { SASSERT(is_app(t)); if (!m_autil.is_arith_expr(t)) { + if (m_use_var_idxs) + throw_not_polynomial(); store_var_poly(t); return true; } @@ -378,19 +408,25 @@ struct expr2polynomial::imp { for (unsigned i = 0; i < sz; i++) { margs.reset(); - polynomial::monomial * m = pm().get_monomial(p, i); + polynomial::monomial * _m = pm().get_monomial(p, i); polynomial::numeral const & a = pm().coeff(p, i); if (!nm().is_one(a)) { margs.push_back(m_autil.mk_numeral(rational(a), is_int)); } - unsigned msz = pm().size(m); + unsigned msz = pm().size(_m); for (unsigned j = 0; j < msz; j++) { - polynomial::var x = pm().get_var(m, j); - expr * t = m_var2expr.get(x); - if (m_wrapper.is_int(x) && !is_int) { - t = m_autil.mk_to_real(t); + polynomial::var x = pm().get_var(_m, j); + expr * t; + if (m_use_var_idxs) { + t = m().mk_var(x, m_autil.mk_real()); } - unsigned d = pm().degree(m, j); + else { + t = m_var2expr.get(x); + if (m_wrapper.is_int(x) && !is_int) { + t = m_autil.mk_to_real(t); + } + } + unsigned d = pm().degree(_m, j); if (use_power && d > 1) { margs.push_back(m_autil.mk_power(t, m_autil.mk_numeral(rational(d), is_int))); } @@ -426,8 +462,8 @@ struct expr2polynomial::imp { } }; -expr2polynomial::expr2polynomial(ast_manager & am, polynomial::manager & pm, expr2var * e2v) { - m_imp = alloc(imp, *this, am, pm, e2v); +expr2polynomial::expr2polynomial(ast_manager & am, polynomial::manager & pm, expr2var * e2v, bool use_var_idxs) { + m_imp = alloc(imp, *this, am, pm, e2v, use_var_idxs); } expr2polynomial::~expr2polynomial() { @@ -451,10 +487,12 @@ void expr2polynomial::to_expr(polynomial::polynomial_ref const & p, bool use_pow } bool expr2polynomial::is_var(expr * t) const { + SASSERT(!m_imp->m_use_var_idxs); return m_imp->m_expr2var->is_var(t); } expr2var const & expr2polynomial::get_mapping() const { + SASSERT(!m_imp->m_use_var_idxs); return *(m_imp->m_expr2var); } diff --git a/src/ast/expr2polynomial.h b/src/ast/expr2polynomial.h index fbeda019d..8934f3f24 100644 --- a/src/ast/expr2polynomial.h +++ b/src/ast/expr2polynomial.h @@ -29,7 +29,24 @@ class expr2polynomial { struct imp; imp * m_imp; public: - expr2polynomial(ast_manager & am, polynomial::manager & pm, expr2var * e2v); + expr2polynomial(ast_manager & am, + polynomial::manager & pm, + expr2var * e2v, + /* + If true, the expressions converted into + polynomials should only contain Z3 free variables. + A Z3 variable x, with idx i, is converted into + the variable i of the polynomial manager pm. + + An exception is thrown if there is a mismatch between + the sorts x and the variable in the polynomial manager. + + The argument e2v is ignored when use_var_idxs is true. + + Moreover, only real variables are allowed. + */ + bool use_var_idxs = false + ); virtual ~expr2polynomial(); ast_manager & m() const; @@ -63,6 +80,8 @@ public: /** \brief Return the mapping from expressions to variables + + \pre the object was created using use_var_idxs = false. */ expr2var const & get_mapping() const; @@ -74,10 +93,10 @@ public: /** \brief Return true if the variable is associated with an expression of integer sort. */ - virtual bool is_int(polynomial::var x) const = 0; + virtual bool is_int(polynomial::var x) const { UNREACHABLE(); return false; } protected: - virtual polynomial::var mk_var(bool is_int) = 0; + virtual polynomial::var mk_var(bool is_int) { UNREACHABLE(); return polynomial::null_var; } }; class default_expr2polynomial : public expr2polynomial { diff --git a/src/ast/float_decl_plugin.h b/src/ast/float_decl_plugin.h index c4503349b..6f1ef5ec2 100644 --- a/src/ast/float_decl_plugin.h +++ b/src/ast/float_decl_plugin.h @@ -141,6 +141,7 @@ public: virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); virtual bool is_value(app* e) const; + virtual bool is_unique_value(app* e) const { return is_value(e); } mpf_manager & fm() { return m_fm; } func_decl * mk_value_decl(mpf const & v); diff --git a/src/ast/pattern/pattern_inference_params_helper.pyg b/src/ast/pattern/pattern_inference_params_helper.pyg index 5d64e8e52..52c6c653e 100644 --- a/src/ast/pattern/pattern_inference_params_helper.pyg +++ b/src/ast/pattern/pattern_inference_params_helper.pyg @@ -5,7 +5,7 @@ def_module_params(class_name='pattern_inference_params_helper', params=(('max_multi_patterns', UINT, 0, 'when patterns are not provided, the prover uses a heuristic to infer them, this option sets the threshold on the number of extra multi-patterns that can be created; by default, the prover creates at most one multi-pattern when there is no unary pattern'), ('block_loop_patterns', BOOL, True, 'block looping patterns during pattern inference'), ('arith', UINT, 1, '0 - do not infer patterns with arithmetic terms, 1 - use patterns with arithmetic terms if there is no other pattern, 2 - always use patterns with arithmetic terms'), - ('use_database', BOOL, True, 'use pattern database'), + ('use_database', BOOL, False, 'use pattern database'), ('arith_weight', UINT, 5, 'default weight for quantifiers where the only available pattern has nested arithmetic terms'), ('non_nested_arith_weight', UINT, 10, 'default weight for quantifiers where the only available pattern has non nested arithmetic terms'), ('pull_quantifiers', BOOL, True, 'pull nested quantifiers, if no pattern was found'), diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 4fcaf02fe..b5f18a461 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -732,7 +732,7 @@ br_status bool_rewriter::mk_distinct_core(unsigned num_args, expr * const * args return BR_DONE; } visited.mark(arg); - if (!m().is_value(arg)) + if (!m().is_unique_value(arg)) all_value = false; } if (all_value) { diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 07ee7698d..966544b78 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -200,7 +200,12 @@ struct th_rewriter_cfg : public default_rewriter_cfg { expr * mk_eq_value(expr * lhs, expr * value) { SASSERT(m().is_value(value)); if (m().is_value(lhs)) { - return lhs == value ? m().mk_true() : m().mk_false(); + if (m().are_equal(lhs, value)) { + return m().mk_true(); + } + else if (m().are_distinct(lhs, value)) { + return m().mk_false(); + } } return m().mk_eq(lhs, value); } @@ -483,10 +488,13 @@ struct th_rewriter_cfg : public default_rewriter_cfg { f = to_app(t1)->get_decl(); return unify_core(to_app(t1), t2, new_t1, new_t2, c, first); } - else { + else if (is_arith_bv_app(t2)) { f = to_app(t2)->get_decl(); return unify_core(to_app(t2), t1, new_t2, new_t1, c, first); } + else { + return false; + } } // Apply transformations of the form diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index c055694eb..1bb3f3581 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -112,7 +112,10 @@ public: virtual void get_sort_names(svector & sort_names, symbol const & logic); - virtual bool is_value(app* e) const; + virtual bool is_value(app * e) const; + + virtual bool is_unique_value(app * e) const { return is_value(e); } + }; diff --git a/src/ast/simplifier/array_simplifier_plugin.cpp b/src/ast/simplifier/array_simplifier_plugin.cpp index 75c3cdbce..bf9ca8ffe 100644 --- a/src/ast/simplifier/array_simplifier_plugin.cpp +++ b/src/ast/simplifier/array_simplifier_plugin.cpp @@ -293,7 +293,7 @@ bool array_simplifier_plugin::all_const_array(unsigned num_args, expr* const* ar bool array_simplifier_plugin::all_values(unsigned num_args, expr* const* args) const { for (unsigned i = 0; i < num_args; ++i) { - if (!m_manager.is_value(args[i])) { + if (!m_manager.is_unique_value(args[i])) { return false; } } @@ -331,7 +331,7 @@ lbool array_simplifier_plugin::eq_default(expr* def, unsigned arity, unsigned nu if (st[i][arity] == def) { continue; } - if (m_manager.is_value(st[i][arity]) && m_manager.is_value(def)) { + if (m_manager.is_unique_value(st[i][arity]) && m_manager.is_unique_value(def)) { return l_false; } return l_undef; @@ -342,7 +342,7 @@ lbool array_simplifier_plugin::eq_default(expr* def, unsigned arity, unsigned nu bool array_simplifier_plugin::insert_table(expr* def, unsigned arity, unsigned num_st, expr*const* const* st, arg_table& table) { for (unsigned i = 0; i < num_st; ++i ) { for (unsigned j = 0; j < arity; ++j) { - if (!m_manager.is_value(st[i][j])) { + if (!m_manager.is_unique_value(st[i][j])) { return false; } } @@ -380,12 +380,12 @@ lbool array_simplifier_plugin::eq_stores(expr* def, unsigned arity, unsigned num table2.erase(e1); continue; } - if (m_manager.is_value(v1) && m_manager.is_value(v2)) { + if (m_manager.is_unique_value(v1) && m_manager.is_unique_value(v2)) { return l_false; } return l_undef; } - else if (m_manager.is_value(v1) && m_manager.is_value(def) && v1 != def) { + else if (m_manager.is_unique_value(v1) && m_manager.is_unique_value(def) && v1 != def) { return l_false; } } @@ -394,7 +394,7 @@ lbool array_simplifier_plugin::eq_stores(expr* def, unsigned arity, unsigned num for (; it != end; ++it) { args_entry const & e = *it; expr* v = e.m_args[arity]; - if (m_manager.is_value(v) && m_manager.is_value(def) && v != def) { + if (m_manager.is_unique_value(v) && m_manager.is_unique_value(def) && v != def) { return l_false; } } @@ -431,7 +431,7 @@ bool array_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & resul return false; } } - else if (m_manager.is_value(c1) && m_manager.is_value(c2)) { + else if (m_manager.is_unique_value(c1) && m_manager.is_unique_value(c2)) { result = m_manager.mk_false(); return true; } @@ -464,7 +464,7 @@ array_simplifier_plugin::mk_select_const(expr* m, app* index, expr_ref& result) // // Unfold and cache the store while searching for value of index. // - while (is_store(a) && m_manager.is_value(to_app(a)->get_arg(1))) { + while (is_store(a) && m_manager.is_unique_value(to_app(a)->get_arg(1))) { app* b = to_app(a); app* c = to_app(b->get_arg(1)); @@ -728,7 +728,7 @@ void array_simplifier_plugin::mk_select(unsigned num_args, expr * const * args, return; } - bool is_const_select = num_args == 2 && m_manager.is_value(args[1]); + bool is_const_select = num_args == 2 && m_manager.is_unique_value(args[1]); app* const_index = is_const_select?to_app(args[1]):0; unsigned num_const_stores = 0; expr_ref tmp(m_manager); @@ -766,7 +766,7 @@ void array_simplifier_plugin::mk_select(unsigned num_args, expr * const * args, expr * else_branch = 0; entry[0] = nested_array; if (is_const_select) { - if (m_manager.is_value(to_app(m)->get_arg(1))) { + if (m_manager.is_unique_value(to_app(m)->get_arg(1))) { app* const_index2 = to_app(to_app(m)->get_arg(1)); // // we found the value, all other stores are different. diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 0380e91ea..e86e56b69 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -37,6 +37,7 @@ Notes: #include"well_sorted.h" #include"model_evaluator.h" #include"for_each_expr.h" +#include"scoped_timer.h" func_decls::func_decls(ast_manager & m, func_decl * f): m_decls(TAG(func_decl*, f, 0)) { @@ -581,8 +582,8 @@ void cmd_context::init_manager_core(bool new_manager) { // it prevents clashes with builtin types. insert(pm().mk_plist_decl()); } - if (m_solver) { - init_solver_options(m_solver.get()); + if (m_solver_factory) { + mk_solver(); } m_check_logic.set_logic(m(), m_logic); } @@ -591,14 +592,9 @@ void cmd_context::init_manager() { SASSERT(m_manager == 0); SASSERT(m_pmanager == 0); m_check_sat_result = 0; - m_manager = alloc(ast_manager, - produce_proofs() ? PGM_FINE : PGM_DISABLED, - m_params.m_trace ? m_params.m_trace_file_name.c_str() : 0); + m_manager = m_params.mk_ast_manager(); m_pmanager = alloc(pdecl_manager, *m_manager); init_manager_core(true); - // PARAM-TODO - // if (params().m_smtlib2_compliant) - // m_manager->enable_int_real_coercions(false); } void cmd_context::init_external_manager() { @@ -1123,7 +1119,7 @@ void cmd_context::reset(bool finalize) { reset_func_decls(); restore_assertions(0); if (m_solver) - m_solver->reset(); + m_solver = 0; m_pp_env = 0; m_dt_eh = 0; if (m_manager) { @@ -1304,9 +1300,11 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions if (m_solver) { m_check_sat_result = m_solver.get(); // solver itself stores the result. m_solver->set_progress_callback(this); + unsigned timeout = m_params.m_timeout; scoped_watch sw(*this); cancel_eh eh(*m_solver); scoped_ctrl_c ctrlc(eh); + scoped_timer timer(timeout, &eh); lbool r; try { r = m_solver->check_sat(num_assumptions, assumptions); @@ -1443,23 +1441,18 @@ void cmd_context::validate_model() { } } -void cmd_context::init_solver_options(solver * s) { - m_solver->set_produce_unsat_cores(produce_unsat_cores()); - m_solver->set_produce_models(produce_models()); - m_solver->set_produce_proofs(produce_proofs()); - m_solver->init(m(), m_logic); - if (!m_params.m_auto_config) { - params_ref p; - p.set_bool("auto_config", false); - m_solver->updt_params(p); - } +void cmd_context::mk_solver() { + bool proofs_enabled, models_enabled, unsat_core_enabled; + params_ref p; + m_params.get_solver_params(m(), p, proofs_enabled, models_enabled, unsat_core_enabled); + m_solver = (*m_solver_factory)(m(), p, proofs_enabled, models_enabled, unsat_core_enabled, m_logic); } -void cmd_context::set_solver(solver * s) { +void cmd_context::set_solver_factory(solver_factory * f) { + m_solver_factory = f; m_check_sat_result = 0; - m_solver = s; - if (has_manager() && s != 0) { - init_solver_options(s); + if (has_manager() && f != 0) { + mk_solver(); // assert formulas and create scopes in the new solver. unsigned lim = 0; svector::iterator it = m_scopes.begin(); diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 2edc87ca6..63e5e7040 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -185,6 +185,7 @@ protected: }; svector m_scopes; + scoped_ptr m_solver_factory; ref m_solver; ref m_check_sat_result; @@ -243,7 +244,7 @@ protected: void print_unsupported_msg() { regular_stream() << "unsupported" << std::endl; } void print_unsupported_info(symbol const& s) { if (s != symbol::null) diagnostic_stream() << "; " << s << std::endl;} - void init_solver_options(solver * s); + void mk_solver(); public: cmd_context(bool main_ctx = true, ast_manager * m = 0, symbol const & l = symbol::null); @@ -289,8 +290,7 @@ public: pdecl_manager & pm() const { if (!m_pmanager) const_cast(this)->init_manager(); return *m_pmanager; } sexpr_manager & sm() const { if (!m_sexpr_manager) const_cast(this)->m_sexpr_manager = alloc(sexpr_manager); return *m_sexpr_manager; } - void set_solver(solver * s); - solver * get_solver() const { return m_solver.get(); } + void set_solver_factory(solver_factory * s); void set_check_sat_result(check_sat_result * r) { m_check_sat_result = r; } check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); } check_sat_state cs_state() const; diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index 495f73b75..2752967dd 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -20,6 +20,8 @@ Notes: #include"context_params.h" #include"gparams.h" #include"params.h" +#include"ast.h" +#include"solver.h" context_params::context_params() { updt_params(); @@ -39,7 +41,7 @@ void context_params::set_bool(bool & opt, char const * param, char const * value void context_params::set(char const * param, char const * value) { std::string p = param; - unsigned n = p.size(); + unsigned n = static_cast(p.size()); for (unsigned i = 0; i < n; i++) { if (p[i] >= 'A' && p[i] <= 'Z') p[i] = p[i] - 'A' + 'a'; @@ -77,6 +79,9 @@ void context_params::set(char const * param, char const * value) { else if (p == "debug_ref_count") { set_bool(m_debug_ref_count, param, value); } + else if (p == "smtlib2_compliant") { + set_bool(m_smtlib2_compliant, param, value); + } else { throw default_exception("unknown parameter '%s'", p.c_str()); } @@ -97,6 +102,7 @@ void context_params::updt_params(params_ref const & p) { m_trace_file_name = p.get_str("trace_file_name", "z3.log"); m_unsat_core = p.get_bool("unsat_core", false); m_debug_ref_count = p.get_bool("debug_ref_count", false); + m_smtlib2_compliant = p.get_bool("smtlib2_compliant", false); } void context_params::collect_param_descrs(param_descrs & d) { @@ -111,4 +117,36 @@ void context_params::collect_param_descrs(param_descrs & d) { d.insert("trace_file_name", CPK_STRING, "trace out file name (see option 'trace')", "z3.log"); d.insert("unsat_core", CPK_BOOL, "unsat-core generation for solvers, this parameter can be overwritten when creating a solver, not every solver in Z3 supports unsat core generation", "false"); d.insert("debug_ref_count", CPK_BOOL, "debug support for AST reference counting", "false"); + d.insert("smtlib2_compliant", CPK_BOOL, "enable/disable SMT-LIB 2.0 compliance", "false"); } + +params_ref context_params::merge_default_params(params_ref const & p) { + if (!m_auto_config && !p.contains("auto_config")) { + params_ref new_p = p; + new_p.set_bool("auto_config", false); + return new_p; + } + else { + return p; + } +} + +void context_params::get_solver_params(ast_manager const & m, params_ref & p, bool & proofs_enabled, bool & models_enabled, bool & unsat_core_enabled) { + proofs_enabled = m.proofs_enabled() && p.get_bool("proof", m_proof); + models_enabled = p.get_bool("model", m_model); + unsat_core_enabled = p.get_bool("unsat_core", m_unsat_core); + p = merge_default_params(p); +} + +ast_manager * context_params::mk_ast_manager() { + ast_manager * r = alloc(ast_manager, + m_proof ? PGM_FINE : PGM_DISABLED, + m_trace ? m_trace_file_name.c_str() : 0); + if (m_smtlib2_compliant) + r->enable_int_real_coercions(false); + if (m_debug_ref_count) + r->debug_ref_count(); + return r; +} + + diff --git a/src/cmd_context/context_params.h b/src/cmd_context/context_params.h index 6b42b5b50..526b4f517 100644 --- a/src/cmd_context/context_params.h +++ b/src/cmd_context/context_params.h @@ -21,6 +21,7 @@ Notes: #define _CONTEXT_PARAMS_H_ #include"params.h" +class ast_manager; class context_params { void set_bool(bool & opt, char const * param, char const * value); @@ -35,6 +36,7 @@ public: bool m_model; bool m_model_validate; bool m_unsat_core; + bool m_smtlib2_compliant; // it must be here because it enable/disable the use of coercions in the ast_manager. unsigned m_timeout; context_params(); @@ -45,6 +47,23 @@ public: /* REG_PARAMS('context_params::collect_param_descrs') */ + + /** + \brief Goodies for extracting parameters for creating a solver object. + */ + void get_solver_params(ast_manager const & m, params_ref & p, bool & proofs_enabled, bool & models_enabled, bool & unsat_core_enabled); + + /** + \brief Include in p parameters derived from this context_params. + These are parameters that are meaningful for tactics and solvers. + Example: auto_config + */ + params_ref merge_default_params(params_ref const & p); + + /** + \brief Create an AST manager using this configuration. + */ + ast_manager * mk_ast_manager(); }; diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index ca87406c7..27014bca9 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -36,7 +36,7 @@ tactic_cmd::~tactic_cmd() { dealloc(m_factory); } -tactic * tactic_cmd::mk(ast_manager & m) { +tactic * tactic_cmd::mk(ast_manager & m) { return (*m_factory)(m, params_ref()); } @@ -185,7 +185,7 @@ public: } virtual void execute(cmd_context & ctx) { - params_ref p = ps(); + params_ref p = ctx.params().merge_default_params(ps()); tactic_ref tref = using_params(sexpr2tactic(ctx, m_tactic), p); tref->set_logic(ctx.get_logic()); ast_manager & m = ctx.m(); @@ -295,7 +295,7 @@ public: } virtual void execute(cmd_context & ctx) { - params_ref p = ps(); + params_ref p = ctx.params().merge_default_params(ps()); tactic_ref tref = using_params(sexpr2tactic(ctx, m_tactic), p); { tactic & t = *(tref.get()); diff --git a/src/math/polynomial/algebraic_numbers.h b/src/math/polynomial/algebraic_numbers.h index 4735a875b..a4669eae6 100644 --- a/src/math/polynomial/algebraic_numbers.h +++ b/src/math/polynomial/algebraic_numbers.h @@ -64,6 +64,8 @@ namespace algebraic_numbers { static void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } void set_cancel(bool f); + + void cancel() { set_cancel(true); } void updt_params(params_ref const & p); diff --git a/src/math/polynomial/polynomial.h b/src/math/polynomial/polynomial.h index 7cefe6f56..d17d2ac5a 100644 --- a/src/math/polynomial/polynomial.h +++ b/src/math/polynomial/polynomial.h @@ -219,6 +219,7 @@ namespace polynomial { void set_zp(uint64 p); void set_cancel(bool f); + void cancel() { set_cancel(true); } /** \brief Abstract event handler. diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 678d95c86..ae4c657bd 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -49,10 +49,10 @@ void func_entry::set_result(ast_manager & m, expr * r) { m_result = r; } -bool func_entry::eq_args(unsigned arity, expr * const * args) const { +bool func_entry::eq_args(ast_manager & m, unsigned arity, expr * const * args) const { unsigned i = 0; for (; i < arity; i++) { - if (m_args[i] != args[i]) + if (!m.are_equal(m_args[i], args[i])) return false; } return true; @@ -131,7 +131,7 @@ bool func_interp::is_constant() const { } /** - \brief Return a func_entry e such that e.m_args[i] == args[i] for all i in [0, m_arity). + \brief Return a func_entry e such that m().are_equal(e.m_args[i], args[i]) for all i in [0, m_arity). If such entry does not exist then return 0, and store set args_are_values to true if for all entries e e.args_are_values() is true. */ @@ -140,7 +140,7 @@ func_entry * func_interp::get_entry(expr * const * args) const { ptr_vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { func_entry * curr = *it; - if (curr->eq_args(m_arity, args)) + if (curr->eq_args(m(), m_arity, args)) return curr; } return 0; diff --git a/src/model/func_interp.h b/src/model/func_interp.h index b10fe0dad..fd21a6a7c 100644 --- a/src/model/func_interp.h +++ b/src/model/func_interp.h @@ -58,9 +58,9 @@ public: expr * get_arg(unsigned idx) const { return m_args[idx]; } expr * const * get_args() const { return m_args; } /** - \brief Return true if m_args[i] == args[i] for all i in [0, arity) + \brief Return true if m.are_equal(m_args[i], args[i]) for all i in [0, arity) */ - bool eq_args(unsigned arity, expr * const * args) const; + bool eq_args(ast_manager & m, unsigned arity, expr * const * args) const; }; class func_interp { diff --git a/src/muz_qe/dl_bmc_engine.cpp b/src/muz_qe/dl_bmc_engine.cpp index 27161320e..b5ffa808a 100644 --- a/src/muz_qe/dl_bmc_engine.cpp +++ b/src/muz_qe/dl_bmc_engine.cpp @@ -29,30 +29,1382 @@ Revision History: #include "model_smt2_pp.h" #include "ast_smt_pp.h" #include "well_sorted.h" +#include "rewriter_def.h" namespace datalog { + + // --------------------------------------------------------------------------- + // Basic linear BMC based on indexed variables using quantified bit-vector domains. + + class bmc::qlinear { + bmc& b; + ast_manager& m; + bv_util m_bv; + unsigned m_bit_width; + public: + qlinear(bmc& b): b(b), m(b.m), m_bv(m), m_bit_width(1) {} + + + lbool check() { + setup(); + m_bit_width = 4; + lbool res = l_false; + while (res == l_false) { + b.m_solver.push(); + IF_VERBOSE(1, verbose_stream() << "bit_width: " << m_bit_width << "\n";); + compile(); + b.checkpoint(); + func_decl_ref q = mk_q_func_decl(b.m_query_pred); + expr* T = m.mk_const(symbol("T"), mk_index_sort()); + expr_ref fml(m.mk_app(q, T), m); + b.assert_expr(fml); + res = b.m_solver.check(); + + if (res == l_true) { + res = get_model(); + } + b.m_solver.pop(1); + ++m_bit_width; + } + return res; + } + private: + + sort_ref mk_index_sort() { + return sort_ref(m_bv.mk_sort(m_bit_width), m); + } + + var_ref mk_index_var() { + return var_ref(m.mk_var(0, mk_index_sort()), m); + } + + void compile() { + sort_ref index_sort = mk_index_sort(); + var_ref var = mk_index_var(); + sort* index_sorts[1] = { index_sort }; + symbol tick("T"); + rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); + rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); + for (; it != end; ++it) { + func_decl* p = it->m_key; + rule_vector const& rls = *it->m_value; + // Assert: forall level . p(T) => body of rule i + equalities for head of rule i + func_decl_ref pr = mk_q_func_decl(p); + expr_ref pred = expr_ref(m.mk_app(pr, var.get()), m); + expr_ref_vector rules(m), sub(m), conjs(m); + expr_ref trm(m), rule_body(m), rule_i(m); + for (unsigned i = 0; i < rls.size(); ++i) { + sub.reset(); + conjs.reset(); + rule& r = *rls[i]; + rule_i = m.mk_app(mk_q_rule(p, i), var.get()); + rules.push_back(rule_i); + + mk_qrule_vars(r, i, sub); + + // apply substitution to body. + var_subst vs(m, false); + for (unsigned k = 0; k < p->get_arity(); ++k) { + vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), trm); + conjs.push_back(m.mk_eq(trm, mk_q_arg(p, k, true))); + } + for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { + func_decl* q = r.get_decl(j); + for (unsigned k = 0; k < q->get_arity(); ++k) { + vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), trm); + conjs.push_back(m.mk_eq(trm, mk_q_arg(q, k, false))); + } + func_decl_ref qr = mk_q_func_decl(q); + conjs.push_back(m.mk_app(qr, m_bv.mk_bv_sub(var, mk_q_one()))); + } + for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { + vs(r.get_tail(j), sub.size(), sub.c_ptr(), trm); + conjs.push_back(trm); + } + if (r.get_uninterpreted_tail_size() > 0) { + conjs.push_back(m_bv.mk_ule(mk_q_one(), var)); + } + bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); + trm = m.mk_implies(rule_i, rule_body); + trm = m.mk_forall(1, index_sorts, &tick, trm, 1); + b.assert_expr(trm); + } + bool_rewriter(m).mk_or(rules.size(), rules.c_ptr(), trm); + trm = m.mk_implies(pred, trm); + trm = m.mk_forall(1, index_sorts, &tick, trm, 1); + SASSERT(is_well_sorted(m, trm)); + b.assert_expr(trm); + } + } + + void setup() { + b.m_fparams.m_relevancy_lvl = 2; + b.m_fparams.m_model = true; + b.m_fparams.m_model_compact = true; + b.m_fparams.m_mbqi = true; + } + + void mk_qrule_vars(datalog::rule const& r, unsigned rule_id, expr_ref_vector& sub) { + ptr_vector sorts; + r.get_vars(sorts); + // populate substitution of bound variables. + sub.reset(); + sub.resize(sorts.size()); + + for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { + expr* arg = r.get_head()->get_arg(k); + if (is_var(arg)) { + unsigned idx = to_var(arg)->get_idx(); + if (!sub[idx].get()) { + sub[idx] = mk_q_arg(r.get_decl(), k, true); + } + } + } + for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { + func_decl* q = r.get_decl(j); + for (unsigned k = 0; k < q->get_arity(); ++k) { + expr* arg = r.get_tail(j)->get_arg(k); + if (is_var(arg)) { + unsigned idx = to_var(arg)->get_idx(); + if (!sub[idx].get()) { + sub[idx] = mk_q_arg(q, k, false); + } + } + } + } + for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { + if (sorts[j] && !sub[j].get()) { + sub[j] = mk_q_var(r.get_decl(), sorts[j], rule_id, idx++); + } + } + } + + expr_ref mk_q_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx) { + std::stringstream _name; + _name << pred->get_name() << "#" << rule_id << "_" << idx; + symbol nm(_name.str().c_str()); + var_ref var = mk_index_var(); + return expr_ref(m.mk_app(m.mk_func_decl(nm, mk_index_sort(), s), var), m); + } + + expr_ref mk_q_arg(func_decl* pred, unsigned idx, bool is_current) { + SASSERT(idx < pred->get_arity()); + std::stringstream _name; + _name << pred->get_name() << "#" << idx; + symbol nm(_name.str().c_str()); + expr_ref var(mk_index_var(), m); + if (!is_current) { + var = m_bv.mk_bv_sub(var, mk_q_one()); + } + return expr_ref(m.mk_app(m.mk_func_decl(nm, mk_index_sort(), pred->get_domain(idx)), var), m); + } + + expr_ref mk_q_one() { + return mk_q_num(1); + } + + expr_ref mk_q_num(unsigned i) { + return expr_ref(m_bv.mk_numeral(i, m_bit_width), m); + } + + func_decl_ref mk_q_func_decl(func_decl* f) { + std::stringstream _name; + _name << f->get_name() << "#"; + symbol nm(_name.str().c_str()); + return func_decl_ref(m.mk_func_decl(nm, mk_index_sort(), f->get_range()), m); + } + + func_decl_ref mk_q_rule(func_decl* f, unsigned rule_id) { + std::stringstream _name; + _name << f->get_name() << "#" << rule_id; + symbol nm(_name.str().c_str()); + return func_decl_ref(m.mk_func_decl(nm, mk_index_sort(), m.mk_bool_sort()), m); + } + + + expr_ref eval_q(model_ref& model, func_decl* f, unsigned i) { + func_decl_ref fn = mk_q_func_decl(f); + expr_ref t(m), result(m); + t = m.mk_app(mk_q_func_decl(f).get(), mk_q_num(i)); + model->eval(t, result); + return result; + } + + expr_ref eval_q(model_ref& model, expr* t, unsigned i) { + expr_ref tmp(m), result(m), num(m); + var_subst vs(m, false); + num = mk_q_num(i); + expr* nums[1] = { num }; + vs(t, 1, nums, tmp); + model->eval(tmp, result); + return result; + } + + lbool get_model() { + rule_manager& rm = b.m_ctx.get_rule_manager(); + func_decl_ref q = mk_q_func_decl(b.m_query_pred); + expr_ref T(m), rule_i(m), vl(m); + model_ref md; + proof_ref pr(m); + rule_unifier unifier(b.m_ctx); + rational num; + unsigned level, bv_size; + + b.m_solver.get_model(md); + func_decl* pred = b.m_query_pred; + dl_decl_util util(m); + T = m.mk_const(symbol("T"), mk_index_sort()); + md->eval(T, vl); + VERIFY (m_bv.is_numeral(vl, num, bv_size)); + SASSERT(num.is_unsigned()); + level = num.get_unsigned(); + SASSERT(m.is_true(eval_q(md, b.m_query_pred, level))); + TRACE("bmc", model_smt2_pp(tout, m, *md, 0);); + + rule_ref r0(rm), r1(rm), r2(rm); + while (true) { + TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); + expr_ref_vector sub(m); + rule_vector const& rls = b.m_rules.get_predicate_rules(pred); + rule* r = 0; + unsigned i = 0; + for (; i < rls.size(); ++i) { + rule_i = m.mk_app(mk_q_rule(pred, i), mk_q_num(level).get()); + TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); + if (m.is_true(eval_q(md, rule_i, level))) { + r = rls[i]; + break; + } + } + SASSERT(r); + mk_qrule_vars(*r, i, sub); + // we have rule, we have variable names of rule. + + // extract values for the variables in the rule. + for (unsigned j = 0; j < sub.size(); ++j) { + expr_ref vl = eval_q(md, sub[j].get(), i); + if (vl) { + // vl can be 0 if the interpretation does not assign a value to it. + sub[j] = vl; + } + else { + sub[j] = m.mk_var(j, m.get_sort(sub[j].get())); + } + } + svector > positions; + vector substs; + expr_ref fml(m), concl(m); + + r->to_formula(fml); + r2 = r; + rm.substitute(r2, sub.size(), sub.c_ptr()); + if (r0) { + VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); + expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); + expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); + apply_subst(sub, sub2); + unifier.apply(*r0.get(), 0, *r2.get(), r1); + r1->to_formula(concl); + scoped_coarse_proof _sp(m); + + proof* p = m.mk_asserted(fml); + proof* premises[2] = { pr, p }; + + positions.push_back(std::make_pair(0, 1)); + + substs.push_back(sub1); + substs.push_back(sub); + pr = m.mk_hyper_resolve(2, premises, concl, positions, substs); + r0 = r1; + } + else { + r2->to_formula(concl); + scoped_coarse_proof _sp(m); + proof* p = m.mk_asserted(fml); + if (sub.empty()) { + pr = p; + } + else { + substs.push_back(sub); + pr = m.mk_hyper_resolve(1, &p, concl, positions, substs); + } + r0 = r2; + } + + if (level == 0) { + SASSERT(r->get_uninterpreted_tail_size() == 0); + break; + } + --level; + SASSERT(r->get_uninterpreted_tail_size() == 1); + pred = r->get_decl(0); + } + scoped_coarse_proof _sp(m); + apply(m, b.m_pc.get(), pr); + b.m_answer = pr; + return l_true; + } + }; + + + // -------------------------------------------------------------------------- + // Basic non-linear BMC based on compiling into quantifiers. + + class bmc::nonlinear { + bmc& b; + ast_manager& m; + + public: + + nonlinear(bmc& b): b(b), m(b.m) {} + + lbool check() { + setup(); + for (unsigned i = 0; ; ++i) { + IF_VERBOSE(1, verbose_stream() << "level: " << i << "\n";); + b.checkpoint(); + expr_ref_vector fmls(m); + compile(b.m_rules, fmls, i); + assert_fmls(fmls); + lbool res = check(i); + if (res == l_undef) { + return res; + } + if (res == l_true) { + get_model(i); + return res; + } + } + } + + expr_ref compile_query(func_decl* query_pred, unsigned level) { + expr_ref_vector vars(m); + func_decl_ref level_p = mk_level_predicate(query_pred, level); + for (unsigned i = 0; i < level_p->get_arity(); ++i) { + std::stringstream _name; + _name << query_pred->get_name() << "#" << level << "_" << i; + symbol nm(_name.str().c_str()); + vars.push_back(m.mk_const(nm, level_p->get_domain(i))); + } + return expr_ref(m.mk_app(level_p, vars.size(), vars.c_ptr()), m); + } + + void compile(rule_set const& rules, expr_ref_vector& result, unsigned level) { + bool_rewriter br(m); + rule_set::decl2rules::iterator it = rules.begin_grouped_rules(); + rule_set::decl2rules::iterator end = rules.end_grouped_rules(); + for (; it != end; ++it) { + func_decl* p = it->m_key; + rule_vector const& rls = *it->m_value; + + // Assert: p_level(vars) => r1_level(vars) \/ r2_level(vars) \/ r3_level(vars) \/ ... + // Assert: r_i_level(vars) => exists aux_vars . body of rule i for level + + func_decl_ref level_pred = mk_level_predicate(p, level); + expr_ref_vector rules(m); + expr_ref body(m), head(m); + for (unsigned i = 0; i < rls.size(); ++i) { + rule& r = *rls[i]; + func_decl_ref rule_i = mk_level_rule(p, i, level); + rules.push_back(apply_vars(rule_i)); + + ptr_vector rule_vars; + expr_ref_vector args(m), conjs(m); + + r.get_vars(rule_vars); + obj_hashtable used_vars; + unsigned num_vars = 0; + for (unsigned i = 0; i < r.get_decl()->get_arity(); ++i) { + expr* arg = r.get_head()->get_arg(i); + if (is_var(arg) && !used_vars.contains(arg)) { + used_vars.insert(arg); + args.push_back(arg); + rule_vars[to_var(arg)->get_idx()] = 0; + } + else { + sort* srt = m.get_sort(arg); + args.push_back(m.mk_var(rule_vars.size()+num_vars, srt)); + conjs.push_back(m.mk_eq(args.back(), arg)); + ++num_vars; + } + } + head = m.mk_app(rule_i, args.size(), args.c_ptr()); + for (unsigned i = 0; i < r.get_tail_size(); ++i) { + conjs.push_back(r.get_tail(i)); + } + br.mk_and(conjs.size(), conjs.c_ptr(), body); + + replace_by_level_predicates(level, body); + body = skolemize_vars(r, args, rule_vars, body); + body = m.mk_implies(head, body); + body = bind_vars(body, head); + result.push_back(body); + } + br.mk_or(rules.size(), rules.c_ptr(), body); + head = apply_vars(level_pred); + body = m.mk_implies(head, body); + body = bind_vars(body, head); + result.push_back(body); + } + } + + private: + + void assert_fmls(expr_ref_vector const& fmls) { + for (unsigned i = 0; i < fmls.size(); ++i) { + b.assert_expr(fmls[i]); + } + } + + void setup() { + b.m_fparams.m_model = true; + b.m_fparams.m_model_compact = true; + b.m_fparams.m_mbqi = true; + b.m_fparams.m_relevancy_lvl = 2; + } + + lbool check(unsigned level) { + expr_ref p = compile_query(b.m_query_pred, level); + expr_ref q(m), q_at_level(m); + q = m.mk_fresh_const("q",m.mk_bool_sort()); + q_at_level = m.mk_implies(q, p); + b.assert_expr(q_at_level); + expr* qr = q.get(); + return b.m_solver.check(1, &qr); + } + + proof_ref get_proof(model_ref& md, func_decl* pred, app* prop, unsigned level) { + TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); + + expr_ref prop_r(m), prop_v(m), fml(m), prop_body(m), tmp(m), body(m); + expr_ref_vector args(m); + proof_ref_vector prs(m); + proof_ref pr(m); + + // find the rule that was triggered by evaluating the arguments to prop. + rule_vector const& rls = b.m_rules.get_predicate_rules(pred); + rule* r = 0; + for (unsigned i = 0; i < rls.size(); ++i) { + func_decl_ref rule_i = mk_level_rule(pred, i, level); + TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); + prop_r = m.mk_app(rule_i, prop->get_num_args(), prop->get_args()); + md->eval(prop_r, prop_v); + if (m.is_true(prop_v)) { + r = rls[i]; + break; + } + } + SASSERT(r); + r->to_formula(fml); + IF_VERBOSE(1, verbose_stream() << mk_pp(fml, m) << "\n";); + prs.push_back(m.mk_asserted(fml)); + unsigned sz = r->get_uninterpreted_tail_size(); + + ptr_vector rule_vars; + r->get_vars(rule_vars); + args.append(prop->get_num_args(), prop->get_args()); + expr_ref_vector sub = mk_skolem_binding(*r, rule_vars, args); + if (sub.empty() && sz == 0) { + pr = prs[0].get(); + return pr; + } + for (unsigned j = 0; j < sub.size(); ++j) { + md->eval(sub[j].get(), tmp); + sub[j] = tmp; + } + + svector > positions; + vector substs; + var_subst vs(m, false); + + substs.push_back(sub); + for (unsigned j = 0; j < sz; ++j) { + func_decl* head_j = r->get_decl(j); + app* body_j = r->get_tail(j); + vs(body_j, sub.size(), sub.c_ptr(), prop_body); + prs.push_back(get_proof(md, head_j, to_app(prop_body), level-1)); + positions.push_back(std::make_pair(j+1,0)); + substs.push_back(expr_ref_vector(m)); + } + pr = m.mk_hyper_resolve(sz+1, prs.c_ptr(), prop, positions, substs); + return pr; + } + + void get_model(unsigned level) { + scoped_coarse_proof _sp(m); + expr_ref level_query = compile_query(b.m_query_pred, level); + model_ref md; + b.m_solver.get_model(md); + IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); + proof_ref pr = get_proof(md, b.m_query_pred, to_app(level_query), level); + apply(m, b.m_pc.get(), pr); + b.m_answer = pr; + } + + func_decl_ref mk_level_predicate(func_decl* p, unsigned level) { + std::stringstream _name; + _name << p->get_name() << "#" << level; + symbol nm(_name.str().c_str()); + return func_decl_ref(m.mk_func_decl(nm, p->get_arity(), p->get_domain(), m.mk_bool_sort()), m); + } + + func_decl_ref mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level) { + std::stringstream _name; + _name << "rule:" << p->get_name() << "#" << level << "_" << rule_idx; + symbol nm(_name.str().c_str()); + return func_decl_ref(m.mk_func_decl(nm, p->get_arity(), p->get_domain(), m.mk_bool_sort()), m); + } + + expr_ref apply_vars(func_decl* p) { + expr_ref_vector vars(m); + for (unsigned i = 0; i < p->get_arity(); ++i) { + vars.push_back(m.mk_var(i, p->get_domain(i))); + } + return expr_ref(m.mk_app(p, vars.size(), vars.c_ptr()), m); + } + + // remove variables from dst that are in src. + void subtract_vars(ptr_vector& dst, ptr_vector const& src) { + for (unsigned i = 0; i < dst.size(); ++i) { + if (i >= src.size()) { + break; + } + if (src[i]) { + dst[i] = 0; + } + } + } + + expr_ref_vector mk_skolem_binding(rule& r, ptr_vector const& vars, expr_ref_vector const& args) { + expr_ref_vector binding(m); + ptr_vector arg_sorts; + for (unsigned i = 0; i < args.size(); ++i) { + arg_sorts.push_back(m.get_sort(args[i])); + } + for (unsigned i = 0; i < vars.size(); ++i) { + if (vars[i]) { + func_decl_ref f = mk_body_func(r, arg_sorts, i, vars[i]); + binding.push_back(m.mk_app(f, args.size(), args.c_ptr())); + } + else { + binding.push_back(0); + } + } + return binding; + } + + expr_ref skolemize_vars(rule& r, expr_ref_vector const& args, ptr_vector const& vars, expr* e) { + expr_ref result(m); + expr_ref_vector binding = mk_skolem_binding(r, vars, args); + var_subst vs(m, false); + vs(e, binding.size(), binding.c_ptr(), result); + return result; + } + + func_decl_ref mk_body_func(rule& r, ptr_vector const& args, unsigned index, sort* s) { + std::stringstream _name; + _name << r.get_decl()->get_name() << "@" << index; + symbol name(_name.str().c_str()); + func_decl* f = m.mk_func_decl(name, args.size(), args.c_ptr(), s); + return func_decl_ref(f, m); + } + + expr_ref bind_vars(expr* e, expr* pat) { + ptr_vector vars, sorts; + svector names; + expr_ref_vector binding(m), patterns(m); + expr_ref tmp(m), head(m); + get_free_vars(e, vars); + for (unsigned i = 0; i < vars.size(); ++i) { + if (vars[i]) { + binding.push_back(m.mk_var(sorts.size(), vars[i])); + sorts.push_back(vars[i]); + names.push_back(symbol(i)); + } + else { + binding.push_back(0); + } + } + sorts.reverse(); + if (sorts.empty()) { + return expr_ref(e, m); + } + var_subst vs(m, false); + vs(e, binding.size(), binding.c_ptr(), tmp); + vs(pat, binding.size(), binding.c_ptr(), head); + patterns.push_back(m.mk_pattern(to_app(head))); + symbol qid, skid; + return expr_ref(m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp, 1, qid, skid, 1, patterns.c_ptr()), m); + } + + public: + class level_replacer { + nonlinear& n; + unsigned m_level; + bool is_predicate(func_decl* f) { + return n.b.m_ctx.is_predicate(f); + } + public: + level_replacer(nonlinear& n, unsigned level): n(n), m_level(level) {} + + br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { + if (is_predicate(f)) { + if (m_level > 0) { + result = n.m.mk_app(n.mk_level_predicate(f, m_level-1), num_args, args); + } + else { + result = n.m.mk_false(); + } + return BR_DONE; + } + return BR_FAILED; + } + + bool reduce_quantifier(quantifier* old_q, expr* new_body, expr_ref& result) { + if (is_ground(new_body)) { + result = new_body; + } + else { + expr * const * no_pats = &new_body; + result = n.m.update_quantifier(old_q, 0, 0, 1, no_pats, new_body); + } + return true; + } + }; + + struct level_replacer_cfg : public default_rewriter_cfg { + level_replacer m_r; + + level_replacer_cfg(nonlinear& nl, unsigned level): + m_r(nl, level) {} + + bool rewrite_patterns() const { return false; } + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + return m_r.mk_app_core(f, num, args, result); + } + bool reduce_quantifier(quantifier * old_q, + expr * new_body, + expr * const * new_patterns, + expr * const * new_no_patterns, + expr_ref & result, + proof_ref & result_pr) { + return m_r.reduce_quantifier(old_q, new_body, result); + } + + }; + + class level_replacer_star : public rewriter_tpl { + level_replacer_cfg m_cfg; + public: + level_replacer_star(nonlinear& nl, unsigned level): + rewriter_tpl(nl.m, false, m_cfg), + m_cfg(nl, level) + {} + }; + + private: + + void replace_by_level_predicates(unsigned level, expr_ref& fml) { + level_replacer_star rep(*this, level); + expr_ref tmp(m); + rep(fml, tmp); + fml = tmp; + } + + + }; + + // -------------------------------------------------------------------------- + // Basic non-linear BMC based on compiling into data-types (it is inefficient) + + class bmc::nonlinear_dt { + bmc& b; + ast_manager& m; + ast_ref_vector m_pinned; + sort_ref m_path_sort; + obj_map m_pred2sort; + obj_map m_sort2pred; + + + public: + nonlinear_dt(bmc& b): b(b), m(b.m), m_pinned(m), m_path_sort(m) {} + + lbool check() { + setup(); + declare_datatypes(); + compile(); + return check_query(); + } + + private: + void setup() { + m_pred2sort.reset(); + m_pinned.reset(); + m_sort2pred.reset(); + b.m_fparams.m_relevancy_lvl = 0; + b.m_fparams.m_model = true; + b.m_fparams.m_model_compact = true; + b.m_fparams.m_mbqi = false; + b.m_fparams.m_relevancy_lvl = 2; + } + + func_decl_ref mk_predicate(func_decl* pred) { + std::stringstream _name; + _name << pred->get_name() << "#"; + symbol nm(_name.str().c_str()); + sort* pred_trace_sort = m_pred2sort.find(pred); + return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m); + } + + func_decl_ref mk_rule(func_decl* p, unsigned rule_idx) { + std::stringstream _name; + _name << "rule:" << p->get_name() << "#" << rule_idx; + symbol nm(_name.str().c_str()); + sort* pred_trace_sort = m_pred2sort.find(p); + return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m); + } + + expr_ref mk_var(func_decl* pred, sort*s, unsigned idx, expr* path_arg, expr* trace_arg) { + std::stringstream _name; + _name << pred->get_name() << "#V_" << idx; + symbol nm(_name.str().c_str()); + func_decl_ref fn(m); + fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, s); + return expr_ref(m.mk_app(fn, trace_arg, path_arg), m); + } + + expr_ref mk_arg(func_decl* pred, unsigned idx, expr* path_arg, expr* trace_arg) { + SASSERT(idx < pred->get_arity()); + std::stringstream _name; + _name << pred->get_name() << "#X_" << idx; + symbol nm(_name.str().c_str()); + func_decl_ref fn(m); + fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, pred->get_domain(idx)); + return expr_ref(m.mk_app(fn, trace_arg, path_arg), m); + } + + void mk_subst(datalog::rule& r, expr* path, app* trace, expr_ref_vector& sub) { + datatype_util dtu(m); + ptr_vector sorts; + func_decl* p = r.get_decl(); + ptr_vector const& succs = *dtu.get_datatype_constructors(m.get_sort(path)); + // populate substitution of bound variables. + r.get_vars(sorts); + sub.reset(); + sub.resize(sorts.size()); + for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { + expr* arg = r.get_head()->get_arg(k); + if (is_var(arg)) { + unsigned idx = to_var(arg)->get_idx(); + if (!sub[idx].get()) { + sub[idx] = mk_arg(p, k, path, trace); + } + } + } + for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { + func_decl* q = r.get_decl(j); + expr_ref path_arg(m); + if (j == 0) { + path_arg = path; + } + else { + path_arg = m.mk_app(succs[j], path); + } + for (unsigned k = 0; k < q->get_arity(); ++k) { + expr* arg = r.get_tail(j)->get_arg(k); + if (is_var(arg)) { + unsigned idx = to_var(arg)->get_idx(); + if (!sub[idx].get()) { + sub[idx] = mk_arg(q, k, path_arg, trace->get_arg(j)); + } + } + } + } + for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { + if (sorts[j] && !sub[j].get()) { + sub[j] = mk_var(r.get_decl(), sorts[j], idx++, path, trace); + } + } + } + + /** + \brief compile Horn rule into co-Horn implication. + forall args . R(path_var, rule_i(trace_vars)) => Body[X(path_var, rule_i(trace_vars)), Y(S_j(path_var), trace_vars_j)] + */ + void compile() { + datatype_util dtu(m); + + rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); + rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); + for (; it != end; ++it) { + func_decl* p = it->m_key; + rule_vector const& rls = *it->m_value; + + // Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ... + // where: r_i_level = body of rule i for level + equalities for head of rule i + + expr_ref rule_body(m), tmp(m), pred(m), trace_arg(m), fml(m); + var_ref path_var(m), trace_var(m); + expr_ref_vector rules(m), sub(m), conjs(m), vars(m), patterns(m); + sort* pred_sort = m_pred2sort.find(p); + path_var = m.mk_var(0, m_path_sort); + trace_var = m.mk_var(1, pred_sort); + // sort* sorts[2] = { pred_sort, m_path_sort }; + ptr_vector const& cnstrs = *dtu.get_datatype_constructors(pred_sort); + ptr_vector const& succs = *dtu.get_datatype_constructors(m_path_sort); + SASSERT(cnstrs.size() == rls.size()); + pred = m.mk_app(mk_predicate(p), trace_var.get(), path_var.get()); + for (unsigned i = 0; i < rls.size(); ++i) { + sub.reset(); + conjs.reset(); + vars.reset(); + rule& r = *rls[i]; + func_decl_ref rule_pred_i = mk_rule(p, i); + + // Create cnstr_rule_i(Vars) + func_decl* cnstr = cnstrs[i]; + rules.push_back(m.mk_app(rule_pred_i, trace_var.get(), path_var.get())); + unsigned arity = cnstr->get_arity(); + for (unsigned j = 0; j < arity; ++j) { + vars.push_back(m.mk_var(arity-j,cnstr->get_domain(j))); + } + trace_arg = m.mk_app(cnstr, vars.size(), vars.c_ptr()); + + mk_subst(r, path_var, to_app(trace_arg), sub); + + // apply substitution to body. + var_subst vs(m, false); + for (unsigned k = 0; k < p->get_arity(); ++k) { + vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp); + expr_ref arg = mk_arg(p, k, path_var, trace_arg); + conjs.push_back(m.mk_eq(tmp, arg)); + } + for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { + expr_ref path_arg(m); + if (j == 0) { + path_arg = path_var.get(); + } + else { + path_arg = m.mk_app(succs[j], path_var.get()); + } + func_decl* q = r.get_decl(j); + for (unsigned k = 0; k < q->get_arity(); ++k) { + vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp); + expr_ref arg = mk_arg(q, k, path_arg, vars[j].get()); + conjs.push_back(m.mk_eq(tmp, arg)); + } + func_decl_ref q_pred = mk_predicate(q); + conjs.push_back(m.mk_app(q_pred, vars[j].get(), path_arg)); + } + for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { + vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp); + conjs.push_back(tmp); + } + bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); + ptr_vector q_sorts; + vector names; + for (unsigned i = 0; i < vars.size(); ++i) { + q_sorts.push_back(m.get_sort(vars[i].get())); + names.push_back(symbol(i+1)); + } + vars.push_back(path_var); + q_sorts.push_back(m.get_sort(path_var)); + names.push_back(symbol("path")); + SASSERT(names.size() == q_sorts.size()); + SASSERT(vars.size() == names.size()); + symbol qid = r.name(), skid; + tmp = m.mk_app(mk_predicate(p), trace_arg.get(), path_var.get()); + patterns.reset(); + patterns.push_back(m.mk_pattern(to_app(tmp))); + fml = m.mk_implies(tmp, rule_body); + fml = m.mk_forall(vars.size(), q_sorts.c_ptr(), names.c_ptr(), fml, 1, qid, skid, 1, patterns.c_ptr()); + b.assert_expr(fml); + } + } + } + + void declare_datatypes() { + rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); + rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); + datatype_util dtu(m); + ptr_vector dts; + + obj_map pred_idx; + for (unsigned i = 0; it != end; ++it, ++i) { + pred_idx.insert(it->m_key, i); + } + + it = b.m_rules.begin_grouped_rules(); + for (; it != end; ++it) { + rule_vector const& rls = *it->m_value; + func_decl* pred = it->m_key; + ptr_vector cnstrs; + for (unsigned i = 0; i < rls.size(); ++i) { + rule* r = rls[i]; + ptr_vector accs; + for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) { + func_decl* q = r->get_decl(j); + unsigned idx = pred_idx.find(q); + std::stringstream _name; + _name << pred->get_name() << "_" << q->get_name() << j; + symbol name(_name.str().c_str()); + type_ref tr(idx); + accs.push_back(mk_accessor_decl(name, tr)); + } + std::stringstream _name; + _name << pred->get_name() << "_" << i; + symbol name(_name.str().c_str()); + _name << "?"; + symbol is_name(_name.str().c_str()); + cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr())); + } + dts.push_back(mk_datatype_decl(pred->get_name(), cnstrs.size(), cnstrs.c_ptr())); + } + + + sort_ref_vector new_sorts(m); + family_id dfid = m.get_family_id("datatype"); + datatype_decl_plugin* dtp = static_cast(m.get_plugin(dfid)); + VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), new_sorts)); + + it = b.m_rules.begin_grouped_rules(); + for (unsigned i = 0; it != end; ++it, ++i) { + m_pred2sort.insert(it->m_key, new_sorts[i].get()); + m_sort2pred.insert(new_sorts[i].get(), it->m_key); + m_pinned.push_back(new_sorts[i].get()); + } + if (new_sorts.size() > 0) { + TRACE("bmc", dtu.display_datatype(new_sorts[0].get(), tout);); + } + del_datatype_decls(dts.size(), dts.c_ptr()); + + // declare path data-type. + { + new_sorts.reset(); + dts.reset(); + ptr_vector cnstrs; + unsigned max_arity = 0; + rule_set::iterator it = b.m_rules.begin(); + rule_set::iterator end = b.m_rules.end(); + for (; it != end; ++it) { + rule* r = *it; + unsigned sz = r->get_uninterpreted_tail_size(); + max_arity = std::max(sz, max_arity); + } + cnstrs.push_back(mk_constructor_decl(symbol("Z#"), symbol("Z#?"), 0, 0)); + + for (unsigned i = 0; i + 1 < max_arity; ++i) { + std::stringstream _name; + _name << "succ#" << i; + symbol name(_name.str().c_str()); + _name << "?"; + symbol is_name(_name.str().c_str()); + std::stringstream _name2; + _name2 << "get_succ#" << i; + symbol acc_name(_name2.str().c_str()); + ptr_vector accs; + type_ref tr(0); + accs.push_back(mk_accessor_decl(name, tr)); + cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr())); + } + dts.push_back(mk_datatype_decl(symbol("Path"), cnstrs.size(), cnstrs.c_ptr())); + VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), new_sorts)); + m_path_sort = new_sorts[0].get(); + } + } + + proof_ref get_proof(model_ref& md, app* trace, app* path) { + datatype_util dtu(m); + sort* trace_sort = m.get_sort(trace); + func_decl* p = m_sort2pred.find(trace_sort); + datalog::rule_vector const& rules = b.m_rules.get_predicate_rules(p); + ptr_vector const& cnstrs = *dtu.get_datatype_constructors(trace_sort); + ptr_vector const& succs = *dtu.get_datatype_constructors(m_path_sort); + for (unsigned i = 0; i < cnstrs.size(); ++i) { + if (trace->get_decl() == cnstrs[i]) { + svector > positions; + scoped_coarse_proof _sc(m); + proof_ref_vector prs(m); + expr_ref_vector sub(m); + vector substs; + proof_ref pr(m); + expr_ref fml(m), head(m), tmp(m); + app_ref path1(m); + + var_subst vs(m, false); + mk_subst(*rules[i], path, trace, sub); + rules[i]->to_formula(fml); + prs.push_back(m.mk_asserted(fml)); + unsigned sz = trace->get_num_args(); + if (sub.empty() && sz == 0) { + pr = prs[0].get(); + return pr; + } + for (unsigned j = 0; j < sub.size(); ++j) { + md->eval(sub[j].get(), tmp); + sub[j] = tmp; + } + rule_ref rl(b.m_ctx.get_rule_manager()); + rl = rules[i]; + b.m_ctx.get_rule_manager().substitute(rl, sub.size(), sub.c_ptr()); + + substs.push_back(sub); + + for (unsigned j = 0; j < sz; ++j) { + if (j == 0) { + path1 = path; + } + else { + path1 = m.mk_app(succs[j], path); + } + + prs.push_back(get_proof(md, to_app(trace->get_arg(j)), path1)); + positions.push_back(std::make_pair(j+1,0)); + substs.push_back(expr_ref_vector(m)); + } + head = rl->get_head(); + pr = m.mk_hyper_resolve(sz+1, prs.c_ptr(), head, positions, substs); + return pr; + } + } + UNREACHABLE(); + return proof_ref(0, m); + } + + // instantiation of algebraic data-types takes care of the rest. + lbool check_query() { + sort* trace_sort = m_pred2sort.find(b.m_query_pred); + func_decl_ref q = mk_predicate(b.m_query_pred); + expr_ref trace(m), path(m), fml(m); + trace = m.mk_const(symbol("trace"), trace_sort); + path = m.mk_const(symbol("path"), m_path_sort); + fml = m.mk_app(q, trace.get(), path.get()); + b.assert_expr(fml); + while (true) { + lbool is_sat = b.m_solver.check(); + model_ref md; + if (is_sat == l_false) { + return is_sat; + } + b.m_solver.get_model(md); + mk_answer(md, trace, path); + return l_true; + } + } + + bool check_model(model_ref& md, expr* trace) { + expr_ref trace_val(m), eq(m); + md->eval(trace, trace_val); + eq = m.mk_eq(trace, trace_val); + b.m_solver.push(); + b.m_solver.assert_expr(eq); + lbool is_sat = b.m_solver.check(); + if (is_sat != l_false) { + b.m_solver.get_model(md); + } + b.m_solver.pop(1); + if (is_sat == l_false) { + IF_VERBOSE(1, verbose_stream() << "infeasible trace " << mk_pp(trace_val, m) << "\n";); + eq = m.mk_not(eq); + b.assert_expr(eq); + } + return is_sat != l_false; + } + + void mk_answer(model_ref& md, expr_ref& trace, expr_ref& path) { + proof_ref pr(m); + IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); + md->eval(trace, trace); + md->eval(path, path); + IF_VERBOSE(2, verbose_stream() << mk_pp(trace, m) << "\n"; + for (unsigned i = 0; i < b.m_solver.size(); ++i) { + verbose_stream() << mk_pp(b.m_solver.get_formulas()[i], m) << "\n"; + }); + b.m_answer = get_proof(md, to_app(trace), to_app(path)); + } + + }; + + // -------------------------------------------------------------------------- + // Basic linear BMC based on incrementally unfolding the transition relation. + + class bmc::linear { + bmc& b; + ast_manager& m; + + public: + linear(bmc& b): b(b), m(b.m) {} + + lbool check() { + setup(); + for (unsigned i = 0; ; ++i) { + IF_VERBOSE(1, verbose_stream() << "level: " << i << "\n";); + b.checkpoint(); + compile(i); + lbool res = check(i); + if (res == l_undef) { + return res; + } + if (res == l_true) { + get_model(i); + return res; + } + } + } + + private: + + void get_model(unsigned level) { + rule_manager& rm = b.m_ctx.get_rule_manager(); + expr_ref level_query = mk_level_predicate(b.m_query_pred, level); + model_ref md; + proof_ref pr(m); + rule_unifier unifier(b.m_ctx); + b.m_solver.get_model(md); + func_decl* pred = b.m_query_pred; + SASSERT(m.is_true(md->get_const_interp(to_app(level_query)->get_decl()))); + + TRACE("bmc", model_smt2_pp(tout, m, *md, 0);); + + rule_ref r0(rm), r1(rm), r2(rm); + while (true) { + TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); + expr_ref_vector sub(m); + rule_vector const& rls = b.m_rules.get_predicate_rules(pred); + rule* r = 0; + unsigned i = 0; + for (; i < rls.size(); ++i) { + expr_ref rule_i = mk_level_rule(pred, i, level); + TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); + if (m.is_true(md->get_const_interp(to_app(rule_i)->get_decl()))) { + r = rls[i]; + break; + } + } + SASSERT(r); + mk_rule_vars(*r, level, i, sub); + // we have rule, we have variable names of rule. + + // extract values for the variables in the rule. + for (unsigned j = 0; j < sub.size(); ++j) { + expr* vl = md->get_const_interp(to_app(sub[j].get())->get_decl()); + if (vl) { + // vl can be 0 if the interpretation does not assign a value to it. + sub[j] = vl; + } + else { + sub[j] = m.mk_var(j, m.get_sort(sub[j].get())); + } + } + svector > positions; + vector substs; + expr_ref fml(m), concl(m); + + r->to_formula(fml); + r2 = r; + rm.substitute(r2, sub.size(), sub.c_ptr()); + if (r0) { + VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); + expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); + expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); + apply_subst(sub, sub2); + unifier.apply(*r0.get(), 0, *r2.get(), r1); + r1->to_formula(concl); + scoped_coarse_proof _sp(m); + + proof* p = m.mk_asserted(fml); + proof* premises[2] = { pr, p }; + + positions.push_back(std::make_pair(0, 1)); + + substs.push_back(sub1); + substs.push_back(sub); + pr = m.mk_hyper_resolve(2, premises, concl, positions, substs); + r0 = r1; + } + else { + r2->to_formula(concl); + scoped_coarse_proof _sp(m); + proof* p = m.mk_asserted(fml); + if (sub.empty()) { + pr = p; + } + else { + substs.push_back(sub); + pr = m.mk_hyper_resolve(1, &p, concl, positions, substs); + } + r0 = r2; + } + + if (level == 0) { + SASSERT(r->get_uninterpreted_tail_size() == 0); + break; + } + --level; + SASSERT(r->get_uninterpreted_tail_size() == 1); + pred = r->get_decl(0); + } + scoped_coarse_proof _sp(m); + apply(m, b.m_pc.get(), pr); + b.m_answer = pr; + } + + + void setup() { + b.m_fparams.m_relevancy_lvl = 0; + b.m_fparams.m_model = true; + b.m_fparams.m_model_compact = true; + b.m_fparams.m_mbqi = false; + // m_fparams.m_auto_config = false; + } + + + lbool check(unsigned level) { + expr_ref level_query = mk_level_predicate(b.m_query_pred, level); + expr* q = level_query.get(); + return b.m_solver.check(1, &q); + } + + expr_ref mk_level_predicate(func_decl* p, unsigned level) { + return mk_level_predicate(p->get_name(), level); + } + + expr_ref mk_level_predicate(symbol const& name, unsigned level) { + std::stringstream _name; + _name << name << "#" << level; + symbol nm(_name.str().c_str()); + return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m); + } + + expr_ref mk_level_arg(func_decl* pred, unsigned idx, unsigned level) { + SASSERT(idx < pred->get_arity()); + std::stringstream _name; + _name << pred->get_name() << "#" << level << "_" << idx; + symbol nm(_name.str().c_str()); + return expr_ref(m.mk_const(nm, pred->get_domain(idx)), m); + } + + expr_ref mk_level_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx, unsigned level) { + std::stringstream _name; + _name << pred->get_name() << "#" << level << "_" << rule_id << "_" << idx; + symbol nm(_name.str().c_str()); + return expr_ref(m.mk_const(nm, s), m); + } + + expr_ref mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level) { + std::stringstream _name; + _name << "rule:" << p->get_name() << "#" << level << "_" << rule_idx; + symbol nm(_name.str().c_str()); + return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m); + } + + void mk_rule_vars(rule& r, unsigned level, unsigned rule_id, expr_ref_vector& sub) { + ptr_vector sorts; + r.get_vars(sorts); + // populate substitution of bound variables. + sub.reset(); + sub.resize(sorts.size()); + + for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { + expr* arg = r.get_head()->get_arg(k); + if (is_var(arg)) { + unsigned idx = to_var(arg)->get_idx(); + if (!sub[idx].get()) { + sub[idx] = mk_level_arg(r.get_decl(), k, level); + } + } + } + for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { + SASSERT(level > 0); + func_decl* q = r.get_decl(j); + for (unsigned k = 0; k < q->get_arity(); ++k) { + expr* arg = r.get_tail(j)->get_arg(k); + if (is_var(arg)) { + unsigned idx = to_var(arg)->get_idx(); + if (!sub[idx].get()) { + sub[idx] = mk_level_arg(q, k, level-1); + } + } + } + } + for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { + if (sorts[j] && !sub[j].get()) { + sub[j] = mk_level_var(r.get_decl(), sorts[j], rule_id, idx++, level); + } + } + } + + void compile(unsigned level) { + rule_set::decl2rules::iterator it = b.m_rules.begin_grouped_rules(); + rule_set::decl2rules::iterator end = b.m_rules.end_grouped_rules(); + for (; it != end; ++it) { + func_decl* p = it->m_key; + rule_vector const& rls = *it->m_value; + + // Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ... + // Assert: r_i_level => body of rule i for level + equalities for head of rule i + + expr_ref level_pred = mk_level_predicate(p, level); + expr_ref_vector rules(m), sub(m), conjs(m); + expr_ref rule_body(m), tmp(m); + for (unsigned i = 0; i < rls.size(); ++i) { + sub.reset(); + conjs.reset(); + rule& r = *rls[i]; + expr_ref rule_i = mk_level_rule(p, i, level); + rules.push_back(rule_i); + if (level == 0 && r.get_uninterpreted_tail_size() > 0) { + tmp = m.mk_not(rule_i); + b.assert_expr(tmp); + continue; + } + + mk_rule_vars(r, level, i, sub); + + // apply substitution to body. + var_subst vs(m, false); + for (unsigned k = 0; k < p->get_arity(); ++k) { + vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp); + conjs.push_back(m.mk_eq(tmp, mk_level_arg(p, k, level))); + } + for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { + SASSERT(level > 0); + func_decl* q = r.get_decl(j); + for (unsigned k = 0; k < q->get_arity(); ++k) { + vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp); + conjs.push_back(m.mk_eq(tmp, mk_level_arg(q, k, level-1))); + } + conjs.push_back(mk_level_predicate(q, level-1)); + } + for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { + vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp); + conjs.push_back(tmp); + } + bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); + rule_body = m.mk_implies(rule_i, rule_body); + b.assert_expr(rule_body); + } + bool_rewriter(m).mk_or(rules.size(), rules.c_ptr(), tmp); + tmp = m.mk_implies(level_pred, tmp); + b.assert_expr(tmp); + } + } + }; + bmc::bmc(context& ctx): m_ctx(ctx), m(ctx.get_manager()), m_solver(m, m_fparams), - m_pinned(m), m_rules(ctx), m_query_pred(m), m_answer(m), - m_cancel(false), - m_path_sort(m), - m_bv(m) { - } + m_cancel(false) { + } bmc::~bmc() {} lbool bmc::query(expr* query) { m_solver.reset(); - m_pinned.reset(); - m_pred2sort.reset(); - m_sort2pred.reset(); - m_pred2newpred.reset(); - m_pred2args.reset(); m_answer = 0; m_ctx.ensure_opened(); @@ -93,984 +1445,39 @@ namespace datalog { if (is_linear()) { if (m_ctx.get_engine() == QBMC_ENGINE) { - return check_qlinear(); + qlinear ql(*this); + return ql.check(); + } + else { + linear lin(*this); + return lin.check(); } - return check_linear(); } else { IF_VERBOSE(0, verbose_stream() << "WARNING: non-linear BMC is highly inefficient\n";); - return check_nonlinear(); + nonlinear nl(*this); + return nl.check(); } } + void bmc::assert_expr(expr* e) { + TRACE("bmc", tout << mk_pp(e, m) << "\n";); + m_solver.assert_expr(e); + } + bool bmc::is_linear() const { unsigned sz = m_rules.get_num_rules(); for (unsigned i = 0; i < sz; ++i) { if (m_rules.get_rule(i)->get_uninterpreted_tail_size() > 1) { return false; } + if (m_rules.get_rule(i)->has_quantifiers()) { + return false; + } } return true; } - // -------------------------------------------------------------------------- - // Basic linear BMC based on incrementally unfolding the transition relation. - - void bmc::get_model_linear(unsigned level) { - rule_manager& rm = m_ctx.get_rule_manager(); - expr_ref level_query = mk_level_predicate(m_query_pred, level); - model_ref md; - proof_ref pr(m); - rule_unifier unifier(m_ctx); - m_solver.get_model(md); - func_decl* pred = m_query_pred; - SASSERT(m.is_true(md->get_const_interp(to_app(level_query)->get_decl()))); - dl_decl_util util(m); - - TRACE("bmc", model_smt2_pp(tout, m, *md, 0);); - - rule_ref r0(rm), r1(rm), r2(rm); - while (true) { - TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); - expr_ref_vector sub(m); - rule_vector const& rls = m_rules.get_predicate_rules(pred); - rule* r = 0; - unsigned i = 0; - for (; i < rls.size(); ++i) { - expr_ref rule_i = mk_level_rule(pred, i, level); - TRACE("bmc", rls[i]->display(m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); - if (m.is_true(md->get_const_interp(to_app(rule_i)->get_decl()))) { - r = rls[i]; - break; - } - } - SASSERT(r); - mk_rule_vars(*r, level, i, sub); - // we have rule, we have variable names of rule. - - // extract values for the variables in the rule. - for (unsigned j = 0; j < sub.size(); ++j) { - expr* vl = md->get_const_interp(to_app(sub[j].get())->get_decl()); - if (vl) { - // vl can be 0 if the interpretation does not assign a value to it. - sub[j] = vl; - } - else { - sub[j] = m.mk_var(j, m.get_sort(sub[j].get())); - } - } - svector > positions; - vector substs; - expr_ref fml(m), concl(m); - - r->to_formula(fml); - r2 = r; - rm.substitute(r2, sub.size(), sub.c_ptr()); - if (r0) { - VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); - expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); - expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); - apply_subst(sub, sub2); - unifier.apply(*r0.get(), 0, *r2.get(), r1); - r1->to_formula(concl); - scoped_coarse_proof _sp(m); - - proof* p = m.mk_asserted(fml); - proof* premises[2] = { pr, p }; - - positions.push_back(std::make_pair(0, 1)); - - substs.push_back(sub1); - substs.push_back(sub); - pr = m.mk_hyper_resolve(2, premises, concl, positions, substs); - r0 = r1; - } - else { - r2->to_formula(concl); - scoped_coarse_proof _sp(m); - proof* p = m.mk_asserted(fml); - if (sub.empty()) { - pr = p; - } - else { - substs.push_back(sub); - pr = m.mk_hyper_resolve(1, &p, concl, positions, substs); - } - r0 = r2; - } - - if (level == 0) { - SASSERT(r->get_uninterpreted_tail_size() == 0); - break; - } - --level; - SASSERT(r->get_uninterpreted_tail_size() == 1); - pred = r->get_decl(0); - } - scoped_coarse_proof _sp(m); - apply(m, m_pc.get(), pr); - m_answer = pr; - } - - - void bmc::setup_linear() { - m_fparams.m_relevancy_lvl = 0; - m_fparams.m_model = true; - m_fparams.m_model_compact = true; - m_fparams.m_mbqi = false; - // m_fparams.m_auto_config = false; - } - - lbool bmc::check_linear() { - setup_linear(); - for (unsigned i = 0; ; ++i) { - IF_VERBOSE(1, verbose_stream() << "level: " << i << "\n";); - checkpoint(); - compile_linear(i); - lbool res = check_linear(i); - if (res == l_undef) { - return res; - } - if (res == l_true) { - get_model_linear(i); - return res; - } - } - } - - lbool bmc::check_linear(unsigned level) { - expr_ref level_query = mk_level_predicate(m_query_pred, level); - expr* q = level_query.get(); - return m_solver.check(1, &q); - } - - void bmc::assert_expr(expr* e) { - TRACE("bmc", tout << mk_pp(e, m) << "\n";); - m_solver.assert_expr(e); - } - - expr_ref bmc::mk_level_predicate(func_decl* p, unsigned level) { - return mk_level_predicate(p->get_name(), level); - } - - expr_ref bmc::mk_level_predicate(symbol const& name, unsigned level) { - std::stringstream _name; - _name << name << "#" << level; - symbol nm(_name.str().c_str()); - return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m); - } - - expr_ref bmc::mk_level_arg(func_decl* pred, unsigned idx, unsigned level) { - SASSERT(idx < pred->get_arity()); - std::stringstream _name; - _name << pred->get_name() << "#" << level << "_" << idx; - symbol nm(_name.str().c_str()); - return expr_ref(m.mk_const(nm, pred->get_domain(idx)), m); - } - - expr_ref bmc::mk_level_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx, unsigned level) { - std::stringstream _name; - _name << pred->get_name() << "#" << level << "_" << rule_id << "_" << idx; - symbol nm(_name.str().c_str()); - return expr_ref(m.mk_const(nm, s), m); - } - - expr_ref bmc::mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level) { - std::stringstream _name; - _name << "rule:" << p->get_name() << "#" << level << "_" << rule_idx; - symbol nm(_name.str().c_str()); - return expr_ref(m.mk_const(nm, m.mk_bool_sort()), m); - } - - void bmc::mk_rule_vars(rule& r, unsigned level, unsigned rule_id, expr_ref_vector& sub) { - sort_ref_vector sorts(m); - r.get_vars(sorts); - // populate substitution of bound variables. - sub.reset(); - sub.resize(sorts.size()); - - for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { - expr* arg = r.get_head()->get_arg(k); - if (is_var(arg)) { - unsigned idx = to_var(arg)->get_idx(); - if (!sub[idx].get()) { - sub[idx] = mk_level_arg(r.get_decl(), k, level); - } - } - } - for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { - SASSERT(level > 0); - func_decl* q = r.get_decl(j); - for (unsigned k = 0; k < q->get_arity(); ++k) { - expr* arg = r.get_tail(j)->get_arg(k); - if (is_var(arg)) { - unsigned idx = to_var(arg)->get_idx(); - if (!sub[idx].get()) { - sub[idx] = mk_level_arg(q, k, level-1); - } - } - } - } - for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { - if (sorts[j].get() && !sub[j].get()) { - sub[j] = mk_level_var(r.get_decl(), sorts[j].get(), rule_id, idx++, level); - } - } - } - - void bmc::compile_linear(unsigned level) { - rule_set::decl2rules::iterator it = m_rules.begin_grouped_rules(); - rule_set::decl2rules::iterator end = m_rules.end_grouped_rules(); - for (; it != end; ++it) { - func_decl* p = it->m_key; - rule_vector const& rls = *it->m_value; - - // Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ... - // Assert: r_i_level => body of rule i for level + equalities for head of rule i - - expr_ref level_pred = mk_level_predicate(p, level); - expr_ref_vector rules(m), sub(m), conjs(m); - expr_ref rule_body(m), tmp(m); - for (unsigned i = 0; i < rls.size(); ++i) { - sub.reset(); - conjs.reset(); - rule& r = *rls[i]; - expr_ref rule_i = mk_level_rule(p, i, level); - rules.push_back(rule_i); - if (level == 0 && r.get_uninterpreted_tail_size() > 0) { - tmp = m.mk_not(rule_i); - assert_expr(tmp); - continue; - } - - mk_rule_vars(r, level, i, sub); - - // apply substitution to body. - var_subst vs(m, false); - for (unsigned k = 0; k < p->get_arity(); ++k) { - vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp); - conjs.push_back(m.mk_eq(tmp, mk_level_arg(p, k, level))); - } - for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { - SASSERT(level > 0); - func_decl* q = r.get_decl(j); - for (unsigned k = 0; k < q->get_arity(); ++k) { - vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp); - conjs.push_back(m.mk_eq(tmp, mk_level_arg(q, k, level-1))); - } - conjs.push_back(mk_level_predicate(q, level-1)); - } - for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { - vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp); - conjs.push_back(tmp); - } - bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); - rule_body = m.mk_implies(rule_i, rule_body); - assert_expr(rule_body); - } - bool_rewriter(m).mk_or(rules.size(), rules.c_ptr(), tmp); - tmp = m.mk_implies(level_pred, tmp); - assert_expr(tmp); - } - } - - // --------------------------------------------------------------------------- - // Basic linear BMC based on indexed variables using quantified bit-vector domains. - - lbool bmc::check_qlinear() { - setup_qlinear(); - m_bit_width = 4; - lbool res = l_false; - while (res == l_false) { - m_solver.push(); - IF_VERBOSE(1, verbose_stream() << "bit_width: " << m_bit_width << "\n";); - compile_qlinear(); - checkpoint(); - func_decl_ref q = mk_q_func_decl(m_query_pred); - expr* T = m.mk_const(symbol("T"), mk_index_sort()); - expr_ref fml(m.mk_app(q, T), m); - assert_expr(fml); - res = m_solver.check(); - - if (res == l_true) { - res = get_model_qlinear(); - } - m_solver.pop(1); - ++m_bit_width; - } - return res; - } - - sort_ref bmc::mk_index_sort() { - return sort_ref(m_bv.mk_sort(m_bit_width), m); - } - - var_ref bmc::mk_index_var() { - return var_ref(m.mk_var(0, mk_index_sort()), m); - } - - void bmc::compile_qlinear() { - sort_ref index_sort = mk_index_sort(); - var_ref var = mk_index_var(); - sort* index_sorts[1] = { index_sort }; - symbol tick("T"); - rule_set::decl2rules::iterator it = m_rules.begin_grouped_rules(); - rule_set::decl2rules::iterator end = m_rules.end_grouped_rules(); - for (; it != end; ++it) { - func_decl* p = it->m_key; - rule_vector const& rls = *it->m_value; - // Assert: forall level . p(T) => body of rule i + equalities for head of rule i - func_decl_ref pr = mk_q_func_decl(p); - expr_ref pred = expr_ref(m.mk_app(pr, var.get()), m); - expr_ref_vector rules(m), sub(m), conjs(m); - expr_ref trm(m), rule_body(m), rule_i(m); - for (unsigned i = 0; i < rls.size(); ++i) { - sub.reset(); - conjs.reset(); - rule& r = *rls[i]; - rule_i = m.mk_app(mk_q_rule(p, i), var.get()); - rules.push_back(rule_i); - - mk_qrule_vars(r, i, sub); - - // apply substitution to body. - var_subst vs(m, false); - for (unsigned k = 0; k < p->get_arity(); ++k) { - vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), trm); - conjs.push_back(m.mk_eq(trm, mk_q_arg(p, k, true))); - } - for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { - func_decl* q = r.get_decl(j); - for (unsigned k = 0; k < q->get_arity(); ++k) { - vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), trm); - conjs.push_back(m.mk_eq(trm, mk_q_arg(q, k, false))); - } - func_decl_ref qr = mk_q_func_decl(q); - conjs.push_back(m.mk_app(qr, m_bv.mk_bv_sub(var, mk_q_one()))); - } - for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { - vs(r.get_tail(j), sub.size(), sub.c_ptr(), trm); - conjs.push_back(trm); - } - if (r.get_uninterpreted_tail_size() > 0) { - conjs.push_back(m_bv.mk_ule(mk_q_one(), var)); - } - bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); - trm = m.mk_implies(rule_i, rule_body); - trm = m.mk_forall(1, index_sorts, &tick, trm, 1); - assert_expr(trm); - } - bool_rewriter(m).mk_or(rules.size(), rules.c_ptr(), trm); - trm = m.mk_implies(pred, trm); - trm = m.mk_forall(1, index_sorts, &tick, trm, 1); - SASSERT(is_well_sorted(m, trm)); - assert_expr(trm); - } - } - - void bmc::setup_qlinear() { - m_fparams.m_relevancy_lvl = 2; - m_fparams.m_model = true; - m_fparams.m_model_compact = true; - m_fparams.m_mbqi = true; - } - - void bmc::mk_qrule_vars(datalog::rule const& r, unsigned rule_id, expr_ref_vector& sub) { - sort_ref_vector sorts(m); - r.get_vars(sorts); - // populate substitution of bound variables. - sub.reset(); - sub.resize(sorts.size()); - - for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { - expr* arg = r.get_head()->get_arg(k); - if (is_var(arg)) { - unsigned idx = to_var(arg)->get_idx(); - if (!sub[idx].get()) { - sub[idx] = mk_q_arg(r.get_decl(), k, true); - } - } - } - for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { - func_decl* q = r.get_decl(j); - for (unsigned k = 0; k < q->get_arity(); ++k) { - expr* arg = r.get_tail(j)->get_arg(k); - if (is_var(arg)) { - unsigned idx = to_var(arg)->get_idx(); - if (!sub[idx].get()) { - sub[idx] = mk_q_arg(q, k, false); - } - } - } - } - for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { - if (sorts[j].get() && !sub[j].get()) { - sub[j] = mk_q_var(r.get_decl(), sorts[j].get(), rule_id, idx++); - } - } - } - - expr_ref bmc::mk_q_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx) { - std::stringstream _name; - _name << pred->get_name() << "#" << rule_id << "_" << idx; - symbol nm(_name.str().c_str()); - var_ref var = mk_index_var(); - return expr_ref(m.mk_app(m.mk_func_decl(nm, mk_index_sort(), s), var), m); - } - - expr_ref bmc::mk_q_arg(func_decl* pred, unsigned idx, bool is_current) { - SASSERT(idx < pred->get_arity()); - std::stringstream _name; - _name << pred->get_name() << "#" << idx; - symbol nm(_name.str().c_str()); - expr_ref var(mk_index_var(), m); - if (!is_current) { - var = m_bv.mk_bv_sub(var, mk_q_one()); - } - return expr_ref(m.mk_app(m.mk_func_decl(nm, mk_index_sort(), pred->get_domain(idx)), var), m); - } - - expr_ref bmc::mk_q_one() { - return mk_q_num(1); - } - - expr_ref bmc::mk_q_num(unsigned i) { - return expr_ref(m_bv.mk_numeral(i, m_bit_width), m); - } - - func_decl_ref bmc::mk_q_func_decl(func_decl* f) { - std::stringstream _name; - _name << f->get_name() << "#"; - symbol nm(_name.str().c_str()); - return func_decl_ref(m.mk_func_decl(nm, mk_index_sort(), f->get_range()), m); - } - - func_decl_ref bmc::mk_q_rule(func_decl* f, unsigned rule_id) { - std::stringstream _name; - _name << f->get_name() << "#" << rule_id; - symbol nm(_name.str().c_str()); - return func_decl_ref(m.mk_func_decl(nm, mk_index_sort(), m.mk_bool_sort()), m); - } - - - expr_ref bmc::eval_q(model_ref& model, func_decl* f, unsigned i) { - func_decl_ref fn = mk_q_func_decl(f); - expr_ref t(m), result(m); - t = m.mk_app(mk_q_func_decl(f).get(), mk_q_num(i)); - model->eval(t, result); - return result; - } - - expr_ref bmc::eval_q(model_ref& model, expr* t, unsigned i) { - expr_ref tmp(m), result(m), num(m); - var_subst vs(m, false); - num = mk_q_num(i); - expr* nums[1] = { num }; - vs(t, 1, nums, tmp); - model->eval(tmp, result); - return result; - } - - lbool bmc::get_model_qlinear() { - rule_manager& rm = m_ctx.get_rule_manager(); - func_decl_ref q = mk_q_func_decl(m_query_pred); - expr_ref T(m), rule_i(m), vl(m); - model_ref md; - proof_ref pr(m); - rule_unifier unifier(m_ctx); - rational num; - unsigned level, bv_size; - - m_solver.get_model(md); - func_decl* pred = m_query_pred; - dl_decl_util util(m); - T = m.mk_const(symbol("T"), mk_index_sort()); - md->eval(T, vl); - VERIFY (m_bv.is_numeral(vl, num, bv_size)); - SASSERT(num.is_unsigned()); - level = num.get_unsigned(); - SASSERT(m.is_true(eval_q(md, m_query_pred, level))); - TRACE("bmc", model_smt2_pp(tout, m, *md, 0);); - - rule_ref r0(rm), r1(rm), r2(rm); - while (true) { - TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); - expr_ref_vector sub(m); - rule_vector const& rls = m_rules.get_predicate_rules(pred); - rule* r = 0; - unsigned i = 0; - for (; i < rls.size(); ++i) { - rule_i = m.mk_app(mk_q_rule(pred, i), mk_q_num(level).get()); - TRACE("bmc", rls[i]->display(m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); - if (m.is_true(eval_q(md, rule_i, level))) { - r = rls[i]; - break; - } - } - SASSERT(r); - mk_qrule_vars(*r, i, sub); - // we have rule, we have variable names of rule. - - // extract values for the variables in the rule. - for (unsigned j = 0; j < sub.size(); ++j) { - expr_ref vl = eval_q(md, sub[j].get(), i); - if (vl) { - // vl can be 0 if the interpretation does not assign a value to it. - sub[j] = vl; - } - else { - sub[j] = m.mk_var(j, m.get_sort(sub[j].get())); - } - } - svector > positions; - vector substs; - expr_ref fml(m), concl(m); - - r->to_formula(fml); - r2 = r; - rm.substitute(r2, sub.size(), sub.c_ptr()); - if (r0) { - VERIFY(unifier.unify_rules(*r0.get(), 0, *r2.get())); - expr_ref_vector sub1 = unifier.get_rule_subst(*r0.get(), true); - expr_ref_vector sub2 = unifier.get_rule_subst(*r2.get(), false); - apply_subst(sub, sub2); - unifier.apply(*r0.get(), 0, *r2.get(), r1); - r1->to_formula(concl); - scoped_coarse_proof _sp(m); - - proof* p = m.mk_asserted(fml); - proof* premises[2] = { pr, p }; - - positions.push_back(std::make_pair(0, 1)); - - substs.push_back(sub1); - substs.push_back(sub); - pr = m.mk_hyper_resolve(2, premises, concl, positions, substs); - r0 = r1; - } - else { - r2->to_formula(concl); - scoped_coarse_proof _sp(m); - proof* p = m.mk_asserted(fml); - if (sub.empty()) { - pr = p; - } - else { - substs.push_back(sub); - pr = m.mk_hyper_resolve(1, &p, concl, positions, substs); - } - r0 = r2; - } - - if (level == 0) { - SASSERT(r->get_uninterpreted_tail_size() == 0); - break; - } - --level; - SASSERT(r->get_uninterpreted_tail_size() == 1); - pred = r->get_decl(0); - } - scoped_coarse_proof _sp(m); - apply(m, m_pc.get(), pr); - m_answer = pr; - return l_true; - } - - // -------------------------------------------------------------------------- - // Basic non-linear BMC based on compiling into data-types (it is inefficient) - - - lbool bmc::check_nonlinear() { - setup_nonlinear(); - declare_datatypes(); - compile_nonlinear(); - return check_query(); - } - - void bmc::setup_nonlinear() { - setup_linear(); - m_fparams.m_relevancy_lvl = 2; - } - - func_decl_ref bmc::mk_predicate(func_decl* pred) { - std::stringstream _name; - _name << pred->get_name() << "#"; - symbol nm(_name.str().c_str()); - sort* pred_trace_sort = m_pred2sort.find(pred); - return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m); - } - - func_decl_ref bmc::mk_rule(func_decl* p, unsigned rule_idx) { - std::stringstream _name; - _name << "rule:" << p->get_name() << "#" << rule_idx; - symbol nm(_name.str().c_str()); - sort* pred_trace_sort = m_pred2sort.find(p); - return func_decl_ref(m.mk_func_decl(nm, pred_trace_sort, m_path_sort, m.mk_bool_sort()), m); - } - - expr_ref bmc::mk_var_nonlinear(func_decl* pred, sort*s, unsigned idx, expr* path_arg, expr* trace_arg) { - std::stringstream _name; - _name << pred->get_name() << "#V_" << idx; - symbol nm(_name.str().c_str()); - func_decl_ref fn(m); - fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, s); - return expr_ref(m.mk_app(fn, trace_arg, path_arg), m); - } - - expr_ref bmc::mk_arg_nonlinear(func_decl* pred, unsigned idx, expr* path_arg, expr* trace_arg) { - SASSERT(idx < pred->get_arity()); - std::stringstream _name; - _name << pred->get_name() << "#X_" << idx; - symbol nm(_name.str().c_str()); - func_decl_ref fn(m); - fn = m.mk_func_decl(nm, m_pred2sort.find(pred), m_path_sort, pred->get_domain(idx)); - return expr_ref(m.mk_app(fn, trace_arg, path_arg), m); - } - - void bmc::mk_subst(datalog::rule& r, expr* path, app* trace, expr_ref_vector& sub) { - datatype_util dtu(m); - sort_ref_vector sorts(m); - func_decl* p = r.get_decl(); - ptr_vector const& succs = *dtu.get_datatype_constructors(m.get_sort(path)); - // populate substitution of bound variables. - r.get_vars(sorts); - sub.reset(); - sub.resize(sorts.size()); - for (unsigned k = 0; k < r.get_decl()->get_arity(); ++k) { - expr* arg = r.get_head()->get_arg(k); - if (is_var(arg)) { - unsigned idx = to_var(arg)->get_idx(); - if (!sub[idx].get()) { - sub[idx] = mk_arg_nonlinear(p, k, path, trace); - } - } - } - for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { - func_decl* q = r.get_decl(j); - expr_ref path_arg(m); - if (j == 0) { - path_arg = path; - } - else { - path_arg = m.mk_app(succs[j], path); - } - for (unsigned k = 0; k < q->get_arity(); ++k) { - expr* arg = r.get_tail(j)->get_arg(k); - if (is_var(arg)) { - unsigned idx = to_var(arg)->get_idx(); - if (!sub[idx].get()) { - sub[idx] = mk_arg_nonlinear(q, k, path_arg, trace->get_arg(j)); - } - } - } - } - for (unsigned j = 0, idx = 0; j < sorts.size(); ++j) { - if (sorts[j].get() && !sub[j].get()) { - sub[j] = mk_var_nonlinear(r.get_decl(), sorts[j].get(), idx++, path, trace); - } - } - } - - /** - \brief compile Horn rule into co-Horn implication. - forall args . R(path_var, rule_i(trace_vars)) => Body[X(path_var, rule_i(trace_vars)), Y(S_j(path_var), trace_vars_j)] - */ - void bmc::compile_nonlinear() { - datatype_util dtu(m); - - rule_set::decl2rules::iterator it = m_rules.begin_grouped_rules(); - rule_set::decl2rules::iterator end = m_rules.end_grouped_rules(); - for (; it != end; ++it) { - func_decl* p = it->m_key; - rule_vector const& rls = *it->m_value; - - // Assert: p_level => r1_level \/ r2_level \/ r3_level \/ ... - // where: r_i_level = body of rule i for level + equalities for head of rule i - - expr_ref rule_body(m), tmp(m), pred(m), trace_arg(m), fml(m); - var_ref path_var(m), trace_var(m); - expr_ref_vector rules(m), sub(m), conjs(m), vars(m), patterns(m); - sort* pred_sort = m_pred2sort.find(p); - path_var = m.mk_var(0, m_path_sort); - trace_var = m.mk_var(1, pred_sort); - // sort* sorts[2] = { pred_sort, m_path_sort }; - ptr_vector const& cnstrs = *dtu.get_datatype_constructors(pred_sort); - ptr_vector const& succs = *dtu.get_datatype_constructors(m_path_sort); - SASSERT(cnstrs.size() == rls.size()); - pred = m.mk_app(mk_predicate(p), trace_var.get(), path_var.get()); - for (unsigned i = 0; i < rls.size(); ++i) { - sub.reset(); - conjs.reset(); - vars.reset(); - rule& r = *rls[i]; - func_decl_ref rule_pred_i = mk_rule(p, i); - - // Create cnstr_rule_i(Vars) - func_decl* cnstr = cnstrs[i]; - rules.push_back(m.mk_app(rule_pred_i, trace_var.get(), path_var.get())); - unsigned arity = cnstr->get_arity(); - for (unsigned j = 0; j < arity; ++j) { - vars.push_back(m.mk_var(arity-j,cnstr->get_domain(j))); - } - trace_arg = m.mk_app(cnstr, vars.size(), vars.c_ptr()); - - mk_subst(r, path_var, to_app(trace_arg), sub); - - // apply substitution to body. - var_subst vs(m, false); - for (unsigned k = 0; k < p->get_arity(); ++k) { - vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp); - expr_ref arg = mk_arg_nonlinear(p, k, path_var, trace_arg); - conjs.push_back(m.mk_eq(tmp, arg)); - } - for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { - expr_ref path_arg(m); - if (j == 0) { - path_arg = path_var.get(); - } - else { - path_arg = m.mk_app(succs[j], path_var.get()); - } - func_decl* q = r.get_decl(j); - for (unsigned k = 0; k < q->get_arity(); ++k) { - vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp); - expr_ref arg = mk_arg_nonlinear(q, k, path_arg, vars[j].get()); - conjs.push_back(m.mk_eq(tmp, arg)); - } - func_decl_ref q_pred = mk_predicate(q); - conjs.push_back(m.mk_app(q_pred, vars[j].get(), path_arg)); - } - for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { - vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp); - conjs.push_back(tmp); - } - bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); - ptr_vector q_sorts; - vector names; - for (unsigned i = 0; i < vars.size(); ++i) { - q_sorts.push_back(m.get_sort(vars[i].get())); - names.push_back(symbol(i+1)); - } - vars.push_back(path_var); - q_sorts.push_back(m.get_sort(path_var)); - names.push_back(symbol("path")); - SASSERT(names.size() == q_sorts.size()); - SASSERT(vars.size() == names.size()); - symbol qid = r.name(), skid; - tmp = m.mk_app(mk_predicate(p), trace_arg.get(), path_var.get()); - patterns.reset(); - patterns.push_back(m.mk_pattern(to_app(tmp))); - fml = m.mk_implies(tmp, rule_body); - fml = m.mk_forall(vars.size(), q_sorts.c_ptr(), names.c_ptr(), fml, 1, qid, skid, 1, patterns.c_ptr()); - assert_expr(fml); - - } - } - } - - void bmc::declare_datatypes() { - rule_set::decl2rules::iterator it = m_rules.begin_grouped_rules(); - rule_set::decl2rules::iterator end = m_rules.end_grouped_rules(); - datatype_util dtu(m); - ptr_vector dts; - - obj_map pred_idx; - for (unsigned i = 0; it != end; ++it, ++i) { - pred_idx.insert(it->m_key, i); - } - - it = m_rules.begin_grouped_rules(); - for (; it != end; ++it) { - rule_vector const& rls = *it->m_value; - func_decl* pred = it->m_key; - ptr_vector cnstrs; - for (unsigned i = 0; i < rls.size(); ++i) { - rule* r = rls[i]; - ptr_vector accs; - for (unsigned j = 0; j < r->get_uninterpreted_tail_size(); ++j) { - func_decl* q = r->get_decl(j); - unsigned idx = pred_idx.find(q); - std::stringstream _name; - _name << pred->get_name() << "_" << q->get_name() << j; - symbol name(_name.str().c_str()); - type_ref tr(idx); - accs.push_back(mk_accessor_decl(name, tr)); - } - std::stringstream _name; - _name << pred->get_name() << "_" << i; - symbol name(_name.str().c_str()); - _name << "?"; - symbol is_name(_name.str().c_str()); - cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr())); - } - dts.push_back(mk_datatype_decl(pred->get_name(), cnstrs.size(), cnstrs.c_ptr())); - } - - - sort_ref_vector new_sorts(m); - family_id dfid = m.get_family_id("datatype"); - datatype_decl_plugin* dtp = static_cast(m.get_plugin(dfid)); - VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), new_sorts)); - - it = m_rules.begin_grouped_rules(); - for (unsigned i = 0; it != end; ++it, ++i) { - m_pred2sort.insert(it->m_key, new_sorts[i].get()); - m_sort2pred.insert(new_sorts[i].get(), it->m_key); - m_pinned.push_back(new_sorts[i].get()); - } - if (new_sorts.size() > 0) { - TRACE("bmc", dtu.display_datatype(new_sorts[0].get(), tout);); - } - del_datatype_decls(dts.size(), dts.c_ptr()); - - // declare path data-type. - { - new_sorts.reset(); - dts.reset(); - ptr_vector cnstrs; - unsigned max_arity = 0; - rule_set::iterator it = m_rules.begin(); - rule_set::iterator end = m_rules.end(); - for (; it != end; ++it) { - rule* r = *it; - unsigned sz = r->get_uninterpreted_tail_size(); - max_arity = std::max(sz, max_arity); - } - cnstrs.push_back(mk_constructor_decl(symbol("Z#"), symbol("Z#?"), 0, 0)); - - for (unsigned i = 0; i + 1 < max_arity; ++i) { - std::stringstream _name; - _name << "succ#" << i; - symbol name(_name.str().c_str()); - _name << "?"; - symbol is_name(_name.str().c_str()); - std::stringstream _name2; - _name2 << "get_succ#" << i; - symbol acc_name(_name2.str().c_str()); - ptr_vector accs; - type_ref tr(0); - accs.push_back(mk_accessor_decl(name, tr)); - cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr())); - } - dts.push_back(mk_datatype_decl(symbol("Path"), cnstrs.size(), cnstrs.c_ptr())); - VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), new_sorts)); - m_path_sort = new_sorts[0].get(); - } - } - - proof_ref bmc::get_proof(model_ref& md, app* trace, app* path) { - datatype_util dtu(m); - sort* trace_sort = m.get_sort(trace); - func_decl* p = m_sort2pred.find(trace_sort); - datalog::rule_vector const& rules = m_rules.get_predicate_rules(p); - ptr_vector const& cnstrs = *dtu.get_datatype_constructors(trace_sort); - ptr_vector const& succs = *dtu.get_datatype_constructors(m_path_sort); - // bool found = false; - for (unsigned i = 0; i < cnstrs.size(); ++i) { - if (trace->get_decl() == cnstrs[i]) { - // found = true; - svector > positions; - scoped_coarse_proof _sc(m); - proof_ref_vector prs(m); - expr_ref_vector sub(m); - vector substs; - proof_ref pr(m); - expr_ref fml(m), head(m), tmp(m); - app_ref path1(m); - - var_subst vs(m, false); - mk_subst(*rules[i], path, trace, sub); - rules[i]->to_formula(fml); - prs.push_back(m.mk_asserted(fml)); - unsigned sz = trace->get_num_args(); - if (sub.empty() && sz == 0) { - pr = prs[0].get(); - return pr; - } - for (unsigned j = 0; j < sub.size(); ++j) { - md->eval(sub[j].get(), tmp); - sub[j] = tmp; - } - rule_ref rl(m_ctx.get_rule_manager()); - rl = rules[i]; - m_ctx.get_rule_manager().substitute(rl, sub.size(), sub.c_ptr()); - - substs.push_back(sub); - - for (unsigned j = 0; j < sz; ++j) { - if (j == 0) { - path1 = path; - } - else { - path1 = m.mk_app(succs[j], path); - } - - prs.push_back(get_proof(md, to_app(trace->get_arg(j)), path1)); - positions.push_back(std::make_pair(j+1,0)); - substs.push_back(expr_ref_vector(m)); - } - head = rl->get_head(); - pr = m.mk_hyper_resolve(sz+1, prs.c_ptr(), head, positions, substs); - return pr; - } - } - UNREACHABLE(); - return proof_ref(0, m); - } - - // instantiation of algebraic data-types takes care of the rest. - lbool bmc::check_query() { - sort* trace_sort = m_pred2sort.find(m_query_pred); - func_decl_ref q = mk_predicate(m_query_pred); - expr_ref trace(m), path(m), fml(m); - trace = m.mk_const(symbol("trace"), trace_sort); - path = m.mk_const(symbol("path"), m_path_sort); - fml = m.mk_app(q, trace.get(), path.get()); - assert_expr(fml); - while (true) { - lbool is_sat = m_solver.check(); - model_ref md; - if (is_sat == l_false) { - return is_sat; - } - m_solver.get_model(md); - mk_answer_nonlinear(md, trace, path); - return l_true; - } - } - - bool bmc::check_model_nonlinear(model_ref& md, expr* trace) { - expr_ref trace_val(m), eq(m); - md->eval(trace, trace_val); - eq = m.mk_eq(trace, trace_val); - m_solver.push(); - m_solver.assert_expr(eq); - lbool is_sat = m_solver.check(); - if (is_sat != l_false) { - m_solver.get_model(md); - } - m_solver.pop(1); - if (is_sat == l_false) { - IF_VERBOSE(1, verbose_stream() << "infeasible trace " << mk_pp(trace_val, m) << "\n";); - eq = m.mk_not(eq); - m_solver.assert_expr(eq); - } - return is_sat != l_false; - } - - void bmc::mk_answer_nonlinear(model_ref& md, expr_ref& trace, expr_ref& path) { - proof_ref pr(m); - IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); - md->eval(trace, trace); - md->eval(path, path); - IF_VERBOSE(2, verbose_stream() << mk_pp(trace, m) << "\n"; - for (unsigned i = 0; i < m_solver.size(); ++i) { - verbose_stream() << mk_pp(m_solver.get_formulas()[i], m) << "\n"; - }); - m_answer = get_proof(md, to_app(trace), to_app(path)); - } - - void bmc::checkpoint() { if (m_cancel) { throw default_exception("bmc canceled"); @@ -1103,4 +1510,16 @@ namespace datalog { return m_answer; } + void bmc::compile(rule_set const& rules, expr_ref_vector& fmls, unsigned level) { + nonlinear nl(*this); + nl.compile(rules, fmls, level); + } + + expr_ref bmc::compile_query(func_decl* query_pred, unsigned level) { + nonlinear nl(*this); + return nl.compile_query(query_pred, level); + } + }; + +template class rewriter_tpl; diff --git a/src/muz_qe/dl_bmc_engine.h b/src/muz_qe/dl_bmc_engine.h index 5b6e433cd..06901a160 100644 --- a/src/muz_qe/dl_bmc_engine.h +++ b/src/muz_qe/dl_bmc_engine.h @@ -35,83 +35,23 @@ namespace datalog { ast_manager& m; smt_params m_fparams; smt::kernel m_solver; - obj_map m_pred2sort; - obj_map m_sort2pred; - obj_map m_pred2newpred; - obj_map > m_pred2args; - ast_ref_vector m_pinned; rule_set m_rules; func_decl_ref m_query_pred; expr_ref m_answer; volatile bool m_cancel; proof_converter_ref m_pc; - sort_ref m_path_sort; - bv_util m_bv; - unsigned m_bit_width; - - lbool check_query(); - - proof_ref get_proof(model_ref& md, app* trace, app* path); void checkpoint(); - void declare_datatypes(); - - void compile_nonlinear(); - - void mk_rule_vars_nonlinear(rule& r, unsigned rule_id, expr* trace_arg, expr* path_arg, expr_ref_vector& sub); - - expr_ref mk_var_nonlinear(func_decl* pred, sort* s, unsigned idx, expr* path_arg, expr* trace_arg); - - expr_ref mk_arg_nonlinear(func_decl* pred, unsigned idx, expr* path_arg, expr* trace_arg); - - void mk_subst(rule& r, expr* path, app* trace, expr_ref_vector& sub); + class nonlinear_dt; + class nonlinear; + class qlinear; + class linear; bool is_linear() const; - - lbool check_nonlinear(); - void setup_nonlinear(); - bool check_model_nonlinear(model_ref& md, expr* trace); - void mk_answer_nonlinear(model_ref& md, expr_ref& trace, expr_ref& path); - func_decl_ref mk_predicate(func_decl* p); - - func_decl_ref mk_rule(func_decl* p, unsigned rule_idx); - - // linear check - lbool check_linear(); - lbool check_linear(unsigned level); - void compile_linear(); - void compile_linear(unsigned level); - void compile_linear(rule& r, unsigned level); - expr_ref mk_level_predicate(symbol const& name, unsigned level); - expr_ref mk_level_predicate(func_decl* p, unsigned level); - expr_ref mk_level_arg(func_decl* pred, unsigned idx, unsigned level); - expr_ref mk_level_rule(func_decl* p, unsigned rule_idx, unsigned level); - expr_ref mk_level_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx, unsigned level); - void get_model_linear(unsigned level); - void setup_linear(); - void assert_expr(expr* e); - void mk_rule_vars(rule& r, unsigned level, unsigned rule_id, expr_ref_vector& sub); - - // quantified linear check - void compile_qlinear(); - void setup_qlinear(); - lbool check_qlinear(); - lbool get_model_qlinear(); - sort_ref mk_index_sort(); - var_ref mk_index_var(); - void mk_qrule_vars(datalog::rule const& r, unsigned i, expr_ref_vector& sub); - expr_ref mk_q_var(func_decl* pred, sort* s, unsigned rule_id, unsigned idx); - expr_ref mk_q_arg(func_decl* pred, unsigned idx, bool is_current); - expr_ref mk_q_one(); - expr_ref mk_q_num(unsigned i); - expr_ref eval_q(model_ref& model, expr* t, unsigned i); - expr_ref eval_q(model_ref& model, func_decl* f, unsigned i); - func_decl_ref mk_q_rule(func_decl* f, unsigned rule_id); - func_decl_ref mk_q_func_decl(func_decl* f); public: bmc(context& ctx); @@ -131,6 +71,10 @@ namespace datalog { void reset_statistics(); expr_ref get_answer(); + + // direct access to (new) non-linear compiler. + void compile(rule_set const& rules, expr_ref_vector& fmls, unsigned level); + expr_ref compile_query(func_decl* query_pred, unsigned level); }; }; diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 7a46228ce..5581732c8 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -715,6 +715,8 @@ namespace datalog { check_positive_predicates(r); break; case BMC_ENGINE: + check_positive_predicates(r); + break; case QBMC_ENGINE: check_existential_tail(r); check_positive_predicates(r); diff --git a/src/muz_qe/dl_mk_array_blast.cpp b/src/muz_qe/dl_mk_array_blast.cpp index a5259ba8a..880f196a2 100644 --- a/src/muz_qe/dl_mk_array_blast.cpp +++ b/src/muz_qe/dl_mk_array_blast.cpp @@ -18,7 +18,8 @@ Revision History: --*/ #include "dl_mk_array_blast.h" -#include "expr_replacer.h" +#include "expr_safe_replace.h" + namespace datalog { @@ -54,9 +55,10 @@ namespace datalog { unsigned tsz = r.get_tail_size(); expr_ref_vector conjs(m), new_conjs(m); expr_ref tmp(m); - expr_substitution sub(m); + expr_safe_replace sub(m); uint_set lhs_vars, rhs_vars; bool change = false; + bool inserted = false; for (unsigned i = 0; i < utsz; ++i) { new_conjs.push_back(r.get_tail(i)); @@ -81,6 +83,7 @@ namespace datalog { } else { sub.insert(x, y); + inserted = true; } } else { @@ -89,7 +92,7 @@ namespace datalog { new_conjs.push_back(tmp); } } - if (sub.empty() && !change) { + if (!inserted && !change) { rules.add_rule(&r); return false; } @@ -99,11 +102,9 @@ namespace datalog { r.to_formula(fml1); body = m.mk_and(new_conjs.size(), new_conjs.c_ptr()); head = r.get_head(); - scoped_ptr replace = mk_default_expr_replacer(m); - replace->set_substitution(&sub); - (*replace)(body); + sub(body); m_rewriter(body); - (*replace)(head); + sub(head); m_rewriter(head); fml2 = m.mk_implies(body, head); rm.mk_rule(fml2, new_rules, r.name()); diff --git a/src/muz_qe/dl_mk_coalesce.cpp b/src/muz_qe/dl_mk_coalesce.cpp index 251988a84..222881bc4 100644 --- a/src/muz_qe/dl_mk_coalesce.cpp +++ b/src/muz_qe/dl_mk_coalesce.cpp @@ -60,7 +60,7 @@ namespace datalog { obj_map indices; bool_rewriter bwr(m); rule_ref r(const_cast(&rl), rm); - sort_ref_vector sorts(m); + ptr_vector sorts; expr_ref_vector revsub(m), conjs(m); rl.get_vars(sorts); revsub.resize(sorts.size()); @@ -72,8 +72,8 @@ namespace datalog { if (is_var(e)) { unsigned v = to_var(e)->get_idx(); SASSERT(v < valid.size()); - if (sorts[v].get()) { - SASSERT(s == sorts[v].get()); + if (sorts[v]) { + SASSERT(s == sorts[v]); if (valid[v]) { revsub[v] = w; valid[v] = false; @@ -92,8 +92,8 @@ namespace datalog { } } for (unsigned i = 0; i < sorts.size(); ++i) { - if (valid[i] && sorts[i].get() && !revsub[i].get()) { - revsub[i] = m.mk_var(m_idx++, sorts[i].get()); + if (valid[i] && sorts[i] && !revsub[i].get()) { + revsub[i] = m.mk_var(m_idx++, sorts[i]); } } var_subst vs(m, false); @@ -112,7 +112,7 @@ namespace datalog { app_ref pred(m), head(m); expr_ref fml1(m), fml2(m), fml(m); app_ref_vector tail(m); - sort_ref_vector sorts1(m), sorts2(m); + ptr_vector sorts1, sorts2; expr_ref_vector conjs1(m), conjs(m); rule_ref res(rm); bool_rewriter bwr(m); diff --git a/src/muz_qe/dl_mk_extract_quantifiers2.cpp b/src/muz_qe/dl_mk_extract_quantifiers2.cpp new file mode 100644 index 000000000..97976a6be --- /dev/null +++ b/src/muz_qe/dl_mk_extract_quantifiers2.cpp @@ -0,0 +1,366 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + dl_mk_extract_quantifiers2.cpp + +Abstract: + + Remove universal quantifiers over interpreted predicates in the body. + +Author: + + Nikolaj Bjorner (nbjorner) 2012-11-21 + +Revision History: + +--*/ + +#include"dl_mk_extract_quantifiers2.h" +#include"ast_pp.h" +#include"dl_bmc_engine.h" +#include"smt_quantifier.h" +#include"smt_context.h" + +namespace datalog { + + + mk_extract_quantifiers2::mk_extract_quantifiers2(context & ctx) : + rule_transformer::plugin(101, false), + m_ctx(ctx), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_query_pred(m), + m_quantifiers(m), + m_refs(m) + {} + + mk_extract_quantifiers2::~mk_extract_quantifiers2() { + reset(); + } + + void mk_extract_quantifiers2::set_query(func_decl* q) { + m_query_pred = q; + } + + bool mk_extract_quantifiers2::matches_signature(func_decl* head, expr_ref_vector const& binding) { + unsigned sz = head->get_arity(); + if (sz != binding.size()) { + return false; + } + for (unsigned i = 0; i < sz; ++i) { + if (head->get_domain(i) != m.get_sort(binding[sz-i-1])) { + return false; + } + } + return true; + } + + bool mk_extract_quantifiers2::matches_quantifier(quantifier* q, expr_ref_vector const& binding) { + unsigned sz = q->get_num_decls(); + if (sz != binding.size()) { + return false; + } + for (unsigned i = 0; i < sz; ++i) { + if (q->get_decl_sort(i) != m.get_sort(binding[sz-i-1])) { + return false; + } + } + return true; + } + + bool mk_extract_quantifiers2::mk_abstract_expr(expr_ref& term) { + if (!is_app(term)) { + return false; + } + expr* r; + if (m_map.find(term, r)) { + term = r; + return true; + } + if (to_app(term)->get_family_id() == null_family_id) { + return false; + } + expr_ref_vector args(m); + expr_ref tmp(m); + for (unsigned i = 0; i < to_app(term)->get_num_args(); ++i) { + tmp = to_app(term)->get_arg(i); + if (!mk_abstract_expr(tmp)) { + return false; + } + args.push_back(tmp); + } + tmp = m.mk_app(to_app(term)->get_decl(), args.size(), args.c_ptr()); + m_refs.push_back(tmp); + m_map.insert(term, tmp); + term = tmp; + return true; + } + + bool mk_extract_quantifiers2::mk_abstract_binding(expr_ref_vector const& binding, expr_ref_vector& result) { + for (unsigned i = 0; i < binding.size(); ++i) { + expr_ref tmp(m); + tmp = binding[i]; + if (!mk_abstract_expr(tmp)) { + return false; + } + result.push_back(tmp); + } + return true; + } + + void mk_extract_quantifiers2::mk_abstraction_map(rule& r, expr_ref_vector const& binding) { + m_map.reset(); + unsigned sz = binding.size(); + SASSERT(sz == r.get_decl()->get_arity()); + for (unsigned i = 0; i < sz; ++i) { + m_map.insert(binding[sz-i-1], r.get_head()->get_arg(i)); + SASSERT(m.get_sort(binding[sz-i-1]) == m.get_sort(r.get_head()->get_arg(i))); + } + // todo: also make bindings for variables in rule body. + } + + void mk_extract_quantifiers2::match_bindings(unsigned i, unsigned j, unsigned k) { + expr_ref_vector resb(m); + rule* r = m_qrules[i]; + quantifier* q = m_quantifiers[i].get(); + expr_ref_vector const& ruleb = m_rule_bindings[i][j]; + expr_ref_vector const& quantb = m_quantifier_bindings[i][k]; + mk_abstraction_map(*r, ruleb); + if (!mk_abstract_binding(quantb, resb)) { + return; + } + expr_ref inst(m), tmp(m); + var_shifter shift(m); + + for (unsigned l = 0; l < resb.size(); ++l) { + tmp = resb[l].get(); + shift(tmp, q->get_num_decls(), tmp); + resb[l] = tmp; + } + + instantiate(m, q, resb.c_ptr(), inst); + if (!m_seen.contains(r)) { + m_seen.insert(r, alloc(obj_hashtable)); + } + obj_hashtable& seen = *m_seen.find(r); + if (seen.contains(inst)) { + return; + } + seen.insert(inst); + m_refs.push_back(inst); + if (!m_quantifier_instantiations.contains(r, q)) { + m_quantifier_instantiations.insert(r, q, alloc(expr_ref_vector, m)); + } + expr_ref_vector* vec = 0; + VERIFY(m_quantifier_instantiations.find(r, q, vec)); + vec->push_back(inst); + TRACE("dl", tout << "matched: " << mk_pp(q, m) << "\n" << mk_pp(inst, m) << "\n";); + } + + app_ref mk_extract_quantifiers2::ensure_app(expr* e) { + if (is_app(e)) { + return app_ref(to_app(e), m); + } + else { + return app_ref(m.mk_eq(e, m.mk_true()), m); + } + } + + void mk_extract_quantifiers2::extract(rule& r, rule_set& new_rules) { + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + bool has_quantifier = false; + expr_ref_vector conjs(m); + for (unsigned i = utsz; i < tsz; ++i) { + conjs.push_back(r.get_tail(i)); + } + datalog::flatten_and(conjs); + for (unsigned j = 0; j < conjs.size(); ++j) { + expr* e = conjs[j].get(); + quantifier* q; + if (rule_manager::is_forall(m, e, q)) { + m_quantifiers.push_back(q); + m_qrules.push_back(&r); + m_rule_bindings.push_back(vector()); + m_quantifier_bindings.push_back(vector()); + has_quantifier = true; + } + } + if (!has_quantifier) { + new_rules.add_rule(&r); + } + } + + void mk_extract_quantifiers2::apply(rule& r, rule_set& new_rules) { + expr_ref_vector tail(m), conjs(m); + expr_ref fml(m); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + for (unsigned i = 0; i < utsz; ++i) { + SASSERT(!r.is_neg_tail(i)); + tail.push_back(r.get_tail(i)); + } + bool has_quantifier = false; + for (unsigned i = utsz; i < tsz; ++i) { + conjs.push_back(r.get_tail(i)); + } + datalog::flatten_and(conjs); + for (unsigned j = 0; j < conjs.size(); ++j) { + expr* e = conjs[j].get(); + quantifier* q; + if (rule_manager::is_forall(m, e, q)) { + expr_ref_vector* ls; + if (m_quantifier_instantiations.find(&r,q,ls)) { + tail.append(*ls); + } + has_quantifier = true; + } + else { + tail.push_back(e); + } + } + if (has_quantifier) { + fml = m.mk_implies(m.mk_and(tail.size(), tail.c_ptr()), r.get_head()); + rule_ref_vector rules(rm); + rm.mk_rule(fml, rules, r.name()); + for (unsigned i = 0; i < rules.size(); ++i) { + new_rules.add_rule(rules[i].get()); + } + } + } + +#if 0 + class mk_extract_quantifiers2::instance_plugin : public smt::quantifier_instance_plugin { + mk_extract_quantifiers2& ex; + ast_manager& m; + expr_ref_vector m_refs; + obj_hashtable m_bindings; + public: + instance_plugin(mk_extract_quantifiers2& ex): ex(ex), m(ex.m), m_refs(m) {} + + virtual void operator()(quantifier* q, unsigned num_bindings, smt::enode*const* bindings) { + expr_ref_vector binding(m); + ptr_vector sorts; + for (unsigned i = 0; i < num_bindings; ++i) { + binding.push_back(bindings[i]->get_owner()); + sorts.push_back(m.get_sort(binding[i].get())); + } + func_decl* f = m.mk_func_decl(symbol("T"), sorts.size(), sorts.c_ptr(), m.mk_bool_sort()); + expr_ref tup(m); + tup = m.mk_app(f, binding.size(), binding.c_ptr()); + if (!m_bindings.contains(tup)) { + m_bindings.insert(tup); + m_refs.push_back(tup); + ex.m_bindings.push_back(binding); + TRACE("dl", tout << "insert\n" << mk_pp(q, m) << "\n" << mk_pp(tup, m) << "\n";); + } + } + }; + +#endif + + void mk_extract_quantifiers2::reset() { + { + obj_pair_map::iterator + it = m_quantifier_instantiations.begin(), + end = m_quantifier_instantiations.end(); + for (; it != end; ++it) { + dealloc(it->get_value()); + } + } + { + obj_map*>::iterator + it = m_seen.begin(), + end = m_seen.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } + } + m_quantifier_instantiations.reset(); + m_seen.reset(); + m_has_quantifiers = false; + m_quantifiers.reset(); + m_qrules.reset(); + m_bindings.reset(); + m_rule_bindings.reset(); + m_quantifier_bindings.reset(); + m_refs.reset(); + } + + rule_set * mk_extract_quantifiers2::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { + reset(); + rule_set::iterator it = source.begin(), end = source.end(); + for (; !m_has_quantifiers && it != end; ++it) { + m_has_quantifiers = (*it)->has_quantifiers(); + } + if (!m_has_quantifiers) { + return 0; + } + + rule_set* rules = alloc(rule_set, m_ctx); + it = source.begin(); + for (; it != end; ++it) { + extract(**it, *rules); + } + + bmc bmc(m_ctx); + expr_ref_vector fmls(m); + bmc.compile(source, fmls, 0); // TBD: use cancel_eh to terminate without base-case. + bmc.compile(source, fmls, 1); + bmc.compile(source, fmls, 2); +// bmc.compile(source, fmls, 3); + expr_ref query = bmc.compile_query(m_query_pred, 2); + fmls.push_back(query); + smt_params fparams; + fparams.m_relevancy_lvl = 0; + fparams.m_model = true; + fparams.m_model_compact = true; + fparams.m_mbqi = true; + smt::kernel solver(m, fparams); + TRACE("dl", + for (unsigned i = 0; i < fmls.size(); ++i) { + tout << mk_pp(fmls[i].get(), m) << "\n"; + }); + + for (unsigned i = 0; i < fmls.size(); ++i) { + solver.assert_expr(fmls[i].get()); + } +#if 0 + smt::context& ctx = solver.get_context(); + smt::quantifier_manager* qm = ctx.get_quantifier_manager(); + qm->get_plugin()->set_instance_plugin(alloc(instance_plugin, *this)); +#endif + lbool res = solver.check(); + + for (unsigned i = 0; i < m_bindings.size(); ++i) { + expr_ref_vector& binding = m_bindings[i]; + for (unsigned j = 0; j < m_qrules.size(); ++j) { + rule* r = m_qrules[j]; + if (matches_signature(r->get_decl(), binding)) { + m_rule_bindings[j].push_back(binding); + } + else if (matches_quantifier(m_quantifiers[j].get(), binding)) { + m_quantifier_bindings[j].push_back(binding); + } + } + } + for (unsigned i = 0; i < m_qrules.size(); ++i) { + for (unsigned j = 0; j < m_rule_bindings[i].size(); ++j) { + for (unsigned k = 0; k < m_quantifier_bindings[i].size(); ++k) { + match_bindings(i, j, k); + } + } + } + it = source.begin(); + for (; it != end; ++it) { + apply(**it, *rules); + } + + return rules; + } + +}; + + diff --git a/src/muz_qe/dl_mk_extract_quantifiers2.h b/src/muz_qe/dl_mk_extract_quantifiers2.h new file mode 100644 index 000000000..30c15b313 --- /dev/null +++ b/src/muz_qe/dl_mk_extract_quantifiers2.h @@ -0,0 +1,91 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + dl_mk_extract_quantifiers2.h + +Abstract: + + Replace universal quantifiers over interpreted predicates in the body + by instantiations mined using bounded model checking search. + +Author: + + Nikolaj Bjorner (nbjorner) 2012-11-21 + +Revision History: + +--*/ +#ifndef _DL_MK_EXTRACT_QUANTIFIERS2_H_ +#define _DL_MK_EXTRACT_QUANTIFIERS2_H_ + +#include"dl_context.h" +#include"dl_rule_set.h" +#include"dl_rule_transformer.h" +#include"obj_pair_hashtable.h" + +namespace datalog { + + /** + \brief Extract universal quantifiers from rules. + */ + class mk_extract_quantifiers2 : public rule_transformer::plugin { + context& m_ctx; + ast_manager& m; + rule_manager& rm; + func_decl_ref m_query_pred; + quantifier_ref_vector m_quantifiers; + ptr_vector m_qrules; + vectorm_bindings; + vector > m_rule_bindings; + vector > m_quantifier_bindings; + obj_pair_map m_quantifier_instantiations; + obj_map*> m_seen; + + bool m_has_quantifiers; + obj_map m_map; + expr_ref_vector m_refs; + + class instance_plugin; + + void reset(); + + void extract(rule& r, rule_set& new_rules); + + void apply(rule& r, rule_set& new_rules); + + app_ref ensure_app(expr* e); + + bool matches_signature(func_decl* head, expr_ref_vector const& binding); + + bool matches_quantifier(quantifier* q, expr_ref_vector const& binding); + + void match_bindings(unsigned i, unsigned j, unsigned k); + + bool mk_abstract_expr(expr_ref& term); + + bool mk_abstract_binding(expr_ref_vector const& binding, expr_ref_vector& result); + + void mk_abstraction_map(rule& r, expr_ref_vector const& binding); + + public: + /** + \brief Create rule transformer that extracts universal quantifiers (over recursive predicates). + */ + mk_extract_quantifiers2(context & ctx); + + virtual ~mk_extract_quantifiers2(); + + void set_query(func_decl* q); + + rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + + bool has_quantifiers() { return m_has_quantifiers; } + + }; + +}; + +#endif /* _DL_MK_EXTRACT_QUANTIFIERS2_H_ */ + diff --git a/src/muz_qe/dl_mk_rule_inliner.cpp b/src/muz_qe/dl_mk_rule_inliner.cpp index fa532ee6c..24ddd9ba5 100644 --- a/src/muz_qe/dl_mk_rule_inliner.cpp +++ b/src/muz_qe/dl_mk_rule_inliner.cpp @@ -136,14 +136,14 @@ namespace datalog { expr_ref_vector rule_unifier::get_rule_subst(const rule& r, bool is_tgt) { SASSERT(m_ready); expr_ref_vector result(m); - sort_ref_vector sorts(m); + ptr_vector sorts; expr_ref v(m), w(m); r.get_vars(sorts); for (unsigned i = 0; i < sorts.size(); ++i) { - if (!sorts[i].get()) { + if (!sorts[i]) { sorts[i] = m.mk_bool_sort(); } - v = m.mk_var(i, sorts[i].get()); + v = m.mk_var(i, sorts[i]); m_subst.apply(2, m_deltas, expr_offset(v, is_tgt?0:1), w); result.push_back(w); } diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index dedd37a78..bd7131fea 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -41,6 +41,7 @@ Revision History: #include"expr_replacer.h" #include"bool_rewriter.h" #include"qe_lite.h" +#include"expr_safe_replace.h" namespace datalog { @@ -130,17 +131,15 @@ namespace datalog { return index; } // replace vars by de-bruijn indices - expr_substitution sub(m); + expr_safe_replace rep(m); for (unsigned i = 0; i < vars.size(); ++i) { app* v = vars[i].get(); if (names) { names->push_back(v->get_decl()->get_name()); } - sub.insert(v, m.mk_var(index++,m.get_sort(v))); + rep.insert(v, m.mk_var(index++,m.get_sort(v))); } - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - (*rep)(fml); + rep(fml); return index; } @@ -936,7 +935,7 @@ namespace datalog { } } - void rule::get_vars(sort_ref_vector& sorts) const { + void rule::get_vars(ptr_vector& sorts) const { sorts.reset(); used_vars used; get_used_vars(used); diff --git a/src/muz_qe/dl_rule.h b/src/muz_qe/dl_rule.h index 79a46052c..a9e360344 100644 --- a/src/muz_qe/dl_rule.h +++ b/src/muz_qe/dl_rule.h @@ -244,7 +244,7 @@ namespace datalog { void norm_vars(rule_manager & rm); - void get_vars(sort_ref_vector& sorts) const; + void get_vars(ptr_vector& sorts) const; void to_formula(expr_ref& result) const; diff --git a/src/muz_qe/pdr_quantifiers.cpp b/src/muz_qe/pdr_quantifiers.cpp index 2816a58d4..9233f6314 100644 --- a/src/muz_qe/pdr_quantifiers.cpp +++ b/src/muz_qe/pdr_quantifiers.cpp @@ -440,7 +440,7 @@ namespace pdr { unsigned ut_size = r.get_uninterpreted_tail_size(); unsigned t_size = r.get_tail_size(); var_subst vs(m, false); - sort_ref_vector vars(m); + ptr_vector vars; uint_set empty_index_set; qe_lite qe(m); diff --git a/src/muz_qe/pdr_util.cpp b/src/muz_qe/pdr_util.cpp index fd08b1aad..1ea705e6e 100644 --- a/src/muz_qe/pdr_util.cpp +++ b/src/muz_qe/pdr_util.cpp @@ -1129,7 +1129,11 @@ namespace pdr { if (a.is_numeral(lhs) || a.is_numeral(rhs)) { return test_ineq(e); } - return test_term(lhs) && test_term(rhs); + return + test_term(lhs) && + test_term(rhs) && + !a.is_mul(lhs) && + !a.is_mul(rhs); } bool test_term(expr* e) const { diff --git a/src/muz_qe/proof_utils.cpp b/src/muz_qe/proof_utils.cpp index 75c9cbb15..1e837b578 100644 --- a/src/muz_qe/proof_utils.cpp +++ b/src/muz_qe/proof_utils.cpp @@ -312,9 +312,10 @@ public: }; void proof_utils::reduce_hypotheses(proof_ref& pr) { - class reduce_hypotheses reduce(pr.get_manager()); + ast_manager& m = pr.get_manager(); + class reduce_hypotheses reduce(m); reduce(pr); - SASSERT(is_closed(pr.get_manager(), pr)); + CTRACE("proof_utils", !is_closed(m, pr), tout << mk_pp(pr, m) << "\n";); } class proof_is_closed { diff --git a/src/muz_qe/qe_lite.cpp b/src/muz_qe/qe_lite.cpp index 8880a9ba5..df4a55475 100644 --- a/src/muz_qe/qe_lite.cpp +++ b/src/muz_qe/qe_lite.cpp @@ -35,7 +35,7 @@ Revision History: #include "dl_util.h" #include "for_each_expr.h" #include "expr_safe_replace.h" - +#include "cooperate.h" class is_variable_proc { public: @@ -91,6 +91,7 @@ namespace eq { expr_ref_vector m_subst_map; expr_ref_buffer m_new_args; th_rewriter m_rewriter; + volatile bool m_cancel; void der_sort_vars(ptr_vector & vars, ptr_vector & definitions, unsigned_vector & order) { order.reset(); @@ -530,6 +531,7 @@ namespace eq { // Find all definitions for (unsigned i = 0; i < num_args; i++) { + checkpoint(); if (is_var_def(is_exists, args[i], v, t)) { unsigned idx = v->get_idx(); if(m_map.get(idx, 0) == 0) { @@ -569,8 +571,14 @@ namespace eq { return false; } + void checkpoint() { + cooperate("der"); + if (m_cancel) + throw tactic_exception(TACTIC_CANCELED_MSG); + } + public: - der(ast_manager & m): m(m), a(m), m_is_variable(0), m_subst(m), m_new_exprs(m), m_subst_map(m), m_new_args(m), m_rewriter(m) {} + der(ast_manager & m): m(m), a(m), m_is_variable(0), m_subst(m), m_new_exprs(m), m_subst_map(m), m_new_args(m), m_rewriter(m), m_cancel(false) {} void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc;} @@ -588,6 +596,7 @@ namespace eq { r = q; // Keep applying reduce_quantifier1 until r doesn't change anymore do { + checkpoint(); proof_ref curr_pr(m); q = to_quantifier(r); reduce_quantifier1(q, r, curr_pr); @@ -605,6 +614,12 @@ namespace eq { } ast_manager& get_manager() const { return m; } + + void set_cancel(bool f) { + m_rewriter.set_cancel(f); + m_cancel = f; + } + }; }; // namespace eq @@ -618,6 +633,7 @@ namespace ar { is_variable_proc* m_is_variable; ptr_vector m_todo; expr_mark m_visited; + volatile bool m_cancel; bool is_variable(expr * e) const { return (*m_is_variable)(e); @@ -723,13 +739,19 @@ namespace ar { return false; } + void checkpoint() { + cooperate("der"); + if (m_cancel) + throw tactic_exception(TACTIC_CANCELED_MSG); + } public: - der(ast_manager& m): m(m), a(m), m_is_variable(0) {} + der(ast_manager& m): m(m), a(m), m_is_variable(0), m_cancel(false) {} void operator()(expr_ref_vector& fmls) { for (unsigned i = 0; i < fmls.size(); ++i) { + checkpoint(); solve_select(fmls, i, fmls[i].get()); solve_neq_select(fmls, i, fmls[i].get()); } @@ -738,6 +760,10 @@ namespace ar { void operator()(expr* e) {} void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc;} + + void set_cancel(bool f) { + m_cancel = f; + } }; }; // namespace ar @@ -866,7 +892,6 @@ namespace fm { unsigned m_fm_cutoff2; unsigned m_fm_extra; bool m_fm_occ; - unsigned long long m_max_memory; unsigned m_counter; bool m_inconsistent; expr_dependency_ref m_inconsistent_core; @@ -1243,7 +1268,7 @@ namespace fm { // // --------------------------- - fm(ast_manager & _m, params_ref const & p): + fm(ast_manager & _m): m(_m), m_allocator("fm-elim"), m_util(m), @@ -1251,7 +1276,6 @@ namespace fm { m_var2expr(m), m_new_fmls(m), m_inconsistent_core(m) { - updt_params(p); m_cancel = false; } @@ -1259,14 +1283,13 @@ namespace fm { reset_constraints(); } - void updt_params(params_ref const & p) { - m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); - m_fm_real_only = p.get_bool("fm_real_only", true); - m_fm_limit = p.get_uint("fm_limit", 5000000); - m_fm_cutoff1 = p.get_uint("fm_cutoff1", 8); - m_fm_cutoff2 = p.get_uint("fm_cutoff2", 256); - m_fm_extra = p.get_uint("fm_extra", 0); - m_fm_occ = p.get_bool("fm_occ", false); + void updt_params() { + m_fm_real_only = true; + m_fm_limit = 5000000; + m_fm_cutoff1 = 8; + m_fm_cutoff2 = 256; + m_fm_extra = 0; + m_fm_occ = false; } void set_cancel(bool f) { @@ -2010,11 +2033,9 @@ namespace fm { } void checkpoint() { - // cooperate("fm"); + cooperate("fm"); if (m_cancel) throw tactic_exception(TACTIC_CANCELED_MSG); - if (memory::get_allocation_size() > m_max_memory) - throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } public: @@ -2083,11 +2104,56 @@ namespace fm { } // namespace fm class qe_lite::impl { +public: + struct elim_cfg : public default_rewriter_cfg { + impl& m_imp; + ast_manager& m; + public: + elim_cfg(impl& i): m_imp(i), m(i.m) {} + + bool reduce_quantifier(quantifier * q, + expr * new_body, + expr * const * new_patterns, + expr * const * new_no_patterns, + expr_ref & result, + proof_ref & result_pr) { + result = new_body; + if (is_forall(q)) { + result = m.mk_not(result); + } + uint_set indices; + for (unsigned i = 0; i < q->get_num_decls(); ++i) { + indices.insert(i); + } + m_imp(indices, true, result); + if (is_forall(q)) { + result = m.mk_not(result); + } + result = m.update_quantifier( + q, + q->get_num_patterns(), new_patterns, + q->get_num_no_patterns(), new_no_patterns, result); + m_imp.m_rewriter(result); + return true; + } + }; + + class elim_star : public rewriter_tpl { + elim_cfg m_cfg; + public: + elim_star(impl& i): + rewriter_tpl(i.m, false, m_cfg), + m_cfg(i) + {} + }; + +private: ast_manager& m; - params_ref m_params; eq::der m_der; fm::fm m_fm; ar::der m_array_der; + elim_star m_elim_star; + th_rewriter m_rewriter; bool has_unique_non_ground(expr_ref_vector const& fmls, unsigned& index) { index = fmls.size(); @@ -2106,7 +2172,13 @@ class qe_lite::impl { } public: - impl(ast_manager& m): m(m), m_der(m), m_fm(m, m_params), m_array_der(m) {} + impl(ast_manager& m): + m(m), + m_der(m), + m_fm(m), + m_array_der(m), + m_elim_star(*this), + m_rewriter(m) {} void operator()(app_ref_vector& vars, expr_ref& fml) { if (vars.empty()) { @@ -2145,14 +2217,12 @@ public: else { fml = tmp; } - } + } void operator()(expr_ref& fml, proof_ref& pr) { - // TODO apply der everywhere as a rewriting rule. - // TODO add cancel method. - if (is_quantifier(fml)) { - m_der(to_quantifier(fml), fml, pr); - } + expr_ref tmp(m); + m_elim_star(fml, tmp, pr); + fml = tmp; } void operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml) { @@ -2195,6 +2265,14 @@ public: TRACE("qe_lite", for (unsigned i = 0; i < fmls.size(); ++i) tout << mk_pp(fmls[i].get(), m) << "\n";); } + void set_cancel(bool f) { + m_der.set_cancel(f); + m_array_der.set_cancel(f); + m_fm.set_cancel(f); + m_elim_star.set_cancel(f); + m_rewriter.set_cancel(f); + } + }; qe_lite::qe_lite(ast_manager& m) { @@ -2209,6 +2287,10 @@ void qe_lite::operator()(app_ref_vector& vars, expr_ref& fml) { (*m_impl)(vars, fml); } +void qe_lite::set_cancel(bool f) { + m_impl->set_cancel(f); +} + void qe_lite::operator()(expr_ref& fml, proof_ref& pr) { (*m_impl)(fml, pr); } @@ -2220,3 +2302,130 @@ void qe_lite::operator()(uint_set const& index_set, bool index_of_bound, expr_re void qe_lite::operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& fmls) { (*m_impl)(index_set, index_of_bound, fmls); } + +class qe_lite_tactic : public tactic { + + struct imp { + ast_manager& m; + qe_lite m_qe; + volatile bool m_cancel; + + imp(ast_manager& m, params_ref const& p): + m(m), + m_qe(m), + m_cancel(false) + {} + + void set_cancel(bool f) { + m_cancel = f; + m_qe.set_cancel(f); + } + + void checkpoint() { + if (m_cancel) + throw tactic_exception(TACTIC_CANCELED_MSG); + cooperate("qe-lite"); + } + + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + SASSERT(g->is_well_sorted()); + mc = 0; pc = 0; core = 0; + tactic_report report("qe-lite", *g); + proof_ref new_pr(m); + expr_ref new_f(m); + bool produce_proofs = g->proofs_enabled(); + + unsigned sz = g->size(); + for (unsigned i = 0; i < sz; i++) { + checkpoint(); + if (g->inconsistent()) + break; + expr * f = g->form(i); + if (!has_quantifiers(f)) + continue; + new_f = f; + m_qe(new_f, new_pr); + if (produce_proofs) { + new_pr = m.mk_modus_ponens(g->pr(i), new_pr); + } + g->update(i, new_f, new_pr, g->dep(i)); + } + g->inc_depth(); + result.push_back(g.get()); + TRACE("qe", g->display(tout);); + SASSERT(g->is_well_sorted()); + } + + }; + + params_ref m_params; + imp * m_imp; + +public: + qe_lite_tactic(ast_manager & m, params_ref const & p): + m_params(p) { + m_imp = alloc(imp, m, p); + } + + virtual tactic * translate(ast_manager & m) { + return alloc(qe_lite_tactic, m, m_params); + } + + virtual ~qe_lite_tactic() { + dealloc(m_imp); + } + + virtual void updt_params(params_ref const & p) { + m_params = p; + // m_imp->updt_params(p); + } + + + virtual void collect_param_descrs(param_descrs & r) { + // m_imp->collect_param_descrs(r); + } + + virtual void operator()(goal_ref const & in, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + (*m_imp)(in, result, mc, pc, core); + } + + + virtual void collect_statistics(statistics & st) const { + // m_imp->collect_statistics(st); + } + + virtual void reset_statistics() { + // m_imp->reset_statistics(); + } + + + virtual void cleanup() { + ast_manager & m = m_imp->m; + imp * d = m_imp; + #pragma omp critical (tactic_cancel) + { + m_imp = 0; + } + dealloc(d); + d = alloc(imp, m, m_params); + #pragma omp critical (tactic_cancel) + { + m_imp = d; + } + } + +}; + +tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p) { + return alloc(qe_lite_tactic, m, p); +} + +template class rewriter_tpl; diff --git a/src/muz_qe/qe_lite.h b/src/muz_qe/qe_lite.h index 3ffbf8fad..7d9239fa7 100644 --- a/src/muz_qe/qe_lite.h +++ b/src/muz_qe/qe_lite.h @@ -23,6 +23,9 @@ Revision History: #include "ast.h" #include "uint_set.h" +#include "params.h" + +class tactic; class qe_lite { class impl; @@ -56,6 +59,13 @@ public: \brief full rewriting based light-weight quantifier elimination round. */ void operator()(expr_ref& fml, proof_ref& pr); + + void set_cancel(bool f); }; +tactic * mk_qe_lite_tactic(ast_manager & m, params_ref const & p = params_ref()); +/* + ADD_TACTIC("qe-light", "apply light-weight quantifier elimination.", "mk_qe_lite_tactic(m, p)") +*/ + #endif diff --git a/src/parsers/smt/smtlib_solver.cpp b/src/parsers/smt/smtlib_solver.cpp index ef28216bf..7c8572ad8 100644 --- a/src/parsers/smt/smtlib_solver.cpp +++ b/src/parsers/smt/smtlib_solver.cpp @@ -86,7 +86,7 @@ namespace smtlib { benchmark.add_formula(m_ast_manager.mk_true()); } m_ctx = alloc(cmd_context, true, &m_ast_manager, benchmark.get_logic()); - m_ctx->set_solver(mk_smt_strategic_solver(false)); + m_ctx->set_solver_factory(mk_smt_strategic_solver_factory()); theory::expr_iterator fit = benchmark.begin_formulas(); theory::expr_iterator fend = benchmark.end_formulas(); for (; fit != fend; ++fit) diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index ee93dca2f..c99c362bd 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -2365,7 +2365,7 @@ namespace smt2 { parser(cmd_context & ctx, std::istream & is, bool interactive, params_ref const & p): m_ctx(ctx), m_params(p), - m_scanner(ctx, is, interactive, p), + m_scanner(ctx, is, interactive), m_curr(scanner::NULL_TOKEN), m_curr_cmd(0), m_num_bindings(0), diff --git a/src/parsers/smt2/smt2scanner.cpp b/src/parsers/smt2/smt2scanner.cpp index e2b2030ac..0f6101a93 100644 --- a/src/parsers/smt2/smt2scanner.cpp +++ b/src/parsers/smt2/smt2scanner.cpp @@ -242,7 +242,7 @@ namespace smt2 { } } - scanner::scanner(cmd_context & ctx, std::istream& stream, bool interactive, params_ref const & _p): + scanner::scanner(cmd_context & ctx, std::istream& stream, bool interactive): m_ctx(ctx), m_interactive(interactive), m_spos(0), @@ -254,9 +254,8 @@ namespace smt2 { m_bend(0), m_stream(stream), m_cache_input(false) { - - parser_params p(_p); - m_smtlib2_compliant = p.smt2_compliant(); + + m_smtlib2_compliant = ctx.params().m_smtlib2_compliant; for (int i = 0; i < 256; ++i) { m_normalized[i] = (char) i; diff --git a/src/parsers/smt2/smt2scanner.h b/src/parsers/smt2/smt2scanner.h index c63a09ff1..7b74c752f 100644 --- a/src/parsers/smt2/smt2scanner.h +++ b/src/parsers/smt2/smt2scanner.h @@ -76,7 +76,7 @@ namespace smt2 { EOF_TOKEN }; - scanner(cmd_context & ctx, std::istream& stream, bool interactive = false, params_ref const & p = params_ref()); + scanner(cmd_context & ctx, std::istream& stream, bool interactive = false); ~scanner() {} diff --git a/src/parsers/util/parser_params.pyg b/src/parsers/util/parser_params.pyg index fa8f17a00..3f7495f43 100644 --- a/src/parsers/util/parser_params.pyg +++ b/src/parsers/util/parser_params.pyg @@ -3,4 +3,4 @@ def_module_params('parser', params=(('ignore_user_patterns', BOOL, False, 'ignore patterns provided by the user'), ('ignore_bad_patterns', BOOL, True, 'ignore malformed patterns'), ('error_for_visual_studio', BOOL, False, 'display error messages in Visual Studio format'), - ('smt2_compliant', BOOL, False, 'enable/disable SMT-LIB 2.0 compliance'))) + )) diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index 1551329c6..ef0b4ad6b 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -30,9 +30,6 @@ Revision History: #include"subpaving_cmds.h" #include"smt_strategic_solver.h" -#include"tactic2solver.h" -#include"qfnra_nlsat_tactic.h" - extern bool g_display_statistics; extern void display_config(); static clock_t g_start_time; @@ -98,8 +95,7 @@ unsigned read_smtlib2_commands(char const * file_name) { signal(SIGINT, on_ctrl_c); cmd_context ctx; - solver * s = mk_smt_strategic_solver(false); - ctx.set_solver(s); + ctx.set_solver_factory(mk_smt_strategic_solver_factory()); install_dl_cmds(ctx); install_dbg_cmds(ctx); diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 24b8e2e7c..c9d6ead88 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -142,6 +142,14 @@ namespace smt { } }; + struct initn : public instruction { + // We need that because starting at Z3 3.0, some associative + // operators (e.g., + and *) are represented using n-ary + // applications. + // We do not need the extra field for INIT1, ..., INIT6. + unsigned m_num_args; + }; + struct compare : public instruction { unsigned m_reg1; unsigned m_reg2; @@ -608,7 +616,18 @@ namespace smt { instruction * mk_init(unsigned n) { SASSERT(n >= 1); opcode op = n <= 6 ? static_cast(INIT1 + n - 1) : INITN; - return mk_instr(op, sizeof(instruction)); + if (op == INITN) { + // We store the actual number of arguments for INITN. + // Starting at Z3 3.0, some associative operators + // (e.g., + and *) are represented using n-ary + // applications. + initn * r = mk_instr(op, sizeof(initn)); + r->m_num_args = n; + return r; + } + else { + return mk_instr(op, sizeof(instruction)); + } } public: @@ -2345,6 +2364,8 @@ namespace smt { case INITN: m_app = m_registers[0]; m_num_args = m_app->get_num_args(); + if (m_num_args != static_cast(m_pc)->m_num_args) + goto backtrack; for (unsigned i = 0; i < m_num_args; i++) m_registers[i+1] = m_app->get_arg(i); m_pc = m_pc->m_next; @@ -3982,3 +4003,8 @@ namespace smt { } }; +#ifdef Z3DEBUG +void pp(smt::code_tree * c) { + c->display(std::cout); +} +#endif diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index 375fef787..4799b8b9f 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -21,7 +21,8 @@ Revision History: void preprocessor_params::updt_local_params(params_ref const & _p) { smt_params_helper p(_p); - m_macro_finder = p.macro_finder(); + m_macro_finder = p.macro_finder(); + m_pull_nested_quantifiers = p.pull_nested_quantifiers(); } void preprocessor_params::updt_params(params_ref const & p) { diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index f74798499..e62a3de21 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -32,6 +32,7 @@ void smt_params::updt_local_params(params_ref const & _p) { m_delay_units = p.delay_units(); m_delay_units_threshold = p.delay_units_threshold(); m_preprocess = _p.get_bool("preprocess", true); // hidden parameter + m_soft_timeout = p.soft_timeout(); if (_p.get_bool("arith.greatest_error_pivot", false)) m_arith_pivot_strategy = ARITH_PIVOT_GREATEST_ERROR; else if (_p.get_bool("arith.least_error_pivot", false)) @@ -48,7 +49,6 @@ void smt_params::updt_params(params_ref const & p) { void smt_params::updt_params(context_params const & p) { m_auto_config = p.m_auto_config; - m_soft_timeout = p.m_timeout; m_model = p.m_model; m_model_validate = p.m_model_validate; } diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index ee583ff85..43dd1b586 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -7,12 +7,14 @@ def_module_params(module_name='smt', ('relevancy', UINT, 2, 'relevancy propagation heuristic: 0 - disabled, 1 - relevancy is tracked by only affects quantifier instantiation, 2 - relevancy is tracked, and an atom is only asserted if it is relevant'), ('macro_finder', BOOL, False, 'try to find universally quantified formulas that can be viewed as macros'), ('ematching', BOOL, True, 'E-Matching based quantifier instantiation'), - ('phase_selection', UINT, 4, 'phase selection heuristic: 0 - always false, 1 - always true, 2 - phase caching, 3 - phase caching conservative, 4 - phase caching conservative 2, 5 - random, 6 - number of occurrences'), + ('phase_selection', UINT, 3, 'phase selection heuristic: 0 - always false, 1 - always true, 2 - phase caching, 3 - phase caching conservative, 4 - phase caching conservative 2, 5 - random, 6 - number of occurrences'), ('restart_strategy', UINT, 1, '0 - geometric, 1 - inner-outer-geometric, 2 - luby, 3 - fixed, 4 - arithmetic'), ('restart_factor', DOUBLE, 1.1, 'when using geometric (or inner-outer-geometric) progression of restarts, it specifies the constant used to multiply the currect restart threshold'), ('case_split', UINT, 1, '0 - case split based on variable activity, 1 - similar to 0, but delay case splits created during the search, 2 - similar to 0, but cache the relevancy, 3 - case split based on relevancy (structural splitting), 4 - case split on relevancy and activity, 5 - case split on relevancy and current goal'), ('delay_units', BOOL, False, 'if true then z3 will not restart when a unit clause is learned'), ('delay_units_threshold', UINT, 32, 'maximum number of learned unit clauses before restarting, ingored if delay_units is false'), + ('pull_nested_quantifiers', BOOL, False, 'pull nested quantifiers'), + ('soft_timeout', UINT, 0, 'soft timeout (0 means no timeout)'), ('mbqi', BOOL, True, 'model based quantifier instantiation (MBQI)'), ('mbqi.max_cexs', UINT, 1, 'initial maximal number of counterexamples used in MBQI, each counterexample generates a quantifier instantiation'), ('mbqi.max_cexs_incr', UINT, 0, 'increment for MBQI_MAX_CEXS, the increment is performed after each round of MBQI'), diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index 34742e3a0..92110bc1a 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -155,7 +155,7 @@ bool eval(func_interp & fi, simplifier & s, expr * const * args, expr_ref & resu basic_simplifier_plugin * bs = static_cast(s.get_plugin(fi.m().get_basic_family_id())); for (unsigned k = 0; k < fi.num_entries(); k++) { func_entry const * curr = fi.get_entry(k); - SASSERT(!curr->eq_args(fi.get_arity(), args)); + SASSERT(!curr->eq_args(fi.m(), fi.get_arity(), args)); if (!actuals_are_values || !curr->args_are_values()) { expr_ref_buffer eqs(fi.m()); unsigned i = fi.get_arity(); diff --git a/src/smt/smt_implied_equalities.cpp b/src/smt/smt_implied_equalities.cpp index c6f28d4b2..70229ccab 100644 --- a/src/smt/smt_implied_equalities.cpp +++ b/src/smt/smt_implied_equalities.cpp @@ -21,23 +21,21 @@ Revision History: #include "smt_implied_equalities.h" #include "union_find.h" -#include "cmd_context.h" -#include "parametric_cmd.h" #include "ast_pp.h" -#include "arith_decl_plugin.h" -#include "datatype_decl_plugin.h" #include "array_decl_plugin.h" #include "uint_set.h" -#include "model_v2_pp.h" #include "smt_value_sort.h" - +#include "model_smt2_pp.h" +#include "stopwatch.h" +#include "model.h" +#include "solver.h" namespace smt { class get_implied_equalities_impl { ast_manager& m; - smt::kernel& m_solver; + solver& m_solver; union_find_default_ctx m_df; union_find m_uf; array_util m_array_util; @@ -98,7 +96,7 @@ namespace smt { ++m_stats_calls; m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(s, t))); - bool is_eq = l_false == m_solver.check(); + bool is_eq = l_false == m_solver.check_sat(0,0); m_solver.pop(1); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << (is_eq?"eq":"unrelated") << "\n";); if (is_eq) { @@ -125,7 +123,7 @@ namespace smt { m_stats_timer.start(); m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(s, t))); - bool is_eq = l_false == m_solver.check(); + bool is_eq = l_false == m_solver.check_sat(0,0); m_solver.pop(1); m_stats_timer.stop(); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << (is_eq?"eq":"unrelated") << "\n";); @@ -168,7 +166,7 @@ namespace smt { terms[i].term = m.mk_app(m_array_util.get_family_id(), OP_SELECT, 0, 0, args.size(), args.c_ptr()); } assert_relevant(terms); - lbool is_sat = m_solver.check(); + lbool is_sat = m_solver.check_sat(0,0); model_ref model1; m_solver.get_model(model1); SASSERT(model1.get()); @@ -218,7 +216,7 @@ namespace smt { expr* s = terms[vec[j]].term; m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(t, s))); - lbool is_sat = m_solver.check(); + lbool is_sat = m_solver.check_sat(0,0); m_solver.pop(1); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << is_sat << "\n";); if (is_sat == l_false) { @@ -237,7 +235,7 @@ namespace smt { if (!non_values.empty()) { - TRACE("get_implied_equalities", model_v2_pp(tout, *model, true);); + TRACE("get_implied_equalities", model_smt2_pp(tout, m, *model, 0);); get_implied_equalities_filter_basic(non_values, terms); //get_implied_equalities_basic(terms); } @@ -321,7 +319,7 @@ namespace smt { public: - get_implied_equalities_impl(smt::kernel& s) : m(s.m()), m_solver(s), m_uf(m_df), m_array_util(m), m_stats_calls(0) {} + get_implied_equalities_impl(ast_manager& m, solver& s) : m(m), m_solver(s), m_uf(m_df), m_array_util(m), m_stats_calls(0) {} lbool operator()(unsigned num_terms, expr* const* terms, unsigned* class_ids) { params_ref p; @@ -338,7 +336,7 @@ namespace smt { m_solver.push(); assert_relevant(num_terms, terms); - lbool is_sat = m_solver.check(); + lbool is_sat = m_solver.check_sat(0,0); if (is_sat != l_false) { model_ref model; @@ -374,8 +372,8 @@ namespace smt { stopwatch get_implied_equalities_impl::s_timer; stopwatch get_implied_equalities_impl::s_stats_val_eq_timer; - lbool implied_equalities(smt::kernel& solver, unsigned num_terms, expr* const* terms, unsigned* class_ids) { - get_implied_equalities_impl gi(solver); + lbool implied_equalities(ast_manager& m, solver& solver, unsigned num_terms, expr* const* terms, unsigned* class_ids) { + get_implied_equalities_impl gi(m, solver); return gi(num_terms, terms, class_ids); } }; @@ -552,7 +550,7 @@ namespace smt { m_solver.assert_expr(m.mk_implies(eq_lit, eq)); } m_solver.assert_expr(m.mk_not(m.mk_and(eqs.size(), eqs.c_ptr()))); - lbool is_sat = m_solver.check(); + lbool is_sat = m_solver.check_sat(0,0); switch(is_sat) { case l_false: for (unsigned i = 0; i + 1 < terms.size(); ++i) { diff --git a/src/smt/smt_implied_equalities.h b/src/smt/smt_implied_equalities.h index 6fc002ff1..ec9b4bd21 100644 --- a/src/smt/smt_implied_equalities.h +++ b/src/smt/smt_implied_equalities.h @@ -23,13 +23,16 @@ Revision History: #ifndef __SMT_IMPLIED_EQUALITIES_H__ #define __SMT_IMPLIED_EQUALITIES_H__ -#include"smt_kernel.h" +#include"smt_solver.h" +#include"lbool.h" +#include"ast.h" namespace smt { lbool implied_equalities( - kernel & solver, + ast_manager & m, + solver & solver, unsigned num_terms, expr* const* terms, unsigned* class_ids); diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 556222c33..b3f16a3ce 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -907,7 +907,7 @@ namespace smt { } enode * e = enode::mk(m_manager, m_region, m_app2enode, n, generation, suppress_args, merge_tf, m_scope_lvl, cgc_enabled, true); TRACE("mk_enode_detail", tout << "e.get_num_args() = " << e->get_num_args() << "\n";); - if (n->get_num_args() == 0 && m_manager.is_value(n)) + if (n->get_num_args() == 0 && m_manager.is_unique_value(n)) e->mark_as_interpreted(); TRACE("mk_var_bug", tout << "mk_enode: " << id << "\n";); TRACE("generation", tout << "mk_enode: " << id << " " << generation << "\n";); diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index e8c1c6698..1fbb58847 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -25,166 +25,114 @@ namespace smt { class solver : public solver_na2as { smt_params m_params; - smt::kernel * m_context; + smt::kernel m_context; progress_callback * m_callback; + symbol m_logic; public: - solver():m_context(0), m_callback(0) {} - + solver(ast_manager & m, params_ref const & p, symbol const & l): + solver_na2as(m), + m_params(p), + m_context(m, m_params) { + m_logic = l; + if (m_logic != symbol::null) + m_context.set_logic(m_logic); + } + virtual ~solver() { - if (m_context != 0) - dealloc(m_context); } virtual void updt_params(params_ref const & p) { m_params.updt_params(p); - if (m_context == 0) - return; - m_context->updt_params(p); + m_context.updt_params(p); } virtual void collect_param_descrs(param_descrs & r) { - if (m_context == 0) { - ast_manager m; - reg_decl_plugins(m); - smt::kernel s(m, m_params); - s.collect_param_descrs(r); - } - else { - m_context->collect_param_descrs(r); - } - } - - virtual void init_core(ast_manager & m, symbol const & logic) { - reset(); - // We can throw exceptions when creating a smt::kernel object - // So, we should create the smt::kernel outside of the criticial section - // block. OMP does not allow exceptions to cross critical section boundaries. - smt::kernel * new_kernel = alloc(smt::kernel, m, m_params); - #pragma omp critical (solver) - { - m_context = new_kernel; - if (m_callback) - m_context->set_progress_callback(m_callback); - } - if (logic != symbol::null) - m_context->set_logic(logic); + m_context.collect_param_descrs(r); } virtual void collect_statistics(statistics & st) const { - if (m_context == 0) { - return; - } - else { - m_context->collect_statistics(st); - } - } - - virtual void reset_core() { - if (m_context != 0) { - #pragma omp critical (solver) - { - dealloc(m_context); - m_context = 0; - } - } - } - - // An exception may be thrown when creating a smt::kernel. - // So, there is no guarantee that m_context != 0 when - // using smt_solver from the SMT 2.0 command line frontend. - void check_context() const { - if (m_context == 0) - throw default_exception("Z3 failed to create solver, check previous error messages"); + m_context.collect_statistics(st); } virtual void assert_expr(expr * t) { - check_context(); - m_context->assert_expr(t); + m_context.assert_expr(t); } virtual void push_core() { - check_context(); - m_context->push(); + m_context.push(); } virtual void pop_core(unsigned n) { - check_context(); - m_context->pop(n); + m_context.pop(n); } virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { - check_context(); TRACE("solver_na2as", tout << "smt_solver::check_sat_core: " << num_assumptions << "\n";); - return m_context->check(num_assumptions, assumptions); + return m_context.check(num_assumptions, assumptions); } virtual void get_unsat_core(ptr_vector & r) { - check_context(); - unsigned sz = m_context->get_unsat_core_size(); + unsigned sz = m_context.get_unsat_core_size(); for (unsigned i = 0; i < sz; i++) - r.push_back(m_context->get_unsat_core_expr(i)); + r.push_back(m_context.get_unsat_core_expr(i)); } virtual void get_model(model_ref & m) { - check_context(); - m_context->get_model(m); + m_context.get_model(m); } virtual proof * get_proof() { - check_context(); - return m_context->get_proof(); + return m_context.get_proof(); } virtual std::string reason_unknown() const { - check_context(); - return m_context->last_failure_as_string(); + return m_context.last_failure_as_string(); } virtual void get_labels(svector & r) { - check_context(); buffer tmp; - m_context->get_relevant_labels(0, tmp); + m_context.get_relevant_labels(0, tmp); r.append(tmp.size(), tmp.c_ptr()); } virtual void set_cancel(bool f) { -#pragma omp critical (solver) - { - if (m_context) - m_context->set_cancel(f); - } + m_context.set_cancel(f); } virtual void set_progress_callback(progress_callback * callback) { m_callback = callback; - if (m_context) - m_context->set_progress_callback(callback); + m_context.set_progress_callback(callback); } virtual unsigned get_num_assertions() const { - if (m_context) - return m_context->size(); - else - return 0; + return m_context.size(); } virtual expr * get_assertion(unsigned idx) const { - SASSERT(m_context); SASSERT(idx < get_num_assertions()); - return m_context->get_formulas()[idx]; + return m_context.get_formulas()[idx]; } virtual void display(std::ostream & out) const { - if (m_context) - m_context->display(out); - else - out << "(solver)"; + m_context.display(out); } }; }; -solver * mk_smt_solver() { - return alloc(smt::solver); +solver * mk_smt_solver(ast_manager & m, params_ref const & p, symbol const & logic) { + return alloc(smt::solver, m, p, logic); } + +class smt_solver_factory : public solver_factory { +public: + virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { + return mk_smt_solver(m, p, logic); + } +}; + +solver_factory * mk_smt_solver_factory() { + return alloc(smt_solver_factory); +} + diff --git a/src/smt/smt_solver.h b/src/smt/smt_solver.h index e9af9aafa..81c9a1319 100644 --- a/src/smt/smt_solver.h +++ b/src/smt/smt_solver.h @@ -21,8 +21,13 @@ Notes: #ifndef _SMT_SOLVER_H_ #define _SMT_SOLVER_H_ -class solver; +#include"ast.h" +#include"params.h" -solver * mk_smt_solver(); +class solver; +class solver_factory; + +solver * mk_smt_solver(ast_manager & m, params_ref const & p, symbol const & logic); +solver_factory * mk_smt_solver_factory(); #endif diff --git a/src/smt/tactic/smt_tactic.cpp b/src/smt/tactic/smt_tactic.cpp index 2c7d58b1a..95b150a8c 100644 --- a/src/smt/tactic/smt_tactic.cpp +++ b/src/smt/tactic/smt_tactic.cpp @@ -139,6 +139,7 @@ public: ast_manager & m = in->m(); TRACE("smt_tactic", tout << this << "\nAUTO_CONFIG: " << fparams().m_auto_config << " HIDIV0: " << fparams().m_hi_div0 << " " << " PREPROCESS: " << fparams().m_preprocess << "\n"; + tout << "RELEVANCY: " << fparams().m_relevancy_lvl << "\n"; tout << "fail-if-inconclusive: " << m_fail_if_inconclusive << "\n"; tout << "params_ref: " << m_params_ref << "\n";); TRACE("smt_tactic_detail", in->display(tout);); diff --git a/src/smt/user_plugin/user_decl_plugin.h b/src/smt/user_plugin/user_decl_plugin.h index dc2fd55a1..9e6bd70c4 100644 --- a/src/smt/user_plugin/user_decl_plugin.h +++ b/src/smt/user_plugin/user_decl_plugin.h @@ -49,6 +49,8 @@ public: virtual bool is_value(app*) const; + virtual bool is_unique_value(app * a) const { return is_value(a); } + bool is_value(func_decl *) const; virtual void get_op_names(svector & op_names, symbol const & logic); diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp new file mode 100644 index 000000000..cd4191e76 --- /dev/null +++ b/src/solver/combined_solver.cpp @@ -0,0 +1,289 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + combined_solver.cpp + +Abstract: + + Implements the solver API by combining two solvers. + + This is a replacement for the strategic_solver class. + +Author: + + Leonardo (leonardo) 2012-12-11 + +Notes: + +--*/ +#include"solver.h" +#include"scoped_timer.h" +#include"combined_solver_params.hpp" +#define PS_VB_LVL 15 + +/** + \brief Implementation of the solver API that combines two given solvers. + + The combined solver has two modes: + - non-incremental + - incremental + In non-incremental mode, the first solver is used. + In incremental mode, the second one is used. + + A timeout for the second solver can be specified. + If the timeout is reached, then the first solver is executed. + + The object switches to incremental when: + - push is used + - assertions are peformed after a check_sat + - parameter ignore_solver1==false +*/ +class combined_solver : public solver { +public: + // Behavior when the incremental solver returns unknown. + enum inc_unknown_behavior { + IUB_RETURN_UNDEF, // just return unknown + IUB_USE_TACTIC_IF_QF, // invoke tactic if problem is quantifier free + IUB_USE_TACTIC // invoke tactic + }; + +private: + bool m_inc_mode; + bool m_check_sat_executed; + bool m_use_solver1_results; + ref m_solver1; + ref m_solver2; + + bool m_ignore_solver1; + inc_unknown_behavior m_inc_unknown_behavior; + unsigned m_inc_timeout; + + void switch_inc_mode() { + m_inc_mode = true; + } + + struct aux_timeout_eh : public event_handler { + solver * m_solver; + volatile bool m_canceled; + aux_timeout_eh(solver * s):m_solver(s), m_canceled(false) {} + virtual void operator()() { + m_solver->cancel(); + m_canceled = true; + } + }; + + void updt_local_params(params_ref const & _p) { + combined_solver_params p(_p); + m_inc_timeout = p.solver2_timeout(); + m_ignore_solver1 = p.ignore_solver1(); + m_inc_unknown_behavior = static_cast(p.solver2_unknown()); + } + + bool has_quantifiers() const { + unsigned sz = get_num_assertions(); + for (unsigned i = 0; i < sz; i++) { + if (::has_quantifiers(get_assertion(i))) + return true; + } + return false; + } + + bool use_solver1_when_undef() const { + switch (m_inc_unknown_behavior) { + case IUB_RETURN_UNDEF: return false; + case IUB_USE_TACTIC_IF_QF: return !has_quantifiers(); + case IUB_USE_TACTIC: return true; + default: + UNREACHABLE(); + return false; + } + } + +public: + combined_solver(solver * s1, solver * s2, params_ref const & p) { + m_solver1 = s1; + m_solver2 = s2; + updt_local_params(p); + m_inc_mode = false; + m_check_sat_executed = false; + m_use_solver1_results = true; + } + + virtual void updt_params(params_ref const & p) { + m_solver1->updt_params(p); + m_solver2->updt_params(p); + updt_local_params(p); + } + + virtual void collect_param_descrs(param_descrs & r) { + m_solver1->collect_param_descrs(r); + m_solver2->collect_param_descrs(r); + combined_solver_params::collect_param_descrs(r); + } + + virtual void set_produce_models(bool f) { + m_solver1->set_produce_models(f); + m_solver2->set_produce_models(f); + } + + virtual void assert_expr(expr * t) { + if (m_check_sat_executed) + switch_inc_mode(); + m_solver1->assert_expr(t); + m_solver2->assert_expr(t); + } + + virtual void assert_expr(expr * t, expr * a) { + if (m_check_sat_executed) + switch_inc_mode(); + m_solver1->assert_expr(t, a); + m_solver2->assert_expr(t, a); + } + + virtual void push() { + switch_inc_mode(); + m_solver1->push(); + m_solver2->push(); + } + + virtual void pop(unsigned n) { + switch_inc_mode(); + m_solver1->pop(n); + m_solver2->pop(n); + } + + virtual unsigned get_scope_level() const { + return m_solver1->get_scope_level(); + } + + virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions) { + m_check_sat_executed = true; + + if (num_assumptions > 0 || // assumptions were provided + m_ignore_solver1) { + // must use incremental solver + switch_inc_mode(); + m_use_solver1_results = false; + return m_solver2->check_sat(num_assumptions, assumptions); + } + + if (m_inc_mode) { + if (m_inc_timeout == UINT_MAX) { + IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"using solver 2 (without a timeout)\")\n";); + lbool r = m_solver2->check_sat(0, 0); + if (r != l_undef || !use_solver1_when_undef()) { + m_use_solver1_results = false; + return r; + } + } + else { + IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"using solver 2 (with timeout)\")\n";); + aux_timeout_eh eh(m_solver2.get()); + lbool r; + { + scoped_timer timer(m_inc_timeout, &eh); + r = m_solver2->check_sat(0, 0); + } + if ((r != l_undef || !use_solver1_when_undef()) && !eh.m_canceled) { + m_use_solver1_results = false; + return r; + } + } + IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"solver 2 failed, trying solver1\")\n";); + } + + IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"using solver 1\")\n";); + m_use_solver1_results = true; + return m_solver1->check_sat(0, 0); + } + + virtual void set_cancel(bool f) { + m_solver1->set_cancel(f); + m_solver2->set_cancel(f); + } + + virtual void set_progress_callback(progress_callback * callback) { + m_solver1->set_progress_callback(callback); + m_solver2->set_progress_callback(callback); + } + + virtual unsigned get_num_assertions() const { + return m_solver1->get_num_assertions(); + } + + virtual expr * get_assertion(unsigned idx) const { + return m_solver1->get_assertion(idx); + } + + virtual void display(std::ostream & out) const { + m_solver1->display(out); + } + + virtual void collect_statistics(statistics & st) const { + if (m_use_solver1_results) + m_solver1->collect_statistics(st); + else + m_solver2->collect_statistics(st); + } + + virtual void get_unsat_core(ptr_vector & r) { + if (m_use_solver1_results) + m_solver1->get_unsat_core(r); + else + m_solver2->get_unsat_core(r); + } + + virtual void get_model(model_ref & m) { + if (m_use_solver1_results) + m_solver1->get_model(m); + else + m_solver2->get_model(m); + } + + virtual proof * get_proof() { + if (m_use_solver1_results) + return m_solver1->get_proof(); + else + return m_solver2->get_proof(); + } + + virtual std::string reason_unknown() const { + if (m_use_solver1_results) + return m_solver1->reason_unknown(); + else + return m_solver2->reason_unknown(); + } + + virtual void get_labels(svector & r) { + if (m_use_solver1_results) + return m_solver1->get_labels(r); + else + return m_solver2->get_labels(r); + } + +}; + + +solver * mk_combined_solver(solver * s1, solver * s2, params_ref const & p) { + return alloc(combined_solver, s1, s2, p); +} + +class combined_solver_factory : public solver_factory { + scoped_ptr m_f1; + scoped_ptr m_f2; +public: + combined_solver_factory(solver_factory * f1, solver_factory * f2):m_f1(f1), m_f2(f2) {} + virtual ~combined_solver_factory() {} + + virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { + return mk_combined_solver((*m_f1)(m, p, proofs_enabled, models_enabled, unsat_core_enabled, logic), + (*m_f2)(m, p, proofs_enabled, models_enabled, unsat_core_enabled, logic), + p); + } +}; + +solver_factory * mk_combined_solver_factory(solver_factory * f1, solver_factory * f2) { + return alloc(combined_solver_factory, f1, f2); +} diff --git a/src/solver/combined_solver.h b/src/solver/combined_solver.h new file mode 100644 index 000000000..2ccace7f0 --- /dev/null +++ b/src/solver/combined_solver.h @@ -0,0 +1,32 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + combined_solver.cpp + +Abstract: + + Implements the solver API by combining two solvers. + + This is a replacement for the strategic_solver class. + +Author: + + Leonardo (leonardo) 2012-12-11 + +Notes: + +--*/ +#ifndef _COMBINED_SOLVER_H_ +#define _COMBINED_SOLVER_H_ + +#include"params.h" + +class solver; +class solver_factory; + +solver * mk_combined_solver(solver * s1, solver * s2, params_ref const & p); +solver_factory * mk_combined_solver_factory(solver_factory * f1, solver_factory * f2); + +#endif diff --git a/src/solver/combined_solver_params.pyg b/src/solver/combined_solver_params.pyg new file mode 100644 index 000000000..7e1635c16 --- /dev/null +++ b/src/solver/combined_solver_params.pyg @@ -0,0 +1,9 @@ +def_module_params('combined_solver', + description='combines two solvers: non-incremental (solver1) and incremental (solver2)', + export=True, + params=(('solver2_timeout', UINT, UINT_MAX, "fallback to solver 1 after timeout even when in incremental model"), + ('ignore_solver1', BOOL, False, "if true, solver 2 is always used"), + ('solver2_unknown', UINT, 1, "what should be done when solver 2 returns unknown: 0 - just return unknown, 1 - execute solver 1 if quantifier free problem, 2 - execute solver 1") + )) + + diff --git a/src/solver/solver.h b/src/solver/solver.h index 5cd3da52b..e047bace1 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -23,6 +23,14 @@ Notes: #include"progress_callback.h" #include"params.h" +class solver; + +class solver_factory { +public: + virtual ~solver_factory() {} + virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) = 0; +}; + /** \brief Abstract interface for making solvers available in the Z3 API and front-ends such as SMT 2.0 and (legacy) SMT 1.0. @@ -34,7 +42,6 @@ Notes: - statistics - results based on check_sat_result API - interruption (set_cancel) - - resets */ class solver : public check_sat_result { public: @@ -50,12 +57,6 @@ public: */ virtual void collect_param_descrs(param_descrs & r) {} - /** - \brief Enable/Disable proof production for this solver object. - - It is invoked before init(m, logic). - */ - virtual void set_produce_proofs(bool f) {} /** \brief Enable/Disable model generation for this solver object. @@ -63,23 +64,7 @@ public: The user may optionally invoke it after init(m, logic). */ virtual void set_produce_models(bool f) {} - /** - \brief Enable/Disable unsat core generation for this solver object. - - It is invoked before init(m, logic). - */ - virtual void set_produce_unsat_cores(bool f) {} - /** - \brief Initialize the solver object with the given ast_manager and logic. - */ - virtual void init(ast_manager & m, symbol const & logic) = 0; - - /** - \brief Reset the solver internal state. All assertions should be removed. - */ - virtual void reset() = 0; - /** \brief Add a new formula to the assertion stack. */ diff --git a/src/solver/solver_na2as.cpp b/src/solver/solver_na2as.cpp index e71a9873b..9889b5872 100644 --- a/src/solver/solver_na2as.cpp +++ b/src/solver/solver_na2as.cpp @@ -22,8 +22,8 @@ Notes: #include"solver_na2as.h" #include"ast_smt2_pp.h" -solver_na2as::solver_na2as() { - m_manager = 0; +solver_na2as::solver_na2as(ast_manager & m): + m_manager(m) { } solver_na2as::~solver_na2as() { @@ -35,23 +35,16 @@ void solver_na2as::assert_expr(expr * t, expr * a) { assert_expr(t); } else { - SASSERT(m_manager != 0); SASSERT(is_uninterp_const(a)); - SASSERT(m_manager->is_bool(a)); - TRACE("solver_na2as", tout << "asserting\n" << mk_ismt2_pp(t, *m_manager) << "\n" << mk_ismt2_pp(a, *m_manager) << "\n";); - m_manager->inc_ref(a); + SASSERT(m_manager.is_bool(a)); + TRACE("solver_na2as", tout << "asserting\n" << mk_ismt2_pp(t, m_manager) << "\n" << mk_ismt2_pp(a, m_manager) << "\n";); + m_manager.inc_ref(a); m_assumptions.push_back(a); - expr_ref new_t(*m_manager); - new_t = m_manager->mk_implies(a, t); + expr_ref new_t(m_manager); + new_t = m_manager.mk_implies(a, t); assert_expr(new_t); } } - -void solver_na2as::init(ast_manager & m, symbol const & logic) { - SASSERT(m_assumptions.empty()); - m_manager = &m; - init_core(m, logic); -} struct append_assumptions { ptr_vector & m_assumptions; @@ -89,9 +82,9 @@ void solver_na2as::pop(unsigned n) { } void solver_na2as::restore_assumptions(unsigned old_sz) { - SASSERT(old_sz == 0 || m_manager != 0); + SASSERT(old_sz == 0); for (unsigned i = old_sz; i < m_assumptions.size(); i++) { - m_manager->dec_ref(m_assumptions[i]); + m_manager.dec_ref(m_assumptions[i]); } m_assumptions.shrink(old_sz); } @@ -100,7 +93,3 @@ unsigned solver_na2as::get_scope_level() const { return m_scopes.size(); } -void solver_na2as::reset() { - reset_core(); - restore_assumptions(0); -} diff --git a/src/solver/solver_na2as.h b/src/solver/solver_na2as.h index eb12479fc..15750344a 100644 --- a/src/solver/solver_na2as.h +++ b/src/solver/solver_na2as.h @@ -25,30 +25,26 @@ Notes: #include"solver.h" class solver_na2as : public solver { - ast_manager * m_manager; + ast_manager & m_manager; ptr_vector m_assumptions; unsigned_vector m_scopes; void restore_assumptions(unsigned old_sz); public: - solver_na2as(); + solver_na2as(ast_manager & m); virtual ~solver_na2as(); virtual void assert_expr(expr * t, expr * a); virtual void assert_expr(expr * t) = 0; // Subclasses of solver_na2as should redefine the following *_core methods instead of these ones. - virtual void init(ast_manager & m, symbol const & logic); virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions); virtual void push(); virtual void pop(unsigned n); virtual unsigned get_scope_level() const; - virtual void reset(); protected: - virtual void init_core(ast_manager & m, symbol const & logic) = 0; virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) = 0; virtual void push_core() = 0; virtual void pop_core(unsigned n) = 0; - virtual void reset_core() = 0; }; diff --git a/src/solver/strategic_solver.cpp b/src/solver/strategic_solver.cpp deleted file mode 100644 index 40d77066e..000000000 --- a/src/solver/strategic_solver.cpp +++ /dev/null @@ -1,533 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - strategic_solver.h - -Abstract: - - Strategies -> Solver - -Author: - - Leonardo (leonardo) 2011-05-19 - -Notes: - ---*/ -#include"strategic_solver.h" -#include"scoped_timer.h" -#include"ast_smt2_pp.h" - -// minimum verbosity level for portfolio verbose messages -#define PS_VB_LVL 15 - - -strategic_solver::ctx::ctx(ast_manager & m): - m_assertions(m), - m_assertion_names(m) { - } - -strategic_solver::strategic_solver(): - m_manager(0), - m_force_tactic(false), - m_inc_mode(false), - m_check_sat_executed(false), - m_inc_solver(0), - m_inc_solver_timeout(UINT_MAX), - m_inc_unknown_behavior(IUB_USE_TACTIC_IF_QF), - m_default_fct(0), - m_curr_tactic(0), - m_proof(0), - m_core(0), - m_callback(0) { - m_use_inc_solver_results = false; - DEBUG_CODE(m_num_scopes = 0;); - m_produce_proofs = false; - m_produce_models = false; - m_produce_unsat_cores = false; - m_auto_config = true; -} - -strategic_solver::~strategic_solver() { - SASSERT(!m_curr_tactic); - dictionary::iterator it = m_logic2fct.begin(); - dictionary::iterator end = m_logic2fct.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - if (m_proof) - m().dec_ref(m_proof); - if (m_core) - m().dec_ref(m_core); -} - -bool strategic_solver::has_quantifiers() const { - unsigned sz = get_num_assertions(); - for (unsigned i = 0; i < sz; i++) { - if (::has_quantifiers(get_assertion(i))) - return true; - } - return false; -} - -/** - \brief Return true if a tactic should be used when the incremental solver returns unknown. -*/ -bool strategic_solver::use_tactic_when_undef() const { - switch (m_inc_unknown_behavior) { - case IUB_RETURN_UNDEF: return false; - case IUB_USE_TACTIC_IF_QF: return !has_quantifiers(); - case IUB_USE_TACTIC: return true; - default: - UNREACHABLE(); - return false; - } -} - -void strategic_solver::set_inc_solver(solver * s) { - SASSERT(m_inc_solver == 0); - SASSERT(m_num_scopes == 0); - m_inc_solver = s; - if (m_callback) - m_inc_solver->set_progress_callback(m_callback); -} - -void strategic_solver::updt_params(params_ref const & p) { - if (m_inc_solver) - m_inc_solver->updt_params(p); - m_params = p; - m_auto_config = p.get_bool("auto_config", true); -} - -void strategic_solver::collect_param_descrs(param_descrs & r) { - if (m_inc_solver) - m_inc_solver->collect_param_descrs(r); -} - -/** - \brief Set a timeout for each check_sat query that is processed by the inc_solver. - timeout == UINT_MAX means infinite - After the timeout a strategy is used. -*/ -void strategic_solver::set_inc_solver_timeout(unsigned timeout) { - m_inc_solver_timeout = timeout; -} - -/** - \brief Set the default tactic factory. - It is used if there is no tactic for a given logic. -*/ -void strategic_solver::set_default_tactic(tactic_factory * fct) { - m_default_fct = fct; -} - -/** - \brief Set a tactic factory for a given logic. -*/ -void strategic_solver::set_tactic_for(symbol const & logic, tactic_factory * fct) { - tactic_factory * old_fct; - if (m_logic2fct.find(logic, old_fct)) { - dealloc(old_fct); - } - m_logic2fct.insert(logic, fct); -} - -void strategic_solver::init(ast_manager & m, symbol const & logic) { - m_manager = &m; - m_logic = logic; - if (m_inc_mode) { - SASSERT(m_inc_solver); - m_inc_solver->init(m, logic); - } - m_ctx = alloc(ctx, m); - TRACE("strategic_solver", tout << "strategic_solver was initialized.\n";); -} - -unsigned strategic_solver::get_num_assertions() const { - if (m_ctx == 0) - return 0; - return m_ctx->m_assertions.size(); -} - -expr * strategic_solver::get_assertion(unsigned idx) const { - SASSERT(m_ctx); - return m_ctx->m_assertions.get(idx); -} - -expr * strategic_solver::get_assertion_name(unsigned idx) const { - SASSERT(m_ctx); - SASSERT(m_produce_unsat_cores); - return m_ctx->m_assertion_names.get(idx); -} - -void strategic_solver::set_produce_proofs(bool f) { - m_produce_proofs = f; - // do not need to propagate to inc_solver since flag cannot be changed after initialization -} - -void strategic_solver::set_produce_models(bool f) { - m_produce_models = f; - if (m_inc_solver) - m_inc_solver->set_produce_models(f); -} - -void strategic_solver::set_produce_unsat_cores(bool f) { - m_produce_unsat_cores = f; - // do not need to propagate to inc_solver since flag cannot be changed after initialization -} - -// delayed inc solver initialization -void strategic_solver::init_inc_solver() { - if (m_inc_mode) - return; // solver was already initialized - if (!m_inc_solver) - return; // inc solver was not installed - m_inc_mode = true; - m_inc_solver->set_produce_proofs(m_produce_proofs); - m_inc_solver->set_produce_models(m_produce_models); - m_inc_solver->set_produce_unsat_cores(m_produce_unsat_cores); - m_inc_solver->init(m(), m_logic); - unsigned sz = get_num_assertions(); - if (m_produce_unsat_cores) { - SASSERT(m_ctx->m_assertions.size() == m_ctx->m_assertion_names.size()); - for (unsigned i = 0; i < sz; i++) { - m_inc_solver->assert_expr(get_assertion(i), get_assertion_name(i)); - } - } - else { - for (unsigned i = 0; i < sz; i++) { - m_inc_solver->assert_expr(get_assertion(i)); - } - } -} - -void strategic_solver::collect_statistics(statistics & st) const { - if (m_use_inc_solver_results) { - SASSERT(m_inc_solver); - m_inc_solver->collect_statistics(st); - } - else { - if (m_curr_tactic) - m_curr_tactic->collect_statistics(st); // m_curr_tactic is still being executed. - else - st.copy(m_stats); - } -} - -void strategic_solver::reset() { - m_ctx = 0; - m_logic = symbol::null; - m_inc_mode = false; - m_check_sat_executed = false; - if (m_inc_solver) - m_inc_solver->reset(); - SASSERT(!m_curr_tactic); - m_use_inc_solver_results = false; - reset_results(); -} - -void strategic_solver::reset_results() { - m_use_inc_solver_results = false; - m_model = 0; - if (m_proof) { - m().dec_ref(m_proof); - m_proof = 0; - } - if (m_core) { - m().dec_ref(m_core); - m_core = 0; - } - m_reason_unknown.clear(); - m_stats.reset(); -} - -void strategic_solver::assert_expr(expr * t) { - if (m_check_sat_executed && !m_inc_mode) { - // a check sat was already executed --> switch to incremental mode - init_inc_solver(); - SASSERT(m_inc_solver == 0 || m_inc_mode); - } - if (m_inc_mode) { - SASSERT(m_inc_solver); - m_inc_solver->assert_expr(t); - } - SASSERT(m_ctx); - m_ctx->m_assertions.push_back(t); - if (m_produce_unsat_cores) - m_ctx->m_assertion_names.push_back(0); -} - -void strategic_solver::assert_expr(expr * t, expr * a) { - if (m_check_sat_executed && !m_inc_mode) { - // a check sat was already executed --> switch to incremental mode - init_inc_solver(); - SASSERT(m_inc_solver == 0 || m_inc_mode); - } - if (m_inc_mode) { - SASSERT(m_inc_solver); - m_inc_solver->assert_expr(t, a); - } - SASSERT(m_ctx); - m_ctx->m_assertions.push_back(t); - if (m_produce_unsat_cores) - m_ctx->m_assertion_names.push_back(a); -} - -void strategic_solver::push() { - DEBUG_CODE(m_num_scopes++;); - init_inc_solver(); - if (m_inc_solver) - m_inc_solver->push(); - m_ctx->m_scopes.push_back(m_ctx->m_assertions.size()); -} - -void strategic_solver::pop(unsigned n) { - DEBUG_CODE({ - SASSERT(n <= m_num_scopes); - m_num_scopes -= n; - }); - init_inc_solver(); - if (m_inc_solver) - m_inc_solver->pop(n); - - SASSERT(m_ctx); - unsigned new_lvl = m_ctx->m_scopes.size() - n; - unsigned old_sz = m_ctx->m_scopes[new_lvl]; - m_ctx->m_assertions.shrink(old_sz); - if (m_produce_unsat_cores) - m_ctx->m_assertion_names.shrink(old_sz); - m_ctx->m_scopes.shrink(new_lvl); -} - -unsigned strategic_solver::get_scope_level() const { - if (m_ctx == 0) - return 0; - return m_ctx->m_scopes.size(); -} - -struct aux_timeout_eh : public event_handler { - solver * m_solver; - volatile bool m_canceled; - aux_timeout_eh(solver * s):m_solver(s), m_canceled(false) {} - virtual void operator()() { - m_solver->cancel(); - m_canceled = true; - } -}; - -struct strategic_solver::mk_tactic { - strategic_solver * m_solver; - - mk_tactic(strategic_solver * s, tactic_factory * f, params_ref const & p):m_solver(s) { - ast_manager & m = s->m(); - tactic * tct = (*f)(m, p); - tct->set_logic(s->m_logic); - if (s->m_callback) - tct->set_progress_callback(s->m_callback); - #pragma omp critical (strategic_solver) - { - s->m_curr_tactic = tct; - } - } - - ~mk_tactic() { - #pragma omp critical (strategic_solver) - { - m_solver->m_curr_tactic = 0; - } - } -}; - -tactic_factory * strategic_solver::get_tactic_factory() const { - tactic_factory * f = 0; - if (m_logic2fct.find(m_logic, f)) - return f; - return m_default_fct.get(); -} - -lbool strategic_solver::check_sat_with_assumptions(unsigned num_assumptions, expr * const * assumptions) { - if (!m_inc_solver) { - IF_VERBOSE(PS_VB_LVL, verbose_stream() << "incremental solver was not installed, returning unknown...\n";); - m_use_inc_solver_results = false; - m_reason_unknown = "incomplete"; - return l_undef; - } - init_inc_solver(); - m_use_inc_solver_results = true; - TRACE("strategic_solver", tout << "invoking inc_solver with " << num_assumptions << " assumptions\n";); - return m_inc_solver->check_sat(num_assumptions, assumptions); -} - -lbool strategic_solver::check_sat(unsigned num_assumptions, expr * const * assumptions) { - TRACE("strategic_solver", tout << "assumptions at strategic_solver:\n"; - for (unsigned i = 0; i < num_assumptions; i++) { - tout << mk_ismt2_pp(assumptions[i], m()) << "\n"; - } - tout << "m_produce_unsat_cores: " << m_produce_unsat_cores << ", m_inc_mode: " << m_inc_mode << "\n";); - reset_results(); - m_check_sat_executed = true; - if (num_assumptions > 0 || // assumptions were provided - (!m_auto_config && !m_force_tactic) // auto config and force_tactic are turned off - ) { - // must use incremental solver - return check_sat_with_assumptions(num_assumptions, assumptions); - } - - tactic_factory * factory = get_tactic_factory(); - if (factory == 0) - init_inc_solver(); // try to switch to incremental solver - - if (m_inc_mode) { - SASSERT(m_inc_solver); - unsigned timeout = m_inc_solver_timeout; - if (factory == 0) - timeout = UINT_MAX; // there is no tactic available - if (timeout == UINT_MAX) { - IF_VERBOSE(PS_VB_LVL, verbose_stream() << "using incremental solver (without a timeout).\n";); - m_use_inc_solver_results = true; - lbool r = m_inc_solver->check_sat(0, 0); - if (r != l_undef || factory == 0 || !use_tactic_when_undef()) { - m_use_inc_solver_results = true; - return r; - } - } - else { - IF_VERBOSE(PS_VB_LVL, verbose_stream() << "using incremental solver (with timeout).\n";); - SASSERT(factory != 0); - aux_timeout_eh eh(m_inc_solver.get()); - lbool r; - { - scoped_timer timer(m_inc_solver_timeout, &eh); - r = m_inc_solver->check_sat(0, 0); - } - if ((r != l_undef || !use_tactic_when_undef()) && !eh.m_canceled) { - m_use_inc_solver_results = true; - return r; - } - } - IF_VERBOSE(PS_VB_LVL, verbose_stream() << "incremental solver failed, trying tactic.\n";); - } - - m_use_inc_solver_results = false; - - if (factory == 0) { - IF_VERBOSE(PS_VB_LVL, verbose_stream() << "there is no tactic available for the current logic.\n";); - m_reason_unknown = "incomplete"; - return l_undef; - } - - goal_ref g = alloc(goal, m(), m_produce_proofs, m_produce_models, m_produce_unsat_cores); - unsigned sz = get_num_assertions(); - if (m_produce_unsat_cores) { - SASSERT(m_ctx->m_assertions.size() == m_ctx->m_assertion_names.size()); - for (unsigned i = 0; i < sz; i++) - g->assert_expr(get_assertion(i), get_assertion_name(i)); - } - else { - for (unsigned i = 0; i < sz; i++) - g->assert_expr(get_assertion(i)); - } - expr_dependency_ref core(m()); - - TRACE("strategic_solver", tout << "using goal...\n"; g->display_with_dependencies(tout);); - - mk_tactic tct_maker(this, factory, m_params); - SASSERT(m_curr_tactic); - - proof_ref pr(m()); - lbool r = ::check_sat(*(m_curr_tactic.get()), g, m_model, pr, core, m_reason_unknown); - m_curr_tactic->collect_statistics(m_stats); - if (pr) { - m_proof = pr; - m().inc_ref(m_proof); - } - if (core) { - m_core = core; - m().inc_ref(m_core); - } - return r; -} - -void strategic_solver::set_cancel(bool f) { - if (m_inc_solver) - m_inc_solver->set_cancel(f); - #pragma omp critical (strategic_solver) - { - if (m_curr_tactic) - m_curr_tactic->set_cancel(f); - } -} - -void strategic_solver::get_unsat_core(ptr_vector & r) { - TRACE("strategic_solver", tout << "get_unsat_core, m_use_inc_solver_results: " << m_use_inc_solver_results << "\n";); - if (m_use_inc_solver_results) { - SASSERT(m_inc_solver); - m_inc_solver->get_unsat_core(r); - } - else { - m().linearize(m_core, r); - } -} - -void strategic_solver::get_model(model_ref & m) { - if (m_use_inc_solver_results) { - SASSERT(m_inc_solver); - m_inc_solver->get_model(m); - } - else { - m = m_model; - } -} - -proof * strategic_solver::get_proof() { - if (m_use_inc_solver_results) { - SASSERT(m_inc_solver); - return m_inc_solver->get_proof(); - } - else { - return m_proof; - } -} - -std::string strategic_solver::reason_unknown() const { - if (m_use_inc_solver_results) { - SASSERT(m_inc_solver); - return m_inc_solver->reason_unknown(); - } - return m_reason_unknown; -} - -void strategic_solver::get_labels(svector & r) { - if (m_use_inc_solver_results) { - SASSERT(m_inc_solver); - m_inc_solver->get_labels(r); - } -} - -void strategic_solver::set_progress_callback(progress_callback * callback) { - m_callback = callback; - if (m_inc_solver) - m_inc_solver->set_progress_callback(callback); -} - -void strategic_solver::display(std::ostream & out) const { - if (m_manager) { - unsigned num = get_num_assertions(); - out << "(solver"; - for (unsigned i = 0; i < num; i++) { - out << "\n " << mk_ismt2_pp(get_assertion(i), m(), 2); - } - out << ")"; - } - else { - out << "(solver)"; - } -} - - - - - - diff --git a/src/solver/strategic_solver.h b/src/solver/strategic_solver.h deleted file mode 100644 index 1883a88cd..000000000 --- a/src/solver/strategic_solver.h +++ /dev/null @@ -1,153 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - strategic_solver.h - -Abstract: - - Strategies -> Solver - -Author: - - Leonardo (leonardo) 2011-05-19 - -Notes: - ---*/ -#ifndef _STRATEGIC_SOLVER_H_ -#define _STRATEGIC_SOLVER_H_ - -#include"solver.h" -#include"tactic.h" - -class progress_callback; - -/** - \brief Implementation of the solver API that supports: - - a different tactic for each logic - - a general purpose tactic - - a default incremental solver - - The strategic solver has two modes: - - non-incremental - - incremental - In non-incremental mode, tactics are used. - In incremental model, the incremental (general purpose) solver is used. - - A timeout for the incremental solver can be specified. - If the timeout is reached, then the strategic_solver tries to solve the problem using tactics. - - The strategic_solver switches to incremental when: - - push is used - - assertions are peformed after a check_sat - It goes back to non_incremental mode when: - - reset is invoked. -*/ -class strategic_solver : public solver { -public: - // Behavior when the incremental solver returns unknown. - enum inc_unknown_behavior { - IUB_RETURN_UNDEF, // just return unknown - IUB_USE_TACTIC_IF_QF, // invoke tactic if problem is quantifier free - IUB_USE_TACTIC // invoke tactic - }; - -private: - ast_manager * m_manager; - params_ref m_params; - symbol m_logic; - bool m_force_tactic; // use tactics even when auto_config = false - bool m_inc_mode; - bool m_check_sat_executed; - scoped_ptr m_inc_solver; - unsigned m_inc_solver_timeout; - inc_unknown_behavior m_inc_unknown_behavior; - scoped_ptr m_default_fct; - dictionary m_logic2fct; - - ref m_curr_tactic; - - bool m_use_inc_solver_results; - model_ref m_model; - proof * m_proof; - expr_dependency * m_core; - std::string m_reason_unknown; - statistics m_stats; - - struct ctx { - expr_ref_vector m_assertions; - expr_ref_vector m_assertion_names; - unsigned_vector m_scopes; - ctx(ast_manager & m); - }; - scoped_ptr m_ctx; - -#ifdef Z3DEBUG - unsigned m_num_scopes; -#endif - - bool m_produce_proofs; - bool m_produce_models; - bool m_produce_unsat_cores; - - bool m_auto_config; - - progress_callback * m_callback; - - void reset_results(); - void init_inc_solver(); - tactic_factory * get_tactic_factory() const; - lbool check_sat_with_assumptions(unsigned num_assumptions, expr * const * assumptions); - - struct mk_tactic; - - bool has_quantifiers() const; - bool use_tactic_when_undef() const; - -public: - strategic_solver(); - ~strategic_solver(); - - ast_manager & m() const { SASSERT(m_manager); return *m_manager; } - - void set_inc_solver(solver * s); - void set_inc_solver_timeout(unsigned timeout); - void set_default_tactic(tactic_factory * fct); - void set_tactic_for(symbol const & logic, tactic_factory * fct); - void set_inc_unknown_behavior(inc_unknown_behavior b) { m_inc_unknown_behavior = b; } - void force_tactic(bool f) { m_force_tactic = f; } - - virtual void updt_params(params_ref const & p); - virtual void collect_param_descrs(param_descrs & r); - - virtual void set_produce_proofs(bool f); - virtual void set_produce_models(bool f); - virtual void set_produce_unsat_cores(bool f); - - unsigned get_num_assertions() const; - expr * get_assertion(unsigned idx) const; - expr * get_assertion_name(unsigned idx) const; - - virtual void display(std::ostream & out) const; - - virtual void init(ast_manager & m, symbol const & logic); - virtual void collect_statistics(statistics & st) const; - virtual void reset(); - virtual void assert_expr(expr * t); - virtual void assert_expr(expr * t, expr * a); - virtual void push(); - virtual void pop(unsigned n); - virtual unsigned get_scope_level() const; - virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions); - virtual void get_unsat_core(ptr_vector & r); - virtual void get_model(model_ref & m); - virtual proof * get_proof(); - virtual std::string reason_unknown() const; - virtual void get_labels(svector & r); - virtual void set_cancel(bool f); - virtual void set_progress_callback(progress_callback * callback); -}; - -#endif diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index e630f9c78..1268fbcea 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -19,96 +19,115 @@ Author: Notes: --*/ -#include"tactic2solver.h" +#include"solver_na2as.h" +#include"tactic.h" #include"ast_smt2_pp.h" -tactic2solver_core::ctx::ctx(ast_manager & m, symbol const & logic): - m_logic(logic), +/** + \brief Simulates the incremental solver interface using a tactic. + + Every query will be solved from scratch. So, this is not a good + option for applications trying to solve many easy queries that a + similar to each other. +*/ +class tactic2solver : public solver_na2as { + expr_ref_vector m_assertions; + unsigned_vector m_scopes; + ref m_result; + tactic_ref m_tactic; + symbol m_logic; + params_ref m_params; + bool m_produce_models; + bool m_produce_proofs; + bool m_produce_unsat_cores; +public: + tactic2solver(ast_manager & m, tactic * t, params_ref const & p, bool produce_proofs, bool produce_models, bool produce_unsat_cores, symbol const & logic); + virtual ~tactic2solver(); + + virtual void updt_params(params_ref const & p); + virtual void collect_param_descrs(param_descrs & r); + + virtual void set_produce_models(bool f) { m_produce_models = f; } + + virtual void assert_expr(expr * t); + + virtual void push_core(); + virtual void pop_core(unsigned n); + virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions); + + virtual void set_cancel(bool f); + + virtual void collect_statistics(statistics & st) const; + virtual void get_unsat_core(ptr_vector & r); + virtual void get_model(model_ref & m); + virtual proof * get_proof(); + virtual std::string reason_unknown() const; + virtual void get_labels(svector & r) {} + + virtual void set_progress_callback(progress_callback * callback) {} + + virtual unsigned get_num_assertions() const; + virtual expr * get_assertion(unsigned idx) const; + + virtual void display(std::ostream & out) const; +}; + +tactic2solver::tactic2solver(ast_manager & m, tactic * t, params_ref const & p, bool produce_proofs, bool produce_models, bool produce_unsat_cores, symbol const & logic): + solver_na2as(m), m_assertions(m) { + + m_tactic = t; + m_logic = logic; + m_params = p; + + m_produce_models = produce_models; + m_produce_proofs = produce_proofs; + m_produce_unsat_cores = produce_unsat_cores; } -tactic2solver_core::~tactic2solver_core() { +tactic2solver::~tactic2solver() { } -void tactic2solver_core::init_core(ast_manager & m, symbol const & logic) { - m_ctx = alloc(ctx, m, logic); -} - -void tactic2solver_core::updt_params(params_ref const & p) { +void tactic2solver::updt_params(params_ref const & p) { m_params = p; } -void tactic2solver_core::collect_param_descrs(param_descrs & r) { - if (m_ctx) { - if (!m_ctx->m_tactic) { - #pragma omp critical (tactic2solver_core) - { - m_ctx->m_tactic = get_tactic(m_ctx->m(), m_params); - } - - if (m_ctx->m_tactic) { - m_ctx->m_tactic->collect_param_descrs(r); - } - - #pragma omp critical (tactic2solver_core) - { - m_ctx->m_tactic = 0; - } - } - else { - m_ctx->m_tactic->collect_param_descrs(r); - } - } +void tactic2solver::collect_param_descrs(param_descrs & r) { + if (m_tactic.get()) + m_tactic->collect_param_descrs(r); } -void tactic2solver_core::reset_core() { - SASSERT(m_ctx); - m_ctx->m_assertions.reset(); - m_ctx->m_scopes.reset(); - m_ctx->m_result = 0; +void tactic2solver::assert_expr(expr * t) { + m_assertions.push_back(t); + m_result = 0; } -void tactic2solver_core::assert_expr(expr * t) { - SASSERT(m_ctx); - m_ctx->m_assertions.push_back(t); - m_ctx->m_result = 0; +void tactic2solver::push_core() { + m_scopes.push_back(m_assertions.size()); + m_result = 0; } -void tactic2solver_core::push_core() { - SASSERT(m_ctx); - m_ctx->m_scopes.push_back(m_ctx->m_assertions.size()); - m_ctx->m_result = 0; +void tactic2solver::pop_core(unsigned n) { + unsigned new_lvl = m_scopes.size() - n; + unsigned old_sz = m_scopes[new_lvl]; + m_assertions.shrink(old_sz); + m_scopes.shrink(new_lvl); + m_result = 0; } -void tactic2solver_core::pop_core(unsigned n) { - SASSERT(m_ctx); - unsigned new_lvl = m_ctx->m_scopes.size() - n; - unsigned old_sz = m_ctx->m_scopes[new_lvl]; - m_ctx->m_assertions.shrink(old_sz); - m_ctx->m_scopes.shrink(new_lvl); - m_ctx->m_result = 0; -} - -lbool tactic2solver_core::check_sat_core(unsigned num_assumptions, expr * const * assumptions) { - SASSERT(m_ctx); - ast_manager & m = m_ctx->m(); - params_ref p = m_params; - #pragma omp critical (tactic2solver_core) - { - m_ctx->m_tactic = get_tactic(m, p); - if (m_ctx->m_tactic) { - m_ctx->m_result = alloc(simple_check_sat_result, m); - } - } - if (!m_ctx->m_tactic) - return l_undef; - tactic & t = *(m_ctx->m_tactic); - simple_check_sat_result & result = *(m_ctx->m_result); +lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + if (m_tactic.get() == 0) + return l_false; + ast_manager & m = m_assertions.m(); + m_result = alloc(simple_check_sat_result, m); + m_tactic->cleanup(); + m_tactic->updt_params(m_params); + m_tactic->set_logic(m_logic); goal_ref g = alloc(goal, m, m_produce_proofs, m_produce_models, m_produce_unsat_cores); - t.set_logic(m_ctx->m_logic); - unsigned sz = m_ctx->m_assertions.size(); + + unsigned sz = m_assertions.size(); for (unsigned i = 0; i < sz; i++) { - g->assert_expr(m_ctx->m_assertions.get(i)); + g->assert_expr(m_assertions.get(i)); } for (unsigned i = 0; i < num_assumptions; i++) { g->assert_expr(assumptions[i], m.mk_asserted(assumptions[i]), m.mk_leaf(assumptions[i])); @@ -119,17 +138,17 @@ lbool tactic2solver_core::check_sat_core(unsigned num_assumptions, expr * const expr_dependency_ref core(m); std::string reason_unknown = "unknown"; try { - switch (::check_sat(t, g, md, pr, core, reason_unknown)) { + switch (::check_sat(*m_tactic, g, md, pr, core, reason_unknown)) { case l_true: - result.set_status(l_true); + m_result->set_status(l_true); break; case l_false: - result.set_status(l_false); + m_result->set_status(l_false); break; default: - result.set_status(l_undef); + m_result->set_status(l_undef); if (reason_unknown != "") - result.m_unknown = reason_unknown; + m_result->m_unknown = reason_unknown; break; } } @@ -137,112 +156,115 @@ lbool tactic2solver_core::check_sat_core(unsigned num_assumptions, expr * const throw ex; } catch (z3_exception & ex) { - TRACE("tactic2solver_core", tout << "exception: " << ex.msg() << "\n";); - result.set_status(l_undef); - result.m_unknown = ex.msg(); + TRACE("tactic2solver", tout << "exception: " << ex.msg() << "\n";); + m_result->set_status(l_undef); + m_result->m_unknown = ex.msg(); } - t.collect_statistics(result.m_stats); - result.m_model = md; - result.m_proof = pr; + m_tactic->collect_statistics(m_result->m_stats); + m_result->m_model = md; + m_result->m_proof = pr; if (m_produce_unsat_cores) { ptr_vector core_elems; m.linearize(core, core_elems); - result.m_core.append(core_elems.size(), core_elems.c_ptr()); + m_result->m_core.append(core_elems.size(), core_elems.c_ptr()); } - - #pragma omp critical (tactic2solver_core) - { - m_ctx->m_tactic = 0; - } - return result.status(); + m_tactic->cleanup(); + return m_result->status(); } -void tactic2solver_core::set_cancel(bool f) { - #pragma omp critical (tactic2solver_core) - { - if (m_ctx && m_ctx->m_tactic) - m_ctx->m_tactic->set_cancel(f); - } +void tactic2solver::set_cancel(bool f) { + if (m_tactic.get()) + m_tactic->set_cancel(f); } -void tactic2solver_core::collect_statistics(statistics & st) const { - if (m_ctx->m_result.get()) - m_ctx->m_result->collect_statistics(st); +void tactic2solver::collect_statistics(statistics & st) const { + if (m_result.get()) + m_result->collect_statistics(st); } -void tactic2solver_core::get_unsat_core(ptr_vector & r) { - if (m_ctx->m_result.get()) - m_ctx->m_result->get_unsat_core(r); +void tactic2solver::get_unsat_core(ptr_vector & r) { + if (m_result.get()) + m_result->get_unsat_core(r); } -void tactic2solver_core::get_model(model_ref & m) { - if (m_ctx->m_result.get()) - m_ctx->m_result->get_model(m); +void tactic2solver::get_model(model_ref & m) { + if (m_result.get()) + m_result->get_model(m); } -proof * tactic2solver_core::get_proof() { - if (m_ctx->m_result.get()) - return m_ctx->m_result->get_proof(); +proof * tactic2solver::get_proof() { + if (m_result.get()) + return m_result->get_proof(); else return 0; } -std::string tactic2solver_core::reason_unknown() const { - if (m_ctx->m_result.get()) - return m_ctx->m_result->reason_unknown(); +std::string tactic2solver::reason_unknown() const { + if (m_result.get()) + return m_result->reason_unknown(); else return std::string("unknown"); } -unsigned tactic2solver_core::get_num_assertions() const { - if (m_ctx) - return m_ctx->m_assertions.size(); - else - return 0; +unsigned tactic2solver::get_num_assertions() const { + return m_assertions.size(); } -expr * tactic2solver_core::get_assertion(unsigned idx) const { - SASSERT(m_ctx); - return m_ctx->m_assertions.get(idx); +expr * tactic2solver::get_assertion(unsigned idx) const { + return m_assertions.get(idx); } -void tactic2solver_core::display(std::ostream & out) const { - if (m_ctx) { - ast_manager & m = m_ctx->m_assertions.m(); - unsigned num = m_ctx->m_assertions.size(); - out << "(solver"; - for (unsigned i = 0; i < num; i++) { - out << "\n " << mk_ismt2_pp(m_ctx->m_assertions.get(i), m, 2); - } - out << ")"; +void tactic2solver::display(std::ostream & out) const { + ast_manager & m = m_assertions.m(); + unsigned num = m_assertions.size(); + out << "(solver"; + for (unsigned i = 0; i < num; i++) { + out << "\n " << mk_ismt2_pp(m_assertions.get(i), m, 2); } - else { - out << "(solver)"; + out << ")"; +} + +solver * mk_tactic2solver(ast_manager & m, + tactic * t, + params_ref const & p, + bool produce_proofs, + bool produce_models, + bool produce_unsat_cores, + symbol const & logic) { + return alloc(tactic2solver, m, t, p, produce_proofs, produce_models, produce_unsat_cores, logic); +} + +class tactic2solver_factory : public solver_factory { + ref m_tactic; +public: + tactic2solver_factory(tactic * t):m_tactic(t) { } + + virtual ~tactic2solver_factory() {} + + virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { + return mk_tactic2solver(m, m_tactic.get(), p, proofs_enabled, models_enabled, unsat_core_enabled, logic); + } +}; + +class tactic_factory2solver_factory : public solver_factory { + scoped_ptr m_factory; +public: + tactic_factory2solver_factory(tactic_factory * f):m_factory(f) { + } + + virtual ~tactic_factory2solver_factory() {} + + virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { + tactic * t = (*m_factory)(m, p); + return mk_tactic2solver(m, t, p, proofs_enabled, models_enabled, unsat_core_enabled, logic); + } +}; + +solver_factory * mk_tactic2solver_factory(tactic * t) { + return alloc(tactic2solver_factory, t); } -tactic2solver::tactic2solver(tactic * t): - m_tactic(t) { -} - -tactic2solver::~tactic2solver() { -} - -tactic * tactic2solver::get_tactic(ast_manager & m, params_ref const & p) { - m_tactic->cleanup(); - m_tactic->updt_params(p); - return m_tactic.get(); -} - -tactic_factory2solver::~tactic_factory2solver() { -} - -void tactic_factory2solver::set_tactic(tactic_factory * f) { - m_tactic_factory = f; -} - -tactic * tactic_factory2solver::get_tactic(ast_manager & m, params_ref const & p) { - if (m_tactic_factory == 0) - return 0; - return (*m_tactic_factory)(m, p); +solver_factory * mk_tactic_factory2solver_factory(tactic_factory * f) { + return alloc(tactic_factory2solver_factory, f); } diff --git a/src/solver/tactic2solver.h b/src/solver/tactic2solver.h index 0a057b04b..f20b1c4dd 100644 --- a/src/solver/tactic2solver.h +++ b/src/solver/tactic2solver.h @@ -22,88 +22,22 @@ Notes: #ifndef _TACTIC2SOLVER_H_ #define _TACTIC2SOLVER_H_ -#include"solver_na2as.h" -#include"tactic.h" +#include"params.h" +class ast_manager; +class tactic; +class tactic_factory; +class solver; +class solver_factory; -/** - \brief Simulates the incremental solver interface using a tactic. - - Every query will be solved from scratch. So, this is not a good - option for applications trying to solve many easy queries that a - similar to each other. -*/ -class tactic2solver_core : public solver_na2as { - struct ctx { - symbol m_logic; - expr_ref_vector m_assertions; - unsigned_vector m_scopes; - ref m_result; - tactic_ref m_tactic; - ctx(ast_manager & m, symbol const & logic); - ast_manager & m() const { return m_assertions.m(); } - }; - scoped_ptr m_ctx; - params_ref m_params; - bool m_produce_models; - bool m_produce_proofs; - bool m_produce_unsat_cores; -public: - tactic2solver_core():m_ctx(0), m_produce_models(false), m_produce_proofs(false), m_produce_unsat_cores(false) {} - virtual ~tactic2solver_core(); - - virtual tactic * get_tactic(ast_manager & m, params_ref const & p) = 0; - - virtual void updt_params(params_ref const & p); - virtual void collect_param_descrs(param_descrs & r); - - virtual void set_produce_proofs(bool f) { m_produce_proofs = f; } - virtual void set_produce_models(bool f) { m_produce_models = f; } - virtual void set_produce_unsat_cores(bool f) { m_produce_unsat_cores = f; } - - virtual void assert_expr(expr * t); - - virtual void init_core(ast_manager & m, symbol const & logic); - virtual void reset_core(); - virtual void push_core(); - virtual void pop_core(unsigned n); - virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions); - - virtual void set_cancel(bool f); - - virtual void collect_statistics(statistics & st) const; - virtual void get_unsat_core(ptr_vector & r); - virtual void get_model(model_ref & m); - virtual proof * get_proof(); - virtual std::string reason_unknown() const; - virtual void get_labels(svector & r) {} - - virtual void set_progress_callback(progress_callback * callback) {} - - virtual unsigned get_num_assertions() const; - virtual expr * get_assertion(unsigned idx) const; - - virtual void display(std::ostream & out) const; -}; - -class tactic2solver : public tactic2solver_core { - tactic_ref m_tactic; -public: - tactic2solver(tactic * t); - virtual ~tactic2solver(); - virtual tactic * get_tactic(ast_manager & m, params_ref const & p); -}; - - -class tactic_factory2solver : public tactic2solver_core { - scoped_ptr m_tactic_factory; -public: - virtual ~tactic_factory2solver(); - /** - \brief Set tactic that will be used to process the satisfiability queries. - */ - void set_tactic(tactic_factory * f); - virtual tactic * get_tactic(ast_manager & m, params_ref const & p); -}; +solver * mk_tactic2solver(ast_manager & m, + tactic * t = 0, + params_ref const & p = params_ref(), + bool produce_proofs = false, + bool produce_models = true, + bool produce_unsat_cores = false, + symbol const & logic = symbol::null); +solver_factory * mk_tactic2solver_factory(tactic * t); +solver_factory * mk_tactic_factory2solver_factory(tactic_factory * f); #endif diff --git a/src/tactic/core/cofactor_elim_term_ite.cpp b/src/tactic/core/cofactor_elim_term_ite.cpp index 78d532357..d81b4fa13 100644 --- a/src/tactic/core/cofactor_elim_term_ite.cpp +++ b/src/tactic/core/cofactor_elim_term_ite.cpp @@ -355,12 +355,12 @@ struct cofactor_elim_term_ite::imp { expr * lhs; expr * rhs; if (m.is_eq(t, lhs, rhs)) { - if (m.is_value(lhs)) { + if (m.is_unique_value(lhs)) { m_term = rhs; m_value = to_app(lhs); TRACE("set_cofactor_atom", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); } - else if (m.is_value(rhs)) { + else if (m.is_unique_value(rhs)) { m_term = lhs; m_value = to_app(rhs); TRACE("set_cofactor_atom", tout << "term:\n" << mk_ismt2_pp(m_term, m) << "\nvalue: " << mk_ismt2_pp(m_value, m) << "\n";); diff --git a/src/tactic/core/reduce_args_tactic.cpp b/src/tactic/core/reduce_args_tactic.cpp index 686c5128d..59ed2dcd3 100644 --- a/src/tactic/core/reduce_args_tactic.cpp +++ b/src/tactic/core/reduce_args_tactic.cpp @@ -128,7 +128,7 @@ struct reduce_args_tactic::imp { unsigned j = n->get_num_args(); while (j > 0) { --j; - if (m_manager.is_value(n->get_arg(j))) + if (m_manager.is_unique_value(n->get_arg(j))) return; } m_non_cadidates.insert(d); @@ -185,13 +185,13 @@ struct reduce_args_tactic::imp { it->m_value.reserve(j); while (j > 0) { --j; - it->m_value.set(j, m_manager.is_value(n->get_arg(j))); + it->m_value.set(j, m_manager.is_unique_value(n->get_arg(j))); } } else { SASSERT(j == it->m_value.size()); while (j > 0) { --j; - it->m_value.set(j, it->m_value.get(j) && m_manager.is_value(n->get_arg(j))); + it->m_value.set(j, it->m_value.get(j) && m_manager.is_unique_value(n->get_arg(j))); } } } diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 7a274a830..4813cbe1a 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -18,7 +18,8 @@ Notes: --*/ #include"cmd_context.h" -#include"strategic_solver.h" +#include"combined_solver.h" +#include"tactic2solver.h" #include"qfbv_tactic.h" #include"qflia_tactic.h" #include"qfnia_tactic.h" @@ -36,57 +37,76 @@ Notes: #include"horn_tactic.h" #include"smt_solver.h" -MK_SIMPLE_TACTIC_FACTORY(qfuf_fct, mk_qfuf_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(qfidl_fct, mk_qfidl_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(qfauflia_fct, mk_qfauflia_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(auflia_fct, mk_auflia_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(auflira_fct, mk_auflira_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(aufnira_fct, mk_aufnira_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(ufnia_fct, mk_ufnia_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(uflra_fct, mk_uflra_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(lra_fct, mk_lra_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(qfbv_fct, mk_qfbv_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(default_fct, mk_default_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(qfaufbv_fct, mk_qfaufbv_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(qflra_fct, mk_qflra_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(qflia_fct, mk_qflia_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(qfufbv_fct, mk_qfufbv_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(qfnia_fct, mk_qfnia_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(qfnra_fct, mk_qfnra_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(qffpa_fct, mk_qffpa_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(ufbv_fct, mk_ufbv_tactic(m, p)); -MK_SIMPLE_TACTIC_FACTORY(horn_fct, mk_horn_tactic(m, p)); - -static void init(strategic_solver * s) { - s->set_default_tactic(alloc(default_fct)); - s->set_tactic_for(symbol("QF_UF"), alloc(qfuf_fct)); - s->set_tactic_for(symbol("QF_BV"), alloc(qfbv_fct)); - s->set_tactic_for(symbol("QF_IDL"), alloc(qfidl_fct)); - s->set_tactic_for(symbol("QF_LIA"), alloc(qflia_fct)); - s->set_tactic_for(symbol("QF_LRA"), alloc(qflra_fct)); - s->set_tactic_for(symbol("QF_NIA"), alloc(qfnia_fct)); - s->set_tactic_for(symbol("QF_NRA"), alloc(qfnra_fct)); - s->set_tactic_for(symbol("QF_AUFLIA"), alloc(qfauflia_fct)); - s->set_tactic_for(symbol("QF_AUFBV"), alloc(qfaufbv_fct)); - s->set_tactic_for(symbol("QF_ABV"), alloc(qfaufbv_fct)); - s->set_tactic_for(symbol("QF_UFBV"), alloc(qfufbv_fct)); - s->set_tactic_for(symbol("AUFLIA"), alloc(auflia_fct)); - s->set_tactic_for(symbol("AUFLIRA"), alloc(auflira_fct)); - s->set_tactic_for(symbol("AUFNIRA"), alloc(aufnira_fct)); - s->set_tactic_for(symbol("UFNIA"), alloc(ufnia_fct)); - s->set_tactic_for(symbol("UFLRA"), alloc(uflra_fct)); - s->set_tactic_for(symbol("LRA"), alloc(lra_fct)); - s->set_tactic_for(symbol("UFBV"), alloc(ufbv_fct)); - s->set_tactic_for(symbol("BV"), alloc(ufbv_fct)); - s->set_tactic_for(symbol("QF_FPA"), alloc(qffpa_fct)); - s->set_tactic_for(symbol("QF_FPABV"), alloc(qffpa_fct)); - s->set_tactic_for(symbol("HORN"), alloc(horn_fct)); +tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const & logic) { + if (logic=="QF_UF") + return mk_qfufbv_tactic(m, p); + else if (logic=="QF_BV") + return mk_qfbv_tactic(m, p); + else if (logic=="QF_IDL") + return mk_qfidl_tactic(m, p); + else if (logic=="QF_LIA") + return mk_qflia_tactic(m, p); + else if (logic=="QF_LRA") + return mk_qflra_tactic(m, p); + else if (logic=="QF_NIA") + return mk_qfnia_tactic(m, p); + else if (logic=="QF_NRA") + return mk_qfnra_tactic(m, p); + else if (logic=="QF_AUFLIA") + return mk_qfauflia_tactic(m, p); + else if (logic=="QF_AUFBV") + return mk_qfaufbv_tactic(m, p); + else if (logic=="QF_ABV") + return mk_qfaufbv_tactic(m, p); + else if (logic=="QF_UFBV") + return mk_qfufbv_tactic(m, p); + else if (logic=="AUFLIA") + return mk_auflia_tactic(m, p); + else if (logic=="AUFLIRA") + return mk_auflira_tactic(m, p); + else if (logic=="AUFNIRA") + return mk_aufnira_tactic(m, p); + else if (logic=="UFNIA") + return mk_ufnia_tactic(m, p); + else if (logic=="UFLRA") + return mk_uflra_tactic(m, p); + else if (logic=="LRA") + return mk_lra_tactic(m, p); + else if (logic=="UFBV") + return mk_ufbv_tactic(m, p); + else if (logic=="BV") + return mk_ufbv_tactic(m, p); + else if (logic=="QF_FPA") + return mk_qffpa_tactic(m, p); + else if (logic=="QF_FPABV") + return mk_qffpa_tactic(m, p); + else if (logic=="HORN") + return mk_horn_tactic(m, p); + else + return mk_default_tactic(m, p); } -solver * mk_smt_strategic_solver(bool force_tactic) { - strategic_solver * s = alloc(strategic_solver); - s->force_tactic(force_tactic); - s->set_inc_solver(mk_smt_solver()); - init(s); - return s; +class smt_strategic_solver_factory : public solver_factory { + symbol m_logic; +public: + smt_strategic_solver_factory(symbol const & logic):m_logic(logic) {} + + virtual ~smt_strategic_solver_factory() {} + + virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { + symbol l; + if (m_logic != symbol::null) + l = m_logic; + else + l = logic; + tactic * t = mk_tactic_for_logic(m, p, l); + return mk_combined_solver(mk_tactic2solver(m, t, p, proofs_enabled, models_enabled, unsat_core_enabled, l), + mk_smt_solver(m, p, l), + p); + } +}; + +solver_factory * mk_smt_strategic_solver_factory(symbol const & logic) { + return alloc(smt_strategic_solver_factory, logic); } + diff --git a/src/tactic/portfolio/smt_strategic_solver.h b/src/tactic/portfolio/smt_strategic_solver.h index 060721e09..1d132532a 100644 --- a/src/tactic/portfolio/smt_strategic_solver.h +++ b/src/tactic/portfolio/smt_strategic_solver.h @@ -20,8 +20,8 @@ Notes: #ifndef _SMT_STRATEGIC_SOLVER_H_ #define _SMT_STRATEGIC_SOLVER_H_ -class solver; -// Create a strategic solver for the Z3 API -solver * mk_smt_strategic_solver(bool force_tactic=false); +class solver_factory; + +solver_factory * mk_smt_strategic_solver_factory(symbol const & logic = symbol::null); #endif diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index 0d61f0916..785a9d82e 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -559,10 +559,10 @@ public: ts.push_back(m_ts.get(i)->translate(*new_m)); } - unsigned finished_id = UINT_MAX; - par_exception_kind ex_kind; + unsigned finished_id = UINT_MAX; + par_exception_kind ex_kind = DEFAULT_EX; std::string ex_msg; - unsigned error_code; + unsigned error_code = 0; #pragma omp parallel for for (int i = 0; i < static_cast(sz); i++) { @@ -734,8 +734,8 @@ public: bool found_solution = false; bool failed = false; - par_exception_kind ex_kind; - unsigned error_code; + par_exception_kind ex_kind = DEFAULT_EX; + unsigned error_code = 0; std::string ex_msg; #pragma omp parallel for @@ -1242,8 +1242,7 @@ public: virtual void updt_params(params_ref const & p) { TRACE("using_params", tout << "before p: " << p << "\n"; - tout << "m_params: " << m_params << "\n"; - ;); + tout << "m_params: " << m_params << "\n";); params_ref new_p = p; new_p.append(m_params); diff --git a/src/tactic/ufbv/ufbv_tactic.cpp b/src/tactic/ufbv/ufbv_tactic.cpp index 80d403b67..9d16d7f9a 100644 --- a/src/tactic/ufbv/ufbv_tactic.cpp +++ b/src/tactic/ufbv/ufbv_tactic.cpp @@ -62,9 +62,8 @@ tactic * mk_ufbv_preprocessor_tactic(ast_manager & m, params_ref const & p) { tactic * mk_ufbv_tactic(ast_manager & m, params_ref const & p) { params_ref main_p(p); main_p.set_bool("mbqi", true); - main_p.set_uint("mbqi_max_iterations", -1); + main_p.set_uint("mbqi.max_iterations", UINT_MAX); main_p.set_bool("elim_and", true); - main_p.set_bool("solver", true); tactic * t = and_then(repeat(mk_ufbv_preprocessor_tactic(m, main_p), 2), mk_smt_tactic_using(false, main_p)); diff --git a/src/util/basic_interval.h b/src/util/basic_interval.h index 2bf5a8f30..ef414149c 100644 --- a/src/util/basic_interval.h +++ b/src/util/basic_interval.h @@ -260,8 +260,8 @@ public: \brief c <- a - b */ void sub(interval const & a, interval const & b, interval & c) { - m().sub(a.m_lower, b.m_lower, c.m_lower); - m().sub(a.m_upper, b.m_upper, c.m_upper); + m().sub(a.m_lower, b.m_upper, c.m_lower); + m().sub(a.m_upper, b.m_lower, c.m_upper); } private: diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index b81531798..8b1fbe40e 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -52,6 +52,7 @@ char const * g_params_renames[] = { "qi_cost", "smt.qi.cost", "qi_eager_threshold", "smt.qi.eager_threshold", "nl_arith", "smt.arith.nl", + "pull_nested_quantifiers", "smt.pull_nested_quantifiers", "nnf_sk_hack", "nnf.sk_hack", "model_v2", "model.v2", "pi_non_nested_arith_weight", "pi.non_nested_arith_weight", @@ -165,7 +166,7 @@ public: if (*name == ':') name++; std::string tmp = name; - unsigned n = tmp.size(); + unsigned n = static_cast(tmp.size()); for (unsigned i = 0; i < n; i++) { if (tmp[i] >= 'A' && tmp[i] <= 'Z') tmp[i] = tmp[i] - 'A' + 'a'; diff --git a/src/util/params.cpp b/src/util/params.cpp index f302f7721..0ee42868d 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -21,13 +21,15 @@ Notes: #include"symbol.h" #include"dictionary.h" +params_ref params_ref::g_empty_params_ref; + std::string norm_param_name(char const * n) { if (n == 0) return "_"; if (*n == ':') n++; std::string r = n; - unsigned sz = r.size(); + unsigned sz = static_cast(r.size()); if (sz == 0) return "_"; for (unsigned i = 0; i < sz; i++) { @@ -133,7 +135,7 @@ struct param_descrs::imp { if (smt2_style) out << ':'; char const * s = it2->bare_str(); - unsigned n = strlen(s); + unsigned n = static_cast(strlen(s)); for (unsigned i = 0; i < n; i++) { if (smt2_style && s[i] == '_') out << '-'; diff --git a/src/util/params.h b/src/util/params.h index 13850758c..15be825b0 100644 --- a/src/util/params.h +++ b/src/util/params.h @@ -31,6 +31,8 @@ class params; class param_descrs; class params_ref { + static params_ref g_empty_params_ref; + params * m_params; void init(); void copy_core(params const * p); @@ -38,7 +40,9 @@ public: params_ref():m_params(0) {} params_ref(params_ref const & p); ~params_ref(); - + + static params_ref const & get_empty() { return g_empty_params_ref; } + params_ref & operator=(params_ref const & p); // copy params from src diff --git a/src/util/scoped_timer.cpp b/src/util/scoped_timer.cpp index 4b191505f..d475e5489 100644 --- a/src/util/scoped_timer.cpp +++ b/src/util/scoped_timer.cpp @@ -43,7 +43,7 @@ Revision History: #define CLOCKID CLOCK_PROCESS_CPUTIME_ID #else // FreeBSD does not support CLOCK_PROCESS_CPUTIME_ID - #define CLOCKID CLOCK_PROF + #define CLOCKID CLOCK_MONOTONIC #endif #define SIG SIGRTMIN // ---------