diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 13957064e..643c9b26f 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -61,6 +61,10 @@ Version 4.3.2 - Fixed bugs in the C++ API (Thanks to Andrey Kupriyanov). +- Fixed bug reported at http://z3.codeplex.com/workitem/23 (Thanks to Paul Jackson). + +- Fixed bug reported at http://stackoverflow.com/questions/15226944/segmentation-fault-in-z3 (Thanks to Tianhai Liu). + Version 4.3.1 ============= diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index 1f127e8e4..ab5d0132c 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -883,6 +883,29 @@ void incremental_example3() { std::cout << s.check(a2) << "\n"; } +void enum_sort_example() { + std::cout << "enumeration sort example\n"; + context ctx; + const char * enum_names[] = { "a", "b", "c" }; + func_decl_vector enum_consts(ctx); + func_decl_vector enum_testers(ctx); + sort s = ctx.enumeration_sort("enumT", 3, enum_names, enum_consts, enum_testers); + // enum_consts[0] is a func_decl of arity 0. + // we convert it to an expression using the operator() + expr a = enum_consts[0](); + expr b = enum_consts[1](); + expr x = ctx.constant("x", s); + expr test = (x==a) && (x==b); + std::cout << "1: " << test << std::endl; + tactic qe(ctx, "ctx-solver-simplify"); + goal g(ctx); + g.add(test); + expr res(ctx); + apply_result result_of_elimination = qe.apply(g); + goal result_goal = result_of_elimination[0]; + std::cout << "2: " << result_goal.as_expr() << std::endl; +} + int main() { try { demorgan(); std::cout << "\n"; @@ -917,6 +940,7 @@ int main() { incremental_example1(); std::cout << "\n"; incremental_example2(); std::cout << "\n"; incremental_example3(); std::cout << "\n"; + enum_sort_example(); std::cout << "\n"; std::cout << "done\n"; } catch (exception & ex) { diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index c2743ece8..48395d8c2 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -197,7 +197,7 @@ class JavaExample TestFailedException { Solver s = ctx.mkSolver(); - s.assert_(f); + s.add(f); if (s.check() != sat) throw new TestFailedException(); if (sat == Status.SATISFIABLE) @@ -213,7 +213,7 @@ class JavaExample System.out.println("\nTactical solver: " + s); for (BoolExpr a : g.getFormulas()) - s.assert_(a); + s.add(a); System.out.println("Solver: " + s); if (s.check() != sat) @@ -266,8 +266,8 @@ class JavaExample p.add("mbqi", useMBQI); s.setParameters(p); for (BoolExpr a : assumptions) - s.assert_(a); - s.assert_(ctx.mkNot(f)); + s.add(a); + s.add(ctx.mkNot(f)); Status q = s.check(); switch (q) @@ -299,8 +299,8 @@ class JavaExample p.add("mbqi", useMBQI); s.setParameters(p); for (BoolExpr a : assumptions) - s.assert_(a); - s.assert_(ctx.mkNot(f)); + s.add(a); + s.add(ctx.mkNot(f)); Status q = s.check(); switch (q) @@ -326,9 +326,9 @@ class JavaExample ArithExpr yr = (ArithExpr) ctx.mkConst(ctx.mkSymbol("y"), ctx.mkRealSort()); Goal g4 = ctx.mkGoal(true, false, false); - g4.assert_(ctx.mkGt(xr, ctx.mkReal(10, 1))); - g4.assert_(ctx.mkEq(yr, ctx.mkAdd(xr, ctx.mkReal(1, 1)))); - g4.assert_(ctx.mkGt(yr, ctx.mkReal(1, 1))); + g4.add(ctx.mkGt(xr, ctx.mkReal(10, 1))); + g4.add(ctx.mkEq(yr, ctx.mkAdd(xr, ctx.mkReal(1, 1)))); + g4.add(ctx.mkGt(yr, ctx.mkReal(1, 1))); ApplyResult ar = applyTactic(ctx, ctx.mkTactic("simplify"), g4); if (ar.getNumSubgoals() == 1 @@ -345,7 +345,7 @@ class JavaExample Solver s = ctx.mkSolver(); for (BoolExpr e : ar.getSubgoals()[0].getFormulas()) - s.assert_(e); + s.add(e); Status q = s.check(); System.out.println("Solver says: " + q); System.out.println("Model: \n" + s.getModel()); @@ -367,7 +367,7 @@ class JavaExample ctx.mkBitVecSort(32)); ArrayExpr aex = (ArrayExpr) ctx.mkConst(ctx.mkSymbol("MyArray"), asort); Expr sel = ctx.mkSelect(aex, ctx.mkInt(0)); - g.assert_(ctx.mkEq(sel, ctx.mkBV(42, 32))); + g.add(ctx.mkEq(sel, ctx.mkBV(42, 32))); Symbol xs = ctx.mkSymbol("x"); IntExpr xc = (IntExpr) ctx.mkConst(xs, ctx.getIntSort()); @@ -377,11 +377,11 @@ class JavaExample Expr[] fargs = { ctx.mkConst(xs, ctx.getIntSort()) }; IntExpr fapp = (IntExpr) ctx.mkApp(fd, fargs); - g.assert_(ctx.mkEq(ctx.mkAdd(xc, fapp), ctx.mkInt(123))); + g.add(ctx.mkEq(ctx.mkAdd(xc, fapp), ctx.mkInt(123))); Solver s = ctx.mkSolver(); for (BoolExpr a : g.getFormulas()) - s.assert_(a); + s.add(a); System.out.println("Solver: " + s); Status q = s.check(); @@ -574,8 +574,8 @@ class JavaExample ctx.mkEq(X[i][j], ctx.mkInt(instance[i][j])))); Solver s = ctx.mkSolver(); - s.assert_(sudoku_c); - s.assert_(instance_c); + s.add(sudoku_c); + s.add(instance_c); if (s.check() == Status.SATISFIABLE) { @@ -798,14 +798,14 @@ class JavaExample BoolExpr nontrivial_eq = ctx.mkEq(fapp, fapp2); Goal g = ctx.mkGoal(true, false, false); - g.assert_(trivial_eq); - g.assert_(nontrivial_eq); + g.add(trivial_eq); + g.add(nontrivial_eq); System.out.println("Goal: " + g); Solver solver = ctx.mkSolver(); for (BoolExpr a : g.getFormulas()) - solver.assert_(a); + solver.add(a); if (solver.check() != Status.SATISFIABLE) throw new TestFailedException(); @@ -820,7 +820,7 @@ class JavaExample if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedSat()) throw new TestFailedException(); - g.assert_(ctx.mkEq(ctx.mkNumeral(1, ctx.mkBitVecSort(32)), + g.add(ctx.mkEq(ctx.mkNumeral(1, ctx.mkBitVecSort(32)), ctx.mkNumeral(2, ctx.mkBitVecSort(32)))); ar = applyTactic(ctx, ctx.mkTactic("smt"), g); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat()) @@ -832,7 +832,7 @@ class JavaExample throw new TestFailedException(); g2 = ctx.mkGoal(true, true, false); - g2.assert_(ctx.mkFalse()); + g2.add(ctx.mkFalse()); ar = applyTactic(ctx, ctx.mkTactic("smt"), g2); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat()) throw new TestFailedException(); @@ -840,10 +840,10 @@ class JavaExample Goal g3 = ctx.mkGoal(true, true, false); Expr xc = ctx.mkConst(ctx.mkSymbol("x"), ctx.getIntSort()); Expr yc = ctx.mkConst(ctx.mkSymbol("y"), ctx.getIntSort()); - g3.assert_(ctx.mkEq(xc, ctx.mkNumeral(1, ctx.getIntSort()))); - g3.assert_(ctx.mkEq(yc, ctx.mkNumeral(2, ctx.getIntSort()))); + g3.add(ctx.mkEq(xc, ctx.mkNumeral(1, ctx.getIntSort()))); + g3.add(ctx.mkEq(yc, ctx.mkNumeral(2, ctx.getIntSort()))); BoolExpr constr = ctx.mkEq(xc, yc); - g3.assert_(constr); + g3.add(constr); ar = applyTactic(ctx, ctx.mkTactic("smt"), g3); if (ar.getNumSubgoals() != 1 || !ar.getSubgoals()[0].isDecidedUnsat()) throw new TestFailedException(); @@ -1110,13 +1110,13 @@ class JavaExample // Use a solver for QF_BV Solver s = ctx.mkSolver("QF_BV"); - s.assert_(eq); + s.add(eq); Status res = s.check(); System.out.println("solver result: " + res); // Or perhaps a tactic for QF_BV Goal g = ctx.mkGoal(true, false, false); - g.assert_(eq); + g.add(eq); Tactic t = ctx.mkTactic("qfbv"); ApplyResult ar = t.apply(g); @@ -1139,7 +1139,7 @@ class JavaExample BoolExpr q = ctx.mkEq(x, y); Goal g = ctx.mkGoal(true, false, false); - g.assert_(q); + g.add(q); Tactic t1 = ctx.mkTactic("qfbv"); Tactic t2 = ctx.mkTactic("qfbv"); @@ -1341,7 +1341,7 @@ class JavaExample /* assert x >= "big number" */ BoolExpr c1 = ctx.mkGe(x, big_number); System.out.println("assert: x >= 'big number'"); - solver.assert_(c1); + solver.add(c1); /* create a backtracking point */ System.out.println("push"); @@ -1350,7 +1350,7 @@ class JavaExample /* assert x <= 3 */ BoolExpr c2 = ctx.mkLe(x, three); System.out.println("assert: x <= 3"); - solver.assert_(c2); + solver.add(c2); /* context is inconsistent at this point */ if (solver.check() != Status.UNSATISFIABLE) @@ -1375,7 +1375,7 @@ class JavaExample /* assert y > x */ BoolExpr c3 = ctx.mkGt(y, x); System.out.println("assert: y > x"); - solver.assert_(c3); + solver.add(c3); /* the context is still consistent. */ if (solver.check() != Status.SATISFIABLE) @@ -1911,10 +1911,10 @@ class JavaExample Solver solver = ctx.mkSolver(); /* assert x < y */ - solver.assert_(ctx.mkLt(x, y)); + solver.add(ctx.mkLt(x, y)); /* assert x > 2 */ - solver.assert_(ctx.mkGt(x, two)); + solver.add(ctx.mkGt(x, two)); /* find model for the constraints above */ Model model = null; @@ -1964,9 +1964,9 @@ class JavaExample Solver solver = ctx.mkSolver(); /* assert tup1 != tup2 */ - solver.assert_(ctx.mkNot(ctx.mkEq(tup1, tup2))); + solver.add(ctx.mkNot(ctx.mkEq(tup1, tup2))); /* assert first tup1 = first tup2 */ - solver.assert_(ctx.mkEq(ctx.mkApp(first, tup1), ctx.mkApp(first, tup2))); + solver.add(ctx.mkEq(ctx.mkApp(first, tup1), ctx.mkApp(first, tup2))); /* find model for the constraints above */ Model model = null; @@ -2014,7 +2014,7 @@ class JavaExample // Assert all feasible bounds. for (int i = 0; i < num_Exprs; ++i) { - solver.assert_(ctx.mkBVULE(to_minimize[i], + solver.add(ctx.mkBVULE(to_minimize[i], ctx.mkBV(upper[i], 32))); } @@ -2050,7 +2050,7 @@ class JavaExample { last_upper = (upper[i] + lower[i]) / 2; last_index = i; - solver.assert_(ctx.mkBVULE(to_minimize[i], + solver.add(ctx.mkBVULE(to_minimize[i], ctx.mkBV(last_upper, 32))); some_work = true; break; @@ -2074,7 +2074,7 @@ class JavaExample Solver solver = ctx.mkSolver(); - solver.assert_(ctx.mkBVULE(x, ctx.mkBVAdd(y, z))); + solver.add(ctx.mkBVULE(x, ctx.mkBVAdd(y, z))); checkSmall(ctx, solver, x, y, z); } @@ -2120,10 +2120,10 @@ class JavaExample BoolExpr f3 = ctx.mkOr(ctx.mkNot(pa), ctx.mkNot(pc)); BoolExpr f4 = pd; - solver.assert_(ctx.mkOr(f1, p1)); - solver.assert_(ctx.mkOr(f2, p2)); - solver.assert_(ctx.mkOr(f3, p3)); - solver.assert_(ctx.mkOr(f4, p4)); + solver.add(ctx.mkOr(f1, p1)); + solver.add(ctx.mkOr(f2, p2)); + solver.add(ctx.mkOr(f3, p3)); + solver.add(ctx.mkOr(f4, p4)); Status result = solver.check(assumptions); if (result == Status.UNSATISFIABLE) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 25686f9a7..06120e2c4 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -30,12 +30,12 @@ CC=getenv("CC", None) CPPFLAGS=getenv("CPPFLAGS", "") CXXFLAGS=getenv("CXXFLAGS", "") LDFLAGS=getenv("LDFLAGS", "") -JAVA=getenv("JAVA", "java") -JAVAC=getenv("JAVAC", "javac") -JAVA_HOME=getenv("JAVA_HOME", None) +JNI_HOME=getenv("JNI_HOME", None) CXX_COMPILERS=['g++', 'clang++'] C_COMPILERS=['gcc', 'clang'] +JAVAC=None +JAR=None PYTHON_PACKAGE_DIR=distutils.sysconfig.get_python_lib() BUILD_DIR='build' REV_BUILD_DIR='..' @@ -206,23 +206,6 @@ def test_openmp(cc): t.commit() return exec_compiler_cmd([cc, CPPFLAGS, 'tstomp.cpp', LDFLAGS, '-fopenmp']) == 0 -def check_java(): - t = TempFile('Hello.java') - t.add('public class Hello { public static void main(String[] args) { System.out.println("Hello, World"); }}\n') - t.commit() - if is_verbose(): - print("Testing %s..." % JAVAC) - r = exec_cmd([JAVAC, 'Hello.java']) - if r != 0: - raise MKException('Failed testing Java compiler. Set environment variable JAVAC with the path to the Java compiler') - if is_verbose(): - print("Testing %s..." % JAVA) - r = exec_cmd([JAVA, 'Hello']) - rmf('Hello.class') - if r != 0: - 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: @@ -230,41 +213,110 @@ def find_jni_h(path): 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(os.path.join(JAVA_HOME, 'include', 'jni.h')): - return +def check_java(): + global JNI_HOME + global JAVAC + global JAR + + JDK_HOME = getenv('JDK_HOME', None) # we only need to check this locally. + if is_verbose(): - print("Finding JAVA_HOME...") - t = TempFile('output') - null = open(os.devnull, 'wb') + print("Finding javac ...") + + if JDK_HOME != None: + if IS_WINDOWS: + JAVAC = os.path.join(JDK_HOME, 'bin', 'javac.exe') + else: + JAVAC = os.path.join(JDK_HOME, 'bin', 'javac') + + if not os.path.exists(JAVAC): + raise MKException("Failed to detect javac at '%s/bin'; the environment variable JDK_HOME is probably set to the wrong path." % os.path.join(JDK_HOME)) + else: + # Search for javac in the path. + ind = 'javac'; + if IS_WINDOWS: + ind = ind + '.exe' + paths = os.getenv('PATH', None) + if paths: + spaths = paths.split(os.pathsep) + for i in range(0, len(spaths)): + cmb = os.path.join(spaths[i], ind) + if os.path.exists(cmb): + JAVAC = cmb + break + + if JAVAC == None: + raise MKException('No java compiler in the path, please adjust your PATH or set JDK_HOME to the location of the JDK.') + + if is_verbose(): + print("Finding jar ...") + + if IS_WINDOWS: + JAR = os.path.join(os.path.dirname(JAVAC), 'jar.exe') + else: + JAR = os.path.join(os.path.dirname(JAVAC), 'jar') + + if not os.path.exists(JAR): + raise MKException("Failed to detect jar at '%s'; the environment variable JDK_HOME is probably set to the wrong path." % os.path.join(JDK_HOME)) + + if is_verbose(): + print("Testing %s..." % JAVAC) + + t = TempFile('Hello.java') + t.add('public class Hello { public static void main(String[] args) { System.out.println("Hello, World"); }}\n') + t.commit() + + oo = TempFile('output') + eo = TempFile('errout') try: - subprocess.call([JAVA, '-verbose'], stdout=t.fname, stderr=null) - t.commit() + subprocess.call([JAVAC, 'Hello.java', '-verbose'], stdout=oo.fname, stderr=eo.fname) + oo.commit() + eo.commit() except: - raise MKException('Failed to find JAVA_HOME') - open_pat = re.compile("\[Opened (.*)\]") - t = open('output', 'r') - for line in t: - m = open_pat.match(line) - if m: - # Remove last 3 directives from m.group(1) - print(m.group(1)) - tmp = m.group(1).split(os.sep) - path = string.join(tmp[:len(tmp) - 3], os.sep) - if is_verbose(): - print("Checking jni.h...") - 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') + raise MKException('Found, but failed to run Java compiler at %s' % (JAVAC)) + + os.remove('Hello.class') + + if is_verbose(): + print("Finding jni.h...") + + if JNI_HOME != None: + if not os.path.exists(os.path.join(JNI_HOME, 'jni.h')): + raise MKException("Failed to detect jni.h '%s'; the environment variable JNI_HOME is probably set to the wrong path." % os.path.join(JNI_HOME)) + else: + # Search for jni.h in the library directories... + t = open('errout', 'r') + open_pat = re.compile("\[search path for class files: (.*)\]") + cdirs = [] + for line in t: + m = open_pat.match(line) + if m: + libdirs = m.group(1).split(',') + for libdir in libdirs: + q = os.path.dirname(libdir) + if cdirs.count(q) == 0: + cdirs.append(q) + + # ... plus some heuristic ones. + extra_dirs = [] + + # For the libraries, even the JDK usually uses a JRE that comes with it. To find the + # headers we have to go a little bit higher up. + for dir in cdirs: + extra_dirs.append(os.path.abspath(os.path.join(dir, '..'))) + + if IS_OSX: # Apparently Apple knows best where to put stuff... + extra_dirs.append('/System/Library/Frameworks/JavaVM.framework/Headers/') + + cdirs[len(cdirs):] = extra_dirs + + for dir in cdirs: + q = find_jni_h(dir) + if q != False: + JNI_HOME = q + + if JNI_HOME == None: + raise MKException("Failed to detect jni.h. Possible solution: set JNI_HOME with the path to JDK.") def is64(): return sys.maxsize >= 2**32 @@ -399,9 +451,8 @@ def display_help(exit_code): print(" LDFLAGS Linker flags, e.g., -L if you have libraries in a non-standard directory") print(" CPPFLAGS Preprocessor flags, e.g., -I if you have header files in a non-standard directory") print(" CXXFLAGS C++ compiler flags") - print(" JAVA Java virtual machine (only relevant if -j or --java option is provided)") - print(" JAVAC Java compiler (only relevant if -j or --java option is provided)") - print(" JAVA_HOME JDK installation directory (only relevant if -j or --java option is provided)") + print(" JDK_HOME JDK installation directory (only relevant if -j or --java option is provided)") + print(" JNI_HOME JNI bindings directory (only relevant if -j or --java option is provided)") exit(exit_code) # Parse configuration option for mk_make script @@ -1082,11 +1133,14 @@ class JavaDLLComponent(Component): self.manifest_file = manifest_file def mk_makefile(self, out): + global JAVAC + global JAR + if is_java_enabled(): mk_dir(os.path.join(BUILD_DIR, 'api', 'java', 'classes')) dllfile = '%s$(SO_EXT)' % self.dll_name out.write('libz3java$(SO_EXT): libz3$(SO_EXT) %s\n' % os.path.join(self.to_src_dir, 'Native.cpp')) - t = '\t$(CXX) $(CXXFLAGS) $(CXX_OUT_FLAG)api/java/Native$(OBJ_EXT) -I"%s/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) + t = '\t$(CXX) $(CXXFLAGS) $(CXX_OUT_FLAG)api/java/Native$(OBJ_EXT) -I"%s" -I"%s/PLATFORM" -I%s %s/Native.cpp\n' % (JNI_HOME, JNI_HOME, get_component('api').to_src_dir, self.to_src_dir) if IS_OSX: t = t.replace('PLATFORM', 'darwin') elif IS_LINUX: @@ -1108,6 +1162,9 @@ class JavaDLLComponent(Component): deps += '%s ' % os.path.join(self.to_src_dir, 'enumerations', jfile) out.write(deps) out.write('\n') + if IS_WINDOWS: + JAVAC = '"%s"' % JAVAC + JAR = '"%s"' % JAR t = ('\t%s %s.java -d %s\n' % (JAVAC, os.path.join(self.to_src_dir, 'enumerations', '*'), os.path.join('api', 'java', 'classes'))) out.write(t) t = ('\t%s -cp %s %s.java -d %s\n' % (JAVAC, @@ -1115,7 +1172,7 @@ class JavaDLLComponent(Component): os.path.join(self.to_src_dir, '*'), os.path.join('api', 'java', 'classes'))) out.write(t) - out.write('\tjar cfm %s.jar %s -C %s .\n' % (self.package_name, + out.write('\t%s cfm %s.jar %s -C %s .\n' % (JAR, self.package_name, os.path.join(self.to_src_dir, 'manifest'), os.path.join('api', 'java', 'classes'))) out.write('java: %s.jar\n\n' % self.package_name) @@ -1387,9 +1444,8 @@ def mk_config(): if is_verbose(): print('64-bit: %s' % is64()) if is_java_enabled(): - print('Java Home: %s' % JAVA_HOME) + print('JNI Bindings: %s' % JNI_HOME) print('Java Compiler: %s' % JAVAC) - print('Java VM: %s' % JAVA) else: global CXX, CC, GMP, CPPFLAGS, CXXFLAGS, LDFLAGS ARITH = "internal" @@ -1491,9 +1547,8 @@ def mk_config(): print('gprof: enabled') print('Python version: %s' % distutils.sysconfig.get_python_version()) if is_java_enabled(): - print('Java Home: %s' % JAVA_HOME) + print('JNI Bindings: %s' % JNI_HOME) print('Java Compiler: %s' % JAVAC) - print('Java VM: %s' % JAVA) def mk_install(out): out.write('install:\n') diff --git a/src/api/api_algebraic.cpp b/src/api/api_algebraic.cpp index 7716cbb59..d03a6aff4 100644 --- a/src/api/api_algebraic.cpp +++ b/src/api/api_algebraic.cpp @@ -373,7 +373,7 @@ extern "C" { scoped_anum_vector roots(_am); { cancel_eh eh(_am); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); vector_var2anum v2a(as); _am.isolate_roots(_p, v2a, roots); @@ -408,7 +408,7 @@ extern "C" { } { cancel_eh eh(_am); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); vector_var2anum v2a(as); int r = _am.eval_sign_at(_p, v2a); diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index e93e1a178..680b59c68 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -681,7 +681,7 @@ extern "C" { th_rewriter m_rw(m, p); expr_ref result(m); cancel_eh eh(m_rw); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index 28fe3ed33..f6eadfea0 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -266,7 +266,7 @@ extern "C" { lbool r = l_undef; cancel_eh eh(*to_fixedpoint_ref(d)); unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); try { @@ -291,7 +291,7 @@ extern "C" { lbool r = l_undef; unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); cancel_eh eh(*to_fixedpoint_ref(d)); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_timer timer(timeout, &eh); try { @@ -358,7 +358,7 @@ extern "C" { v->m_ast_vector.push_back(coll.m_queries[i].get()); } for (unsigned i = 0; i < coll.m_rels.size(); ++i) { - to_fixedpoint_ref(d)->ctx().register_predicate(coll.m_rels[i].get()); + to_fixedpoint_ref(d)->ctx().register_predicate(coll.m_rels[i].get(), true); } for (unsigned i = 0; i < coll.m_rules.size(); ++i) { to_fixedpoint_ref(d)->add_rule(coll.m_rules[i].get(), coll.m_names[i]); @@ -415,7 +415,7 @@ extern "C" { void Z3_API Z3_fixedpoint_register_relation(Z3_context c,Z3_fixedpoint d, Z3_func_decl f) { Z3_TRY; LOG_Z3_fixedpoint_register_relation(c, d, f); - to_fixedpoint_ref(d)->ctx().register_predicate(to_func_decl(f)); + to_fixedpoint_ref(d)->ctx().register_predicate(to_func_decl(f), true); Z3_CATCH; } diff --git a/src/api/api_polynomial.cpp b/src/api/api_polynomial.cpp index 3148f972b..25d4ca292 100644 --- a/src/api/api_polynomial.cpp +++ b/src/api/api_polynomial.cpp @@ -67,7 +67,7 @@ extern "C" { expr_ref _r(mk_c(c)->m()); { cancel_eh eh(pm); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); scoped_timer timer(mk_c(c)->params().m_timeout, &eh); pm.psc_chain(_p, _q, v_x, rs); } diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 9ace149af..ac30a0c21 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -243,7 +243,7 @@ extern "C" { unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); bool use_ctrl_c = to_solver(s)->m_params.get_bool("ctrl_c", false); cancel_eh eh(*to_solver_ref(s)); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); lbool result; { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); diff --git a/src/api/api_solver_old.cpp b/src/api/api_solver_old.cpp index c89f89873..e0533fbd9 100644 --- a/src/api/api_solver_old.cpp +++ b/src/api/api_solver_old.cpp @@ -73,7 +73,7 @@ extern "C" { RESET_ERROR_CODE(); CHECK_SEARCHING(c); cancel_eh eh(mk_c(c)->get_smt_kernel()); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); flet _model(mk_c(c)->fparams().m_model, true); lbool result; try { @@ -123,7 +123,7 @@ extern "C" { expr * const* _assumptions = to_exprs(assumptions); flet _model(mk_c(c)->fparams().m_model, true); cancel_eh eh(mk_c(c)->get_smt_kernel()); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); lbool result; result = mk_c(c)->get_smt_kernel().check(num_assumptions, _assumptions); if (result != l_false && m) { diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index 5bce218e6..911360047 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -410,7 +410,7 @@ extern "C" { to_tactic_ref(t)->updt_params(p); - api::context::set_interruptable(*(mk_c(c)), eh); + api::context::set_interruptable si(*(mk_c(c)), eh); { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 5af4bf60b..43975fd04 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -63,6 +63,11 @@ namespace z3 { class statistics; class apply_result; class fixedpoint; + template class ast_vector_tpl; + typedef ast_vector_tpl ast_vector; + typedef ast_vector_tpl expr_vector; + typedef ast_vector_tpl sort_vector; + typedef ast_vector_tpl func_decl_vector; inline void set_param(char const * param, char const * value) { Z3_global_param_set(param, value); } inline void set_param(char const * param, bool value) { Z3_global_param_set(param, value ? "true" : "false"); } @@ -190,7 +195,13 @@ namespace z3 { Example: Given a context \c c, c.array_sort(c.int_sort(), c.bool_sort()) is an array sort from integer to Boolean. */ sort array_sort(sort d, sort r); - + /** + \brief Return an enumeration sort: enum_names[0], ..., enum_names[n-1]. + \c cs and \c ts are output parameters. The method stores in \c cs the constants corresponding to the enumerated elements, + and in \c ts the predicates for testing if terms of the enumeration sort correspond to an enumeration. + */ + sort enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts); + func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range); func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range); func_decl function(char const * name, sort const & domain, sort const & range); @@ -240,8 +251,8 @@ namespace z3 { array(unsigned sz):m_size(sz) { m_array = new T[sz]; } ~array() { delete[] m_array; } unsigned size() const { return m_size; } - T & operator[](unsigned i) { assert(i < m_size); return m_array[i]; } - T const & operator[](unsigned i) const { assert(i < m_size); return m_array[i]; } + T & operator[](int i) { assert(0 <= i); assert(static_cast(i) < m_size); return m_array[i]; } + T const & operator[](int i) const { assert(0 <= i); assert(static_cast(i) < m_size); return m_array[i]; } T const * ptr() const { return m_array; } T * ptr() { return m_array; } }; @@ -414,6 +425,7 @@ namespace z3 { bool is_const() const { return arity() == 0; } + expr operator()() const; expr operator()(unsigned n, expr const * args) const; expr operator()(expr const & a) const; expr operator()(int a) const; @@ -1004,7 +1016,7 @@ namespace z3 { ~ast_vector_tpl() { Z3_ast_vector_dec_ref(ctx(), m_vector); } operator Z3_ast_vector() const { return m_vector; } unsigned size() const { return Z3_ast_vector_size(ctx(), m_vector); } - T operator[](unsigned i) const { Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } + T operator[](int i) const { assert(0 <= i); Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } void push_back(T const & e) { Z3_ast_vector_push(ctx(), m_vector, e); check_error(); } void resize(unsigned sz) { Z3_ast_vector_resize(ctx(), m_vector, sz); check_error(); } T back() const { return operator[](size() - 1); } @@ -1020,11 +1032,6 @@ namespace z3 { friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } }; - typedef ast_vector_tpl ast_vector; - typedef ast_vector_tpl expr_vector; - typedef ast_vector_tpl sort_vector; - typedef ast_vector_tpl func_decl_vector; - class func_entry : public object { Z3_func_entry m_entry; void init(Z3_func_entry e) { @@ -1105,7 +1112,10 @@ namespace z3 { func_decl get_const_decl(unsigned i) const { Z3_func_decl r = Z3_model_get_const_decl(ctx(), m_model, i); check_error(); return func_decl(ctx(), r); } func_decl get_func_decl(unsigned i) const { Z3_func_decl r = Z3_model_get_func_decl(ctx(), m_model, i); check_error(); return func_decl(ctx(), r); } unsigned size() const { return num_consts() + num_funcs(); } - func_decl operator[](unsigned i) const { return i < num_consts() ? get_const_decl(i) : get_func_decl(i - num_consts()); } + func_decl operator[](int i) const { + assert(0 <= i); + return static_cast(i) < num_consts() ? get_const_decl(i) : get_func_decl(i - num_consts()); + } expr get_const_interp(func_decl c) const { check_context(*this, c); @@ -1253,7 +1263,7 @@ namespace z3 { } void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } unsigned size() const { return Z3_goal_size(ctx(), m_goal); } - expr operator[](unsigned i) const { Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } + expr operator[](int i) const { assert(0 <= i); Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } Z3_goal_prec precision() const { return Z3_goal_precision(ctx(), m_goal); } bool inconsistent() const { return Z3_goal_inconsistent(ctx(), m_goal) != 0; } unsigned depth() const { return Z3_goal_depth(ctx(), m_goal); } @@ -1261,6 +1271,19 @@ namespace z3 { unsigned num_exprs() const { return Z3_goal_num_exprs(ctx(), m_goal); } bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal) != 0; } bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal) != 0; } + expr as_expr() const { + unsigned n = size(); + if (n == 0) + return ctx().bool_val(false); + else if (n == 1) + return operator[](0); + else { + array args(n); + for (unsigned i = 0; i < n; i++) + args[i] = operator[](i); + return expr(ctx(), Z3_mk_and(ctx(), n, args.ptr())); + } + } friend std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } }; @@ -1283,8 +1306,7 @@ namespace z3 { return *this; } unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } - goal operator[](unsigned i) const { Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } - goal operator[](int i) const { assert(i >= 0); return this->operator[](static_cast(i)); } + goal operator[](int i) const { assert(0 <= i); Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } model convert_model(model const & m, unsigned i = 0) const { check_context(*this, m); Z3_model new_m = Z3_apply_result_convert_model(ctx(), m_apply_result, i, m); @@ -1437,6 +1459,17 @@ namespace z3 { inline sort context::real_sort() { Z3_sort s = Z3_mk_real_sort(m_ctx); check_error(); return sort(*this, s); } inline sort context::bv_sort(unsigned sz) { Z3_sort s = Z3_mk_bv_sort(m_ctx, sz); check_error(); return sort(*this, s); } inline sort context::array_sort(sort d, sort r) { Z3_sort s = Z3_mk_array_sort(m_ctx, d, r); check_error(); return sort(*this, s); } + inline sort context::enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts) { + array _enum_names(n); + for (unsigned i = 0; i < n; i++) { _enum_names[i] = Z3_mk_string_symbol(*this, enum_names[i]); } + array _cs(n); + array _ts(n); + Z3_symbol _name = Z3_mk_string_symbol(*this, name); + sort s = to_sort(*this, Z3_mk_enumeration_sort(*this, _name, n, _enum_names.ptr(), _cs.ptr(), _ts.ptr())); + check_error(); + for (unsigned i = 0; i < n; i++) { cs.push_back(func_decl(*this, _cs[i])); ts.push_back(func_decl(*this, _ts[i])); } + return s; + } inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { array args(arity); @@ -1538,6 +1571,11 @@ namespace z3 { return expr(ctx(), r); } + inline expr func_decl::operator()() const { + Z3_ast r = Z3_mk_app(ctx(), *this, 0, 0); + ctx().check_error(); + return expr(ctx(), r); + } inline expr func_decl::operator()(expr const & a) const { check_context(*this, a); Z3_ast args[1] = { a }; diff --git a/src/api/dotnet/Constructor.cs b/src/api/dotnet/Constructor.cs index 043eb3a1e..527b8bc13 100644 --- a/src/api/dotnet/Constructor.cs +++ b/src/api/dotnet/Constructor.cs @@ -34,8 +34,7 @@ namespace Microsoft.Z3 public uint NumFields { get - { - init(); + { return n; } } @@ -48,8 +47,11 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - init(); - return m_constructorDecl; + IntPtr constructor = IntPtr.Zero; + IntPtr tester = IntPtr.Zero; + IntPtr[] accessors = new IntPtr[n]; + Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); + return new FuncDecl(Context, constructor); } } @@ -61,8 +63,11 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - init(); - return m_testerDecl; + IntPtr constructor = IntPtr.Zero; + IntPtr tester = IntPtr.Zero; + IntPtr[] accessors = new IntPtr[n]; + Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); + return new FuncDecl(Context, tester); } } @@ -74,8 +79,14 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - init(); - return m_accessorDecls; + IntPtr constructor = IntPtr.Zero; + IntPtr tester = IntPtr.Zero; + IntPtr[] accessors = new IntPtr[n]; + Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); + FuncDecl[] t = new FuncDecl[n]; + for (uint i = 0; i < n; i++) + t[i] = new FuncDecl(Context, accessors[i]); + return t; } } @@ -85,25 +96,11 @@ namespace Microsoft.Z3 ~Constructor() { Native.Z3_del_constructor(Context.nCtx, NativeObject); - } - - #region Object invariant - - [ContractInvariantMethod] - private void ObjectInvariant() - { - Contract.Invariant(m_testerDecl == null || m_constructorDecl != null); - Contract.Invariant(m_testerDecl == null || m_accessorDecls != null); - } - - #endregion + } #region Internal - readonly private uint n = 0; - private FuncDecl m_testerDecl = null; - private FuncDecl m_constructorDecl = null; - private FuncDecl[] m_accessorDecls = null; - + private uint n = 0; + internal Constructor(Context ctx, Symbol name, Symbol recognizer, Symbol[] fieldNames, Sort[] sorts, uint[] sortRefs) : base(ctx) @@ -129,24 +126,6 @@ namespace Microsoft.Z3 } - private void init() - { - Contract.Ensures(m_constructorDecl != null); - Contract.Ensures(m_testerDecl != null); - Contract.Ensures(m_accessorDecls != null); - - if (m_testerDecl != null) return; - IntPtr constructor = IntPtr.Zero; - IntPtr tester = IntPtr.Zero; - IntPtr[] accessors = new IntPtr[n]; - Native.Z3_query_constructor(Context.nCtx, NativeObject, n, ref constructor, ref tester, accessors); - m_constructorDecl = new FuncDecl(Context, constructor); - m_testerDecl = new FuncDecl(Context, tester); - m_accessorDecls = new FuncDecl[n]; - for (uint i = 0; i < n; i++) - m_accessorDecls[i] = new FuncDecl(Context, accessors[i]); - } - #endregion } } diff --git a/src/api/dotnet/EnumSort.cs b/src/api/dotnet/EnumSort.cs index 1a9b0536c..e62043078 100644 --- a/src/api/dotnet/EnumSort.cs +++ b/src/api/dotnet/EnumSort.cs @@ -36,8 +36,11 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - - return _constdecls; + uint n = Native.Z3_get_datatype_sort_num_constructors(Context.nCtx, NativeObject); + FuncDecl[] t = new FuncDecl[n]; + for (uint i = 0; i < n; i++) + t[i] = new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, i)); + return t; } } @@ -49,8 +52,11 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - - return _consts; + FuncDecl[] cds = ConstDecls; + Expr[] t = new Expr[cds.Length]; + for (uint i = 0; i < t.Length; i++) + t[i] = Context.MkApp(cds[i]); + return t; } } @@ -62,28 +68,15 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - - return _testerdecls; + uint n = Native.Z3_get_datatype_sort_num_constructors(Context.nCtx, NativeObject); + FuncDecl[] t = new FuncDecl[n]; + for (uint i = 0; i < n; i++) + t[i] = new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, i)); + return t; } } - #region Object Invariant - - [ContractInvariantMethod] - private void ObjectInvariant() - { - Contract.Invariant(this._constdecls != null); - Contract.Invariant(this._testerdecls != null); - Contract.Invariant(this._consts != null); - } - - - #endregion - #region Internal - readonly private FuncDecl[] _constdecls = null, _testerdecls = null; - readonly private Expr[] _consts = null; - internal EnumSort(Context ctx, Symbol name, Symbol[] enumNames) : base(ctx) { @@ -96,15 +89,6 @@ namespace Microsoft.Z3 IntPtr[] n_testers = new IntPtr[n]; NativeObject = Native.Z3_mk_enumeration_sort(ctx.nCtx, name.NativeObject, (uint)n, Symbol.ArrayToNative(enumNames), n_constdecls, n_testers); - _constdecls = new FuncDecl[n]; - for (uint i = 0; i < n; i++) - _constdecls[i] = new FuncDecl(ctx, n_constdecls[i]); - _testerdecls = new FuncDecl[n]; - for (uint i = 0; i < n; i++) - _testerdecls[i] = new FuncDecl(ctx, n_testers[i]); - _consts = new Expr[n]; - for (uint i = 0; i < n; i++) - _consts[i] = ctx.MkApp(_constdecls[i]); } #endregion }; diff --git a/src/api/dotnet/Fixedpoint.cs b/src/api/dotnet/Fixedpoint.cs index 8d13fcb43..8c6e6c4c6 100644 --- a/src/api/dotnet/Fixedpoint.cs +++ b/src/api/dotnet/Fixedpoint.cs @@ -78,6 +78,14 @@ namespace Microsoft.Z3 } } + /// + /// Alias for Assert. + /// + public void Add(params BoolExpr[] constraints) + { + Assert(constraints); + } + /// /// Register predicate as recursive relation. /// diff --git a/src/api/dotnet/Goal.cs b/src/api/dotnet/Goal.cs index e648ac80c..b108683c9 100644 --- a/src/api/dotnet/Goal.cs +++ b/src/api/dotnet/Goal.cs @@ -90,6 +90,14 @@ namespace Microsoft.Z3 } } + /// + /// Alias for Assert. + /// + public void Add(params BoolExpr[] constraints) + { + Assert(constraints); + } + /// /// Indicates whether the goal contains `false'. /// diff --git a/src/api/dotnet/ListSort.cs b/src/api/dotnet/ListSort.cs index c099259a2..7dbafb385 100644 --- a/src/api/dotnet/ListSort.cs +++ b/src/api/dotnet/ListSort.cs @@ -36,7 +36,7 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - return nilDecl; + return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, 0)); } } @@ -48,7 +48,7 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - return nilConst; + return Context.MkApp(NilDecl); } } @@ -60,7 +60,7 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - return isNilDecl; + return new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, 0)); } } @@ -72,7 +72,7 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - return consDecl; + return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor(Context.nCtx, NativeObject, 1)); } } @@ -85,7 +85,7 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - return isConsDecl; + return new FuncDecl(Context, Native.Z3_get_datatype_sort_recognizer(Context.nCtx, NativeObject, 1)); } } @@ -97,7 +97,7 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - return headDecl; + return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor_accessor(Context.nCtx, NativeObject, 1, 0)); } } @@ -109,31 +109,11 @@ namespace Microsoft.Z3 get { Contract.Ensures(Contract.Result() != null); - return tailDecl; + return new FuncDecl(Context, Native.Z3_get_datatype_sort_constructor_accessor(Context.nCtx, NativeObject, 1, 1)); } } - #region Object Invariant - - [ContractInvariantMethod] - private void ObjectInvariant() - { - Contract.Invariant(nilConst != null); - Contract.Invariant(nilDecl != null); - Contract.Invariant(isNilDecl != null); - Contract.Invariant(consDecl != null); - Contract.Invariant(isConsDecl != null); - Contract.Invariant(headDecl != null); - Contract.Invariant(tailDecl != null); - } - - - #endregion - - #region Internal - readonly private FuncDecl nilDecl, isNilDecl, consDecl, isConsDecl, headDecl, tailDecl; - readonly private Expr nilConst; - + #region Internal internal ListSort(Context ctx, Symbol name, Sort elemSort) : base(ctx) { @@ -141,22 +121,12 @@ namespace Microsoft.Z3 Contract.Requires(name != null); Contract.Requires(elemSort != null); - IntPtr inil = IntPtr.Zero, - iisnil = IntPtr.Zero, - icons = IntPtr.Zero, - iiscons = IntPtr.Zero, - ihead = IntPtr.Zero, - itail = IntPtr.Zero; + IntPtr inil = IntPtr.Zero, iisnil = IntPtr.Zero, + icons = IntPtr.Zero, iiscons = IntPtr.Zero, + ihead = IntPtr.Zero, itail = IntPtr.Zero; NativeObject = Native.Z3_mk_list_sort(ctx.nCtx, name.NativeObject, elemSort.NativeObject, - ref inil, ref iisnil, ref icons, ref iiscons, ref ihead, ref itail); - nilDecl = new FuncDecl(ctx, inil); - isNilDecl = new FuncDecl(ctx, iisnil); - consDecl = new FuncDecl(ctx, icons); - isConsDecl = new FuncDecl(ctx, iiscons); - headDecl = new FuncDecl(ctx, ihead); - tailDecl = new FuncDecl(ctx, itail); - nilConst = ctx.MkConst(nilDecl); + ref inil, ref iisnil, ref icons, ref iiscons, ref ihead, ref itail); } #endregion }; diff --git a/src/api/dotnet/Solver.cs b/src/api/dotnet/Solver.cs index 8c07ef31e..274f8dc4a 100644 --- a/src/api/dotnet/Solver.cs +++ b/src/api/dotnet/Solver.cs @@ -117,6 +117,14 @@ namespace Microsoft.Z3 } } + /// + /// Alias for Assert. + /// + public void Add(params BoolExpr[] constraints) + { + Assert(constraints); + } + /// /// Assert multiple constraints into the solver, and track them (in the unsat) core /// using the Boolean constants in ps. diff --git a/src/api/java/Constructor.java b/src/api/java/Constructor.java index c12521bc5..0c53da73c 100644 --- a/src/api/java/Constructor.java +++ b/src/api/java/Constructor.java @@ -16,8 +16,7 @@ public class Constructor extends Z3Object * @throws Z3Exception **/ public int getNumFields() throws Z3Exception - { - init(); + { return n; } @@ -27,8 +26,11 @@ public class Constructor extends Z3Object **/ public FuncDecl ConstructorDecl() throws Z3Exception { - init(); - return m_constructorDecl; + Native.LongPtr constructor = new Native.LongPtr(); + Native.LongPtr tester = new Native.LongPtr(); + long[] accessors = new long[n]; + Native.queryConstructor(getContext().nCtx(), getNativeObject(), n, constructor, tester, accessors); + return new FuncDecl(getContext(), constructor.value); } /** @@ -37,8 +39,11 @@ public class Constructor extends Z3Object **/ public FuncDecl getTesterDecl() throws Z3Exception { - init(); - return m_testerDecl; + Native.LongPtr constructor = new Native.LongPtr(); + Native.LongPtr tester = new Native.LongPtr(); + long[] accessors = new long[n]; + Native.queryConstructor(getContext().nCtx(), getNativeObject(), n, constructor, tester, accessors); + return new FuncDecl(getContext(), tester.value); } /** @@ -47,8 +52,14 @@ public class Constructor extends Z3Object **/ public FuncDecl[] getAccessorDecls() throws Z3Exception { - init(); - return m_accessorDecls; + Native.LongPtr constructor = new Native.LongPtr(); + Native.LongPtr tester = new Native.LongPtr(); + long[] accessors = new long[n]; + Native.queryConstructor(getContext().nCtx(), getNativeObject(), n, constructor, tester, accessors); + FuncDecl[] t = new FuncDecl[n]; + for (int i = 0; i < n; i++) + t[i] = new FuncDecl(getContext(), accessors[i]); + return t; } /** @@ -60,9 +71,6 @@ public class Constructor extends Z3Object } private int n = 0; - private FuncDecl m_testerDecl = null; - private FuncDecl m_constructorDecl = null; - private FuncDecl[] m_accessorDecls = null; Constructor(Context ctx, Symbol name, Symbol recognizer, Symbol[] fieldNames, Sort[] sorts, int[] sortRefs) @@ -87,21 +95,4 @@ public class Constructor extends Z3Object Sort.arrayToNative(sorts), sortRefs)); } - - private void init() throws Z3Exception - { - if (m_testerDecl != null) - return; - Native.LongPtr constructor = new Native.LongPtr(); - Native.LongPtr tester = new Native.LongPtr(); - long[] accessors = new long[n]; - Native.queryConstructor(getContext().nCtx(), getNativeObject(), n, - constructor, tester, accessors); - m_constructorDecl = new FuncDecl(getContext(), constructor.value); - m_testerDecl = new FuncDecl(getContext(), tester.value); - m_accessorDecls = new FuncDecl[n]; - for (int i = 0; i < n; i++) - m_accessorDecls[i] = new FuncDecl(getContext(), accessors[i]); - } - } diff --git a/src/api/java/EnumSort.java b/src/api/java/EnumSort.java index f3cbda954..c0fb6d1d6 100644 --- a/src/api/java/EnumSort.java +++ b/src/api/java/EnumSort.java @@ -14,30 +14,39 @@ public class EnumSort extends Sort /** * The function declarations of the constants in the enumeration. **/ - public FuncDecl[] getConstDecls() + public FuncDecl[] getConstDecls() throws Z3Exception { - return _constdecls; + int n = Native.getDatatypeSortNumConstructors(getContext().nCtx(), getNativeObject()); + FuncDecl[] t = new FuncDecl[n]; + for (int i = 0; i < n; i++) + t[i] = new FuncDecl(getContext(), Native.getDatatypeSortConstructor(getContext().nCtx(), getNativeObject(), i)); + return t; } /** * The constants in the enumeration. **/ - public Expr[] getConsts() - { - return _consts; + public Expr[] getConsts() throws Z3Exception + { + FuncDecl[] cds = getConstDecls(); + Expr[] t = new Expr[cds.length]; + for (int i = 0; i < t.length; i++) + t[i] = getContext().mkApp(cds[i]); + return t; } /** * The test predicates for the constants in the enumeration. **/ - public FuncDecl[] getTesterDecls() + public FuncDecl[] getTesterDecls() throws Z3Exception { - return _testerdecls; + int n = Native.getDatatypeSortNumConstructors(getContext().nCtx(), getNativeObject()); + FuncDecl[] t = new FuncDecl[n]; + for (int i = 0; i < n; i++) + t[i] = new FuncDecl(getContext(), Native.getDatatypeSortRecognizer(getContext().nCtx(), getNativeObject(), i)); + return t; } - private FuncDecl[] _constdecls = null, _testerdecls = null; - private Expr[] _consts = null; - EnumSort(Context ctx, Symbol name, Symbol[] enumNames) throws Z3Exception { super(ctx); @@ -47,15 +56,6 @@ public class EnumSort extends Sort long[] n_testers = new long[n]; setNativeObject(Native.mkEnumerationSort(ctx.nCtx(), name.getNativeObject(), (int) n, Symbol.arrayToNative(enumNames), - n_constdecls, n_testers)); - _constdecls = new FuncDecl[n]; - for (int i = 0; i < n; i++) - _constdecls[i] = new FuncDecl(ctx, n_constdecls[i]); - _testerdecls = new FuncDecl[n]; - for (int i = 0; i < n; i++) - _testerdecls[i] = new FuncDecl(ctx, n_testers[i]); - _consts = new Expr[n]; - for (int i = 0; i < n; i++) - _consts[i] = ctx.mkApp(_constdecls[i], (Expr[])null); + n_constdecls, n_testers)); } }; diff --git a/src/api/java/Fixedpoint.java b/src/api/java/Fixedpoint.java index 6a8cafae2..4710368aa 100644 --- a/src/api/java/Fixedpoint.java +++ b/src/api/java/Fixedpoint.java @@ -51,7 +51,7 @@ public class Fixedpoint extends Z3Object * * @throws Z3Exception **/ - public void assert_(BoolExpr ... constraints) throws Z3Exception + public void add(BoolExpr ... constraints) throws Z3Exception { getContext().checkContextMatch(constraints); for (BoolExpr a : constraints) diff --git a/src/api/java/Goal.java b/src/api/java/Goal.java index 520a7af15..c486a57b3 100644 --- a/src/api/java/Goal.java +++ b/src/api/java/Goal.java @@ -65,7 +65,7 @@ public class Goal extends Z3Object * * @throws Z3Exception **/ - public void assert_(BoolExpr ... constraints) throws Z3Exception + public void add(BoolExpr ... constraints) throws Z3Exception { getContext().checkContextMatch(constraints); for (BoolExpr c : constraints) diff --git a/src/api/java/ListSort.java b/src/api/java/ListSort.java index df1c51a95..af1645187 100644 --- a/src/api/java/ListSort.java +++ b/src/api/java/ListSort.java @@ -13,65 +13,68 @@ public class ListSort extends Sort { /** * The declaration of the nil function of this list sort. + * @throws Z3Exception **/ - public FuncDecl getNilDecl() + public FuncDecl getNilDecl() throws Z3Exception { - return nilDecl; + return new FuncDecl(getContext(), Native.getDatatypeSortConstructor(getContext().nCtx(), getNativeObject(), 0)); } /** * The empty list. + * @throws Z3Exception **/ - public Expr getNil() + public Expr getNil() throws Z3Exception { - return nilConst; + return getContext().mkApp(getNilDecl()); } /** * The declaration of the isNil function of this list sort. + * @throws Z3Exception **/ - public FuncDecl getIsNilDecl() + public FuncDecl getIsNilDecl() throws Z3Exception { - return isNilDecl; + return new FuncDecl(getContext(), Native.getDatatypeSortRecognizer(getContext().nCtx(), getNativeObject(), 0)); } /** * The declaration of the cons function of this list sort. + * @throws Z3Exception **/ - public FuncDecl getConsDecl() + public FuncDecl getConsDecl() throws Z3Exception { - return consDecl; + return new FuncDecl(getContext(), Native.getDatatypeSortConstructor(getContext().nCtx(), getNativeObject(), 1)); } /** * The declaration of the isCons function of this list sort. + * @throws Z3Exception * **/ - public FuncDecl getIsConsDecl() + public FuncDecl getIsConsDecl() throws Z3Exception { - return isConsDecl; + return new FuncDecl(getContext(), Native.getDatatypeSortRecognizer(getContext().nCtx(), getNativeObject(), 1)); } /** * The declaration of the head function of this list sort. + * @throws Z3Exception **/ - public FuncDecl getHeadDecl() + public FuncDecl getHeadDecl() throws Z3Exception { - return headDecl; + return new FuncDecl(getContext(), Native.getDatatypeSortConstructorAccessor(getContext().nCtx(), getNativeObject(), 1, 0)); } /** * The declaration of the tail function of this list sort. + * @throws Z3Exception **/ - public FuncDecl getTailDecl() + public FuncDecl getTailDecl() throws Z3Exception { - return tailDecl; + return new FuncDecl(getContext(), Native.getDatatypeSortConstructorAccessor(getContext().nCtx(), getNativeObject(), 1, 1)); } - private FuncDecl nilDecl, isNilDecl, consDecl, isConsDecl, headDecl, - tailDecl; - private Expr nilConst; - ListSort(Context ctx, Symbol name, Sort elemSort) throws Z3Exception { super(ctx); @@ -83,12 +86,5 @@ public class ListSort extends Sort setNativeObject(Native.mkListSort(ctx.nCtx(), name.getNativeObject(), elemSort.getNativeObject(), inil, iisnil, icons, iiscons, ihead, itail)); - nilDecl = new FuncDecl(ctx, inil.value); - isNilDecl = new FuncDecl(ctx, iisnil.value); - consDecl = new FuncDecl(ctx, icons.value); - isConsDecl = new FuncDecl(ctx, iiscons.value); - headDecl = new FuncDecl(ctx, ihead.value); - tailDecl = new FuncDecl(ctx, itail.value); - nilConst = ctx.mkConst(nilDecl); } }; diff --git a/src/api/java/Solver.java b/src/api/java/Solver.java index c60d3f88c..3827de07a 100644 --- a/src/api/java/Solver.java +++ b/src/api/java/Solver.java @@ -94,7 +94,7 @@ public class Solver extends Z3Object * * @throws Z3Exception **/ - public void assert_(BoolExpr... constraints) throws Z3Exception + public void add(BoolExpr... constraints) throws Z3Exception { getContext().checkContextMatch(constraints); for (BoolExpr a : constraints) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index ea3b05e40..6d59cf650 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -701,7 +701,7 @@ typedef enum over Boolean connectives 'and' and 'or'. - - Z3_OP_PR_NFF_NEG: Proof for a (negative) NNF step. Examples: + - Z3_OP_PR_NNF_NEG: Proof for a (negative) NNF step. Examples: \nicebox{ T1: (not s_1) ~ r_1 ... diff --git a/src/ast/ast_list.h b/src/ast/ast_list.h deleted file mode 100644 index c02b2d66d..000000000 --- a/src/ast/ast_list.h +++ /dev/null @@ -1,160 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - ast_list.h - -Abstract: - - Quick hack to have lists of ASTs. - The lists have hash-consing. - This is a substitute for the old expr_lists implemented on top of the ASTs. - The new lists live in a different manager and do not affect the ast_manager. - -Author: - - Leonardo de Moura (leonardo) 2011-01-06. - -Revision History: - ---*/ -#ifndef _AST_LIST_H_ -#define _AST_LIST_H_ - -#include"ast.h" - -template -class ast_list_manager_tpl; - -template -class ast_list_tpl { -public: - typedef ast_list_tpl list; -private: - unsigned m_id; - unsigned m_is_nil:1; - unsigned m_ref_count:31; - AST * m_head; - list * m_tail; - - ast_list_tpl(): - m_is_nil(true), - m_ref_count(0), - m_head(0), - m_tail(0) { - } - - ast_list_tpl(AST * h, list * t): - m_is_nil(false), - m_ref_count(0), - m_head(h), - m_tail(t) { - } - - friend class ast_list_manager_tpl; - - struct hash_proc; - friend struct hash_proc; - - struct hash_proc { - unsigned operator()(ast_list_tpl * l) const { - unsigned h1 = l->m_head ? l->m_head->get_id() : 5; - unsigned h2 = l->m_tail ? l->m_tail->get_id() : 7; - return hash_u_u(h1, h2); - } - }; - - struct eq_proc; - friend struct eq_proc; - - struct eq_proc { - bool operator()(ast_list_tpl * l1, ast_list_tpl * l2) const { - return l1->m_head == l2->m_head && l1->m_tail == l2->m_tail; - } - }; - - typedef ptr_hashtable table; // for hash consing - -public: - unsigned get_id() const { return m_id; } - unsigned get_ref_count() const { return m_ref_count; } - unsigned hash() const { return m_id; } - - friend inline bool is_nil(list const * l) { return l->m_is_nil == true; } - friend inline bool is_cons(list const * l) { return !is_nil(l); } - friend inline AST * head(list const * l) { SASSERT(is_cons(l)); return l->m_head; } - friend inline list * tail(list const * l) { SASSERT(is_cons(l)); return l->m_tail; } -}; - -template -class ast_list_manager_tpl { -public: - typedef ast_list_tpl list; - typedef obj_hashtable list_set; -private: - typedef typename list::table table; - ast_manager & m_manager; - small_object_allocator m_allocator; - table m_table; - id_gen m_id_gen; - list m_nil; - -public: - ast_list_manager_tpl(ast_manager & m): - m_manager(m), - m_nil() { - m_nil.m_id = m_id_gen.mk(); - m_nil.m_ref_count = 1; - } - - void inc_ref(list * l) { - if (l != 0) { - l->m_ref_count++; - } - } - - void dec_ref(list * l) { - while (l != 0) { - SASSERT(l->m_ref_count > 0); - l->m_ref_count--; - if (l->m_ref_count > 0) - return; - SASSERT(l != &m_nil); - m_table.erase(l); - m_manager.dec_ref(l->m_head); - m_id_gen.recycle(l->m_id); - list * old_l = l; - l = l->m_tail; - m_allocator.deallocate(sizeof(list), old_l); - } - } - - list * mk_nil() { return &m_nil; } - - list * mk_cons(AST * h, list * l) { - list tmp(h, l); - list * r = 0; - if (m_table.find(&tmp, r)) - return r; - r = new (m_allocator.allocate(sizeof(list))) list(h, l); - m_manager.inc_ref(h); - inc_ref(l); - r->m_id = m_id_gen.mk(); - m_table.insert(r); - return r; - } -}; - -typedef ast_list_tpl expr_list; -typedef ast_list_manager_tpl expr_list_manager; - -typedef ast_list_tpl quantifier_list; -typedef ast_list_manager_tpl quantifier_list_manager; - -typedef obj_ref expr_list_ref; -typedef obj_ref quantifier_list_ref; -typedef ref_vector expr_list_ref_vector; -typedef ref_vector quantifier_list_ref_vector; - -#endif diff --git a/src/ast/ast_lt.cpp b/src/ast/ast_lt.cpp index 1bf153f4f..2fba4f7a6 100644 --- a/src/ast/ast_lt.cpp +++ b/src/ast/ast_lt.cpp @@ -134,6 +134,16 @@ bool lt(ast * n1, ast * n2) { } } +bool is_sorted(unsigned num, expr * const * ns) { + for (unsigned i = 1; i < num; i++) { + ast * prev = ns[i-1]; + ast * curr = ns[i]; + if (lt(curr, prev)) + return false; + } + return true; +} + bool lex_lt(unsigned num, ast * const * n1, ast * const * n2) { for (unsigned i = 0; i < num; i ++) { if (n1[i] == n2[i]) diff --git a/src/ast/ast_lt.h b/src/ast/ast_lt.h index 5eb268997..b405ab229 100644 --- a/src/ast/ast_lt.h +++ b/src/ast/ast_lt.h @@ -22,6 +22,7 @@ Revision History: class ast; bool lt(ast * n1, ast * n2); +bool is_sorted(unsigned num, expr * const * ns); struct ast_to_lt { bool operator()(ast * n1, ast * n2) const { return lt(n1, n2); } diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index 0ef3b60d6..8b77244f9 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -766,7 +766,7 @@ bool bv_recognizers::is_zero(expr const * n) const { return decl->get_parameter(0).get_rational().is_zero(); } -bool bv_recognizers::is_extract(expr const* e, unsigned& low, unsigned& high, expr*& b) { +bool bv_recognizers::is_extract(expr const* e, unsigned& low, unsigned& high, expr*& b) const { if (!is_extract(e)) return false; low = get_extract_low(e); high = get_extract_high(e); @@ -774,7 +774,7 @@ bool bv_recognizers::is_extract(expr const* e, unsigned& low, unsigned& high, ex return true; } -bool bv_recognizers::is_bv2int(expr const* e, expr*& r) { +bool bv_recognizers::is_bv2int(expr const* e, expr*& r) const { if (!is_bv2int(e)) return false; r = to_app(e)->get_arg(0); return true; diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index 8ea90f844..c5ebfb2d9 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -288,10 +288,10 @@ public: bool is_extract(expr const * e) const { return is_app_of(e, get_fid(), OP_EXTRACT); } unsigned get_extract_high(func_decl const * f) const { return f->get_parameter(0).get_int(); } unsigned get_extract_low(func_decl const * f) const { return f->get_parameter(1).get_int(); } - unsigned get_extract_high(expr const * n) { SASSERT(is_extract(n)); return get_extract_high(to_app(n)->get_decl()); } - unsigned get_extract_low(expr const * n) { SASSERT(is_extract(n)); return get_extract_low(to_app(n)->get_decl()); } - bool is_extract(expr const * e, unsigned & low, unsigned & high, expr * & b); - bool is_bv2int(expr const * e, expr * & r); + unsigned get_extract_high(expr const * n) const { SASSERT(is_extract(n)); return get_extract_high(to_app(n)->get_decl()); } + unsigned get_extract_low(expr const * n) const { SASSERT(is_extract(n)); return get_extract_low(to_app(n)->get_decl()); } + bool is_extract(expr const * e, unsigned & low, unsigned & high, expr * & b) const; + bool is_bv2int(expr const * e, expr * & r) const; bool is_bv_add(expr const * e) const { return is_app_of(e, get_fid(), OP_BADD); } bool is_bv_sub(expr const * e) const { return is_app_of(e, get_fid(), OP_BSUB); } bool is_bv_mul(expr const * e) const { return is_app_of(e, get_fid(), OP_BMUL); } diff --git a/src/ast/float_decl_plugin.cpp b/src/ast/float_decl_plugin.cpp index 7f6d7f764..26131bc28 100644 --- a/src/ast/float_decl_plugin.cpp +++ b/src/ast/float_decl_plugin.cpp @@ -151,6 +151,10 @@ sort * float_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) { m_manager->raise_exception("expecting two integer parameters to floating point sort"); } + if (parameters[0].get_int() <= 1 || parameters[1].get_int() <= 1) + m_manager->raise_exception("floating point sorts need parameters > 1"); + if (parameters[0].get_int() > parameters[1].get_int()) + m_manager->raise_exception("floating point sorts with ebits > sbits are currently not supported"); return mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); case ROUNDING_MODE_SORT: return mk_rm_sort(); @@ -349,27 +353,22 @@ func_decl * float_decl_plugin::mk_to_float(decl_kind k, unsigned num_parameters, sort * fp = mk_float_sort(domain[2]->get_parameter(0).get_int(), domain[1]->get_parameter(0).get_int()+1); symbol name("asFloat"); return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); - } + } else { // .. Otherwise we only know how to convert rationals/reals. if (!(num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int())) m_manager->raise_exception("expecting two integer parameters to asFloat"); if (arity != 2 && arity != 3) - m_manager->raise_exception("invalid number of arguments to asFloat operator"); - if (!is_rm_sort(domain[0]) || domain[1] != m_real_sort) + m_manager->raise_exception("invalid number of arguments to asFloat operator"); + if (arity == 3 && domain[2] != m_int_sort) + m_manager->raise_exception("sort mismatch"); + if (!is_rm_sort(domain[0]) || + !(domain[1] == m_real_sort || is_sort_of(domain[1], m_family_id, FLOAT_SORT))) m_manager->raise_exception("sort mismatch"); - if (arity == 2) { - sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); - symbol name("asFloat"); - return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); - } - else { - if (domain[2] != m_int_sort) - m_manager->raise_exception("sort mismatch"); - sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); - symbol name("asFloat"); - return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); - } + + sort * fp = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); + symbol name("asFloat"); + return m_manager->mk_func_decl(name, arity, domain, fp, func_decl_info(m_family_id, k, num_parameters, parameters)); } } @@ -492,6 +491,15 @@ void float_decl_plugin::get_sort_names(svector & sort_names, symbo sort_names.push_back(builtin_name("RoundingMode", ROUNDING_MODE_SORT)); } +expr * float_decl_plugin::get_some_value(sort * s) { + SASSERT(s->is_sort_of(m_family_id, FLOAT_SORT)); + mpf tmp; + m_fm.mk_nan(s->get_parameter(0).get_int(), s->get_parameter(1).get_int(), tmp); + expr * res = this->mk_value(tmp); + m_fm.del(tmp); + return res; +} + bool float_decl_plugin::is_value(app * e) const { if (e->get_family_id() != m_family_id) return false; diff --git a/src/ast/float_decl_plugin.h b/src/ast/float_decl_plugin.h index 6f1ef5ec2..4ec17addf 100644 --- a/src/ast/float_decl_plugin.h +++ b/src/ast/float_decl_plugin.h @@ -140,6 +140,7 @@ public: unsigned arity, sort * const * domain, sort * range); virtual void get_op_names(svector & op_names, symbol const & logic); virtual void get_sort_names(svector & sort_names, symbol const & logic); + virtual expr * get_some_value(sort * s); virtual bool is_value(app* e) const; virtual bool is_unique_value(app* e) const { return is_value(e); } diff --git a/src/ast/macros/macro_util.cpp b/src/ast/macros/macro_util.cpp index cc710a82e..de55de632 100644 --- a/src/ast/macros/macro_util.cpp +++ b/src/ast/macros/macro_util.cpp @@ -596,8 +596,9 @@ void hint_to_macro_head(ast_manager & m, app * head, unsigned num_decls, app_ref is_hint_head(head, vars) must also return true */ bool macro_util::is_poly_hint(expr * n, app * head, expr * exception) { - TRACE("macro_util_hint", tout << "is_poly_hint n:\n" << mk_pp(n, m_manager) << "\nhead:\n" << mk_pp(head, m_manager) << "\nexception:\n" - << mk_pp(exception, m_manager) << "\n";); + TRACE("macro_util_hint", tout << "is_poly_hint n:\n" << mk_pp(n, m_manager) << "\nhead:\n" << mk_pp(head, m_manager) << "\nexception:\n"; + if (exception) tout << mk_pp(exception, m_manager); else tout << ""; + tout << "\n";); ptr_buffer vars; if (!is_hint_head(head, vars)) { TRACE("macro_util_hint", tout << "failed because head is not hint head\n";); @@ -791,7 +792,10 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest); expr_ref def(m_manager); mk_sub(rhs, rest, def); - add_arith_macro_candidate(to_app(arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); + // If is_poly_hint, rhs may contain variables that do not occur in to_app(arg). + // So, we should re-check. + if (!_is_poly_hint || is_poly_hint(def, to_app(arg), 0)) + add_arith_macro_candidate(to_app(arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); } else if (is_times_minus_one(arg, neg_arg) && is_app(neg_arg)) { f = to_app(neg_arg)->get_decl(); @@ -809,7 +813,10 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a mk_add(args.size(), args.c_ptr(), m_manager.get_sort(arg), rest); expr_ref def(m_manager); mk_sub(rest, rhs, def); - add_arith_macro_candidate(to_app(neg_arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); + // If is_poly_hint, rhs may contain variables that do not occur in to_app(neg_arg). + // So, we should re-check. + if (!_is_poly_hint || is_poly_hint(def, to_app(neg_arg), 0)) + add_arith_macro_candidate(to_app(neg_arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); } } } diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 91d87cbfb..0856ab5b8 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -981,14 +981,6 @@ br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) { result = m_util.mk_numeral(a, false); return BR_DONE; } -#if 0 - // The following rewriting rule is not correct. - // It is used only for making experiments. - if (m_util.is_to_int(arg)) { - result = to_app(arg)->get_arg(0); - return BR_DONE; - } -#endif // push to_real over OP_ADD, OP_MUL if (m_push_to_real) { if (m_util.is_add(arg) || m_util.is_mul(arg)) { diff --git a/src/ast/rewriter/ast_counter.cpp b/src/ast/rewriter/ast_counter.cpp new file mode 100644 index 000000000..c542abb60 --- /dev/null +++ b/src/ast/rewriter/ast_counter.cpp @@ -0,0 +1,165 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + ast_counter.cpp + +Abstract: + + Routines for counting features of terms, such as free variables. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-18. + +Revision History: + +--*/ + +#include "ast_counter.h" +#include "var_subst.h" + +void counter::update(unsigned el, int delta) { + int & counter = get(el); + SASSERT(!m_stay_non_negative || counter>=0); + SASSERT(!m_stay_non_negative || static_cast(counter)>=-delta); + counter += delta; +} + +int & counter::get(unsigned el) { + return m_data.insert_if_not_there2(el, 0)->get_data().m_value; +} + +counter & counter::count(unsigned sz, const unsigned * els, int delta) { + for(unsigned i=0; im_value>0 ) { + cnt++; + } + } + return cnt; +} + +void counter::collect_positive(uint_set & acc) const { + iterator eit = begin(); + iterator eend = end(); + for(; eit!=eend; ++eit) { + if(eit->m_value>0) { acc.insert(eit->m_key); } + } +} + +bool counter::get_max_positive(unsigned & res) const { + bool found = false; + iterator eit = begin(); + iterator eend = end(); + for(; eit!=eend; ++eit) { + if( eit->m_value>0 && (!found || eit->m_key>res) ) { + found = true; + res = eit->m_key; + } + } + return found; +} + +unsigned counter::get_max_positive() const { + unsigned max_pos; + VERIFY(get_max_positive(max_pos)); + return max_pos; +} + +int counter::get_max_counter_value() const { + int res = 0; + iterator eit = begin(); + iterator eend = end(); + for (; eit!=eend; ++eit) { + if( eit->m_value>res ) { + res = eit->m_value; + } + } + return res; +} + +void var_counter::count_vars(ast_manager & m, const app * pred, int coef) { + unsigned n = pred->get_num_args(); + for (unsigned i = 0; i < n; i++) { + m_sorts.reset(); + ::get_free_vars(pred->get_arg(i), m_sorts); + for (unsigned j = 0; j < m_sorts.size(); ++j) { + if (m_sorts[j]) { + update(j, coef); + } + } + } +} + + +unsigned var_counter::get_max_var(bool& has_var) { + has_var = false; + unsigned max_var = 0; + while (!m_todo.empty()) { + expr* e = m_todo.back(); + unsigned scope = m_scopes.back(); + m_todo.pop_back(); + m_scopes.pop_back(); + if (m_visited.is_marked(e)) { + continue; + } + m_visited.mark(e, true); + switch(e->get_kind()) { + case AST_QUANTIFIER: { + quantifier* q = to_quantifier(e); + m_todo.push_back(q->get_expr()); + m_scopes.push_back(scope + q->get_num_decls()); + break; + } + case AST_VAR: { + if (to_var(e)->get_idx() >= scope + max_var) { + has_var = true; + max_var = to_var(e)->get_idx() - scope; + } + break; + } + case AST_APP: { + app* a = to_app(e); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + m_todo.push_back(a->get_arg(i)); + m_scopes.push_back(scope); + } + break; + } + default: + UNREACHABLE(); + break; + } + } + m_visited.reset(); + return max_var; +} + + +unsigned var_counter::get_max_var(expr* e) { + bool has_var = false; + m_todo.push_back(e); + m_scopes.push_back(0); + return get_max_var(has_var); +} + +unsigned var_counter::get_next_var(expr* e) { + bool has_var = false; + m_todo.push_back(e); + m_scopes.push_back(0); + unsigned mv = get_max_var(has_var); + if (has_var) mv++; + return mv; +} + diff --git a/src/ast/rewriter/ast_counter.h b/src/ast/rewriter/ast_counter.h new file mode 100644 index 000000000..2a581c302 --- /dev/null +++ b/src/ast/rewriter/ast_counter.h @@ -0,0 +1,107 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + ast_counter.h + +Abstract: + + Routines for counting features of terms, such as free variables. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-18. + Krystof Hoder (t-khoder) 2010-10-10. + +Revision History: + + Hoisted from dl_util.h 2013-03-18. + +--*/ + + +#ifndef _AST_COUNTER_H_ +#define _AST_COUNTER_H_ + +#include "ast.h" +#include "map.h" +#include "uint_set.h" + +class counter { +protected: + typedef u_map map_impl; + map_impl m_data; + const bool m_stay_non_negative; +public: + typedef map_impl::iterator iterator; + + counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} + + iterator begin() const { return m_data.begin(); } + iterator end() const { return m_data.end(); } + void update(unsigned el, int delta); + int & get(unsigned el); + + /** + \brief Increase values of elements in \c els by \c delta. + + The function returns a reference to \c *this to allow for expressions like + counter().count(sz, arr).get_positive_count() + */ + counter & count(unsigned sz, const unsigned * els, int delta = 1); + counter & count(const unsigned_vector & els, int delta = 1) { + return count(els.size(), els.c_ptr(), delta); + } + + void collect_positive(uint_set & acc) const; + unsigned get_positive_count() const; + + bool get_max_positive(unsigned & res) const; + unsigned get_max_positive() const; + + /** + Since the default counter value of a counter is zero, the result is never negative. + */ + int get_max_counter_value() const; +}; + +class var_counter : public counter { +protected: + ptr_vector m_sorts; + expr_fast_mark1 m_visited; + ptr_vector m_todo; + unsigned_vector m_scopes; + unsigned get_max_var(bool & has_var); +public: + var_counter(bool stay_non_negative = true): counter(stay_non_negative) {} + void count_vars(ast_manager & m, const app * t, int coef = 1); + unsigned get_max_var(expr* e); + unsigned get_next_var(expr* e); +}; + +class ast_counter { + typedef obj_map map_impl; + map_impl m_data; + bool m_stay_non_negative; + public: + typedef map_impl::iterator iterator; + + ast_counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} + + iterator begin() const { return m_data.begin(); } + iterator end() const { return m_data.end(); } + + int & get(ast * el) { + return m_data.insert_if_not_there2(el, 0)->get_data().m_value; + } + void update(ast * el, int delta){ + get(el) += delta; + SASSERT(!m_stay_non_negative || get(el) >= 0); + } + + void inc(ast * el) { update(el, 1); } + void dec(ast * el) { update(el, -1); } +}; + +#endif diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index 44efe0199..11b09003b 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -64,6 +64,7 @@ void bv_rewriter::updt_local_params(params_ref const & _p) { m_split_concat_eq = p.split_concat_eq(); m_udiv2mul = p.udiv2mul(); m_bvnot2arith = p.bvnot2arith(); + m_bv_sort_ac = p.bv_sort_ac(); m_mkbv2num = _p.get_bool("mkbv2num", false); } @@ -1315,7 +1316,7 @@ br_status bv_rewriter::mk_bv_or(unsigned num, expr * const * args, expr_ref & re return BR_REWRITE2; } - if (!flattened && !merged && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero()))) { + if (!flattened && !merged && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero())) && (!m_bv_sort_ac || is_sorted(num, args))) { return BR_FAILED; } @@ -1331,6 +1332,8 @@ br_status bv_rewriter::mk_bv_or(unsigned num, expr * const * args, expr_ref & re result = new_args[0]; return BR_DONE; default: + if (m_bv_sort_ac) + std::sort(new_args.begin(), new_args.end(), ast_to_lt()); result = m_util.mk_bv_or(new_args.size(), new_args.c_ptr()); return BR_DONE; } @@ -1456,7 +1459,8 @@ br_status bv_rewriter::mk_bv_xor(unsigned num, expr * const * args, expr_ref & r return BR_REWRITE3; } - if (!merged && !flattened && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero() && v1 != (rational::power_of_two(sz) - numeral(1))))) + if (!merged && !flattened && (num_coeffs == 0 || (num_coeffs == 1 && !v1.is_zero() && v1 != (rational::power_of_two(sz) - numeral(1)))) && + (!m_bv_sort_ac || is_sorted(num, args))) return BR_FAILED; ptr_buffer new_args; @@ -1497,6 +1501,8 @@ br_status bv_rewriter::mk_bv_xor(unsigned num, expr * const * args, expr_ref & r } __fallthrough; default: + if (m_bv_sort_ac) + std::sort(new_args.begin(), new_args.end(), ast_to_lt()); result = m_util.mk_bv_xor(new_args.size(), new_args.c_ptr()); return BR_DONE; } diff --git a/src/ast/rewriter/bv_rewriter.h b/src/ast/rewriter/bv_rewriter.h index a94fd18c1..6d8c21666 100644 --- a/src/ast/rewriter/bv_rewriter.h +++ b/src/ast/rewriter/bv_rewriter.h @@ -68,6 +68,7 @@ class bv_rewriter : public poly_rewriter { bool m_split_concat_eq; bool m_udiv2mul; bool m_bvnot2arith; + bool m_bv_sort_ac; bool is_zero_bit(expr * x, unsigned idx); diff --git a/src/ast/rewriter/bv_rewriter_params.pyg b/src/ast/rewriter/bv_rewriter_params.pyg index d9ee9f7a3..5feece753 100644 --- a/src/ast/rewriter/bv_rewriter_params.pyg +++ b/src/ast/rewriter/bv_rewriter_params.pyg @@ -8,5 +8,6 @@ def_module_params(module_name='rewriter', ("elim_sign_ext", BOOL, True, "expand sign-ext operator using concat and extract"), ("hi_div0", BOOL, True, "use the 'hardware interpretation' for division by zero (for bit-vector terms)"), ("mul2concat", BOOL, False, "replace multiplication by a power of two into a concatenation"), - ("bvnot2arith", BOOL, False, "replace (bvnot x) with (bvsub -1 x)") + ("bvnot2arith", BOOL, False, "replace (bvnot x) with (bvsub -1 x)"), + ("bv_sort_ac", BOOL, False, "sort the arguments of all AC operators") )) diff --git a/src/muz_qe/expr_safe_replace.cpp b/src/ast/rewriter/expr_safe_replace.cpp similarity index 96% rename from src/muz_qe/expr_safe_replace.cpp rename to src/ast/rewriter/expr_safe_replace.cpp index b3b4d5138..1ec810414 100644 --- a/src/muz_qe/expr_safe_replace.cpp +++ b/src/ast/rewriter/expr_safe_replace.cpp @@ -100,3 +100,9 @@ void expr_safe_replace::operator()(expr* e, expr_ref& res) { } res = cache.find(e); } + +void expr_safe_replace::reset() { + m_src.reset(); + m_dst.reset(); + m_subst.reset(); +} diff --git a/src/muz_qe/expr_safe_replace.h b/src/ast/rewriter/expr_safe_replace.h similarity index 97% rename from src/muz_qe/expr_safe_replace.h rename to src/ast/rewriter/expr_safe_replace.h index 6af819596..b6131906a 100644 --- a/src/muz_qe/expr_safe_replace.h +++ b/src/ast/rewriter/expr_safe_replace.h @@ -38,6 +38,8 @@ public: void operator()(expr_ref& e) { (*this)(e.get(), e); } void operator()(expr* src, expr_ref& e); + + void reset(); }; #endif /* __EXPR_SAFE_REPLACE_H__ */ diff --git a/src/ast/rewriter/float_rewriter.cpp b/src/ast/rewriter/float_rewriter.cpp index 482e280e3..70ba09581 100644 --- a/src/ast/rewriter/float_rewriter.cpp +++ b/src/ast/rewriter/float_rewriter.cpp @@ -77,14 +77,23 @@ br_status float_rewriter::mk_to_float(func_decl * f, unsigned num_args, expr * c return BR_FAILED; rational q; - if (!m_util.au().is_numeral(args[1], q)) + mpf q_mpf; + if (m_util.au().is_numeral(args[1], q)) { + mpf v; + m_util.fm().set(v, ebits, sbits, rm, q.to_mpq()); + result = m_util.mk_value(v); + m_util.fm().del(v); + return BR_DONE; + } + else if (m_util.is_value(args[1], q_mpf)) { + mpf v; + m_util.fm().set(v, ebits, sbits, rm, q_mpf); + result = m_util.mk_value(v); + m_util.fm().del(v); + return BR_DONE; + } + else return BR_FAILED; - - mpf v; - m_util.fm().set(v, ebits, sbits, rm, q.to_mpq()); - result = m_util.mk_value(v); - m_util.fm().del(v); - return BR_DONE; } else if (num_args == 3 && m_util.is_rm(m().get_sort(args[0])) && @@ -217,8 +226,7 @@ br_status float_rewriter::mk_abs(expr * arg1, expr_ref & result) { result = arg1; return BR_DONE; } - sort * s = m().get_sort(arg1); - result = m().mk_ite(m_util.mk_lt(arg1, m_util.mk_pzero(s)), + result = m().mk_ite(m_util.mk_is_sign_minus(arg1), m_util.mk_uminus(arg1), arg1); return BR_REWRITE2; diff --git a/src/ast/rewriter/quant_hoist.cpp b/src/ast/rewriter/quant_hoist.cpp index b64e71866..dd9062828 100644 --- a/src/ast/rewriter/quant_hoist.cpp +++ b/src/ast/rewriter/quant_hoist.cpp @@ -25,7 +25,8 @@ Revision History: #include "bool_rewriter.h" #include "var_subst.h" #include "ast_pp.h" - +#include "ast_counter.h" +#include "expr_safe_replace.h" // // Bring quantifiers of common type into prenex form. @@ -42,7 +43,7 @@ public: void operator()(expr* fml, app_ref_vector& vars, bool& is_fa, expr_ref& result) { quantifier_type qt = Q_none_pos; - pull_quantifiers(fml, qt, vars, result); + pull_quantifier(fml, qt, vars, result); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); @@ -52,7 +53,7 @@ public: void pull_exists(expr* fml, app_ref_vector& vars, expr_ref& result) { quantifier_type qt = Q_exists_pos; - pull_quantifiers(fml, qt, vars, result); + pull_quantifier(fml, qt, vars, result); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); @@ -61,7 +62,7 @@ public: void pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars) { quantifier_type qt = is_forall?Q_forall_pos:Q_exists_pos; expr_ref result(m); - pull_quantifiers(fml, qt, vars, result); + pull_quantifier(fml, qt, vars, result); TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); @@ -78,7 +79,57 @@ public: expr * const * exprs = (expr* const*) (vars.c_ptr() + vars.size()- nd); instantiate(m, q, exprs, result); } - + + unsigned pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names) { + unsigned index = var_counter().get_next_var(fml); + while (is_quantifier(fml) && (is_forall == to_quantifier(fml)->is_forall())) { + quantifier* q = to_quantifier(fml); + index += q->get_num_decls(); + if (names) { + names->append(q->get_num_decls(), q->get_decl_names()); + } + if (sorts) { + sorts->append(q->get_num_decls(), q->get_decl_sorts()); + } + fml = q->get_expr(); + } + if (!has_quantifiers(fml)) { + return index; + } + app_ref_vector vars(m); + pull_quantifier(is_forall, fml, vars); + if (vars.empty()) { + return index; + } + // replace vars by de-bruijn indices + expr_safe_replace rep(m); + svector bound_names; + ptr_vector bound_sorts; + for (unsigned i = 0; i < vars.size(); ++i) { + app* v = vars[i].get(); + if (names) { + bound_names.push_back(v->get_decl()->get_name()); + } + if (sorts) { + bound_sorts.push_back(m.get_sort(v)); + } + rep.insert(v, m.mk_var(index++, m.get_sort(v))); + } + if (names && !bound_names.empty()) { + bound_names.reverse(); + bound_names.append(*names); + names->reset(); + names->append(bound_names); + } + if (sorts && !bound_sorts.empty()) { + bound_sorts.reverse(); + bound_sorts.append(*sorts); + sorts->reset(); + sorts->append(bound_sorts); + } + rep(fml); + return index; + } private: @@ -143,7 +194,7 @@ private: } - void pull_quantifiers(expr* fml, quantifier_type& qt, app_ref_vector& vars, expr_ref& result) { + void pull_quantifier(expr* fml, quantifier_type& qt, app_ref_vector& vars, expr_ref& result) { if (!has_quantifiers(fml)) { result = fml; @@ -159,7 +210,7 @@ private: if (m.is_and(fml)) { num_args = a->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { - pull_quantifiers(a->get_arg(i), qt, vars, tmp); + pull_quantifier(a->get_arg(i), qt, vars, tmp); args.push_back(tmp); } m_rewriter.mk_and(args.size(), args.c_ptr(), result); @@ -167,25 +218,25 @@ private: else if (m.is_or(fml)) { num_args = to_app(fml)->get_num_args(); for (unsigned i = 0; i < num_args; ++i) { - pull_quantifiers(to_app(fml)->get_arg(i), qt, vars, tmp); + pull_quantifier(to_app(fml)->get_arg(i), qt, vars, tmp); args.push_back(tmp); } m_rewriter.mk_or(args.size(), args.c_ptr(), result); } else if (m.is_not(fml)) { - pull_quantifiers(to_app(fml)->get_arg(0), negate(qt), vars, tmp); + pull_quantifier(to_app(fml)->get_arg(0), negate(qt), vars, tmp); negate(qt); result = m.mk_not(tmp); } else if (m.is_implies(fml)) { - pull_quantifiers(to_app(fml)->get_arg(0), negate(qt), vars, tmp); + pull_quantifier(to_app(fml)->get_arg(0), negate(qt), vars, tmp); negate(qt); - pull_quantifiers(to_app(fml)->get_arg(1), qt, vars, result); + pull_quantifier(to_app(fml)->get_arg(1), qt, vars, result); result = m.mk_implies(tmp, result); } else if (m.is_ite(fml)) { - pull_quantifiers(to_app(fml)->get_arg(1), qt, vars, tmp); - pull_quantifiers(to_app(fml)->get_arg(2), qt, vars, result); + pull_quantifier(to_app(fml)->get_arg(1), qt, vars, tmp); + pull_quantifier(to_app(fml)->get_arg(2), qt, vars, result); result = m.mk_ite(to_app(fml)->get_arg(0), tmp, result); } else { @@ -203,7 +254,7 @@ private: } set_quantifier_type(qt, q->is_forall()); extract_quantifier(q, vars, tmp); - pull_quantifiers(tmp, qt, vars, result); + pull_quantifier(tmp, qt, vars, result); break; } case AST_VAR: @@ -215,6 +266,7 @@ private: break; } } + }; quantifier_hoister::quantifier_hoister(ast_manager& m) { @@ -237,3 +289,6 @@ void quantifier_hoister::pull_quantifier(bool is_forall, expr_ref& fml, app_ref_ m_impl->pull_quantifier(is_forall, fml, vars); } +unsigned quantifier_hoister::pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names) { + return m_impl->pull_quantifier(is_forall, fml, sorts, names); +} diff --git a/src/ast/rewriter/quant_hoist.h b/src/ast/rewriter/quant_hoist.h index 878f7840d..50cbd1ba4 100644 --- a/src/ast/rewriter/quant_hoist.h +++ b/src/ast/rewriter/quant_hoist.h @@ -59,6 +59,15 @@ public: The list of variables is empty if there are no top-level universal/existential quantifier. */ void pull_quantifier(bool is_forall, expr_ref& fml, app_ref_vector& vars); + + /** + \brief Pull top-most universal (is_forall true) or existential (is_forall=false) quantifier up. + Return an expression with de-Bruijn indices and the list of names that were used. + Return index of maximal variable. + */ + + unsigned pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names); + }; #endif diff --git a/src/ast/simplifier/poly_simplifier_plugin.cpp b/src/ast/simplifier/poly_simplifier_plugin.cpp index 13e5748dc..402b078a8 100644 --- a/src/ast/simplifier/poly_simplifier_plugin.cpp +++ b/src/ast/simplifier/poly_simplifier_plugin.cpp @@ -18,6 +18,7 @@ Author: #include"ast_pp.h" #include"ast_util.h" #include"ast_smt2_pp.h" +#include"ast_ll_pp.h" poly_simplifier_plugin::poly_simplifier_plugin(symbol const & fname, ast_manager & m, decl_kind add, decl_kind mul, decl_kind uminus, decl_kind sub, decl_kind num): @@ -173,7 +174,7 @@ void poly_simplifier_plugin::mk_monomial(unsigned num_args, expr * * args, expr_ result = args[0]; break; default: - std::sort(args, args + num_args, monomial_element_lt_proc(*this)); + std::stable_sort(args, args + num_args, monomial_element_lt_proc(*this)); result = mk_mul(num_args, args); SASSERT(wf_monomial(result)); break; @@ -465,7 +466,9 @@ void poly_simplifier_plugin::mk_sum_of_monomials(expr_ref_vector & monomials, ex result = monomials.get(0); break; default: { - std::sort(monomials.c_ptr(), monomials.c_ptr() + monomials.size(), monomial_lt_proc(*this)); + TRACE("mk_sum_sort", tout << "before\n"; for (unsigned i = 0; i < monomials.size(); i++) tout << mk_ll_pp(monomials.get(i), m_manager) << "\n";); + std::stable_sort(monomials.c_ptr(), monomials.c_ptr() + monomials.size(), monomial_lt_proc(*this)); + TRACE("mk_sum_sort", tout << "after\n"; for (unsigned i = 0; i < monomials.size(); i++) tout << mk_ll_pp(monomials.get(i), m_manager) << "\n";); if (is_simple_sum_of_monomials(monomials)) { mk_sum_of_monomials_core(monomials.size(), monomials.c_ptr(), result); return; diff --git a/src/cmd_context/echo_tactic.cpp b/src/cmd_context/echo_tactic.cpp index 10d671542..218d9c196 100644 --- a/src/cmd_context/echo_tactic.cpp +++ b/src/cmd_context/echo_tactic.cpp @@ -34,9 +34,9 @@ public: expr_dependency_ref & core) { #pragma omp critical (echo_tactic) { - m_ctx.diagnostic_stream() << m_msg; + m_ctx.regular_stream() << m_msg; if (m_newline) - m_ctx.diagnostic_stream() << std::endl; + m_ctx.regular_stream() << std::endl; } skip_tactic::operator()(in, result, mc, pc, core); } diff --git a/src/model/model_v2_pp.cpp b/src/model/model_v2_pp.cpp index 9073ddece..4600ccc9e 100644 --- a/src/model/model_v2_pp.cpp +++ b/src/model/model_v2_pp.cpp @@ -80,3 +80,8 @@ void model_v2_pp(std::ostream & out, model_core const & md, bool partial) { display_constants(out, md); display_functions(out, md, partial); } + +// debugging support +void pp(model_core const & md) { + model_v2_pp(std::cout, md, false); +} diff --git a/src/muz_qe/datalog_parser.cpp b/src/muz_qe/datalog_parser.cpp index cfe283410..545f3e14a 100644 --- a/src/muz_qe/datalog_parser.cpp +++ b/src/muz_qe/datalog_parser.cpp @@ -441,6 +441,7 @@ protected: unsigned m_sym_idx; std::string m_path; str2sort m_sort_dict; + // true if an error occured during the current call to the parse_stream // function @@ -812,7 +813,8 @@ protected: } f = m_manager.mk_func_decl(s, domain.size(), domain.c_ptr(), m_manager.mk_bool_sort()); - m_context.register_predicate(f); + m_context.register_predicate(f, true); + while (tok == TK_ID) { char const* pred_pragma = m_lexer->get_token_data(); if(strcmp(pred_pragma, "printtuples")==0 || strcmp(pred_pragma, "outputtuples")==0) { diff --git a/src/muz_qe/dl_base.cpp b/src/muz_qe/dl_base.cpp index 89ebc7e4e..0f250d76c 100644 --- a/src/muz_qe/dl_base.cpp +++ b/src/muz_qe/dl_base.cpp @@ -233,7 +233,7 @@ namespace datalog { table_fact row; for(; it!=iend; ++it) { it->get_fact(row); - to_remove.append(row); + to_remove.push_back(row); } remove_facts(to_remove.size(), to_remove.c_ptr()); } @@ -325,9 +325,20 @@ namespace datalog { return res; } + /** + \brief Default method for complementation. + + It assumes that the compiler creates only tables with + at most one column (0 or 1 columns). + Complementation of tables with more than one columns + is transformed into a cross product of complements and/or + difference. + + */ table_base * table_base::complement(func_decl* p, const table_element * func_columns) const { const table_signature & sig = get_signature(); SASSERT(sig.functional_columns()==0 || func_columns!=0); + SASSERT(sig.first_functional() <= 1); table_base * res = get_plugin().mk_empty(sig); @@ -335,16 +346,14 @@ namespace datalog { fact.resize(sig.first_functional()); fact.append(sig.functional_columns(), func_columns); - if(sig.first_functional()==0) { - if(empty()) { + if (sig.first_functional() == 0) { + if (empty()) { res->add_fact(fact); } return res; } - if(sig.first_functional()!=1) { //now we support only tables with one non-functional column - NOT_IMPLEMENTED_YET(); - } + VERIFY(sig.first_functional() == 1); uint64 upper_bound = get_signature()[0]; bool empty_table = empty(); @@ -356,51 +365,13 @@ namespace datalog { warning_msg(buffer.str().c_str()); } - for(table_element i=0; iadd_fact(fact); } } return res; -#if 0 - svector var_arg_indexes(arity); - var_arg_indexes.fill(0); - - svector var_arg_domain_sizes = s; - - unsigned var_cnt=var_arg_indexes.size(); - table_fact fact; - fact.resize(arity); - fact.fill(0); - unsigned depth=arity; - - while(true) { - if(depth==arity) { - SASSERT(!res->contains_fact(fact)); - if(empty_table || !contains_fact(fact)) { - res->add_fact(fact); - } - depth--; - } - else if(fact[depth]==s[depth]-1) { - val_indexes[depth]=0; - if(depth==0) { - break; - } - depth--; - } - else { - SASSERT(val_indexes[depth]to_formula(concl); scoped_proof _sp(m); - proof* p = m.mk_asserted(fml); + proof* p = r->get_proof(); proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); @@ -320,7 +320,7 @@ namespace datalog { else { r2->to_formula(concl); scoped_proof _sp(m); - proof* p = m.mk_asserted(fml); + proof* p = r->get_proof(); if (sub.empty()) { pr = p; } @@ -340,7 +340,7 @@ namespace datalog { pred = r->get_decl(0); } scoped_proof _sp(m); - apply(m, b.m_pc.get(), pr); + apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; return l_true; } @@ -474,6 +474,9 @@ namespace datalog { } proof_ref get_proof(model_ref& md, func_decl* pred, app* prop, unsigned level) { + if (b.m_cancel) { + return proof_ref(0, m); + } 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); @@ -497,7 +500,7 @@ namespace datalog { SASSERT(r); r->to_formula(fml); IF_VERBOSE(1, verbose_stream() << mk_pp(fml, m) << "\n";); - prs.push_back(m.mk_asserted(fml)); + prs.push_back(r->get_proof()); unsigned sz = r->get_uninterpreted_tail_size(); ptr_vector rule_vars; @@ -536,8 +539,9 @@ namespace datalog { 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); + proof_ref pr(m); + pr = get_proof(md, b.m_query_pred, to_app(level_query), level); + apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } @@ -1034,7 +1038,7 @@ namespace datalog { var_subst vs(m, false); mk_subst(*rules[i], path, trace, sub); rules[i]->to_formula(fml); - prs.push_back(m.mk_asserted(fml)); + prs.push_back(rules[i]->get_proof()); unsigned sz = trace->get_num_args(); if (sub.empty() && sz == 0) { pr = prs[0].get(); @@ -1112,7 +1116,6 @@ namespace datalog { } 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); @@ -1120,7 +1123,11 @@ namespace datalog { 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)); + scoped_proof _sp(m); + proof_ref pr(m); + pr = get_proof(md, to_app(trace), to_app(path)); + apply(m, b.m_ctx.get_proof_converter().get(), pr); + b.m_answer = pr; } }; @@ -1155,6 +1162,9 @@ namespace datalog { private: void get_model(unsigned level) { + if (b.m_cancel) { + return; + } rule_manager& rm = b.m_ctx.get_rule_manager(); expr_ref level_query = mk_level_predicate(b.m_query_pred, level); model_ref md; @@ -1212,7 +1222,7 @@ namespace datalog { r1->to_formula(concl); scoped_proof _sp(m); - proof* p = m.mk_asserted(fml); + proof* p = r->get_proof(); proof* premises[2] = { pr, p }; positions.push_back(std::make_pair(0, 1)); @@ -1225,7 +1235,7 @@ namespace datalog { else { r2->to_formula(concl); scoped_proof _sp(m); - proof* p = m.mk_asserted(fml); + proof* p = r->get_proof(); if (sub.empty()) { pr = p; } @@ -1245,7 +1255,7 @@ namespace datalog { pred = r->get_decl(0); } scoped_proof _sp(m); - apply(m, b.m_pc.get(), pr); + apply(m, b.m_ctx.get_proof_converter().get(), pr); b.m_answer = pr; } @@ -1409,6 +1419,7 @@ namespace datalog { m_ctx.ensure_opened(); m_rules.reset(); + datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); datalog::rule_set old_rules(m_ctx.get_rules()); datalog::rule_ref_vector query_rules(rule_manager); @@ -1417,16 +1428,14 @@ namespace datalog { m_ctx.add_rules(query_rules); expr_ref bg_assertion = m_ctx.get_background_assertion(); - model_converter_ref mc = datalog::mk_skip_model_converter(); - m_pc = datalog::mk_skip_proof_converter(); m_ctx.set_output_predicate(m_query_pred); - m_ctx.apply_default_transformation(mc, m_pc); + m_ctx.apply_default_transformation(); if (m_ctx.get_params().slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); - m_ctx.transform_rules(transformer, mc, m_pc); + m_ctx.transform_rules(transformer); m_query_pred = slice->get_predicate(m_query_pred.get()); m_ctx.set_output_predicate(m_query_pred); } diff --git a/src/muz_qe/dl_bmc_engine.h b/src/muz_qe/dl_bmc_engine.h index 06901a160..5911f5f72 100644 --- a/src/muz_qe/dl_bmc_engine.h +++ b/src/muz_qe/dl_bmc_engine.h @@ -39,7 +39,6 @@ namespace datalog { func_decl_ref m_query_pred; expr_ref m_answer; volatile bool m_cancel; - proof_converter_ref m_pc; void checkpoint(); diff --git a/src/muz_qe/dl_check_table.cpp b/src/muz_qe/dl_check_table.cpp index d7dfbe1ae..5081654b5 100644 --- a/src/muz_qe/dl_check_table.cpp +++ b/src/muz_qe/dl_check_table.cpp @@ -287,9 +287,6 @@ namespace datalog { bool check_table::well_formed() const { get_plugin().m_count++; - if (get_plugin().m_count == 497) { - std::cout << "here\n"; - } iterator it = m_tocheck->begin(), end = m_tocheck->end(); for (; it != end; ++it) { table_fact fact; @@ -354,8 +351,8 @@ namespace datalog { return result; } - table_base * check_table::complement(func_decl* p) const { - check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->complement(p), m_checker->complement(p)); + table_base * check_table::complement(func_decl* p, const table_element * func_columns) const { + check_table* result = alloc(check_table, get_plugin(), get_signature(), m_tocheck->complement(p, func_columns), m_checker->complement(p, func_columns)); return result; } diff --git a/src/muz_qe/dl_check_table.h b/src/muz_qe/dl_check_table.h index 6f098f8bc..7126bde66 100644 --- a/src/muz_qe/dl_check_table.h +++ b/src/muz_qe/dl_check_table.h @@ -119,7 +119,7 @@ namespace datalog { virtual void add_fact(const table_fact & f); virtual void remove_fact(const table_element* fact); virtual bool contains_fact(const table_fact & f) const; - virtual table_base * complement(func_decl* p) const; + virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; virtual table_base * clone() const; virtual iterator begin() const { SASSERT(well_formed()); return m_tocheck->begin(); } diff --git a/src/muz_qe/dl_cmds.cpp b/src/muz_qe/dl_cmds.cpp index c88e7346e..c1e3f85f9 100644 --- a/src/muz_qe/dl_cmds.cpp +++ b/src/muz_qe/dl_cmds.cpp @@ -229,12 +229,12 @@ public: status = dlctx.query(m_target); } catch (z3_error & ex) { + ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl; throw ex; } catch (z3_exception& ex) { ctx.regular_stream() << "(error \"query failed: " << ex.msg() << "\")" << std::endl; } - dlctx.cleanup(); } switch (status) { case l_false: @@ -250,6 +250,7 @@ public: ctx.regular_stream() << "unknown\n"; switch(dlctx.get_status()) { case datalog::INPUT_ERROR: + ctx.regular_stream() << "input error\n"; break; case datalog::MEMOUT: @@ -261,12 +262,21 @@ public: break; case datalog::OK: + UNREACHABLE(); break; + + case datalog::CANCELED: + ctx.regular_stream() << "canceled\n"; + dlctx.display_profile(ctx.regular_stream()); + break; + default: UNREACHABLE(); + break; } break; } + dlctx.cleanup(); print_statistics(ctx); m_target = 0; } diff --git a/src/muz_qe/dl_compiler.cpp b/src/muz_qe/dl_compiler.cpp index 44a449779..acedc3619 100644 --- a/src/muz_qe/dl_compiler.cpp +++ b/src/muz_qe/dl_compiler.cpp @@ -384,8 +384,8 @@ namespace datalog { void compiler::get_local_indexes_for_projection(rule * r, unsigned_vector & res) { SASSERT(r->get_positive_tail_size()==2); ast_manager & m = m_context.get_manager(); - var_counter counter; - counter.count_vars(m, r); + rule_counter counter; + counter.count_rule_vars(m, r); app * t1 = r->get_tail(0); app * t2 = r->get_tail(1); counter.count_vars(m, t1, -1); @@ -707,6 +707,7 @@ namespace datalog { //update the head relation make_union(new_head_reg, head_reg, delta_reg, use_widening, acc); + make_dealloc_non_void(new_head_reg, acc); } finish: @@ -1028,21 +1029,23 @@ namespace datalog { func_decl * head_pred = *preds.begin(); const rule_vector & rules = m_rule_set.get_predicate_rules(head_pred); + + reg_idx output_delta; - if(!output_deltas.find(head_pred, output_delta)) { + if (!output_deltas.find(head_pred, output_delta)) { output_delta = execution_context::void_register; } rule_vector::const_iterator it = rules.begin(); rule_vector::const_iterator end = rules.end(); - for(; it!=end; ++it) { + for (; it != end; ++it) { rule * r = *it; SASSERT(r->get_head()->get_decl()==head_pred); compile_rule_evaluation(r, input_deltas, output_delta, false, acc); } - if(add_saturation_marks) { + if (add_saturation_marks) { //now the predicate is saturated, so we may mark it as such acc.push_back(instruction::mk_mark_saturated(m_context.get_manager(), head_pred)); } @@ -1068,7 +1071,7 @@ namespace datalog { for(; sit!=send; ++sit) { func_decl_set & strat_preds = **sit; - if(all_saturated(strat_preds)) { + if (all_saturated(strat_preds)) { //all predicates in stratum are saturated, so no need to compile rules for them continue; } @@ -1084,7 +1087,7 @@ namespace datalog { tout << "\n"; ); - if(is_nonrecursive_stratum(strat_preds)) { + if (is_nonrecursive_stratum(strat_preds)) { //this stratum contains just a single non-recursive rule compile_nonrecursive_stratum(strat_preds, input_deltas, output_deltas, add_saturation_marks, acc); } diff --git a/src/muz_qe/dl_context.cpp b/src/muz_qe/dl_context.cpp index 7c9d2c965..7a1e3b8fc 100644 --- a/src/muz_qe/dl_context.cpp +++ b/src/muz_qe/dl_context.cpp @@ -45,6 +45,9 @@ Revision History: #include"dl_mk_partial_equiv.h" #include"dl_mk_bit_blast.h" #include"dl_mk_array_blast.h" +#include"dl_mk_karr_invariants.h" +#include"dl_mk_quantifier_abstraction.h" +#include"dl_mk_quantifier_instantiation.h" #include"datatype_decl_plugin.h" #include"expr_abstract.h" @@ -223,14 +226,18 @@ namespace datalog { m_rewriter(m), m_var_subst(m), m_rule_manager(*this), + m_transf(*this), m_trail(*this), m_pinned(m), m_vars(m), m_rule_set(*this), + m_transformed_rule_set(*this), m_rule_fmls(m), m_background(m), + m_mc(0), m_closed(false), m_saturation_was_run(false), + m_last_status(OK), m_last_answer(m), m_engine(LAST_ENGINE), m_cancel(false) { @@ -295,14 +302,6 @@ namespace datalog { return m_preds.contains(pred); } - func_decl * context::try_get_predicate_decl(symbol pred_name) const { - func_decl * res; - if (!m_preds_by_name.find(pred_name, res)) { - return 0; - } - return res; - } - void context::register_variable(func_decl* var) { m_vars.push_back(m.mk_const(var)); } @@ -354,7 +353,6 @@ namespace datalog { m_pinned.push_back(decl); m_preds.insert(decl); if (named) { - SASSERT(!m_preds_by_name.contains(decl->get_name())); m_preds_by_name.insert(decl->get_name(), decl); } } @@ -441,7 +439,7 @@ namespace datalog { func_decl* new_pred = m.mk_fresh_func_decl(prefix, suffix, arity, domain, m.mk_bool_sort()); - register_predicate(new_pred); + register_predicate(new_pred, true); if (m_rel.get()) { m_rel->inherit_predicate_kind(new_pred, orig_pred); @@ -472,8 +470,11 @@ namespace datalog { void context::flush_add_rules() { datalog::rule_manager& rm = get_rule_manager(); datalog::rule_ref_vector rules(rm); + scoped_proof_mode _scp(m, generate_proof_trace()?PGM_FINE:PGM_DISABLED); for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { - rm.mk_rule(m_rule_fmls[i].get(), rules, m_rule_names[i]); + expr* fml = m_rule_fmls[i].get(); + proof* p = generate_proof_trace()?m.mk_asserted(fml):0; + rm.mk_rule(fml, p, rules, m_rule_names[i]); } add_rules(rules); m_rule_fmls.reset(); @@ -487,7 +488,11 @@ namespace datalog { void context::update_rule(expr* rl, symbol const& name) { datalog::rule_manager& rm = get_rule_manager(); datalog::rule_ref_vector rules(rm); - rm.mk_rule(rl, rules, name); + proof* p = 0; + if (generate_proof_trace()) { + p = m.mk_asserted(rl); + } + rm.mk_rule(rl, p, rules, name); if (rules.size() != 1) { std::stringstream strm; strm << "Rule " << name << " has a non-trivial body. It cannot be modified"; @@ -681,7 +686,7 @@ namespace datalog { todo.push_back(e2); } else if (is_quantifier(e)) { - todo.append(to_quantifier(e)->get_expr()); + todo.push_back(to_quantifier(e)->get_expr()); } else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && m.is_true(e1)) { @@ -735,6 +740,9 @@ namespace datalog { UNREACHABLE(); break; } + if (generate_proof_trace() && !r->get_proof()) { + m_rule_manager.mk_rule_asserted_proof(*r.get()); + } } void context::add_rule(rule_ref& r) { @@ -772,6 +780,10 @@ namespace datalog { add_fact(head->get_decl(), fact); } + bool context::has_facts(func_decl * pred) const { + return m_rel && m_rel->has_facts(pred); + } + void context::add_table_fact(func_decl * pred, const table_fact & fact) { if (get_engine() == DATALOG_ENGINE) { ensure_rel(); @@ -824,28 +836,28 @@ namespace datalog { m_closed = false; } - void context::transform_rules(model_converter_ref& mc, proof_converter_ref& pc) { - rule_transformer transf(*this); - transf.register_plugin(alloc(mk_filter_rules,*this)); - transf.register_plugin(alloc(mk_simple_joins,*this)); + void context::transform_rules() { + m_transf.reset(); + m_transf.register_plugin(alloc(mk_filter_rules,*this)); + m_transf.register_plugin(alloc(mk_simple_joins,*this)); if (unbound_compressor()) { - transf.register_plugin(alloc(mk_unbound_compressor,*this)); + m_transf.register_plugin(alloc(mk_unbound_compressor,*this)); } if (similarity_compressor()) { - transf.register_plugin(alloc(mk_similarity_compressor, *this, + m_transf.register_plugin(alloc(mk_similarity_compressor, *this, similarity_compressor_threshold())); } - transf.register_plugin(alloc(datalog::mk_partial_equivalence_transformer, *this)); + m_transf.register_plugin(alloc(datalog::mk_partial_equivalence_transformer, *this)); - transform_rules(transf, mc, pc); + transform_rules(m_transf); } - - void context::transform_rules(rule_transformer& transf, model_converter_ref& mc, proof_converter_ref& pc) { + + void context::transform_rules(rule_transformer& transf) { SASSERT(m_closed); //we must finish adding rules before we start transforming them TRACE("dl", display_rules(tout);); - if (transf(m_rule_set, mc, pc)) { + if (transf(m_rule_set)) { //we have already ensured the negation is stratified and transformations //should not break the stratification m_rule_set.ensure_closed(); @@ -860,34 +872,47 @@ namespace datalog { m_rule_set.add_rules(rs); } - void context::apply_default_transformation(model_converter_ref& mc, proof_converter_ref& pc) { - ensure_closed(); - datalog::rule_transformer transf(*this); - transf.register_plugin(alloc(datalog::mk_coi_filter, *this)); - transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this)); + void context::record_transformed_rules() { + m_transformed_rule_set.reset(); + m_transformed_rule_set.add_rules(m_rule_set); + } - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 35005)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 35000)); - transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34990)); - transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34980)); + void context::apply_default_transformation() { + ensure_closed(); + m_transf.reset(); + m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this)); + m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this)); + + m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 35005)); + m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 35000)); + m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34990)); + m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34980)); //and another round of inlining - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34975)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34970)); - transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34960)); - transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34950)); + m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34975)); + m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34970)); + m_transf.register_plugin(alloc(datalog::mk_coi_filter, *this, 34960)); + m_transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, *this, 34950)); - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34940)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34930)); - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34920)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34910)); - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34900)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34890)); - transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34880)); + m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34940)); + m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34930)); + m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34920)); + m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34910)); + m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34900)); + m_transf.register_plugin(alloc(datalog::mk_rule_inliner, *this, 34890)); + m_transf.register_plugin(alloc(datalog::mk_subsumption_checker, *this, 34880)); - transf.register_plugin(alloc(datalog::mk_bit_blast, *this, 35000)); - transf.register_plugin(alloc(datalog::mk_array_blast, *this, 36000)); - transform_rules(transf, mc, pc); + + if (get_params().quantify_arrays()) { + m_transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, *this, 33000)); + m_transf.register_plugin(alloc(datalog::mk_array_blast, *this, 32500)); + } + m_transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, *this, 32000)); + + m_transf.register_plugin(alloc(datalog::mk_bit_blast, *this, 35000)); + m_transf.register_plugin(alloc(datalog::mk_array_blast, *this, 36000)); + m_transf.register_plugin(alloc(datalog::mk_karr_invariants, *this, 36010)); + transform_rules(m_transf); } void context::collect_params(param_descrs& p) { @@ -928,18 +953,21 @@ namespace datalog { void context::cancel() { m_cancel = true; + m_last_status = CANCELED; + m_transf.cancel(); if (m_pdr.get()) m_pdr->cancel(); if (m_bmc.get()) m_bmc->cancel(); - if (m_rel.get()) m_rel->cancel(); if (m_tab.get()) m_tab->cancel(); + if (m_rel.get()) m_rel->set_cancel(true); } void context::cleanup() { m_cancel = false; + m_last_status = OK; if (m_pdr.get()) m_pdr->cleanup(); if (m_bmc.get()) m_bmc->cleanup(); - if (m_rel.get()) m_rel->cleanup(); if (m_tab.get()) m_tab->cleanup(); + if (m_rel.get()) m_rel->set_cancel(false); } class context::engine_type_proc { @@ -1031,6 +1059,8 @@ namespace datalog { } void context::new_query() { + m_mc = mk_skip_model_converter(); + flush_add_rules(); m_last_status = OK; m_last_answer = 0; @@ -1142,6 +1172,7 @@ namespace datalog { switch(get_engine()) { case DATALOG_ENGINE: return false; + case PDR_ENGINE: case QPDR_ENGINE: ensure_pdr(); m_pdr->display_certificate(out); @@ -1160,6 +1191,20 @@ namespace datalog { } } + void context::display_profile(std::ostream& out) const { + out << "\n---------------\n"; + out << "Original rules\n"; + display_rules(out); + + out << "\n---------------\n"; + out << "Transformed rules\n"; + m_transformed_rule_set.display(out); + + if (m_rel) { + m_rel->display_profile(out); + } + } + void context::reset_statistics() { if (m_pdr) { m_pdr->reset_statistics(); @@ -1192,7 +1237,7 @@ namespace datalog { return m_rel->result_contains_fact(f); } - // TBD: algebraic data-types declarations will not be printed. + // NB: algebraic data-types declarations will not be printed. class free_func_visitor { ast_manager& m; func_decl_set m_funcs; @@ -1235,7 +1280,7 @@ namespace datalog { ptr_vector sorts; get_free_vars(m_rule_fmls[i].get(), sorts); if (!sorts.empty()) { - rm.mk_rule(m_rule_fmls[i].get(), rule_refs, m_rule_names[i]); + rm.mk_rule(m_rule_fmls[i].get(), 0, rule_refs, m_rule_names[i]); m_rule_fmls[i] = m_rule_fmls.back(); m_rule_names[i] = m_rule_names.back(); m_rule_fmls.pop_back(); @@ -1252,7 +1297,7 @@ namespace datalog { } for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { rules.push_back(m_rule_fmls[i].get()); - names.push_back(m_rule_names[i]); + names.push_back(m_rule_names[i]); } } diff --git a/src/muz_qe/dl_context.h b/src/muz_qe/dl_context.h index 98380c9c8..d23cf2c0c 100644 --- a/src/muz_qe/dl_context.h +++ b/src/muz_qe/dl_context.h @@ -42,19 +42,18 @@ Revision History: #include"params.h" #include"trail.h" #include"model_converter.h" -#include"proof_converter.h" #include"model2expr.h" #include"smt_params.h" +#include"dl_rule_transformer.h" namespace datalog { - class rule_transformer; - enum execution_result { OK, TIMEOUT, MEMOUT, - INPUT_ERROR + INPUT_ERROR, + CANCELED }; class context { @@ -85,7 +84,7 @@ namespace datalog { th_rewriter m_rewriter; var_subst m_var_subst; rule_manager m_rule_manager; - + rule_transformer m_transf; trail_stack m_trail; ast_ref_vector m_pinned; app_ref_vector m_vars; @@ -94,9 +93,12 @@ namespace datalog { sym2decl m_preds_by_name; pred2syms m_argument_var_names; rule_set m_rule_set; + rule_set m_transformed_rule_set; expr_ref_vector m_rule_fmls; svector m_rule_names; expr_ref_vector m_background; + model_converter_ref m_mc; + proof_converter_ref m_pc; scoped_ptr m_pdr; scoped_ptr m_bmc; @@ -110,6 +112,8 @@ namespace datalog { DL_ENGINE m_engine; volatile bool m_cancel; + + bool is_fact(app * head) const; bool has_sort_domain(relation_sort s) const; sort_domain & get_sort_domain(relation_sort s); @@ -140,6 +144,7 @@ namespace datalog { var_subst & get_var_subst() { return m_var_subst; } dl_decl_util & get_decl_util() { return m_decl_util; } + bool generate_proof_trace() const { return m_params.generate_proof_trace(); } bool output_profile() const { return m_params.output_profile(); } bool fix_unbound_vars() const { return m_params.fix_unbound_vars(); } symbol default_table() const { return m_params.default_table(); } @@ -178,18 +183,22 @@ namespace datalog { retrieved by the try_get_predicate_decl() function. Auxiliary predicates introduced e.g. by rule transformations do not need to be named. */ - void register_predicate(func_decl * pred, bool named = true); + void register_predicate(func_decl * pred, bool named); bool is_predicate(func_decl * pred) const; - + /** \brief If a predicate name has a \c func_decl object assigned, return pointer to it; otherwise return 0. - + Not all \c func_decl object used as relation identifiers need to be assigned to their names. Generally, the names coming from the parses are registered here. - */ - func_decl * try_get_predicate_decl(symbol pred_name) const; + */ + func_decl * try_get_predicate_decl(symbol const& pred_name) const { + func_decl * res = 0; + m_preds_by_name.find(pred_name, res); + return res; + } /** \brief Create a fresh head predicate declaration. @@ -240,6 +249,7 @@ namespace datalog { void add_fact(app * head); void add_fact(func_decl * pred, const relation_fact & fact); + bool has_facts(func_decl * pred) const; void add_rule(rule_ref& r); void add_rules(rule_ref_vector& rs); @@ -313,11 +323,17 @@ namespace datalog { void reopen(); void ensure_opened(); - void transform_rules(model_converter_ref& mc, proof_converter_ref& pc); - void transform_rules(rule_transformer& trans, model_converter_ref& mc, proof_converter_ref& pc); - void replace_rules(rule_set & rs); + model_converter_ref& get_model_converter() { return m_mc; } + void add_model_converter(model_converter* mc) { m_mc = concat(m_mc.get(), mc); } + proof_converter_ref& get_proof_converter() { return m_pc; } + void add_proof_converter(proof_converter* pc) { m_pc = concat(m_pc.get(), pc); } - void apply_default_transformation(model_converter_ref& mc, proof_converter_ref& pc); + void transform_rules(); + void transform_rules(rule_transformer& transf); + void replace_rules(rule_set & rs); + void record_transformed_rules(); + + void apply_default_transformation(); void collect_params(param_descrs& r); @@ -342,6 +358,8 @@ namespace datalog { void display_smt2(unsigned num_queries, expr* const* queries, std::ostream& out); + void display_profile(std::ostream& out) const; + // ----------------------------------- // // basic usage methods @@ -349,6 +367,7 @@ namespace datalog { // ----------------------------------- void cancel(); + bool canceled() const { return m_cancel; } void cleanup(); void reset_cancel() { cleanup(); } diff --git a/src/muz_qe/dl_instruction.cpp b/src/muz_qe/dl_instruction.cpp index 503ffec3b..401a42e98 100644 --- a/src/muz_qe/dl_instruction.cpp +++ b/src/muz_qe/dl_instruction.cpp @@ -58,14 +58,18 @@ namespace datalog { reset_timelimit(); } + rel_context& execution_context::get_rel_context() { + return m_context.get_rel_context(); + } + struct compare_size_proc { typedef std::pair pr; bool operator()(pr const& a, pr const& b) const { return a.second > b.second; } - }; - void execution_context::report_big_relations(unsigned threshold, std::ostream & out) { + + void execution_context::report_big_relations(unsigned threshold, std::ostream & out) const { unsigned n = register_count(); svector > sizes; size_t total_bytes = 0; @@ -110,6 +114,7 @@ namespace datalog { bool execution_context::should_terminate() { return + m_context.canceled() || memory::above_high_watermark() || (m_stopwatch && m_timelimit_ms != 0 && @@ -135,7 +140,7 @@ namespace datalog { process_costs(); } - void instruction::display_indented(rel_context & ctx, std::ostream & out, std::string indentation) const { + void instruction::display_indented(rel_context const & ctx, std::ostream & out, std::string indentation) const { out << indentation; display_head_impl(ctx, out); if (ctx.output_profile()) { @@ -182,7 +187,7 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) { ctx.set_register_annotation(m_reg, m_pred->get_name().bare_str()); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { const char * rel_name = m_pred->get_name().bare_str(); if (m_store) { out << "store " << m_reg << " into " << rel_name; @@ -213,7 +218,7 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) { ctx.set_register_annotation(m_reg, "alloc"); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "dealloc " << m_reg; } }; @@ -248,7 +253,7 @@ namespace datalog { ctx.set_register_annotation(m_src, str); } } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << (m_clone ? "clone " : "move ") << m_src << " into " << m_tgt; } }; @@ -304,11 +309,11 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) { m_body->make_annotations(ctx); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const & ctx, std::ostream & out) const { out << "while"; print_container(m_controls, out); } - virtual void display_body_impl(rel_context & ctx, std::ostream & out, std::string indentation) const { + virtual void display_body_impl(rel_context const & ctx, std::ostream & out, std::string indentation) const { m_body->display_indented(ctx, out, indentation+" "); } }; @@ -385,7 +390,7 @@ namespace datalog { ctx.get_register_annotation(m_rel1, a1); ctx.set_register_annotation(m_res, "join " + a1 + " " + a2); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const & ctx, std::ostream & out) const { out << "join " << m_rel1; print_container(m_cols1, out); out << " and " << m_rel2; @@ -434,7 +439,7 @@ namespace datalog { a << "filter_equal " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); ctx.set_register_annotation(m_reg, a.str()); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "filter_equal " << m_reg << " col: " << m_col << " val: " << ctx.get_rmanager().to_nice_string(m_value); } @@ -476,7 +481,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "filter_identical " << m_reg << " "; print_container(m_cols, out); } @@ -519,7 +524,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "filter_interpreted " << m_reg << " using " << mk_pp(m_cond, m_cond.get_manager()); } @@ -619,12 +624,16 @@ namespace datalog { return true; } virtual void make_annotations(execution_context & ctx) { - std::string str; - if (ctx.get_register_annotation(m_tgt, str) && m_delta!=execution_context::void_register) { - ctx.set_register_annotation(m_delta, "delta of "+str); + std::string str = "union"; + if (!ctx.get_register_annotation(m_tgt, str)) { + ctx.set_register_annotation(m_tgt, "union"); } + if (m_delta != execution_context::void_register) { + str = "delta of " + str; + } + ctx.set_register_annotation(m_delta, str); } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << (m_widen ? "widen " : "union ") << m_src << " into " << m_tgt; if (m_delta!=execution_context::void_register) { out << " with delta " << m_delta; @@ -678,7 +687,7 @@ namespace datalog { return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << (m_projection ? "project " : "rename ") << m_src << " into " << m_tgt; out << (m_projection ? " deleting columns " : " with cycle "); print_container(m_cols, out); @@ -739,7 +748,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "join_project " << m_rel1; print_container(m_cols1, out); out << " and " << m_rel2; @@ -800,7 +809,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "select_equal_and_project " << m_src <<" into " << m_result << " col: " << m_col << " val: " << ctx.get_rmanager().to_nice_string(m_value); } @@ -854,7 +863,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "filter_by_negation on " << m_tgt; print_container(m_cols1, out); out << " with " << m_neg_rel; @@ -892,7 +901,7 @@ namespace datalog { ctx.set_reg(m_tgt, rel); return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "mk_unary_singleton into " << m_tgt << " sort:" << ctx.get_rmanager().to_nice_string(m_sig[0]) << " val:" << ctx.get_rmanager().to_nice_string(m_sig[0], m_fact[0]); @@ -922,7 +931,7 @@ namespace datalog { ctx.set_reg(m_tgt, ctx.get_rel_context().get_rmanager().mk_full_relation(m_sig, m_pred)); return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "mk_total into " << m_tgt << " sort:" << ctx.get_rmanager().to_nice_string(m_sig); } @@ -947,7 +956,7 @@ namespace datalog { ctx.get_rel_context().get_rmanager().mark_saturated(m_pred); return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "mark_saturated " << m_pred->get_name().bare_str(); } virtual void make_annotations(execution_context & ctx) { @@ -970,7 +979,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const& ctx, std::ostream & out) const { out << "instr_assert_signature of " << m_tgt << " signature:"; print_container(m_sig, out); } @@ -1042,7 +1051,7 @@ namespace datalog { } } - void instruction_block::display_indented(rel_context & ctx, std::ostream & out, std::string indentation) const { + void instruction_block::display_indented(rel_context const& ctx, std::ostream & out, std::string indentation) const { instr_seq_type::const_iterator it = m_data.begin(); instr_seq_type::const_iterator end = m_data.end(); for(; it!=end; ++it) { diff --git a/src/muz_qe/dl_instruction.h b/src/muz_qe/dl_instruction.h index 89f95c860..ae6310ba6 100644 --- a/src/muz_qe/dl_instruction.h +++ b/src/muz_qe/dl_instruction.h @@ -31,6 +31,7 @@ namespace datalog { class execution_context; class instruction_block; + class rel_context; inline void check_overflow(unsigned i) { if (i == UINT_MAX) { @@ -78,7 +79,7 @@ namespace datalog { void reset(); - rel_context & get_rel_context() { return m_context.get_rel_context(); }; + rel_context & get_rel_context(); void set_timelimit(unsigned time_in_ms); void reset_timelimit(); @@ -91,10 +92,9 @@ namespace datalog { If register contains zero, it should be treated as if it contains an empty relation. */ - reg_type reg(reg_idx i) { - if (i>=m_registers.size()) { - check_overflow(i); - m_registers.resize(i+1,0); + reg_type reg(reg_idx i) const { + if (i >= m_registers.size()) { + return 0; } return m_registers[i]; } @@ -102,27 +102,29 @@ namespace datalog { \brief Return value of the register and assign zero into it place. */ reg_type release_reg(reg_idx i) { - SASSERT(i=m_registers.size()) { + if (i >= m_registers.size()) { check_overflow(i); m_registers.resize(i+1,0); } - if(m_registers[i]) { + if (m_registers[i]) { m_registers[i]->deallocate(); } - m_registers[i]=val; + m_registers[i] = val; } + void make_empty(reg_idx i) { - if(reg(i)) { + if (reg(i)) { set_reg(i, 0); } } @@ -130,14 +132,16 @@ namespace datalog { unsigned register_count() const { return m_registers.size(); } + bool get_register_annotation(reg_idx reg, std::string & res) const { return m_reg_annotation.find(reg, res); } + void set_register_annotation(reg_idx reg, std::string str) { m_reg_annotation.insert(reg, str); } - void report_big_relations(unsigned threshold, std::ostream & out); + void report_big_relations(unsigned threshold, std::ostream & out) const; }; @@ -208,7 +212,7 @@ namespace datalog { The newline character at the end should not be printed. */ - virtual void display_head_impl(rel_context & ctx, std::ostream & out) const { + virtual void display_head_impl(rel_context const & ctx, std::ostream & out) const { out << ""; } /** @@ -216,7 +220,7 @@ namespace datalog { Each line must be prepended by \c indentation and ended by a newline character. */ - virtual void display_body_impl(rel_context & ctx, std::ostream & out, std::string indentation) const {} + virtual void display_body_impl(rel_context const & ctx, std::ostream & out, std::string indentation) const {} public: typedef execution_context::reg_type reg_type; typedef execution_context::reg_idx reg_idx; @@ -227,10 +231,10 @@ namespace datalog { virtual void make_annotations(execution_context & ctx) = 0; - void display(rel_context & ctx, std::ostream & out) const { + void display(rel_context const& ctx, std::ostream & out) const { display_indented(ctx, out, ""); } - void display_indented(rel_context & ctx, std::ostream & out, std::string indentation) const; + void display_indented(rel_context const & ctx, std::ostream & out, std::string indentation) const; static instruction * mk_load(ast_manager & m, func_decl * pred, reg_idx tgt); /** @@ -329,10 +333,10 @@ namespace datalog { void make_annotations(execution_context & ctx); - void display(rel_context & ctx, std::ostream & out) const { + void display(rel_context const & ctx, std::ostream & out) const { display_indented(ctx, out, ""); } - void display_indented(rel_context & ctx, std::ostream & out, std::string indentation) const; + void display_indented(rel_context const & ctx, std::ostream & out, std::string indentation) const; }; diff --git a/src/muz_qe/dl_interval_relation.cpp b/src/muz_qe/dl_interval_relation.cpp index 150ba1c78..3397f2db0 100644 --- a/src/muz_qe/dl_interval_relation.cpp +++ b/src/muz_qe/dl_interval_relation.cpp @@ -21,6 +21,7 @@ Revision History: #include "optional.h" #include "ast_pp.h" #include "dl_interval_relation.h" +#include "bool_rewriter.h" namespace datalog { @@ -30,8 +31,7 @@ namespace datalog { interval_relation_plugin::interval_relation_plugin(relation_manager& m): relation_plugin(interval_relation_plugin::get_name(), m), m_empty(m_dep), - m_arith(get_ast_manager()), - m_bsimp(get_ast_manager()) { + m_arith(get_ast_manager()) { } bool interval_relation_plugin::can_handle_signature(const relation_signature & sig) { @@ -374,7 +374,6 @@ namespace datalog { void interval_relation::to_formula(expr_ref& fml) const { ast_manager& m = get_plugin().get_ast_manager(); arith_util& arith = get_plugin().m_arith; - basic_simplifier_plugin& bsimp = get_plugin().m_bsimp; expr_ref_vector conjs(m); relation_signature const& sig = get_signature(); for (unsigned i = 0; i < sig.size(); ++i) { @@ -405,7 +404,8 @@ namespace datalog { } } } - bsimp.mk_and(conjs.size(), conjs.c_ptr(), fml); + bool_rewriter br(m); + br.mk_and(conjs.size(), conjs.c_ptr(), fml); } diff --git a/src/muz_qe/dl_interval_relation.h b/src/muz_qe/dl_interval_relation.h index 0ff05719e..1a25f430f 100644 --- a/src/muz_qe/dl_interval_relation.h +++ b/src/muz_qe/dl_interval_relation.h @@ -34,7 +34,6 @@ namespace datalog { v_dependency_manager m_dep; interval m_empty; arith_util m_arith; - basic_simplifier_plugin m_bsimp; class join_fn; class project_fn; diff --git a/src/muz_qe/dl_mk_array_blast.cpp b/src/muz_qe/dl_mk_array_blast.cpp index b22fdf7ef..67bf52da8 100644 --- a/src/muz_qe/dl_mk_array_blast.cpp +++ b/src/muz_qe/dl_mk_array_blast.cpp @@ -30,7 +30,8 @@ namespace datalog { m(ctx.get_manager()), a(m), rm(ctx.get_rule_manager()), - m_rewriter(m, m_params){ + m_rewriter(m, m_params), + m_simplifier(ctx) { m_params.set_bool("expand_select_store",true); m_rewriter.updt_params(m_params); } @@ -197,39 +198,31 @@ namespace datalog { } fml2 = m.mk_implies(body, head); - rm.mk_rule(fml2, new_rules, r.name()); + proof_ref p(m); + rm.mk_rule(fml2, p, new_rules, r.name()); SASSERT(new_rules.size() == 1); TRACE("dl", new_rules[0]->display(m_ctx, tout << "new rule\n");); - - rules.add_rule(new_rules[0].get()); - if (m_pc) { - new_rules[0]->to_formula(fml2); - m_pc->insert(fml1, fml2); + rule_ref new_rule(rm); + if (m_simplifier.transform_rule(new_rules[0].get(), new_rule)) { + rules.add_rule(new_rule.get()); + rm.mk_rule_rewrite_proof(r, *new_rule.get()); } return true; } - rule_set * mk_array_blast::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - ref epc; - if (pc) { - epc = alloc(equiv_proof_converter, m); - } - m_pc = epc.get(); + rule_set * mk_array_blast::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); rule_set::iterator it = source.begin(), end = source.end(); bool change = false; - for (; it != end; ++it) { + for (; !m_ctx.canceled() && it != end; ++it) { change = blast(**it, *rules) || change; } if (!change) { dealloc(rules); rules = 0; } - if (pc) { - pc = concat(pc.get(), epc.get()); - } return rules; } diff --git a/src/muz_qe/dl_mk_array_blast.h b/src/muz_qe/dl_mk_array_blast.h index 1618e4fa8..21f2a0bf7 100644 --- a/src/muz_qe/dl_mk_array_blast.h +++ b/src/muz_qe/dl_mk_array_blast.h @@ -22,6 +22,7 @@ Revision History: #include"dl_context.h" #include"dl_rule_set.h" #include"dl_rule_transformer.h" +#include"dl_mk_interp_tail_simplifier.h" #include "equiv_proof_converter.h" #include "array_decl_plugin.h" @@ -37,7 +38,7 @@ namespace datalog { rule_manager& rm; params_ref m_params; th_rewriter m_rewriter; - equiv_proof_converter* m_pc; + mk_interp_tail_simplifier m_simplifier; typedef obj_map defs_t; @@ -49,13 +50,13 @@ namespace datalog { public: /** - \brief Create rule transformer that extracts universal quantifiers (over recursive predicates). + \brief Create rule transformer that removes array stores and selects by ackermannization. */ mk_array_blast(context & ctx, unsigned priority); virtual ~mk_array_blast(); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; diff --git a/src/muz_qe/dl_mk_bit_blast.cpp b/src/muz_qe/dl_mk_bit_blast.cpp index 1fa93a0ca..3d6c35bb3 100644 --- a/src/muz_qe/dl_mk_bit_blast.cpp +++ b/src/muz_qe/dl_mk_bit_blast.cpp @@ -21,7 +21,9 @@ Revision History: #include "bit_blaster_rewriter.h" #include "rewriter_def.h" #include "ast_pp.h" - +#include "expr_safe_replace.h" +#include "filter_model_converter.h" +#include "dl_mk_interp_tail_simplifier.h" namespace datalog { @@ -35,6 +37,73 @@ namespace datalog { // P(bv(x,y)) :- P_bv(x,y) // Query + // this model converter should be composed with a filter converter + // that gets rid of the new functions. + class bit_blast_model_converter : public model_converter { + ast_manager& m; + bv_util m_bv; + func_decl_ref_vector m_old_funcs; + func_decl_ref_vector m_new_funcs; + public: + bit_blast_model_converter(ast_manager& m): + m(m), + m_bv(m), + m_old_funcs(m), + m_new_funcs(m) {} + + void insert(func_decl* old_f, func_decl* new_f) { + m_old_funcs.push_back(old_f); + m_new_funcs.push_back(new_f); + } + + virtual model_converter * translate(ast_translation & translator) { + return alloc(bit_blast_model_converter, m); + } + + virtual void operator()(model_ref & model) { + for (unsigned i = 0; i < m_new_funcs.size(); ++i) { + func_decl* p = m_new_funcs[i].get(); + func_decl* q = m_old_funcs[i].get(); + func_interp* f = model->get_func_interp(p); + expr_ref body(m); + unsigned arity_p = p->get_arity(); + unsigned arity_q = q->get_arity(); + SASSERT(0 < arity_p); + model->register_decl(p, f); + func_interp* g = alloc(func_interp, m, arity_q); + + if (f) { + body = f->get_interp(); + SASSERT(!f->is_partial()); + SASSERT(body); + } + else { + body = m.mk_false(); + } + unsigned idx = 0; + expr_ref arg(m), proj(m); + expr_safe_replace sub(m); + for (unsigned j = 0; j < arity_q; ++j) { + sort* s = q->get_domain(j); + arg = m.mk_var(j, s); + if (m_bv.is_bv_sort(s)) { + expr* args[1] = { arg }; + unsigned sz = m_bv.get_bv_size(s); + for (unsigned k = 0; k < sz; ++k) { + proj = m.mk_app(m_bv.get_family_id(), OP_BIT2BOOL, 1, args); + sub.insert(m.mk_var(idx++, m.mk_bool_sort()), proj); + } + } + else { + sub.insert(m.mk_var(idx++, s), arg); + } + } + sub(body); + g->set_else(body); + model->register_decl(q, g); + } + } + }; class expand_mkbv_cfg : public default_rewriter_cfg { @@ -43,7 +112,8 @@ namespace datalog { ast_manager& m; bv_util m_util; expr_ref_vector m_args, m_f_vars, m_g_vars; - func_decl_ref_vector m_pinned; + func_decl_ref_vector m_old_funcs; + func_decl_ref_vector m_new_funcs; obj_map m_pred2blast; @@ -57,10 +127,14 @@ namespace datalog { m_args(m), m_f_vars(m), m_g_vars(m), - m_pinned(m) + m_old_funcs(m), + m_new_funcs(m) {} ~expand_mkbv_cfg() {} + + func_decl_ref_vector const& old_funcs() const { return m_old_funcs; } + func_decl_ref_vector const& new_funcs() const { return m_new_funcs; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { rule_manager& rm = m_context.get_rule_manager(); @@ -105,13 +179,18 @@ namespace datalog { domain.push_back(m.get_sort(m_args[i].get())); } g = m_context.mk_fresh_head_predicate(f->get_name(), symbol("bv"), m_args.size(), domain.c_ptr(), f); - m_pinned.push_back(g); + m_old_funcs.push_back(f); + m_new_funcs.push_back(g); m_pred2blast.insert(f, g); // Create rule f(mk_mkbv(args)) :- g(args) fml = m.mk_implies(m.mk_app(g, m_g_vars.size(), m_g_vars.c_ptr()), m.mk_app(f, m_f_vars.size(), m_f_vars.c_ptr())); - rm.mk_rule(fml, m_rules, g->get_name()); + proof_ref pr(m); + if (m_context.generate_proof_trace()) { + pr = m.mk_asserted(fml); // or a def? + } + rm.mk_rule(fml, pr, m_rules, g->get_name()); } result = m.mk_app(g, m_args.size(), m_args.c_ptr()); result_pr = 0; @@ -134,18 +213,25 @@ namespace datalog { ast_manager & m; params_ref m_params; rule_ref_vector m_rules; + mk_interp_tail_simplifier m_simplifier; bit_blaster_rewriter m_blaster; expand_mkbv m_rewriter; - bool blast(expr_ref& fml) { + bool blast(rule *r, expr_ref& fml) { proof_ref pr(m); - expr_ref fml1(m), fml2(m); - m_blaster(fml, fml1, pr); - m_rewriter(fml1, fml2); - TRACE("dl", tout << mk_pp(fml, m) << " -> " << mk_pp(fml1, m) << " -> " << mk_pp(fml2, m) << "\n";); - if (fml2 != fml) { - fml = fml2; + expr_ref fml1(m), fml2(m), fml3(m); + rule_ref r2(m_context.get_rule_manager()); + // We need to simplify rule before bit-blasting. + if (!m_simplifier.transform_rule(r, r2)) { + r2 = r; + } + r2->to_formula(fml1); + m_blaster(fml1, fml2, pr); + m_rewriter(fml2, fml3); + TRACE("dl", tout << mk_pp(fml, m) << " -> " << mk_pp(fml2, m) << " -> " << mk_pp(fml3, m) << "\n";); + if (fml3 != fml) { + fml = fml3; return true; } else { @@ -163,6 +249,7 @@ namespace datalog { m(ctx.get_manager()), m_params(ctx.get_params().p), m_rules(ctx.get_rule_manager()), + m_simplifier(ctx), m_blaster(ctx.get_manager(), m_params), m_rewriter(ctx.get_manager(), ctx, m_rules) { m_params.set_bool("blast_full", true); @@ -170,24 +257,25 @@ namespace datalog { m_blaster.updt_params(m_params); } - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * operator()(rule_set const & source) { + // TODO pc if (!m_context.get_params().bit_blast()) { return 0; } - if (m_context.get_engine() != PDR_ENGINE) { - return 0; - } rule_manager& rm = m_context.get_rule_manager(); unsigned sz = source.get_num_rules(); expr_ref fml(m); reset(); rule_set * result = alloc(rule_set, m_context); - for (unsigned i = 0; i < sz; ++i) { + for (unsigned i = 0; !m_context.canceled() && i < sz; ++i) { rule * r = source.get_rule(i); - r->to_formula(fml); - if (blast(fml)) { - rm.mk_rule(fml, m_rules, r->name()); + r->to_formula(fml); + if (blast(r, fml)) { + proof_ref pr(m); + if (m_context.generate_proof_trace()) { + pr = m.mk_asserted(fml); // loses original proof of r. + } + rm.mk_rule(fml, pr, m_rules, r->name()); } else { m_rules.push_back(r); @@ -197,6 +285,18 @@ namespace datalog { for (unsigned i = 0; i < m_rules.size(); ++i) { result->add_rule(m_rules.get(i)); } + + if (m_context.get_model_converter()) { + filter_model_converter* fmc = alloc(filter_model_converter, m); + bit_blast_model_converter* bvmc = alloc(bit_blast_model_converter, m); + func_decl_ref_vector const& old_funcs = m_rewriter.m_cfg.old_funcs(); + func_decl_ref_vector const& new_funcs = m_rewriter.m_cfg.new_funcs(); + for (unsigned i = 0; i < old_funcs.size(); ++i) { + fmc->insert(new_funcs[i]); + bvmc->insert(old_funcs[i], new_funcs[i]); + } + m_context.add_model_converter(concat(bvmc, fmc)); + } return result; } @@ -210,8 +310,8 @@ namespace datalog { dealloc(m_impl); } - rule_set * mk_bit_blast::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - return (*m_impl)(source, mc, pc); + rule_set * mk_bit_blast::operator()(rule_set const & source) { + return (*m_impl)(source); } }; diff --git a/src/muz_qe/dl_mk_bit_blast.h b/src/muz_qe/dl_mk_bit_blast.h index e16c2058b..da91c5804 100644 --- a/src/muz_qe/dl_mk_bit_blast.h +++ b/src/muz_qe/dl_mk_bit_blast.h @@ -7,7 +7,7 @@ Module Name: Abstract: - + Functor for bit-blasting a rule set Author: @@ -19,32 +19,17 @@ Revision History: #ifndef _DL_MK_BIT_BLAST_H_ #define _DL_MK_BIT_BLAST_H_ -#include - -#include"map.h" -#include"obj_pair_hashtable.h" -#include"dl_context.h" -#include"dl_rule_set.h" #include"dl_rule_transformer.h" namespace datalog { - /** - \brief Functor for bit-blasting a rule set. - */ - class mk_bit_blast : public rule_transformer::plugin { class impl; - - impl* m_impl; - void blast(expr_ref& b); - void reset(); - + impl* m_impl; public: mk_bit_blast(context & ctx, unsigned priority = 35000); - ~mk_bit_blast(); - - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + ~mk_bit_blast(); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_coalesce.cpp b/src/muz_qe/dl_mk_coalesce.cpp index 222881bc4..94c42e33b 100644 --- a/src/muz_qe/dl_mk_coalesce.cpp +++ b/src/muz_qe/dl_mk_coalesce.cpp @@ -133,7 +133,7 @@ namespace datalog { tail.push_back(to_app(fml)); is_neg.push_back(false); res = rm.mk(head, tail.size(), tail.c_ptr(), is_neg.c_ptr(), tgt->name()); - if (m_pc) { + if (m_ctx.generate_proof_trace()) { src.to_formula(fml1); tgt->to_formula(fml2); res->to_formula(fml); @@ -142,12 +142,13 @@ namespace datalog { sort* domain[3] = { ps, ps, m.mk_bool_sort() }; func_decl* merge = m.mk_func_decl(symbol("merge-clauses"), 3, domain, ps); // TBD: ad-hoc proof rule expr* args[3] = { m.mk_asserted(fml1), m.mk_asserted(fml2), fml }; - m_pc->insert(m.mk_app(merge, 3, args)); + // ...m_pc->insert(m.mk_app(merge, 3, args)); #else svector > pos; vector substs; - proof* p = m.mk_asserted(fml1); - m_pc->insert(m.mk_hyper_resolve(1, &p, fml, pos, substs)); + proof* p = src.get_proof(); + p = m.mk_hyper_resolve(1, &p, fml, pos, substs); + res->set_proof(m, p); #endif } tgt = res; @@ -170,13 +171,7 @@ namespace datalog { return true; } - rule_set * mk_coalesce::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - m_pc = 0; - ref rpc; - if (pc) { - rpc = alloc(replace_proof_converter, m); - m_pc = rpc.get(); - } + rule_set * mk_coalesce::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); rule_set::decl2rules::iterator it = source.begin_grouped_rules(), end = source.end_grouped_rules(); for (; it != end; ++it) { @@ -195,9 +190,6 @@ namespace datalog { rules->add_rule(r1.get()); } } - if (pc) { - pc = concat(pc.get(), rpc.get()); - } return rules; } diff --git a/src/muz_qe/dl_mk_coalesce.h b/src/muz_qe/dl_mk_coalesce.h index 4259d31fe..4a4065174 100644 --- a/src/muz_qe/dl_mk_coalesce.h +++ b/src/muz_qe/dl_mk_coalesce.h @@ -37,7 +37,6 @@ namespace datalog { rule_manager& rm; expr_ref_vector m_sub1, m_sub2; unsigned m_idx; - replace_proof_converter* m_pc; void mk_pred(app_ref& pred, app* p1, app* p2); @@ -53,7 +52,7 @@ namespace datalog { */ mk_coalesce(context & ctx); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_coi_filter.cpp b/src/muz_qe/dl_mk_coi_filter.cpp index 04d654120..6aa688a3c 100644 --- a/src/muz_qe/dl_mk_coi_filter.cpp +++ b/src/muz_qe/dl_mk_coi_filter.cpp @@ -33,10 +33,7 @@ namespace datalog { // ----------------------------------- - rule_set * mk_coi_filter::operator()( - rule_set const & source, - model_converter_ref& mc, - proof_converter_ref& pc) + rule_set * mk_coi_filter::operator()(rule_set const & source) { if (source.get_num_rules()==0) { return 0; @@ -81,7 +78,7 @@ namespace datalog { if (interesting_preds.contains(pred)) { res->add_rule(r); } - else if (mc.get()) { + else if (m_context.get_model_converter()) { pruned_preds.insert(pred); } } @@ -90,14 +87,14 @@ namespace datalog { res = 0; } - if (res && mc) { + if (res && m_context.get_model_converter()) { decl_set::iterator end = pruned_preds.end(); decl_set::iterator it = pruned_preds.begin(); extension_model_converter* mc0 = alloc(extension_model_converter, m); for (; it != end; ++it) { mc0->insert(*it, m.mk_true()); } - mc = concat(mc.get(), mc0); + m_context.add_model_converter(mc0); } return res.detach(); diff --git a/src/muz_qe/dl_mk_coi_filter.h b/src/muz_qe/dl_mk_coi_filter.h index b8fb37964..b02bed9ec 100644 --- a/src/muz_qe/dl_mk_coi_filter.h +++ b/src/muz_qe/dl_mk_coi_filter.h @@ -38,10 +38,7 @@ namespace datalog { m(ctx.get_manager()), m_context(ctx) {} - - rule_set * operator()(rule_set const & source, - model_converter_ref& mc, - proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_explanations.cpp b/src/muz_qe/dl_mk_explanations.cpp index b4683bdbe..646a0bcbe 100644 --- a/src/muz_qe/dl_mk_explanations.cpp +++ b/src/muz_qe/dl_mk_explanations.cpp @@ -174,11 +174,9 @@ namespace datalog { } } -#if 1 virtual void deallocate() { get_plugin().recycle(this); } -#endif public: @@ -708,8 +706,8 @@ namespace datalog { } rule * mk_explanations::get_e_rule(rule * r) { - var_counter ctr; - ctr.count_vars(m_manager, r); + rule_counter ctr; + ctr.count_rule_vars(m_manager, r); unsigned max_var; unsigned next_var = ctr.get_max_positive(max_var) ? (max_var+1) : 0; unsigned head_var = next_var++; @@ -875,14 +873,12 @@ namespace datalog { } } - rule_set * mk_explanations::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - SASSERT(!mc && !pc); + rule_set * mk_explanations::operator()(rule_set const & source) { + if(source.get_num_rules()==0) { return 0; } - m_context.collect_predicates(m_original_preds); - rule_set * res = alloc(rule_set, m_context); transform_facts(m_context.get_rel_context().get_rmanager()); transform_rules(source, *res); diff --git a/src/muz_qe/dl_mk_explanations.h b/src/muz_qe/dl_mk_explanations.h index 36fb1a3a1..4e7e23e98 100644 --- a/src/muz_qe/dl_mk_explanations.h +++ b/src/muz_qe/dl_mk_explanations.h @@ -82,7 +82,7 @@ namespace datalog { return get_union_decl(m_context); } - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); static expr* get_explanation(relation_base const& r); }; diff --git a/src/muz_qe/dl_mk_extract_quantifiers.cpp b/src/muz_qe/dl_mk_extract_quantifiers.cpp index 35f7485d5..76e329d79 100644 --- a/src/muz_qe/dl_mk_extract_quantifiers.cpp +++ b/src/muz_qe/dl_mk_extract_quantifiers.cpp @@ -168,7 +168,15 @@ namespace datalog { fml = m.mk_implies(m.mk_and(fmls.size(), fmls.c_ptr()), r.get_head()); TRACE("dl", tout << "new rule\n" << mk_pp(fml, m) << "\n";); rule_ref_vector rules(rm); - rm.mk_rule(fml, rules, r.name()); + proof_ref pr(m); + if (m_ctx.generate_proof_trace()) { + scoped_proof _scp(m); + expr_ref fml1(m); + r.to_formula(fml1); + pr = m.mk_rewrite(fml1, fml); + pr = m.mk_modus_ponens(r.get_proof(), pr); + } + rm.mk_rule(fml, pr, rules, r.name()); for (unsigned i = 0; i < rules.size(); ++i) { new_rules.add_rule(rules[i].get()); m_quantifiers.insert(rules[i].get(), alloc(quantifier_ref_vector, qs)); @@ -347,7 +355,7 @@ namespace datalog { m_quantifiers.reset(); } - rule_set * mk_extract_quantifiers::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { + rule_set * mk_extract_quantifiers::operator()(rule_set const & source) { reset(); rule_set::iterator it = source.begin(), end = source.end(); for (; !m_has_quantifiers && it != end; ++it) { diff --git a/src/muz_qe/dl_mk_extract_quantifiers.h b/src/muz_qe/dl_mk_extract_quantifiers.h index b32dbc32d..27b13cd71 100644 --- a/src/muz_qe/dl_mk_extract_quantifiers.h +++ b/src/muz_qe/dl_mk_extract_quantifiers.h @@ -77,7 +77,7 @@ namespace datalog { void set_query(func_decl* q); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); bool has_quantifiers() { return m_has_quantifiers; } diff --git a/src/muz_qe/dl_mk_filter_rules.cpp b/src/muz_qe/dl_mk_filter_rules.cpp index 4f01a3651..62abd78c4 100644 --- a/src/muz_qe/dl_mk_filter_rules.cpp +++ b/src/muz_qe/dl_mk_filter_rules.cpp @@ -90,6 +90,7 @@ namespace datalog { rule * filter_rule = m_context.get_rule_manager().mk(filter_head, 1, &filter_tail, (const bool *)0); filter_rule->set_accounting_parent_object(m_context, m_current); m_result->add_rule(filter_rule); + m_context.get_rule_manager().mk_rule_asserted_proof(*filter_rule); } else { dealloc(key); @@ -135,12 +136,13 @@ namespace datalog { } new_is_negated.push_back(r->is_neg_tail(i)); } - if(rule_modified) { + if (rule_modified) { remove_duplicate_tails(new_tail, new_is_negated); SASSERT(new_tail.size() == new_is_negated.size()); rule * new_rule = m_context.get_rule_manager().mk(new_head, new_tail.size(), new_tail.c_ptr(), new_is_negated.c_ptr()); new_rule->set_accounting_parent_object(m_context, m_current); m_result->add_rule(new_rule); + m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule); m_modified = true; } else { @@ -148,7 +150,7 @@ namespace datalog { } } - rule_set * mk_filter_rules::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { + rule_set * mk_filter_rules::operator()(rule_set const & source) { // TODO mc, pc m_tail2filter.reset(); m_result = alloc(rule_set, m_context); diff --git a/src/muz_qe/dl_mk_filter_rules.h b/src/muz_qe/dl_mk_filter_rules.h index cd1d10997..4a247fdb5 100644 --- a/src/muz_qe/dl_mk_filter_rules.h +++ b/src/muz_qe/dl_mk_filter_rules.h @@ -72,7 +72,7 @@ namespace datalog { /** \brief Return a new rule set where only filter rules contain atoms with repeated variables and/or values. */ - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp index d028c8751..0d468f9ef 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz_qe/dl_mk_interp_tail_simplifier.cpp @@ -35,7 +35,7 @@ namespace datalog { // ----------------------------------- void mk_interp_tail_simplifier::rule_substitution::reset(rule * r) { - unsigned var_cnt = m_context.get_rule_manager().get_var_counter().get_max_var(*r)+1; + unsigned var_cnt = m_context.get_rule_manager().get_counter().get_max_rule_var(*r)+1; m_subst.reset(); m_subst.reserve(1, var_cnt); m_rule = r; @@ -296,18 +296,40 @@ namespace datalog { br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - if (m.is_not(f)) { + + if (m.is_not(f) && (m.is_and(args[0]) || m.is_or(args[0]))) { SASSERT(num==1); - if (m.is_and(args[0]) || m.is_or(args[0])) { - expr_ref e(m.mk_not(args[0]),m); - if (push_toplevel_junction_negation_inside(e)) { - result = e; - return BR_REWRITE2; - } + expr_ref tmp(m); + app* a = to_app(args[0]); + m_app_args.reset(); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + m_brwr.mk_not(a->get_arg(i), tmp); + m_app_args.push_back(tmp); } + if (m.is_and(args[0])) { + result = m.mk_or(m_app_args.size(), m_app_args.c_ptr()); + } + else { + result = m.mk_and(m_app_args.size(), m_app_args.c_ptr()); + } + return BR_REWRITE2; + } + if (!m.is_and(f) && !m.is_or(f)) { + return BR_FAILED; + } + if (num == 0) { + if (m.is_and(f)) { + result = m.mk_true(); + } + else { + result = m.mk_false(); + } + return BR_DONE; + } + if (num == 1) { + result = args[0]; + return BR_DONE; } - if (!m.is_and(f) && !m.is_or(f)) { return BR_FAILED; } - if (num<2) { return BR_FAILED; } m_app_args.reset(); m_app_args.append(num, args); @@ -318,30 +340,18 @@ namespace datalog { bool have_rewritten_args = false; - if (m.is_or(f) || m.is_and(f)) { - have_rewritten_args = detect_equivalences(m_app_args, m.is_or(f)); -#if 0 - if (have_rewritten_args) { - std::sort(m_app_args.c_ptr(), m_app_args.c_ptr()+m_app_args.size(), m_expr_cmp); - - app_ref orig(m.mk_app(f, num, args),m); - app_ref res(m.mk_app(f, m_app_args.size(), m_app_args.c_ptr()),m); - std::cout<<"s:"<get_uninterpreted_tail_size(); unsigned len = r->get_tail_size(); - if (u_len==len) { + if (u_len == len) { res = r; return true; } @@ -504,34 +514,29 @@ namespace datalog { expr_ref simp_res(m); simplify_expr(itail.get(), simp_res); - modified |= itail.get()!=simp_res.get(); - - if (is_app(simp_res.get())) { - itail = to_app(simp_res.get()); - } - else if (m.is_bool(simp_res)) { - itail = m.mk_eq(simp_res, m.mk_true()); - } - else { - throw default_exception("simplification resulted in non-boolean non-function"); - } - - if (m.is_false(itail.get())) { - //the tail member is never true, so we may delete the rule + modified |= itail.get() != simp_res.get(); + + if (m.is_false(simp_res)) { TRACE("dl", r->display(m_context, tout << "rule is infeasible\n");); return false; } - if (!m.is_true(itail.get())) { - //if the simplified tail is not a tautology, we add it to the rule - tail.push_back(itail); - tail_neg.push_back(false); - } - else { - modified = true; - } + SASSERT(m.is_bool(simp_res)); - SASSERT(tail.size() == tail_neg.size()); if (modified) { + expr_ref_vector conjs(m); + flatten_and(simp_res, conjs); + for (unsigned i = 0; i < conjs.size(); ++i) { + expr* e = conjs[i].get(); + if (is_app(e)) { + tail.push_back(to_app(e)); + } + else { + tail.push_back(m.mk_eq(e, m.mk_true())); + } + tail_neg.push_back(false); + } + + SASSERT(tail.size() == tail_neg.size()); res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); res->set_accounting_parent_object(m_context, r); } @@ -541,8 +546,8 @@ namespace datalog { rule_ref pro_var_eq_result(m_context.get_rule_manager()); if (propagate_variable_equivalences(res, pro_var_eq_result)) { - SASSERT(var_counter().get_max_var(*r.get())==0 || - var_counter().get_max_var(*r.get()) > var_counter().get_max_var(*pro_var_eq_result.get())); + SASSERT(rule_counter().get_max_rule_var(*r.get())==0 || + rule_counter().get_max_rule_var(*r.get()) > rule_counter().get_max_rule_var(*pro_var_eq_result.get())); r = pro_var_eq_result; goto start; } @@ -554,11 +559,13 @@ namespace datalog { bool mk_interp_tail_simplifier::transform_rules(const rule_set & orig, rule_set & tgt) { bool modified = false; + rule_manager& rm = m_context.get_rule_manager(); rule_set::iterator rit = orig.begin(); rule_set::iterator rend = orig.end(); for (; rit!=rend; ++rit) { - rule_ref new_rule(m_context.get_rule_manager()); + rule_ref new_rule(rm); if (transform_rule(*rit, new_rule)) { + rm.mk_rule_rewrite_proof(**rit, *new_rule.get()); bool is_modified = *rit != new_rule; modified |= is_modified; tgt.add_rule(new_rule); @@ -570,8 +577,7 @@ namespace datalog { return modified; } - rule_set * mk_interp_tail_simplifier::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_interp_tail_simplifier::operator()(rule_set const & source) { if (source.get_num_rules() == 0) { return 0; } diff --git a/src/muz_qe/dl_mk_interp_tail_simplifier.h b/src/muz_qe/dl_mk_interp_tail_simplifier.h index 5e4fd8fbc..247b20755 100644 --- a/src/muz_qe/dl_mk_interp_tail_simplifier.h +++ b/src/muz_qe/dl_mk_interp_tail_simplifier.h @@ -93,7 +93,7 @@ namespace datalog { */ bool transform_rule(rule * r, rule_ref& res); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_karr_invariants.cpp b/src/muz_qe/dl_mk_karr_invariants.cpp new file mode 100644 index 000000000..c4a6a3cdb --- /dev/null +++ b/src/muz_qe/dl_mk_karr_invariants.cpp @@ -0,0 +1,1062 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_karr_invariants.cpp + +Abstract: + + Extract integer linear invariants. + + The linear invariants are extracted according to Karr's method. + A short description is in + Nikolaj Bjorner, Anca Browne and Zohar Manna. Automatic Generation + of Invariants and Intermediate Assertions, in CP 95. + + The algorithm is here adapted to Horn clauses. + The idea is to maintain two data-structures for each recursive relation. + We call them R and RD + - R - set of linear congruences that are true of R. + - RD - the dual basis of of solutions for R. + + RD is updated by accumulating basis vectors for solutions + to R (the homogeneous dual of R) + R is updated from the inhomogeneous dual of RD. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-09 + +Revision History: + +--*/ + +#include"dl_mk_karr_invariants.h" +#include"expr_safe_replace.h" +#include"bool_rewriter.h" + +namespace datalog { + + + mk_karr_invariants::mk_karr_invariants(context & ctx, unsigned priority): + rule_transformer::plugin(priority, false), + m_ctx(ctx), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_inner_ctx(m, ctx.get_fparams()), + a(m) { + params_ref params; + params.set_sym("default_relation", symbol("karr_relation")); + params.set_sym("engine", symbol("datalog")); + params.set_bool("karr", false); + m_inner_ctx.updt_params(params); + } + + mk_karr_invariants::~mk_karr_invariants() { } + + matrix& matrix::operator=(matrix const& other) { + reset(); + append(other); + return *this; + } + + void matrix::display_row( + std::ostream& out, vector const& row, rational const& b, bool is_eq) { + for (unsigned j = 0; j < row.size(); ++j) { + out << row[j] << " "; + } + out << (is_eq?" = ":" >= ") << -b << "\n"; + } + + void matrix::display_ineq( + std::ostream& out, vector const& row, rational const& b, bool is_eq) { + bool first = true; + for (unsigned j = 0; j < row.size(); ++j) { + if (!row[j].is_zero()) { + if (!first && row[j].is_pos()) { + out << "+ "; + } + if (row[j].is_minus_one()) { + out << "- "; + } + if (row[j] > rational(1) || row[j] < rational(-1)) { + out << row[j] << "*"; + } + out << "x" << j << " "; + first = false; + } + } + out << (is_eq?"= ":">= ") << -b << "\n"; + } + + void matrix::display(std::ostream& out) const { + for (unsigned i = 0; i < A.size(); ++i) { + display_row(out, A[i], b[i], eq[i]); + } + } + + + class mk_karr_invariants::add_invariant_model_converter : public model_converter { + ast_manager& m; + arith_util a; + func_decl_ref_vector m_funcs; + expr_ref_vector m_invs; + public: + + add_invariant_model_converter(ast_manager& m): m(m), a(m), m_funcs(m), m_invs(m) {} + + virtual ~add_invariant_model_converter() { } + + void add(func_decl* p, expr* inv) { + if (!m.is_true(inv)) { + m_funcs.push_back(p); + m_invs.push_back(inv); + } + } + + virtual void operator()(model_ref & mr) { + for (unsigned i = 0; i < m_funcs.size(); ++i) { + func_decl* p = m_funcs[i].get(); + func_interp* f = mr->get_func_interp(p); + expr_ref body(m); + unsigned arity = p->get_arity(); + SASSERT(0 < arity); + if (f) { + SASSERT(f->num_entries() == 0); + if (!f->is_partial()) { + bool_rewriter(m).mk_and(f->get_else(), m_invs[i].get(), body); + } + } + else { + f = alloc(func_interp, m, arity); + mr->register_decl(p, f); + body = m.mk_false(); // fragile: assume that relation was pruned by being infeasible. + } + f->set_else(body); + } + } + + virtual model_converter * translate(ast_translation & translator) { + add_invariant_model_converter* mc = alloc(add_invariant_model_converter, m); + for (unsigned i = 0; i < m_funcs.size(); ++i) { + mc->add(translator(m_funcs[i].get()), m_invs[i].get()); + } + return mc; + } + + private: + void mk_body(matrix const& M, expr_ref& body) { + expr_ref_vector conj(m); + for (unsigned i = 0; i < M.size(); ++i) { + mk_body(M.A[i], M.b[i], M.eq[i], conj); + } + bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), body); + } + + void mk_body(vector const& row, rational const& b, bool is_eq, expr_ref_vector& conj) { + expr_ref_vector sum(m); + expr_ref zero(m), lhs(m); + zero = a.mk_numeral(rational(0), true); + + for (unsigned i = 0; i < row.size(); ++i) { + if (row[i].is_zero()) { + continue; + } + var* var = m.mk_var(i, a.mk_int()); + if (row[i].is_one()) { + sum.push_back(var); + } + else { + sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var)); + } + } + if (!b.is_zero()) { + sum.push_back(a.mk_numeral(b, true)); + } + lhs = a.mk_add(sum.size(), sum.c_ptr()); + if (is_eq) { + conj.push_back(m.mk_eq(lhs, zero)); + } + else { + conj.push_back(a.mk_ge(lhs, zero)); + } + } + }; + + void mk_karr_invariants::cancel() { + m_inner_ctx.cancel(); + } + + rule_set * mk_karr_invariants::operator()(rule_set const & source) { + if (!m_ctx.get_params().karr()) { + return 0; + } + rule_set::iterator it = source.begin(), end = source.end(); + for (; it != end; ++it) { + rule const& r = **it; + if (r.has_negation()) { + return 0; + } + } + rel_context& rctx = m_inner_ctx.get_rel_context(); + ptr_vector heads; + m_inner_ctx.ensure_opened(); + it = source.begin(); + for (; it != end; ++it) { + rule_ref r(*it, m_inner_ctx.get_rule_manager()); + m_inner_ctx.add_rule(r); + m_inner_ctx.register_predicate(r->get_decl(), false); + } + m_inner_ctx.close(); + rule_set::decl2rules::iterator dit = source.begin_grouped_rules(); + rule_set::decl2rules::iterator dend = source.end_grouped_rules(); + for (; dit != dend; ++dit) { + heads.push_back(dit->m_key); + } + m_inner_ctx.rel_query(heads.size(), heads.c_ptr()); + + rule_set* rules = alloc(rule_set, m_ctx); + it = source.begin(); + for (; it != end; ++it) { + update_body(rctx, *rules, **it); + } + if (m_ctx.get_model_converter()) { + add_invariant_model_converter* kmc = alloc(add_invariant_model_converter, m); + rule_set::decl2rules::iterator git = source.begin_grouped_rules(); + rule_set::decl2rules::iterator gend = source.end_grouped_rules(); + for (; git != gend; ++git) { + func_decl* p = git->m_key; + expr_ref fml(m); + relation_base* rb = rctx.try_get_relation(p); + if (rb) { + rb->to_formula(fml); + kmc->add(p, fml); + } + } + m_ctx.add_model_converter(kmc); + } + TRACE("dl", rules->display(tout);); + return rules; + } + + void mk_karr_invariants::update_body(rel_context& rctx, rule_set& rules, rule& r) { + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + app_ref_vector tail(m); + expr_ref fml(m); + for (unsigned i = 0; i < tsz; ++i) { + tail.push_back(r.get_tail(i)); + } + for (unsigned i = 0; i < utsz; ++i) { + func_decl* q = r.get_decl(i); + relation_base* rb = rctx.try_get_relation(r.get_decl(i)); + if (rb) { + rb->to_formula(fml); + expr_safe_replace rep(m); + for (unsigned j = 0; j < q->get_arity(); ++j) { + rep.insert(m.mk_var(j, q->get_domain(j)), + r.get_tail(i)->get_arg(j)); + } + rep(fml); + tail.push_back(to_app(fml)); + } + } + rule* new_rule = &r; + if (tail.size() != tsz) { + new_rule = rm.mk(r.get_head(), tail.size(), tail.c_ptr(), 0, r.name()); + } + rules.add_rule(new_rule); + rm.mk_rule_rewrite_proof(r, *new_rule); // should be weakening rule. + } + + + + class karr_relation : public relation_base { + friend class karr_relation_plugin; + friend class karr_relation_plugin::filter_equal_fn; + + karr_relation_plugin& m_plugin; + ast_manager& m; + mutable arith_util a; + func_decl_ref m_fn; + mutable bool m_empty; + mutable matrix m_ineqs; + mutable bool m_ineqs_valid; + mutable matrix m_basis; + mutable bool m_basis_valid; + + public: + karr_relation(karr_relation_plugin& p, func_decl* f, relation_signature const& s, bool is_empty): + relation_base(p, s), + m_plugin(p), + m(p.get_ast_manager()), + a(m), + m_fn(f, m), + m_empty(is_empty), + m_ineqs_valid(!is_empty), + m_basis_valid(false) + { + } + + virtual bool empty() const { + return m_empty; + } + + virtual void add_fact(const relation_fact & f) { + SASSERT(m_empty); + SASSERT(!m_basis_valid); + m_empty = false; + m_ineqs_valid = true; + for (unsigned i = 0; i < f.size(); ++i) { + rational n; + if (a.is_numeral(f[i], n) && n.is_int()) { + vector row; + row.resize(f.size()); + row[i] = rational(1); + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(-n); + m_ineqs.eq.push_back(true); + } + } + } + + virtual bool contains_fact(const relation_fact & f) const { + UNREACHABLE(); + return false; + } + + virtual void display(std::ostream & out) const { + if (m_fn) { + out << m_fn->get_name() << "\n"; + } + if (empty()) { + out << "empty\n"; + } + else { + if (m_ineqs_valid) { + m_ineqs.display(out << "ineqs:\n"); + } + if (m_basis_valid) { + m_basis.display(out << "basis:\n"); + } + } + } + + virtual karr_relation * clone() const { + karr_relation* result = alloc(karr_relation, m_plugin, m_fn, get_signature(), m_empty); + result->copy(*this); + return result; + } + + virtual karr_relation * complement(func_decl*) const { + UNREACHABLE(); + return 0; + } + + virtual void to_formula(expr_ref& fml) const { + if (empty()) { + fml = m.mk_false(); + } + else { + matrix const& M = get_ineqs(); + expr_ref_vector conj(m); + for (unsigned i = 0; i < M.size(); ++i) { + to_formula(M.A[i], M.b[i], M.eq[i], conj); + } + bool_rewriter(m).mk_and(conj.size(), conj.c_ptr(), fml); + } + } + + karr_relation_plugin& get_plugin() const { return m_plugin; } + + void filter_interpreted(app* cond) { + rational one(1), mone(-1); + expr* e1, *e2, *en; + var* v, *w; + rational n1, n2; + expr_ref_vector conjs(m); + datalog::flatten_and(cond, conjs); + matrix& M = get_ineqs(); + unsigned num_columns = get_signature().size(); + + for (unsigned i = 0; i < conjs.size(); ++i) { + expr* e = conjs[i].get(); + rational b(0); + vector row; + row.resize(num_columns, rational(0)); + bool processed = true; + if (m.is_eq(e, e1, e2) && is_linear(e1, row, b, one) && is_linear(e2, row, b, mone)) { + M.A.push_back(row); + M.b.push_back(b); + M.eq.push_back(true); + } + else if ((a.is_le(e, e1, e2) || a.is_ge(e, e2, e1)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b); + M.eq.push_back(false); + } + else if ((a.is_lt(e, e1, e2) || a.is_gt(e, e2, e1)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b - rational(1)); + M.eq.push_back(false); + } + else if (m.is_not(e, en) && (a.is_lt(en, e2, e1) || a.is_gt(en, e1, e2)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b); + M.eq.push_back(false); + } + else if (m.is_not(e, en) && (a.is_le(en, e2, e1) || a.is_ge(en, e1, e2)) && + is_linear(e1, row, b, mone) && is_linear(e2, row, b, one)) { + M.A.push_back(row); + M.b.push_back(b - rational(1)); + M.eq.push_back(false); + } + else if (m.is_or(e, e1, e2) && is_eq(e1, v, n1) && is_eq(e2, w, n2) && v == w) { + if (n1 > n2) { + std::swap(n1, n2); + } + SASSERT(n1 <= n2); + row[v->get_idx()] = rational(1); + // v - n1 >= 0 + M.A.push_back(row); + M.b.push_back(-n1); + M.eq.push_back(false); + // -v + n2 >= 0 + row[v->get_idx()] = rational(-1); + M.A.push_back(row); + M.b.push_back(n2); + M.eq.push_back(false); + } + else { + processed = false; + } + TRACE("dl", tout << (processed?"+ ":"- ") << mk_pp(e, m) << "\n"; + if (processed) matrix::display_ineq(tout, row, M.b.back(), M.eq.back()); + ); + } + TRACE("dl", display(tout);); + } + + void mk_join(karr_relation const& r1, karr_relation const& r2, + unsigned col_cnt, unsigned const* cols1, unsigned const* cols2) { + if (r1.empty() || r2.empty()) { + m_empty = true; + return; + } + matrix const& M1 = r1.get_ineqs(); + matrix const& M2 = r2.get_ineqs(); + unsigned sig1_size = r1.get_signature().size(); + unsigned sig_size = get_signature().size(); + m_ineqs.reset(); + for (unsigned i = 0; i < M1.size(); ++i) { + vector row; + row.append(M1.A[i]); + row.resize(sig_size); + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(M1.b[i]); + m_ineqs.eq.push_back(M1.eq[i]); + } + for (unsigned i = 0; i < M2.size(); ++i) { + vector row; + row.resize(sig_size); + for (unsigned j = 0; j < M2.A[i].size(); ++j) { + row[sig1_size + j] = M2.A[i][j]; + } + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(M2.b[i]); + m_ineqs.eq.push_back(M2.eq[i]); + } + for (unsigned i = 0; i < col_cnt; ++i) { + vector row; + row.resize(sig_size); + row[cols1[i]] = rational(1); + row[sig1_size + cols2[i]] = rational(-1); + m_ineqs.A.push_back(row); + m_ineqs.b.push_back(rational(0)); + m_ineqs.eq.push_back(true); + } + m_ineqs_valid = true; + m_basis_valid = false; + m_empty = false; + if (r1.m_fn) { + m_fn = r1.m_fn; + } + if (r2.m_fn) { + m_fn = r2.m_fn; + } + } + + void mk_project(karr_relation const& r, unsigned cnt, unsigned const* cols) { + if (r.m_empty) { + m_empty = true; + return; + } + matrix const& M = r.get_basis(); + m_basis.reset(); + for (unsigned i = 0; i < M.size(); ++i) { + vector row; + unsigned k = 0; + for (unsigned j = 0; j < M.A[i].size(); ++j) { + if (k < cnt && j == cols[k]) { + ++k; + } + else { + row.push_back(M.A[i][j]); + } + } + SASSERT(row.size() + cnt == M.A[i].size()); + SASSERT(M.eq[i]); + m_basis.A.push_back(row); + m_basis.b.push_back(M.b[i]); + m_basis.eq.push_back(true); + } + m_basis_valid = true; + m_ineqs_valid = false; + m_empty = false; + m_fn = r.m_fn; + + TRACE("dl", + for (unsigned i = 0; i < cnt; ++i) { + tout << cols[i] << " "; + } + tout << "\n"; + r.display(tout); + display(tout);); + } + + void mk_rename(const karr_relation & r, unsigned col_cnt, const unsigned * cols) { + if (r.empty()) { + m_empty = true; + return; + } + m_ineqs.reset(); + m_basis.reset(); + m_ineqs_valid = r.m_ineqs_valid; + m_basis_valid = r.m_basis_valid; + if (m_ineqs_valid) { + m_ineqs.append(r.m_ineqs); + mk_rename(m_ineqs, col_cnt, cols); + } + if (m_basis_valid) { + m_basis.append(r.m_basis); + mk_rename(m_basis, col_cnt, cols); + } + m_fn = r.m_fn; + TRACE("dl", r.display(tout); display(tout);); + } + + void mk_union(karr_relation const& src, karr_relation* delta) { + if (src.empty()) { + if (delta) { + delta->m_empty = true; + } + return; + } + matrix const& M = src.get_basis(); + if (empty()) { + m_basis = M; + m_basis_valid = true; + m_empty = false; + m_ineqs_valid = false; + if (delta) { + delta->copy(*this); + } + return; + } + matrix& N = get_basis(); + unsigned N_size = N.size(); + for (unsigned i = 0; i < M.size(); ++i) { + bool found = false; + for (unsigned j = 0; !found && j < N_size; ++j) { + found = + same_row(M.A[i], N.A[j]) && + M.b[i] == N.b[j] && + M.eq[i] == N.eq[j]; + } + if (!found) { + N.A.push_back(M.A[i]); + N.b.push_back(M.b[i]); + N.eq.push_back(M.eq[i]); + } + } + m_ineqs_valid = false; + if (N_size != N.size()) { + if (delta) { + delta->copy(*this); + } + } + } + + matrix const& get_basis() const { + init_basis(); + return m_basis; + } + + matrix& get_basis() { + init_basis(); + return m_basis; + } + + matrix const& get_ineqs() const { + init_ineqs(); + return m_ineqs; + } + + matrix & get_ineqs() { + init_ineqs(); + return m_ineqs; + } + + private: + + void copy(karr_relation const& other) { + m_ineqs = other.m_ineqs; + m_basis = other.m_basis; + m_basis_valid = other.m_basis_valid; + m_ineqs_valid = other.m_ineqs_valid; + m_empty = other.m_empty; + } + + bool same_row(vector const& r1, vector const& r2) const { + SASSERT(r1.size() == r2.size()); + for (unsigned i = 0; i < r1.size(); ++i) { + if (r1[i] != r2[i]) { + return false; + } + } + return true; + } + + void mk_rename(matrix& M, unsigned col_cnt, unsigned const* cols) { + for (unsigned j = 0; j < M.size(); ++j) { + vector & row = M.A[j]; + rational tmp = row[cols[0]]; + for (unsigned i = 0; i + 1 < col_cnt; ++i) { + row[cols[i]] = row[cols[i+1]]; + } + row[cols[col_cnt-1]] = tmp; + } + } + + bool is_eq(expr* e, var*& v, rational& n) { + expr* e1, *e2; + if (!m.is_eq(e, e1, e2)) { + return false; + } + if (!is_var(e1)) { + std::swap(e1, e2); + } + if (!is_var(e1)) { + return false; + } + v = to_var(e1); + if (!a.is_numeral(e2, n)) { + return false; + } + return true; + } + + bool is_linear(expr* e, vector& row, rational& b, rational const& mul) { + if (!a.is_int(e)) { + return false; + } + if (is_var(e)) { + row[to_var(e)->get_idx()] += mul; + return true; + } + if (!is_app(e)) { + return false; + } + rational n; + if (a.is_numeral(e, n)) { + b += mul*n; + return true; + } + if (a.is_add(e)) { + for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { + if (!is_linear(to_app(e)->get_arg(i), row, b, mul)) { + return false; + } + } + return true; + } + expr* e1, *e2; + if (a.is_sub(e, e1, e2)) { + return is_linear(e1, row, b, mul) && is_linear(e2, row, b, -mul); + } + if (a.is_mul(e, e1, e2) && a.is_numeral(e1, n)) { + return is_linear(e2, row, b, mul*n); + } + if (a.is_mul(e, e1, e2) && a.is_numeral(e2, n)) { + return is_linear(e1, row, b, mul*n); + } + if (a.is_uminus(e, e1)) { + return is_linear(e1, row, b, -mul); + } + return false; + } + + void init_ineqs() const { + if (!m_ineqs_valid) { + SASSERT(m_basis_valid); + m_plugin.dualizeH(m_ineqs, m_basis); + m_ineqs_valid = true; + } + } + + void init_basis() const { + if (!m_basis_valid) { + SASSERT(m_ineqs_valid); + if (m_plugin.dualizeI(m_basis, m_ineqs)) { + m_basis_valid = true; + } + else { + m_empty = true; + } + } + } + + void to_formula(vector const& row, rational const& b, bool is_eq, expr_ref_vector& conj) const { + expr_ref_vector sum(m); + expr_ref zero(m), lhs(m); + zero = a.mk_numeral(rational(0), true); + + for (unsigned i = 0; i < row.size(); ++i) { + if (row[i].is_zero()) { + continue; + } + var* var = m.mk_var(i, a.mk_int()); + if (row[i].is_one()) { + sum.push_back(var); + } + else { + sum.push_back(a.mk_mul(a.mk_numeral(row[i], true), var)); + } + } + if (!b.is_zero()) { + sum.push_back(a.mk_numeral(b, true)); + } + lhs = a.mk_add(sum.size(), sum.c_ptr()); + if (is_eq) { + conj.push_back(m.mk_eq(lhs, zero)); + } + else { + conj.push_back(a.mk_ge(lhs, zero)); + } + } + }; + + + karr_relation& karr_relation_plugin::get(relation_base& r) { + return dynamic_cast(r); + } + + karr_relation const & karr_relation_plugin::get(relation_base const& r) { + return dynamic_cast(r); + } + + void karr_relation_plugin::set_cancel(bool f) { + m_hb.set_cancel(f); + } + + relation_base * karr_relation_plugin::mk_empty(const relation_signature & s) { + return alloc(karr_relation, *this, 0, s, true); + } + + relation_base * karr_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { + return alloc(karr_relation, *this, p, s, false); + } + + class karr_relation_plugin::join_fn : public convenient_relation_join_fn { + public: + join_fn(const relation_signature & o1_sig, const relation_signature & o2_sig, unsigned col_cnt, + const unsigned * cols1, const unsigned * cols2) + : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ + } + + virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + karr_relation const& r1 = get(_r1); + karr_relation const& r2 = get(_r2); + karr_relation_plugin& p = r1.get_plugin(); + karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); + return result; + } + }; + + relation_join_fn * karr_relation_plugin::mk_join_fn( + const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { + if (!check_kind(t1) || !check_kind(t2)) { + return 0; + } + return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); + } + + + class karr_relation_plugin::project_fn : public convenient_relation_project_fn { + public: + project_fn(const relation_signature & orig_sig, unsigned removed_col_cnt, const unsigned * removed_cols) + : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { + } + + virtual relation_base * operator()(const relation_base & _r) { + karr_relation const& r = get(_r); + karr_relation_plugin& p = r.get_plugin(); + karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); + return result; + } + }; + + relation_transformer_fn * karr_relation_plugin::mk_project_fn(const relation_base & r, + unsigned col_cnt, const unsigned * removed_cols) { + return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); + } + + class karr_relation_plugin::rename_fn : public convenient_relation_rename_fn { + public: + rename_fn(karr_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) + : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) {} + + virtual relation_base * operator()(const relation_base & _r) { + karr_relation const& r = get(_r); + karr_relation_plugin& p = r.get_plugin(); + karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); + return result; + } + }; + + relation_transformer_fn * karr_relation_plugin::mk_rename_fn(const relation_base & r, + unsigned cycle_len, const unsigned * permutation_cycle) { + if (!check_kind(r)) { + return 0; + } + return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle); + } + + bool karr_relation_plugin::dualizeI(matrix& dst, matrix const& src) { + dst.reset(); + m_hb.reset(); + for (unsigned i = 0; i < src.size(); ++i) { + if (src.eq[i]) { + m_hb.add_eq(src.A[i], -src.b[i]); + } + else { + m_hb.add_ge(src.A[i], -src.b[i]); + } + } + for (unsigned i = 0; !src.A.empty() && i < src.A[0].size(); ++i) { + m_hb.set_is_int(i); + } + lbool is_sat = l_undef; + + try { + is_sat = m_hb.saturate(); + } + catch (...) { + is_sat = l_undef; + } + TRACE("dl_verbose", m_hb.display(tout);); + if (is_sat == l_false) { + return false; + } + if (is_sat == l_undef) { + return true; + } + unsigned basis_size = m_hb.get_basis_size(); + bool first_initial = true; + for (unsigned i = 0; i < basis_size; ++i) { + bool is_initial; + vector soln; + m_hb.get_basis_solution(i, soln, is_initial); + if (is_initial && first_initial) { + dst.A.push_back(soln); + dst.b.push_back(rational(1)); + dst.eq.push_back(true); + first_initial = false; + } + else if (!is_initial) { + dst.A.push_back(soln); + dst.b.push_back(rational(0)); + dst.eq.push_back(true); + } + } + return true; + } + + void karr_relation_plugin::dualizeH(matrix& dst, matrix const& src) { + dst.reset(); + if (src.size() == 0) { + return; + } + m_hb.reset(); + for (unsigned i = 0; i < src.size(); ++i) { + vector v(src.A[i]); + v.push_back(src.b[i]); + if (src.eq[i]) { + m_hb.add_eq(v, rational(0)); + } + else { + m_hb.add_ge(v, rational(0)); + } + } + for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) { + m_hb.set_is_int(i); + } + lbool is_sat = l_undef; + try { + is_sat = m_hb.saturate(); + } + catch (...) { + is_sat = l_undef; + } + if (is_sat != l_true) { + return; + } + TRACE("dl_verbose", m_hb.display(tout);); + SASSERT(is_sat == l_true); + unsigned basis_size = m_hb.get_basis_size(); + for (unsigned i = 0; i < basis_size; ++i) { + bool is_initial; + vector soln; + m_hb.get_basis_solution(i, soln, is_initial); + if (!is_initial) { + dst.b.push_back(soln.back()); + dst.eq.push_back(true); + soln.pop_back(); + dst.A.push_back(soln); + } + } + } + + + class karr_relation_plugin::union_fn : public relation_union_fn { + karr_relation_plugin& m_plugin; + public: + union_fn(karr_relation_plugin& p) : + m_plugin(p) { + } + + virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + + karr_relation& r = get(_r); + karr_relation const& src = get(_src); + TRACE("dl", r.display(tout << "dst:\n"); src.display(tout << "src:\n");); + + if (_delta) { + karr_relation& d = get(*_delta); + r.mk_union(src, &d); + } + else { + r.mk_union(src, 0); + } + TRACE("dl", r.display(tout << "result:\n");); + } + }; + + relation_union_fn * karr_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) { + if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { + return 0; + } + return alloc(union_fn, *this); + } + + class karr_relation_plugin::filter_identical_fn : public relation_mutator_fn { + unsigned_vector m_identical_cols; + public: + filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) + : m_identical_cols(col_cnt, identical_cols) {} + + virtual void operator()(relation_base & _r) { + karr_relation & r = get(_r); + TRACE("dl", r.display(tout << "src:\n");); + r.get_ineqs(); + for (unsigned i = 1; i < m_identical_cols.size(); ++i) { + unsigned c1 = m_identical_cols[0]; + unsigned c2 = m_identical_cols[i]; + vector row; + row.resize(r.get_signature().size()); + row[c1] = rational(1); + row[c2] = rational(-1); + r.m_ineqs.A.push_back(row); + r.m_ineqs.b.push_back(rational(0)); + r.m_ineqs.eq.push_back(true); + r.m_basis_valid = false; + } + TRACE("dl", r.display(tout << "result:\n");); + } + }; + + relation_mutator_fn * karr_relation_plugin::mk_filter_identical_fn( + const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { + if(!check_kind(t)) { + return 0; + } + return alloc(filter_identical_fn, col_cnt, identical_cols); + } + + + class karr_relation_plugin::filter_equal_fn : public relation_mutator_fn { + unsigned m_col; + rational m_value; + public: + filter_equal_fn(relation_manager & m, const relation_element & value, unsigned col) + : m_col(col) { + arith_util arith(m.get_context().get_manager()); + VERIFY(arith.is_numeral(value, m_value)); + } + + virtual void operator()(relation_base & _r) { + karr_relation & r = get(_r); + if (m_value.is_int()) { + r.get_ineqs(); + vector row; + row.resize(r.get_signature().size()); + row[m_col] = rational(1); + r.m_ineqs.A.push_back(row); + r.m_ineqs.b.push_back(rational(-1)); + r.m_ineqs.eq.push_back(true); + r.m_basis_valid = false; + } + TRACE("dl", tout << m_value << "\n"; r.display(tout);); + } + }; + + relation_mutator_fn * karr_relation_plugin::mk_filter_equal_fn(const relation_base & r, + const relation_element & value, unsigned col) { + if(check_kind(r)) { + return alloc(filter_equal_fn, get_manager(), value, col); + } + return 0; + } + + + class karr_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { + app_ref m_cond; + public: + filter_interpreted_fn(karr_relation const& t, app* cond): + m_cond(cond, t.get_plugin().get_ast_manager()) { + } + + void operator()(relation_base& t) { + get(t).filter_interpreted(m_cond); + TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); + } + }; + + relation_mutator_fn * karr_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { + if (check_kind(t)) { + return alloc(filter_interpreted_fn, get(t), condition); + } + return 0; + } + +}; + diff --git a/src/muz_qe/dl_mk_karr_invariants.h b/src/muz_qe/dl_mk_karr_invariants.h new file mode 100644 index 000000000..414953e4f --- /dev/null +++ b/src/muz_qe/dl_mk_karr_invariants.h @@ -0,0 +1,136 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_karr_invariants.h + +Abstract: + + Extract integer linear invariants. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-08 + +Revision History: + +--*/ +#ifndef _DL_MK_KARR_INVARIANTS_H_ +#define _DL_MK_KARR_INVARIANTS_H_ + +#include"dl_context.h" +#include"dl_rule_set.h" +#include"dl_rule_transformer.h" +#include"arith_decl_plugin.h" +#include"hilbert_basis.h" + +namespace datalog { + + /** + \brief Rule transformer that strengthens bodies with invariants. + */ + + struct matrix { + vector > A; + vector b; + svector eq; + unsigned size() const { return A.size(); } + void reset() { A.reset(); b.reset(); eq.reset(); } + matrix& operator=(matrix const& other); + void append(matrix const& other) { A.append(other.A); b.append(other.b); eq.append(other.eq); } + void display(std::ostream& out) const; + static void display_row( + std::ostream& out, vector const& row, rational const& b, bool is_eq); + static void display_ineq( + std::ostream& out, vector const& row, rational const& b, bool is_eq); + }; + + class mk_karr_invariants : public rule_transformer::plugin { + + class add_invariant_model_converter; + + context& m_ctx; + ast_manager& m; + rule_manager& rm; + context m_inner_ctx; + arith_util a; + void update_body(rel_context& rctx, rule_set& result, rule& r); + + public: + mk_karr_invariants(context & ctx, unsigned priority); + + virtual ~mk_karr_invariants(); + + virtual void cancel(); + + rule_set * operator()(rule_set const & source); + + }; + + class karr_relation; + + class karr_relation_plugin : public relation_plugin { + arith_util a; + hilbert_basis m_hb; + + class join_fn; + class project_fn; + class rename_fn; + class union_fn; + class filter_equal_fn; + class filter_identical_fn; + class filter_interpreted_fn; + friend class karr_relation; + public: + karr_relation_plugin(relation_manager& rm): + relation_plugin(karr_relation_plugin::get_name(), rm), + a(get_ast_manager()) + {} + + virtual bool can_handle_signature(const relation_signature & sig) { + for (unsigned i = 0; i < sig.size(); ++i) { + if (a.is_int(sig[i])) { + return true; + } + } + return false; + } + + static symbol get_name() { return symbol("karr_relation"); } + + virtual void set_cancel(bool f); + + virtual relation_base * mk_empty(const relation_signature & s); + + virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + + static karr_relation& get(relation_base& r); + static karr_relation const & get(relation_base const& r); + + virtual relation_join_fn * mk_join_fn( + const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols); + virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle); + virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta); + virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols); + virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col); + virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + private: + bool dualizeI(matrix& dst, matrix const& src); + void dualizeH(matrix& dst, matrix const& src); + + + }; + + +}; + +#endif /* _DL_MK_KARR_INVARIANTS_H_ */ + diff --git a/src/muz_qe/dl_mk_loop_counter.cpp b/src/muz_qe/dl_mk_loop_counter.cpp new file mode 100644 index 000000000..6b95671f2 --- /dev/null +++ b/src/muz_qe/dl_mk_loop_counter.cpp @@ -0,0 +1,113 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_loop_counter.cpp + +Abstract: + + Add loop counter argument to relations. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-31 + +Revision History: + +--*/ + +#include"dl_mk_loop_counter.h" +#include"dl_context.h" + +namespace datalog { + + mk_loop_counter::mk_loop_counter(context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + a(m), + m_refs(m) { + } + + mk_loop_counter::~mk_loop_counter() { } + + app_ref mk_loop_counter::add_arg(app* fn, unsigned idx) { + expr_ref_vector args(m); + func_decl* new_fn, *old_fn = fn->get_decl(); + args.append(fn->get_num_args(), fn->get_args()); + args.push_back(m.mk_var(idx, a.mk_int())); + + if (!m_old2new.find(old_fn, new_fn)) { + ptr_vector domain; + domain.append(fn->get_num_args(), old_fn->get_domain()); + domain.push_back(a.mk_int()); + new_fn = m.mk_func_decl(old_fn->get_name(), domain.size(), domain.c_ptr(), old_fn->get_range()); + m_old2new.insert(old_fn, new_fn); + m_new2old.insert(new_fn, old_fn); + m_refs.push_back(new_fn); + } + return app_ref(m.mk_app(new_fn, args.size(), args.c_ptr()), m); + } + + rule_set * mk_loop_counter::operator()(rule_set const & source) { + m_refs.reset(); + m_old2new.reset(); + m_new2old.reset(); + context& ctx = source.get_context(); + rule_manager& rm = source.get_rule_manager(); + rule_set * result = alloc(rule_set, ctx); + unsigned sz = source.get_num_rules(); + rule_ref new_rule(rm); + app_ref_vector tail(m); + app_ref head(m); + svector neg; + rule_counter& vc = rm.get_counter(); + for (unsigned i = 0; i < sz; ++i) { + tail.reset(); + neg.reset(); + rule & r = *source.get_rule(i); + unsigned cnt = vc.get_max_rule_var(r)+1; + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + for (unsigned j = 0; j < utsz; ++j, ++cnt) { + tail.push_back(add_arg(r.get_tail(j), cnt)); + neg.push_back(r.is_neg_tail(j)); + ctx.register_predicate(tail.back()->get_decl(), false); + } + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + neg.push_back(false); + } + head = add_arg(r.get_head(), cnt); + ctx.register_predicate(head->get_decl(), false); + // set the loop counter to be an increment of the previous + bool found = false; + unsigned last = head->get_num_args()-1; + for (unsigned j = 0; !found && j < utsz; ++j) { + if (head->get_decl() == tail[j]->get_decl()) { + tail.push_back(m.mk_eq(head->get_arg(last), + a.mk_add(tail[j]->get_arg(last), + a.mk_numeral(rational(1), true)))); + neg.push_back(false); + found = true; + } + } + // initialize loop counter to 0 if none was found. + if (!found) { + expr_ref_vector args(m); + args.append(head->get_num_args(), head->get_args()); + args[last] = a.mk_numeral(rational(0), true); + head = m.mk_app(head->get_decl(), args.size(), args.c_ptr()); + } + + new_rule = rm.mk(head, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); + result->add_rule(new_rule); + } + + // model converter: remove references to extra argument. + // proof converter: remove references to extra argument as well. + + return result; + } + +}; diff --git a/src/muz_qe/dl_mk_loop_counter.h b/src/muz_qe/dl_mk_loop_counter.h new file mode 100644 index 000000000..6601f837f --- /dev/null +++ b/src/muz_qe/dl_mk_loop_counter.h @@ -0,0 +1,47 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_loop_counter.h + +Abstract: + + Add loop counter argument to relations. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-31 + +Revision History: + +--*/ +#ifndef _DL_MK_LOOP_COUNTER_H_ +#define _DL_MK_LOOP_COUNTER_H_ + +#include"dl_rule_transformer.h" +#include"arith_decl_plugin.h" + +namespace datalog { + + class mk_loop_counter : public rule_transformer::plugin { + ast_manager& m; + arith_util a; + func_decl_ref_vector m_refs; + obj_map m_new2old; + obj_map m_old2new; + + app_ref add_arg(app* fn, unsigned idx); + public: + mk_loop_counter(context & ctx, unsigned priority = 33000); + ~mk_loop_counter(); + + rule_set * operator()(rule_set const & source); + + func_decl* get_old(func_decl* f) const { return m_new2old.find(f); } + }; + +}; + +#endif /* _DL_MK_LOOP_COUNTER_H_ */ + diff --git a/src/muz_qe/dl_mk_magic_sets.cpp b/src/muz_qe/dl_mk_magic_sets.cpp index 373a90969..6885edc4e 100644 --- a/src/muz_qe/dl_mk_magic_sets.cpp +++ b/src/muz_qe/dl_mk_magic_sets.cpp @@ -317,8 +317,8 @@ namespace datalog { m_rules.push_back(r); } - rule_set * mk_magic_sets::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - SASSERT(!mc && !pc); + rule_set * mk_magic_sets::operator()(rule_set const & source) { + SASSERT(!m_context.get_model_converter()); unsigned init_rule_cnt = source.get_num_rules(); { func_decl_set intentional; diff --git a/src/muz_qe/dl_mk_magic_sets.h b/src/muz_qe/dl_mk_magic_sets.h index f98ad55a3..2dc91c7e8 100644 --- a/src/muz_qe/dl_mk_magic_sets.h +++ b/src/muz_qe/dl_mk_magic_sets.h @@ -121,7 +121,7 @@ namespace datalog { */ mk_magic_sets(context & ctx, rule * goal_rule); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_partial_equiv.cpp b/src/muz_qe/dl_mk_partial_equiv.cpp index 367a15743..35f8ff8df 100644 --- a/src/muz_qe/dl_mk_partial_equiv.cpp +++ b/src/muz_qe/dl_mk_partial_equiv.cpp @@ -86,8 +86,8 @@ namespace datalog { } - rule_set * mk_partial_equivalence_transformer::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_partial_equivalence_transformer::operator()(rule_set const & source) { + // TODO mc if (source.get_num_rules() == 0) { return 0; diff --git a/src/muz_qe/dl_mk_partial_equiv.h b/src/muz_qe/dl_mk_partial_equiv.h index 8fef4aea9..54a70b3c0 100644 --- a/src/muz_qe/dl_mk_partial_equiv.h +++ b/src/muz_qe/dl_mk_partial_equiv.h @@ -35,7 +35,7 @@ namespace datalog { m(ctx.get_manager()), m_context(ctx) {} - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); private: diff --git a/src/muz_qe/dl_mk_quantifier_abstraction.cpp b/src/muz_qe/dl_mk_quantifier_abstraction.cpp new file mode 100644 index 000000000..d4abb1ffc --- /dev/null +++ b/src/muz_qe/dl_mk_quantifier_abstraction.cpp @@ -0,0 +1,367 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_quantifier_abstraction.cpp + +Abstract: + + Create quantified Horn clauses from benchmarks with arrays. + +Author: + + Ken McMillan + Andrey Rybalchenko + Nikolaj Bjorner (nbjorner) 2013-04-02 + +Revision History: + +--*/ + +#include "dl_mk_quantifier_abstraction.h" +#include "dl_context.h" +#include "expr_safe_replace.h" +#include "expr_abstract.h" + +namespace datalog { + + + // model converter: + // Given model for P^(x, y, i, a[i]) + // create model: P(x,y,a) == forall i . P^(x,y,i,a[i]) + // requires substitution and list of bound variables. + + class mk_quantifier_abstraction::qa_model_converter : public model_converter { + ast_manager& m; + func_decl_ref_vector m_old_funcs; + func_decl_ref_vector m_new_funcs; + vector m_subst; + vector m_sorts; + vector > m_bound; + + public: + + qa_model_converter(ast_manager& m): + m(m), m_old_funcs(m), m_new_funcs(m) {} + + virtual ~qa_model_converter() {} + + virtual model_converter * translate(ast_translation & translator) { + return alloc(qa_model_converter, m); + } + + void insert(func_decl* old_p, func_decl* new_p, expr_ref_vector& sub, sort_ref_vector& sorts, svector const& bound) { + m_old_funcs.push_back(old_p); + m_new_funcs.push_back(new_p); + m_subst.push_back(sub); + m_bound.push_back(bound); + m_sorts.push_back(sorts); + } + + virtual void operator()(model_ref & old_model) { + model_ref new_model = alloc(model, m); + for (unsigned i = 0; i < m_new_funcs.size(); ++i) { + func_decl* p = m_new_funcs[i].get(); + func_decl* q = m_old_funcs[i].get(); + expr_ref_vector const& sub = m_subst[i]; + sort_ref_vector const& sorts = m_sorts[i]; + svector const& is_bound = m_bound[i]; + func_interp* f = old_model->get_func_interp(p); + expr_ref body(m); + unsigned arity_p = p->get_arity(); + unsigned arity_q = q->get_arity(); + SASSERT(0 < arity_p); + func_interp* g = alloc(func_interp, m, arity_q); + + if (f) { + body = f->get_interp(); + SASSERT(!f->is_partial()); + SASSERT(body); + } + else { + body = m.mk_false(); + } + // Create quantifier wrapper around body. + + TRACE("dl", tout << mk_pp(body, m) << "\n";); + // 1. replace variables by the compound terms from + // the original predicate. + expr_safe_replace rep(m); + for (unsigned i = 0; i < sub.size(); ++i) { + rep.insert(m.mk_var(i, m.get_sort(sub[i])), sub[i]); + } + rep(body); + rep.reset(); + + TRACE("dl", tout << mk_pp(body, m) << "\n";); + // 2. replace bound variables by constants. + expr_ref_vector consts(m), bound(m), free(m); + svector names; + ptr_vector bound_sorts; + for (unsigned i = 0; i < sorts.size(); ++i) { + sort* s = sorts[i]; + consts.push_back(m.mk_fresh_const("C", s)); + rep.insert(m.mk_var(i, s), consts.back()); + if (is_bound[i]) { + bound.push_back(consts.back()); + names.push_back(symbol(i)); + bound_sorts.push_back(s); + } + else { + free.push_back(consts.back()); + } + } + rep(body); + rep.reset(); + + TRACE("dl", tout << mk_pp(body, m) << "\n";); + // 3. abstract and quantify those variables that should be bound. + expr_abstract(m, 0, bound.size(), bound.c_ptr(), body, body); + body = m.mk_forall(names.size(), bound_sorts.c_ptr(), names.c_ptr(), body); + + TRACE("dl", tout << mk_pp(body, m) << "\n";); + // 4. replace remaining constants by variables. + for (unsigned i = 0; i < free.size(); ++i) { + rep.insert(free[i].get(), m.mk_var(i, m.get_sort(free[i].get()))); + } + rep(body); + g->set_else(body); + TRACE("dl", tout << mk_pp(body, m) << "\n";); + + new_model->register_decl(q, g); + } + old_model = new_model; + } + }; + + mk_quantifier_abstraction::mk_quantifier_abstraction( + context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx), + a(m), + m_refs(m) { + } + + mk_quantifier_abstraction::~mk_quantifier_abstraction() { + } + + func_decl* mk_quantifier_abstraction::declare_pred(func_decl* old_p) { + + if (m_ctx.is_output_predicate(old_p)) { + return 0; + } + + unsigned sz = old_p->get_arity(); + unsigned num_arrays = 0; + for (unsigned i = 0; i < sz; ++i) { + if (a.is_array(old_p->get_domain(i))) { + num_arrays++; + } + } + if (num_arrays == 0) { + return 0; + } + + func_decl* new_p = 0; + if (!m_old2new.find(old_p, new_p)) { + expr_ref_vector sub(m), vars(m); + svector bound; + sort_ref_vector domain(m), sorts(m); + expr_ref arg(m); + for (unsigned i = 0; i < sz; ++i) { + sort* s0 = old_p->get_domain(i); + unsigned lookahead = 0; + sort* s = s0; + while (a.is_array(s)) { + lookahead += get_array_arity(s); + s = get_array_range(s); + } + arg = m.mk_var(bound.size() + lookahead, s0); + s = s0; + while (a.is_array(s)) { + unsigned arity = get_array_arity(s); + expr_ref_vector args(m); + for (unsigned j = 0; j < arity; ++j) { + sort* s1 = get_array_domain(s, j); + domain.push_back(s1); + args.push_back(m.mk_var(bound.size(), s1)); + bound.push_back(true); + sorts.push_back(s1); + } + arg = mk_select(arg, args.size(), args.c_ptr()); + s = get_array_range(s); + } + domain.push_back(s); + bound.push_back(false); + sub.push_back(arg); + sorts.push_back(s0); + } + SASSERT(old_p->get_range() == m.mk_bool_sort()); + new_p = m.mk_func_decl(old_p->get_name(), domain.size(), domain.c_ptr(), old_p->get_range()); + m_refs.push_back(new_p); + m_ctx.register_predicate(new_p, false); + if (m_mc) { + m_mc->insert(old_p, new_p, sub, sorts, bound); + } + m_old2new.insert(old_p, new_p); + } + return new_p; + } + + app_ref mk_quantifier_abstraction::mk_head(app* p, unsigned idx) { + func_decl* new_p = declare_pred(p->get_decl()); + if (!new_p) { + return app_ref(p, m); + } + expr_ref_vector args(m); + expr_ref arg(m); + unsigned sz = p->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + arg = p->get_arg(i); + sort* s = m.get_sort(arg); + while (a.is_array(s)) { + unsigned arity = get_array_arity(s); + for (unsigned j = 0; j < arity; ++j) { + args.push_back(m.mk_var(idx++, get_array_domain(s, j))); + } + arg = mk_select(arg, arity, args.c_ptr()+args.size()-arity); + s = get_array_range(s); + } + args.push_back(arg); + } + TRACE("dl", + tout << mk_pp(new_p, m) << "\n"; + for (unsigned i = 0; i < args.size(); ++i) { + tout << mk_pp(args[i].get(), m) << "\n"; + }); + return app_ref(m.mk_app(new_p, args.size(), args.c_ptr()), m); + } + + app_ref mk_quantifier_abstraction::mk_tail(app* p) { + func_decl* old_p = p->get_decl(); + func_decl* new_p = declare_pred(old_p); + if (!new_p) { + return app_ref(p, m); + } + SASSERT(new_p->get_arity() > old_p->get_arity()); + unsigned num_extra_args = new_p->get_arity() - old_p->get_arity(); + var_shifter shift(m); + expr_ref p_shifted(m); + shift(p, num_extra_args, p_shifted); + app* ps = to_app(p_shifted); + expr_ref_vector args(m); + app_ref_vector pats(m); + sort_ref_vector vars(m); + svector names; + expr_ref arg(m); + unsigned idx = 0; + unsigned sz = p->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + arg = ps->get_arg(i); + sort* s = m.get_sort(arg); + bool is_pattern = false; + while (a.is_array(s)) { + is_pattern = true; + unsigned arity = get_array_arity(s); + for (unsigned j = 0; j < arity; ++j) { + vars.push_back(get_array_domain(s, j)); + names.push_back(symbol(idx)); + args.push_back(m.mk_var(idx++, vars.back())); + } + arg = mk_select(arg, arity, args.c_ptr()+args.size()-arity); + s = get_array_range(s); + } + if (is_pattern) { + pats.push_back(to_app(arg)); + } + args.push_back(arg); + } + expr* pat = 0; + expr_ref pattern(m); + pattern = m.mk_pattern(pats.size(), pats.c_ptr()); + pat = pattern.get(); + app_ref result(m); + symbol qid, skid; + result = m.mk_app(new_p, args.size(), args.c_ptr()); + result = m.mk_eq(m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), result, 1, qid, skid, 1, &pat), m.mk_true()); + return result; + } + + expr * mk_quantifier_abstraction::mk_select(expr* arg, unsigned num_args, expr* const* args) { + ptr_vector args2; + args2.push_back(arg); + args2.append(num_args, args); + return a.mk_select(args2.size(), args2.c_ptr()); + } + + rule_set * mk_quantifier_abstraction::operator()(rule_set const & source) { + TRACE("dl", tout << "quantify " << source.get_num_rules() << " " << m_ctx.get_params().quantify_arrays() << "\n";); + if (!m_ctx.get_params().quantify_arrays()) { + return 0; + } + unsigned sz = source.get_num_rules(); + for (unsigned i = 0; i < sz; ++i) { + rule& r = *source.get_rule(i); + if (r.has_negation()) { + return 0; + } + } + + m_refs.reset(); + m_old2new.reset(); + m_new2old.reset(); + rule_manager& rm = source.get_rule_manager(); + rule_ref new_rule(rm); + expr_ref_vector tail(m); + app_ref head(m); + expr_ref fml(m); + rule_counter& vc = rm.get_counter(); + + if (m_ctx.get_model_converter()) { + m_mc = alloc(qa_model_converter, m); + } + rule_set * result = alloc(rule_set, m_ctx); + + for (unsigned i = 0; i < sz; ++i) { + tail.reset(); + rule & r = *source.get_rule(i); + TRACE("dl", r.display(m_ctx, tout); ); + unsigned cnt = vc.get_max_rule_var(r)+1; + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + for (unsigned j = 0; j < utsz; ++j) { + tail.push_back(mk_tail(r.get_tail(j))); + } + for (unsigned j = utsz; j < tsz; ++j) { + tail.push_back(r.get_tail(j)); + } + head = mk_head(r.get_head(), cnt); + fml = m.mk_implies(m.mk_and(tail.size(), tail.c_ptr()), head); + rule_ref_vector added_rules(rm); + proof_ref pr(m); + rm.mk_rule(fml, pr, added_rules); + result->add_rules(added_rules.size(), added_rules.c_ptr()); + TRACE("dl", added_rules.back()->display(m_ctx, tout);); + } + + // proof converter: proofs are not necessarily preserved using this transformation. + + if (m_old2new.empty()) { + dealloc(result); + dealloc(m_mc); + result = 0; + } + else { + m_ctx.add_model_converter(m_mc); + } + m_mc = 0; + + return result; + } + + +}; + + diff --git a/src/muz_qe/dl_mk_quantifier_abstraction.h b/src/muz_qe/dl_mk_quantifier_abstraction.h new file mode 100644 index 000000000..c7e6c6bb4 --- /dev/null +++ b/src/muz_qe/dl_mk_quantifier_abstraction.h @@ -0,0 +1,64 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_quantifier_abstraction.h + +Abstract: + + Convert clauses with array arguments to predicates + into Quantified Horn clauses. + +Author: + + Ken McMillan + Andrey Rybalchenko + Nikolaj Bjorner (nbjorner) 2013-04-02 + +Revision History: + + Based on approach suggested in SAS 2013 paper + "On Solving Universally Quantified Horn Clauses" + +--*/ +#ifndef _DL_MK_QUANTIFIER_ABSTRACTION_H_ +#define _DL_MK_QUANTIFIER_ABSTRACTION_H_ + + +#include"dl_rule_transformer.h" +#include"array_decl_plugin.h" + +namespace datalog { + + class context; + + class mk_quantifier_abstraction : public rule_transformer::plugin { + class qa_model_converter; + ast_manager& m; + context& m_ctx; + array_util a; + func_decl_ref_vector m_refs; + obj_map m_new2old; + obj_map m_old2new; + qa_model_converter* m_mc; + + func_decl* declare_pred(func_decl* old_p); + app_ref mk_head(app* p, unsigned idx); + app_ref mk_tail(app* p); + expr* mk_select(expr* a, unsigned num_args, expr* const* args); + + public: + mk_quantifier_abstraction(context & ctx, unsigned priority); + + virtual ~mk_quantifier_abstraction(); + + rule_set * operator()(rule_set const & source); + }; + + + +}; + +#endif /* _DL_MK_QUANTIFIER_ABSTRACTION_H_ */ + diff --git a/src/muz_qe/dl_mk_quantifier_instantiation.cpp b/src/muz_qe/dl_mk_quantifier_instantiation.cpp new file mode 100644 index 000000000..1d80d9b77 --- /dev/null +++ b/src/muz_qe/dl_mk_quantifier_instantiation.cpp @@ -0,0 +1,299 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_quantifier_instantiation.cpp + +Abstract: + + Convert Quantified Horn clauses into non-quantified clauses using + instantiation. + +Author: + + Ken McMillan + Andrey Rybalchenko + Nikolaj Bjorner (nbjorner) 2013-04-02 + +Revision History: + + Based on approach suggested in the SAS 2013 paper + "On Solving Universally Quantified Horn Clauses" + +--*/ + +#include "dl_mk_quantifier_instantiation.h" +#include "dl_context.h" +#include "pattern_inference.h" + +namespace datalog { + + mk_quantifier_instantiation::mk_quantifier_instantiation( + context & ctx, unsigned priority): + plugin(priority), + m(ctx.get_manager()), + m_ctx(ctx), + m_var2cnst(m), + m_cnst2var(m) { + } + + mk_quantifier_instantiation::~mk_quantifier_instantiation() { + } + + void mk_quantifier_instantiation::extract_quantifiers(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs) { + conjs.reset(); + qs.reset(); + unsigned tsz = r.get_tail_size(); + for (unsigned j = 0; j < tsz; ++j) { + conjs.push_back(r.get_tail(j)); + } + 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)) { + qs.push_back(q); + conjs[j] = conjs.back(); + conjs.pop_back(); + --j; + } + } + } + + void mk_quantifier_instantiation::instantiate_quantifier(quantifier* q, expr_ref_vector & conjs) { + expr_ref qe(m); + qe = q; + m_var2cnst(qe); + q = to_quantifier(qe); + if (q->get_num_patterns() == 0) { + proof_ref new_pr(m); + pattern_inference_params params; + pattern_inference infer(m, params); + infer(q, qe, new_pr); + q = to_quantifier(qe); + } + unsigned num_patterns = q->get_num_patterns(); + for (unsigned i = 0; i < num_patterns; ++i) { + expr * pat = q->get_pattern(i); + SASSERT(m.is_pattern(pat)); + instantiate_quantifier(q, to_app(pat), conjs); + } + } + + + void mk_quantifier_instantiation::instantiate_quantifier(quantifier* q, app* pat, expr_ref_vector & conjs) { + m_binding.reset(); + m_binding.resize(q->get_num_decls()); + term_pairs todo; + match(0, pat, 0, todo, q, conjs); + } + + void mk_quantifier_instantiation::match(unsigned i, app* pat, unsigned j, term_pairs& todo, quantifier* q, expr_ref_vector& conjs) { + TRACE("dl", tout << "match" << mk_pp(pat, m) << "\n";); + while (j < todo.size()) { + expr* p = todo[j].first; + expr* t = todo[j].second; + if (is_var(p)) { + unsigned idx = to_var(p)->get_idx(); + if (!m_binding[idx]) { + m_binding[idx] = t; + match(i, pat, j + 1, todo, q, conjs); + m_binding[idx] = 0; + return; + } + ++j; + continue; + } + if (!is_app(p)) { + return; + } + app* a1 = to_app(p); + unsigned id = t->get_id(); + unsigned next_id = id; + unsigned sz = todo.size(); + do { + expr* t2 = m_terms[next_id]; + if (is_app(t2)) { + app* a2 = to_app(t2); + if (a1->get_decl() == a2->get_decl() && + a1->get_num_args() == a2->get_num_args()) { + for (unsigned k = 0; k < a1->get_num_args(); ++k) { + todo.push_back(std::make_pair(a1->get_arg(k), a2->get_arg(k))); + } + match(i, pat, j + 1, todo, q, conjs); + todo.resize(sz); + } + } + next_id = m_uf.next(next_id); + } + while (next_id != id); + return; + } + + if (i == pat->get_num_args()) { + yield_binding(q, conjs); + return; + } + expr* arg = pat->get_arg(i); + ptr_vector* terms = 0; + + if (m_funs.find(to_app(arg)->get_decl(), terms)) { + for (unsigned k = 0; k < terms->size(); ++k) { + todo.push_back(std::make_pair(arg, (*terms)[k])); + match(i + 1, pat, j, todo, q, conjs); + todo.pop_back(); + } + } + } + + void mk_quantifier_instantiation::yield_binding(quantifier* q, expr_ref_vector& conjs) { + DEBUG_CODE( + for (unsigned i = 0; i < m_binding.size(); ++i) { + SASSERT(m_binding[i]); + }); + m_binding.reverse(); + expr_ref res(m); + instantiate(m, q, m_binding.c_ptr(), res); + m_binding.reverse(); + m_cnst2var(res); + conjs.push_back(res); + TRACE("dl", tout << mk_pp(q, m) << "\n==>\n" << mk_pp(res, m) << "\n";); + } + + void mk_quantifier_instantiation::collect_egraph(expr* e) { + expr* e1, *e2; + m_todo.push_back(e); + expr_fast_mark1 visited; + while (!m_todo.empty()) { + e = m_todo.back(); + m_todo.pop_back(); + if (visited.is_marked(e)) { + continue; + } + unsigned n = e->get_id(); + if (n >= m_terms.size()) { + m_terms.resize(n+1); + } + m_terms[n] = e; + visited.mark(e); + if (m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) { + m_uf.merge(e1->get_id(), e2->get_id()); + } + if (is_app(e)) { + app* ap = to_app(e); + ptr_vector* terms = 0; + if (!m_funs.find(ap->get_decl(), terms)) { + terms = alloc(ptr_vector); + m_funs.insert(ap->get_decl(), terms); + } + terms->push_back(e); + m_todo.append(ap->get_num_args(), ap->get_args()); + } + } + } + + void mk_quantifier_instantiation::instantiate_rule(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs, rule_set& rules) { + rule_manager& rm = m_ctx.get_rule_manager(); + expr_ref fml(m), cnst(m); + var_ref var(m); + ptr_vector sorts; + r.get_vars(sorts); + m_uf.reset(); + m_terms.reset(); + m_var2cnst.reset(); + m_cnst2var.reset(); + fml = m.mk_and(conjs.size(), conjs.c_ptr()); + + for (unsigned i = 0; i < sorts.size(); ++i) { + if (!sorts[i]) { + sorts[i] = m.mk_bool_sort(); + } + var = m.mk_var(i, sorts[i]); + cnst = m.mk_fresh_const("C", sorts[i]); + m_var2cnst.insert(var, cnst); + m_cnst2var.insert(cnst, var); + } + + fml = m.mk_and(conjs.size(), conjs.c_ptr()); + m_var2cnst(fml); + collect_egraph(fml); + + for (unsigned i = 0; i < qs.size(); ++i) { + instantiate_quantifier(qs[i].get(), conjs); + } + obj_map*>::iterator it = m_funs.begin(), end = m_funs.end(); + for (; it != end; ++it) { + dealloc(it->m_value); + } + m_funs.reset(); + + fml = m.mk_and(conjs.size(), conjs.c_ptr()); + fml = m.mk_implies(fml, r.get_head()); + TRACE("dl", r.display(m_ctx, tout); tout << mk_pp(fml, m) << "\n";); + + rule_ref_vector added_rules(rm); + proof_ref pr(m); + rm.mk_rule(fml, pr, added_rules); + if (r.get_proof()) { + // use def-axiom to encode that new rule is a weakening of the original. + proof* p1 = r.get_proof(); + for (unsigned i = 0; i < added_rules.size(); ++i) { + rule* r2 = added_rules[i].get(); + r2->to_formula(fml); + pr = m.mk_modus_ponens(m.mk_def_axiom(m.mk_implies(m.get_fact(p1), fml)), p1); + r2->set_proof(m, pr); + } + } + rules.add_rules(added_rules.size(), added_rules.c_ptr()); + } + + rule_set * mk_quantifier_instantiation::operator()(rule_set const & source) { + TRACE("dl", tout << m_ctx.get_params().instantiate_quantifiers() << "\n";); + if (!m_ctx.get_params().instantiate_quantifiers()) { + return 0; + } + bool has_quantifiers = false; + unsigned sz = source.get_num_rules(); + for (unsigned i = 0; !has_quantifiers && i < sz; ++i) { + rule& r = *source.get_rule(i); + has_quantifiers = has_quantifiers || r.has_quantifiers(); + if (r.has_negation()) { + return 0; + } + } + if (!has_quantifiers) { + return 0; + } + + expr_ref_vector conjs(m); + quantifier_ref_vector qs(m); + rule_set * result = alloc(rule_set, m_ctx); + + bool instantiated = false; + + for (unsigned i = 0; i < sz; ++i) { + rule * r = source.get_rule(i); + extract_quantifiers(*r, conjs, qs); + if (qs.empty()) { + result->add_rule(r); + } + else { + instantiate_rule(*r, conjs, qs, *result); + instantiated = true; + } + } + + // model convertion: identity function. + + if (!instantiated) { + dealloc(result); + result = 0; + } + return result; + } + + +}; + + diff --git a/src/muz_qe/dl_mk_quantifier_instantiation.h b/src/muz_qe/dl_mk_quantifier_instantiation.h new file mode 100644 index 000000000..138d5abee --- /dev/null +++ b/src/muz_qe/dl_mk_quantifier_instantiation.h @@ -0,0 +1,136 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + dl_mk_quantifier_instantiation.h + +Abstract: + + Convert Quantified Horn clauses into non-quantified clauses using + instantiation. + +Author: + + Ken McMillan + Andrey Rybalchenko + Nikolaj Bjorner (nbjorner) 2013-04-02 + +Revision History: + + Based on approach suggested in the SAS 2013 paper + "On Solving Universally Quantified Horn Clauses" + +--*/ +#ifndef _DL_MK_QUANTIFIER_INSTANTIATION_H_ +#define _DL_MK_QUANTIFIER_INSTANTIATION_H_ + + +#include"dl_rule_transformer.h" +#include"expr_safe_replace.h" + + +namespace datalog { + + class context; + + class mk_quantifier_instantiation : public rule_transformer::plugin { + typedef svector > term_pairs; + + class union_find { + unsigned_vector m_find; + unsigned_vector m_size; + unsigned_vector m_next; + + void ensure_size(unsigned v) { + while (v >= get_num_vars()) { + mk_var(); + } + } + public: + unsigned mk_var() { + unsigned r = m_find.size(); + m_find.push_back(r); + m_size.push_back(1); + m_next.push_back(r); + return r; + } + unsigned get_num_vars() const { return m_find.size(); } + + unsigned find(unsigned v) const { + if (v >= get_num_vars()) { + return v; + } + while (true) { + unsigned new_v = m_find[v]; + if (new_v == v) + return v; + v = new_v; + } + } + + unsigned next(unsigned v) const { + if (v >= get_num_vars()) { + return v; + } + return m_next[v]; + } + + bool is_root(unsigned v) const { + return v >= get_num_vars() || m_find[v] == v; + } + + void merge(unsigned v1, unsigned v2) { + unsigned r1 = find(v1); + unsigned r2 = find(v2); + if (r1 == r2) + return; + ensure_size(v1); + ensure_size(v2); + if (m_size[r1] > m_size[r2]) + std::swap(r1, r2); + m_find[r1] = r2; + m_size[r2] += m_size[r1]; + std::swap(m_next[r1], m_next[r2]); + } + + void reset() { + m_find.reset(); + m_next.reset(); + m_size.reset(); + } + }; + + ast_manager& m; + context& m_ctx; + expr_safe_replace m_var2cnst; + expr_safe_replace m_cnst2var; + union_find m_uf; + ptr_vector m_todo; + ptr_vector m_terms; + ptr_vector m_binding; + obj_map*> m_funs; + + + void extract_quantifiers(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs); + void collect_egraph(expr* e); + void instantiate_rule(rule& r, expr_ref_vector& conjs, quantifier_ref_vector& qs, rule_set& rules); + void instantiate_quantifier(quantifier* q, expr_ref_vector & conjs); + void instantiate_quantifier(quantifier* q, app* pat, expr_ref_vector & conjs); + void match(unsigned i, app* pat, unsigned j, term_pairs& todo, quantifier* q, expr_ref_vector& conjs); + void yield_binding(quantifier* q, expr_ref_vector& conjs); + + public: + mk_quantifier_instantiation(context & ctx, unsigned priority); + + virtual ~mk_quantifier_instantiation(); + + rule_set * operator()(rule_set const & source); + }; + + + +}; + +#endif /* _DL_MK_QUANTIFIER_INSTANTIATION_H_ */ + diff --git a/src/muz_qe/dl_mk_rule_inliner.cpp b/src/muz_qe/dl_mk_rule_inliner.cpp index 0919e2ff0..91cfbe3fc 100644 --- a/src/muz_qe/dl_mk_rule_inliner.cpp +++ b/src/muz_qe/dl_mk_rule_inliner.cpp @@ -65,8 +65,8 @@ namespace datalog { // ----------------------------------- bool rule_unifier::unify_rules(const rule& tgt, unsigned tgt_idx, const rule& src) { - var_counter& vc = m_rm.get_var_counter(); - unsigned var_cnt = std::max(vc.get_max_var(tgt), vc.get_max_var(src))+1; + rule_counter& vc = m_rm.get_counter(); + unsigned var_cnt = std::max(vc.get_max_rule_var(tgt), vc.get_max_rule_var(src))+1; m_subst.reset(); m_subst.reserve(2, var_cnt); @@ -181,10 +181,10 @@ namespace datalog { } if (m_unifier.apply(tgt, tail_index, src, res)) { - if (m_pc) { + if (m_context.generate_proof_trace()) { expr_ref_vector s1 = m_unifier.get_rule_subst(tgt, true); expr_ref_vector s2 = m_unifier.get_rule_subst(src, false); - datalog::resolve_rule(m_pc, tgt, src, tail_index, s1, s2, *res.get()); + datalog::resolve_rule(tgt, src, tail_index, s1, s2, *res.get()); } return true; } @@ -241,8 +241,10 @@ namespace datalog { return false; } - //these conditions are optional, they avoid possible exponential increase - //in the size of the problem + // + // these conditions are optional, they avoid possible exponential increase + // in the size of the problem + // return //m_head_pred_non_empty_tails_ctr.get(pred)<=1 @@ -733,7 +735,7 @@ namespace datalog { } // initialize substitution. - var_counter& vc = m_rm.get_var_counter(); + rule_counter& vc = m_rm.get_counter(); unsigned max_var = 0; for (unsigned i = 0; i < sz; ++i) { rule* r = acc[i].get(); @@ -820,7 +822,7 @@ namespace datalog { del_rule(r2, j); } - max_var = std::max(max_var, vc.get_max_var(*r.get())); + max_var = std::max(max_var, vc.get_max_rule_var(*r.get())); m_subst.reserve_vars(max_var+1); } @@ -837,11 +839,10 @@ namespace datalog { return done_something; } - rule_set * mk_rule_inliner::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { + rule_set * mk_rule_inliner::operator()(rule_set const & source) { bool something_done = false; ref hsmc; - ref hpc; if (source.get_num_rules() == 0) { return 0; @@ -855,14 +856,10 @@ namespace datalog { } - if (mc) { + if (m_context.get_model_converter()) { hsmc = alloc(horn_subsume_model_converter, m); } - if (pc) { - hpc = alloc(replace_proof_converter, m); - } m_mc = hsmc.get(); - m_pc = hpc.get(); scoped_ptr res = alloc(rule_set, m_context); @@ -886,12 +883,7 @@ namespace datalog { res = 0; } else { - if (mc) { - mc = concat(mc.get(), hsmc.get()); - } - if (pc) { - pc = concat(pc.get(), hpc.get()); - } + m_context.add_model_converter(hsmc.get()); } return res.detach(); diff --git a/src/muz_qe/dl_mk_rule_inliner.h b/src/muz_qe/dl_mk_rule_inliner.h index 6d5448cd6..5ef8db7eb 100644 --- a/src/muz_qe/dl_mk_rule_inliner.h +++ b/src/muz_qe/dl_mk_rule_inliner.h @@ -114,7 +114,6 @@ namespace datalog { ast_counter m_tail_pred_ctr; rule_set m_inlined_rules; horn_subsume_model_converter* m_mc; - replace_proof_converter* m_pc; //used in try_to_inline_rule and do_eager_inlining @@ -188,7 +187,6 @@ namespace datalog { m_pinned(m_rm), m_inlined_rules(m_context), m_mc(0), - m_pc(0), m_unifier(ctx), m_head_index(m), m_tail_index(m), @@ -198,7 +196,7 @@ namespace datalog { {} virtual ~mk_rule_inliner() { } - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_similarity_compressor.cpp b/src/muz_qe/dl_mk_similarity_compressor.cpp index 42a5ba367..9855196c9 100644 --- a/src/muz_qe/dl_mk_similarity_compressor.cpp +++ b/src/muz_qe/dl_mk_similarity_compressor.cpp @@ -42,7 +42,7 @@ namespace datalog { /** Allows to traverse head and positive tails in a single for loop starting from -1 */ - app * get_by_tail_index(rule * r, int idx) { + static app * get_by_tail_index(rule * r, int idx) { if(idx==-1) { return r->get_head(); } @@ -51,11 +51,11 @@ namespace datalog { } template - int aux_compare(T a, T b) { + static int aux_compare(T a, T b) { return (a>b) ? 1 : ( (a==b) ? 0 : -1); } - int compare_var_args(app* t1, app* t2) { + static int compare_var_args(app* t1, app* t2) { SASSERT(t1->get_num_args()==t2->get_num_args()); int res; unsigned n = t1->get_num_args(); @@ -73,7 +73,7 @@ namespace datalog { return 0; } - int compare_args(app* t1, app* t2, int & skip_countdown) { + static int compare_args(app* t1, app* t2, int & skip_countdown) { SASSERT(t1->get_num_args()==t2->get_num_args()); int res; unsigned n = t1->get_num_args(); @@ -98,7 +98,7 @@ namespace datalog { Two rules are in the same rough similarity class if they differ only in constant arguments of positive uninterpreted predicates. */ - int rough_compare(rule * r1, rule * r2) { + static int rough_compare(rule * r1, rule * r2) { int res = aux_compare(r1->get_tail_size(), r2->get_tail_size()); if(res!=0) { return res; } res = aux_compare(r1->get_uninterpreted_tail_size(), r2->get_uninterpreted_tail_size()); @@ -129,7 +129,7 @@ namespace datalog { \c r1 and \c r2 must be equal according to the \c rough_compare function for this function to be called. */ - int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) { + static int total_compare(rule * r1, rule * r2, int skipped_arg_index = INT_MAX) { SASSERT(rough_compare(r1, r2)==0); int pos_tail_sz = r1->get_positive_tail_size(); for(int i=-1; i info_vector; - void collect_const_indexes(app * t, int tail_index, info_vector & res) { + static void collect_const_indexes(app * t, int tail_index, info_vector & res) { unsigned n = t->get_num_args(); for(unsigned i=0; iget_arg(i))) { @@ -175,7 +175,7 @@ namespace datalog { } } - void collect_const_indexes(rule * r, info_vector & res) { + static void collect_const_indexes(rule * r, info_vector & res) { collect_const_indexes(r->get_head(), -1, res); unsigned pos_tail_sz = r->get_positive_tail_size(); for(unsigned i=0; i - void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) { + static void collect_orphan_consts(rule * r, const info_vector & const_infos, T & tgt) { unsigned const_cnt = const_infos.size(); tgt.reset(); for(unsigned i=0; i - void collect_orphan_sorts(rule * r, const info_vector & const_infos, T & tgt) { + static void collect_orphan_sorts(rule * r, const info_vector & const_infos, T & tgt) { unsigned const_cnt = const_infos.size(); tgt.reset(); for(unsigned i=0; i1); unsigned const_cnt = const_infos.size(); @@ -252,7 +252,7 @@ namespace datalog { first constant that is equal to it in all the rules. If there is no such, it will contain its own index. */ - void detect_equal_constants(rule_vector::iterator first, rule_vector::iterator after_last, + static void detect_equal_constants(rule_vector::iterator first, rule_vector::iterator after_last, info_vector & const_infos) { SASSERT(first!=after_last); unsigned const_cnt = const_infos.size(); @@ -302,7 +302,7 @@ namespace datalog { } } - unsigned get_constant_count(rule * r) { + static unsigned get_constant_count(rule * r) { unsigned res = r->get_head()->get_num_args() - count_variable_arguments(r->get_head()); unsigned pos_tail_sz = r->get_positive_tail_size(); for(unsigned i=0; i0; } return total_compare(r1, r2)>0; @@ -372,10 +372,10 @@ namespace datalog { new_negs.push_back(r->is_neg_tail(i)); } - var_counter var_ctr; - var_ctr.count_vars(m_manager, r); + rule_counter ctr; + ctr.count_rule_vars(m_manager, r); unsigned max_var_idx, new_var_idx_base; - if(var_ctr.get_max_positive(max_var_idx)) { + if(ctr.get_max_positive(max_var_idx)) { new_var_idx_base = max_var_idx+1; } else { @@ -500,8 +500,8 @@ namespace datalog { } } - rule_set * mk_similarity_compressor::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_similarity_compressor::operator()(rule_set const & source) { + // TODO mc m_modified = false; unsigned init_rule_cnt = source.get_num_rules(); SASSERT(m_rules.empty()); diff --git a/src/muz_qe/dl_mk_similarity_compressor.h b/src/muz_qe/dl_mk_similarity_compressor.h index ad4a5e246..6e0ca9db5 100644 --- a/src/muz_qe/dl_mk_similarity_compressor.h +++ b/src/muz_qe/dl_mk_similarity_compressor.h @@ -69,7 +69,7 @@ namespace datalog { public: mk_similarity_compressor(context & ctx, unsigned threshold_count); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_simple_joins.cpp b/src/muz_qe/dl_mk_simple_joins.cpp index 363a4f0f7..3a3f96acf 100644 --- a/src/muz_qe/dl_mk_simple_joins.cpp +++ b/src/muz_qe/dl_mk_simple_joins.cpp @@ -310,8 +310,8 @@ namespace datalog { } void register_rule(rule * r) { - var_counter counter; - counter.count_vars(m, r, 1); + rule_counter counter; + counter.count_rule_vars(m, r, 1); ptr_vector & rule_content = m_rules_content.insert_if_not_there2(r, ptr_vector())->get_data().m_value; @@ -706,19 +706,19 @@ namespace datalog { negs.c_ptr()); new_rule->set_accounting_parent_object(m_context, orig_r); - + m_context.get_rule_manager().mk_rule_rewrite_proof(*orig_r, *new_rule); result->add_rule(new_rule); } - while(!m_introduced_rules.empty()) { + while (!m_introduced_rules.empty()) { result->add_rule(m_introduced_rules.back()); + m_context.get_rule_manager().mk_rule_asserted_proof(*m_introduced_rules.back()); m_introduced_rules.pop_back(); } return result; } }; - rule_set * mk_simple_joins::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_simple_joins::operator()(rule_set const & source) { rule_set rs_aux_copy(m_context); rs_aux_copy.add_rules(source); if(!rs_aux_copy.is_closed()) { diff --git a/src/muz_qe/dl_mk_simple_joins.h b/src/muz_qe/dl_mk_simple_joins.h index 5431c4117..89832626f 100644 --- a/src/muz_qe/dl_mk_simple_joins.h +++ b/src/muz_qe/dl_mk_simple_joins.h @@ -53,7 +53,7 @@ namespace datalog { public: mk_simple_joins(context & ctx); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_slice.cpp b/src/muz_qe/dl_mk_slice.cpp index 84b732280..c98d6503e 100644 --- a/src/muz_qe/dl_mk_slice.cpp +++ b/src/muz_qe/dl_mk_slice.cpp @@ -164,10 +164,8 @@ namespace datalog { TRACE("dl", tout << "does not have fact\n" << mk_pp(fact, m) << "\n";); return false; } - expr_ref fml(m); proof_ref new_p(m); - r->to_formula(fml); - new_p = m.mk_asserted(fml); + new_p = r->get_proof(); m_pinned_exprs.push_back(new_p); m_todo.pop_back(); m_new_proof.insert(p, new_p); @@ -784,6 +782,9 @@ namespace datalog { rm.fix_unbound_vars(new_rule, false); TRACE("dl", r.display(m_ctx, tout << "replacing:\n"); new_rule->display(m_ctx, tout << "by:\n");); + if (m_ctx.generate_proof_trace()) { + rm.mk_rule_asserted_proof(*new_rule.get()); + } } else { new_rule = &r; @@ -801,7 +802,7 @@ namespace datalog { } } - rule_set * mk_slice::operator()(rule_set const & src, model_converter_ref& mc, proof_converter_ref& pc) { + rule_set * mk_slice::operator()(rule_set const & src) { for (unsigned i = 0; i < src.get_num_rules(); ++i) { if (src.get_rule(i)->has_quantifiers()) { return 0; @@ -809,10 +810,10 @@ namespace datalog { } ref spc; ref smc; - if (pc) { - spc = alloc(slice_proof_converter, m_ctx); + if (m_ctx.generate_proof_trace()) { + spc = alloc(slice_proof_converter, m_ctx); } - if (mc) { + if (m_ctx.get_model_converter()) { smc = alloc(slice_model_converter, *this, m); } m_pc = spc.get(); @@ -834,8 +835,8 @@ namespace datalog { m_mc->add_sliceable(it->m_key, it->m_value); } } - pc = concat(pc.get(), spc.get()); - mc = concat(mc.get(), smc.get()); + m_ctx.add_proof_converter(spc.get()); + m_ctx.add_model_converter(smc.get()); return result; } diff --git a/src/muz_qe/dl_mk_slice.h b/src/muz_qe/dl_mk_slice.h index 26a8175f2..1b4312e77 100644 --- a/src/muz_qe/dl_mk_slice.h +++ b/src/muz_qe/dl_mk_slice.h @@ -102,7 +102,7 @@ namespace datalog { virtual ~mk_slice() { } - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); func_decl* get_predicate(func_decl* p) { func_decl* q = p; m_predicates.find(p, q); return q; } diff --git a/src/muz_qe/dl_mk_subsumption_checker.cpp b/src/muz_qe/dl_mk_subsumption_checker.cpp index 19c28c036..fb55a377c 100644 --- a/src/muz_qe/dl_mk_subsumption_checker.cpp +++ b/src/muz_qe/dl_mk_subsumption_checker.cpp @@ -166,7 +166,8 @@ namespace datalog { res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); res->set_accounting_parent_object(m_context, r); m_context.get_rule_manager().fix_unbound_vars(res, true); - + m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *res.get()); + return true; } @@ -208,10 +209,10 @@ namespace datalog { continue; } rule * defining_rule; - TRUSTME(m_total_relation_defining_rules.find(head_pred, defining_rule)); - if(defining_rule) { + VERIFY(m_total_relation_defining_rules.find(head_pred, defining_rule)); + if (defining_rule) { rule_ref totality_rule(m_context.get_rule_manager()); - TRUSTME(transform_rule(defining_rule, subs_index, totality_rule)); + VERIFY(transform_rule(defining_rule, subs_index, totality_rule)); if(defining_rule!=totality_rule) { modified = true; } @@ -331,8 +332,8 @@ namespace datalog { } } - rule_set * mk_subsumption_checker::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_subsumption_checker::operator()(rule_set const & source) { + // TODO mc m_have_new_total_rule = false; collect_ground_unconditional_rule_heads(source); diff --git a/src/muz_qe/dl_mk_subsumption_checker.h b/src/muz_qe/dl_mk_subsumption_checker.h index ce33e7574..59904b3ef 100644 --- a/src/muz_qe/dl_mk_subsumption_checker.h +++ b/src/muz_qe/dl_mk_subsumption_checker.h @@ -84,7 +84,7 @@ namespace datalog { reset_dealloc_values(m_ground_unconditional_rule_heads); } - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_unbound_compressor.cpp b/src/muz_qe/dl_mk_unbound_compressor.cpp index e3db82759..40926c2a8 100644 --- a/src/muz_qe/dl_mk_unbound_compressor.cpp +++ b/src/muz_qe/dl_mk_unbound_compressor.cpp @@ -238,6 +238,7 @@ namespace datalog { unsigned new_rule_index = m_rules.size(); m_rules.push_back(new_rule); + m_context.get_rule_manager().mk_rule_rewrite_proof(*r, *new_rule.get()); m_head_occurrence_ctr.inc(new_rule->get_head()->get_decl()); @@ -333,8 +334,8 @@ namespace datalog { } } - rule_set * mk_unbound_compressor::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - // TODO mc, pc + rule_set * mk_unbound_compressor::operator()(rule_set const & source) { + // TODO mc m_modified = false; m_context.get_rel_context().get_rmanager().collect_non_empty_predicates(m_non_empty_rels); diff --git a/src/muz_qe/dl_mk_unbound_compressor.h b/src/muz_qe/dl_mk_unbound_compressor.h index 0e13bad75..cad953783 100644 --- a/src/muz_qe/dl_mk_unbound_compressor.h +++ b/src/muz_qe/dl_mk_unbound_compressor.h @@ -82,7 +82,7 @@ namespace datalog { public: mk_unbound_compressor(context & ctx); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_mk_unfold.cpp b/src/muz_qe/dl_mk_unfold.cpp index d26a0a290..dfbd87122 100644 --- a/src/muz_qe/dl_mk_unfold.cpp +++ b/src/muz_qe/dl_mk_unfold.cpp @@ -42,29 +42,20 @@ namespace datalog { if (m_unify.unify_rules(r, tail_idx, r2) && m_unify.apply(r, tail_idx, r2, new_rule)) { expr_ref_vector s1 = m_unify.get_rule_subst(r, true); - expr_ref_vector s2 = m_unify.get_rule_subst(r2, false); - resolve_rule(m_pc, r, r2, tail_idx, s1, s2, *new_rule.get()); + expr_ref_vector s2 = m_unify.get_rule_subst(r2, false); + resolve_rule(r, r2, tail_idx, s1, s2, *new_rule.get()); expand_tail(*new_rule.get(), tail_idx+r2.get_uninterpreted_tail_size(), src, dst); } } } } - rule_set * mk_unfold::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { - m_pc = 0; - ref rpc; - if (pc) { - rpc = alloc(replace_proof_converter, m); - m_pc = rpc.get(); - } + rule_set * mk_unfold::operator()(rule_set const & source) { rule_set* rules = alloc(rule_set, m_ctx); rule_set::iterator it = source.begin(), end = source.end(); for (; it != end; ++it) { expand_tail(**it, 0, source, *rules); } - if (pc) { - pc = concat(pc.get(), rpc.get()); - } return rules; } diff --git a/src/muz_qe/dl_mk_unfold.h b/src/muz_qe/dl_mk_unfold.h index 3d20686a7..26f64926d 100644 --- a/src/muz_qe/dl_mk_unfold.h +++ b/src/muz_qe/dl_mk_unfold.h @@ -35,7 +35,6 @@ namespace datalog { ast_manager& m; rule_manager& rm; rule_unifier m_unify; - replace_proof_converter* m_pc; void expand_tail(rule& r, unsigned tail_idx, rule_set const& src, rule_set& dst); @@ -45,7 +44,7 @@ namespace datalog { */ mk_unfold(context & ctx); - rule_set * operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc); + rule_set * operator()(rule_set const & source); }; }; diff --git a/src/muz_qe/dl_relation_manager.cpp b/src/muz_qe/dl_relation_manager.cpp index 76d538b08..e001dd1a4 100644 --- a/src/muz_qe/dl_relation_manager.cpp +++ b/src/muz_qe/dl_relation_manager.cpp @@ -243,15 +243,14 @@ namespace datalog { relation_plugin & relation_manager::get_appropriate_plugin(const relation_signature & s) { relation_plugin * res = try_get_appropriate_plugin(s); - if(!res) { + if (!res) { throw default_exception("no suitable plugin found for given relation signature"); - throw 0; } return *res; } table_plugin * relation_manager::try_get_appropriate_plugin(const table_signature & t) { - if(m_favourite_table_plugin && m_favourite_table_plugin->can_handle_signature(t)) { + if (m_favourite_table_plugin && m_favourite_table_plugin->can_handle_signature(t)) { return m_favourite_table_plugin; } table_plugin_vector::iterator tpit = m_table_plugins.begin(); @@ -346,7 +345,7 @@ namespace datalog { return p->mk_empty(s); } - if(mk_empty_table_relation(s, res)) { + if (mk_empty_table_relation(s, res)) { return res; } @@ -469,6 +468,12 @@ namespace datalog { } } + void relation_manager::set_cancel(bool f) { + for (unsigned i = 0; i < m_relation_plugins.size(); ++i) { + m_relation_plugins[i]->set_cancel(f); + } + } + std::string relation_manager::to_nice_string(const relation_element & el) const { uint64 val; std::stringstream stm; @@ -884,10 +889,10 @@ namespace datalog { SASSERT(plugin->can_handle_signature(res_sign)); table_base * res = plugin->mk_empty(res_sign); - unsigned t1cols=t1.get_signature().size(); - unsigned t2cols=t2.get_signature().size(); - unsigned t1first_func=t1.get_signature().first_functional(); - unsigned t2first_func=t2.get_signature().first_functional(); + unsigned t1cols = t1.get_signature().size(); + unsigned t2cols = t2.get_signature().size(); + unsigned t1first_func = t1.get_signature().first_functional(); + unsigned t2first_func = t2.get_signature().first_functional(); table_base::iterator els1it = t1.begin(); table_base::iterator els1end = t1.end(); diff --git a/src/muz_qe/dl_relation_manager.h b/src/muz_qe/dl_relation_manager.h index ecba3d598..6b350642e 100644 --- a/src/muz_qe/dl_relation_manager.h +++ b/src/muz_qe/dl_relation_manager.h @@ -223,6 +223,7 @@ namespace datalog { relation_fact & to); + void set_cancel(bool f); // ----------------------------------- diff --git a/src/muz_qe/dl_rule.cpp b/src/muz_qe/dl_rule.cpp index d34f8a67f..b7d6d9fae 100644 --- a/src/muz_qe/dl_rule.cpp +++ b/src/muz_qe/dl_rule.cpp @@ -42,13 +42,13 @@ Revision History: #include"bool_rewriter.h" #include"qe_lite.h" #include"expr_safe_replace.h" +#include"hnf.h" namespace datalog { rule_manager::rule_manager(context& ctx) : m(ctx.get_manager()), - m_ctx(ctx), - m_refs(ctx.get_manager()) {} + m_ctx(ctx) {} bool rule_manager::is_predicate(func_decl * f) const { return m_ctx.is_predicate(f); @@ -56,16 +56,16 @@ namespace datalog { void rule_manager::inc_ref(rule * r) { if (r) { - SASSERT(r->m_ref_cnt!=UINT_MAX); + SASSERT(r->m_ref_cnt != UINT_MAX); r->m_ref_cnt++; } } void rule_manager::dec_ref(rule * r) { if (r) { - SASSERT(r->m_ref_cnt>0); + SASSERT(r->m_ref_cnt > 0); r->m_ref_cnt--; - if (r->m_ref_cnt==0) { + if (r->m_ref_cnt == 0) { r->deallocate(m); } } @@ -89,106 +89,32 @@ namespace datalog { } }; - void rule_manager::remove_labels(expr_ref& fml) { + void rule_manager::remove_labels(expr_ref& fml, proof_ref& pr) { expr_ref tmp(m); remove_label_cfg r_cfg(m); rewriter_tpl rwr(m, false, r_cfg); rwr(fml, tmp); + if (pr && fml != tmp) { + + pr = m.mk_modus_ponens(pr, m.mk_rewrite(fml, tmp)); + } fml = tmp; } - void rule_manager::mk_rule(expr* fml, rule_ref_vector& rules, symbol const& name) { + void rule_manager::mk_rule(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name) { + scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_FINE:PGM_DISABLED); + proof_ref pr(p, m); expr_ref fml1(m); - m_memoize_disj.reset(); - m_refs.reset(); bind_variables(fml, true, fml1); - remove_labels(fml1); - mk_rule_core(fml1, rules, name); + if (fml1 != fml && pr) { + pr = m.mk_asserted(fml1); + } + remove_labels(fml1, pr); + mk_rule_core_new(fml1, pr, rules, name); } - // - // Hoist quantifier from rule (universal) or query (existential) - // - unsigned rule_manager::hoist_quantifier(bool is_forall, expr_ref& fml, svector* names) { - - unsigned index = var_counter().get_next_var(fml); - while (is_quantifier(fml) && (is_forall == to_quantifier(fml)->is_forall())) { - quantifier* q = to_quantifier(fml); - index += q->get_num_decls(); - if (names) { - names->append(q->get_num_decls(), q->get_decl_names()); - } - fml = q->get_expr(); - } - if (!has_quantifiers(fml)) { - return index; - } - app_ref_vector vars(m); - quantifier_hoister qh(m); - qh.pull_quantifier(is_forall, fml, vars); - if (vars.empty()) { - return index; - } - // replace vars by de-bruijn indices - 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()); - } - rep.insert(v, m.mk_var(index++,m.get_sort(v))); - } - rep(fml); - return index; - } - - void rule_manager::mk_rule_core(expr* _fml, rule_ref_vector& rules, symbol const& name) { - app_ref_vector body(m); - app_ref head(m); - expr_ref e(m), fml(_fml, m); - svector is_negated; - TRACE("dl_rule", tout << mk_pp(fml, m) << "\n";); - unsigned index = hoist_quantifier(true, fml, 0); - check_app(fml); - head = to_app(fml); - - while (m.is_implies(head)) { - e = head->get_arg(0); - th_rewriter th_rwr(m); - th_rwr(e); - body.push_back(ensure_app(e)); - e = to_app(head)->get_arg(1); - check_app(e); - head = to_app(e.get()); - } - symbol head_name = (name == symbol::null)?head->get_decl()->get_name():name; - flatten_body(body); - if (body.size() == 1 && m.is_or(body[0].get()) && contains_predicate(body[0].get())) { - app* _or = to_app(body[0].get()); - for (unsigned i = 0; i < _or->get_num_args(); ++i) { - e = m.mk_implies(_or->get_arg(i), head); - mk_rule_core(e, rules, head_name); - } - return; - } - - eliminate_disjunctions(body, rules, head_name); - eliminate_quantifier_body(body, rules, head_name); - hoist_compound(index, head, body); - unsigned sz = body.size(); - for (unsigned i = 0; i < sz; ++i) { - app_ref b(body[i].get(), m); - hoist_compound(index, b, body); - body[i] = b; - } - TRACE("dl_rule", - tout << mk_pp(head, m) << " :- "; - for (unsigned i = 0; i < body.size(); ++i) { - tout << mk_pp(body[i].get(), m) << " "; - } - tout << "\n";); - + void rule_manager::mk_negations(app_ref_vector& body, svector& is_negated) { for (unsigned i = 0; i < body.size(); ++i) { expr* e = body[i].get(), *e1; if (m.is_not(e, e1) && is_predicate(e1)) { @@ -199,23 +125,105 @@ namespace datalog { else { is_negated.push_back(false); } - } - check_valid_rule(head, body.size(), body.c_ptr()); + } + } - rules.push_back(mk(head.get(), body.size(), body.c_ptr(), is_negated.c_ptr(), name)); - - if (m_ctx.fix_unbound_vars()) { - unsigned rule_cnt = rules.size(); - for (unsigned i=0; i is_negated; + unsigned index = extract_horn(fml, body, head); + hoist_compound_predicates(index, head, body); + TRACE("dl_rule", + tout << mk_pp(head, m) << " :- "; + for (unsigned i = 0; i < body.size(); ++i) { + tout << mk_pp(body[i].get(), m) << " "; + } + tout << "\n";); + + + mk_negations(body, is_negated); + check_valid_rule(head, body.size(), body.c_ptr()); + + rule_ref r(*this); + r = mk(head.get(), body.size(), body.c_ptr(), is_negated.c_ptr(), name); + + expr_ref fml1(m); + if (p) { + r->to_formula(fml1); + if (fml1 == fml) { + // no-op. + } + else if (is_quantifier(fml1)) { + p = m.mk_modus_ponens(p, m.mk_symmetry(m.mk_der(to_quantifier(fml1), fml))); + } + else { + p = m.mk_modus_ponens(p, m.mk_rewrite(fml, fml1)); + } + } + + if (m_ctx.fix_unbound_vars()) { + fix_unbound_vars(r, true); + } + + if (p) { + expr_ref fml2(m); + r->to_formula(fml2); + if (fml1 != fml2) { + p = m.mk_modus_ponens(p, m.mk_rewrite(fml1, fml2)); + } + r->set_proof(m, p); + } + rules.push_back(r); + } + + unsigned rule_manager::extract_horn(expr* fml, app_ref_vector& body, app_ref& head) { + expr* e1, *e2; + unsigned index = m_counter.get_next_var(fml); + if (::is_forall(fml)) { + index += to_quantifier(fml)->get_num_decls(); + fml = to_quantifier(fml)->get_expr(); + } + if (m.is_implies(fml, e1, e2)) { + expr_ref_vector es(m); + head = ensure_app(e2); + datalog::flatten_and(e1, es); + for (unsigned i = 0; i < es.size(); ++i) { + body.push_back(ensure_app(es[i].get())); + } + } + else { + head = ensure_app(fml); + } + return index; + } + + void rule_manager::hoist_compound_predicates(unsigned index, app_ref& head, app_ref_vector& body) { + unsigned sz = body.size(); + hoist_compound(index, head, body); + for (unsigned i = 0; i < sz; ++i) { + app_ref b(body[i].get(), m); + hoist_compound(index, b, body); + body[i] = b; + } + } + + void rule_manager::mk_query(expr* query, func_decl_ref& qpred, rule_ref_vector& query_rules, rule_ref& query_rule) { ptr_vector vars; svector names; @@ -225,7 +233,8 @@ namespace datalog { // Add implicit variables. // Remove existential prefix. bind_variables(query, false, q); - hoist_quantifier(false, q, &names); + quantifier_hoister qh(m); + qh.pull_quantifier(false, q, 0, &names); // retrieve free variables. get_free_vars(q, vars); if (vars.contains(static_cast(0))) { @@ -283,7 +292,12 @@ namespace datalog { rule_expr = m.mk_forall(vars.size(), vars.c_ptr(), names.c_ptr(), impl); } - mk_rule(rule_expr, query_rules); + scoped_proof_mode _sc(m, m_ctx.generate_proof_trace()?PGM_FINE:PGM_DISABLED); + proof_ref pr(m); + if (m_ctx.generate_proof_trace()) { + pr = m.mk_asserted(rule_expr); + } + mk_rule(rule_expr, pr, query_rules); SASSERT(query_rules.size() >= 1); query_rule = query_rules.back(); } @@ -363,73 +377,6 @@ namespace datalog { return false; } - void rule_manager::eliminate_disjunctions(app_ref_vector::element_ref& body, rule_ref_vector& rules, symbol const& name) { - - app* b = body.get(); - expr* e1; - bool negate_args = false; - bool is_disj = false; - unsigned num_disj = 0; - expr* const* disjs = 0; - if (!contains_predicate(b)) { - return; - } - TRACE("dl_rule", tout << mk_ismt2_pp(b, m) << "\n";); - if (m.is_or(b)) { - is_disj = true; - negate_args = false; - num_disj = b->get_num_args(); - disjs = b->get_args(); - } - if (m.is_not(b, e1) && m.is_and(e1)) { - is_disj = true; - negate_args = true; - num_disj = to_app(e1)->get_num_args(); - disjs = to_app(e1)->get_args(); - } - if (is_disj) { - ptr_vector sorts0, sorts1; - get_free_vars(b, sorts0); - expr_ref_vector args(m); - for (unsigned i = 0; i < sorts0.size(); ++i) { - if (sorts0[i]) { - args.push_back(m.mk_var(i,sorts0[i])); - sorts1.push_back(sorts0[i]); - } - } - app* old_head = 0; - if (m_memoize_disj.find(b, old_head)) { - body = old_head; - } - else { - app_ref head(m); - func_decl_ref f(m); - f = m.mk_fresh_func_decl(name.str().c_str(),"", sorts1.size(), sorts1.c_ptr(), m.mk_bool_sort()); - m_ctx.register_predicate(f); - head = m.mk_app(f, args.size(), args.c_ptr()); - - for (unsigned i = 0; i < num_disj; ++i) { - expr_ref fml(m); - expr* e = disjs[i]; - if (negate_args) e = m.mk_not(e); - fml = m.mk_implies(e,head); - mk_rule_core(fml, rules, name); - } - m_memoize_disj.insert(b, head); - m_refs.push_back(b); - m_refs.push_back(head); - // update the body to be the newly introduced head relation - body = head; - } - } - } - - void rule_manager::eliminate_disjunctions(app_ref_vector& body, rule_ref_vector& rules, symbol const& name) { - for (unsigned i = 0; i < body.size(); ++i) { - app_ref_vector::element_ref t = body[i]; - eliminate_disjunctions(t, rules, name); - } - } bool rule_manager::is_forall(ast_manager& m, expr* e, quantifier*& q) { expr* e1, *e2; @@ -448,40 +395,6 @@ namespace datalog { return false; } - void rule_manager::eliminate_quantifier_body(app_ref_vector::element_ref& body, rule_ref_vector& rules, symbol const& name) { - quantifier* q; - if (is_forall(m, body.get(), q) && contains_predicate(q)) { - expr* e = q->get_expr(); - if (!is_predicate(e)) { - ptr_vector sorts0, sorts1; - get_free_vars(e, sorts0); - expr_ref_vector args(m); - for (unsigned i = 0; i < sorts0.size(); ++i) { - if (sorts0[i]) { - args.push_back(m.mk_var(i,sorts0[i])); - sorts1.push_back(sorts0[i]); - } - } - app_ref head(m), fml(m); - func_decl_ref f(m); - f = m.mk_fresh_func_decl(name.str().c_str(),"", sorts1.size(), sorts1.c_ptr(), m.mk_bool_sort()); - m_ctx.register_predicate(f); - head = m.mk_app(f, args.size(), args.c_ptr()); - fml = m.mk_implies(e, head); - mk_rule_core(fml, rules, name); - // update the body to be the newly introduced head relation - body = m.mk_eq(m.mk_true(), m.update_quantifier(q, head)); - } - } - } - - void rule_manager::eliminate_quantifier_body(app_ref_vector& body, rule_ref_vector& rules, symbol const& name) { - for (unsigned i = 0; i < body.size(); ++i) { - app_ref_vector::element_ref t = body[i]; - eliminate_quantifier_body(t, rules, name); - } - } - app_ref rule_manager::ensure_app(expr* e) { SASSERT(m.is_bool(e)); @@ -509,6 +422,7 @@ namespace datalog { r->m_head = head; r->m_name = name; r->m_tail_size = n; + r->m_proof = 0; m.inc_ref(r->m_head); app * * uninterp_tail = r->m_tail; //grows upwards @@ -566,6 +480,11 @@ namespace datalog { if (normalize) { r->norm_vars(*this); } + DEBUG_CODE(ptr_vector sorts; + ::get_free_vars(head, sorts); + for (unsigned i = 0; i < n; ++i) { + ::get_free_vars(tail[i], sorts); + }); return r; } @@ -583,6 +502,7 @@ namespace datalog { r->m_tail_size = n; r->m_positive_cnt = source->m_positive_cnt; r->m_uninterp_cnt = source->m_uninterp_cnt; + r->m_proof = 0; m.inc_ref(r->m_head); for (unsigned i = 0; i < n; i++) { r->m_tail[i] = source->m_tail[i]; @@ -705,7 +625,7 @@ namespace datalog { bool_rewriter(m).mk_and(tails_with_unbound.size(), tails_with_unbound.c_ptr(), unbound_tail); unsigned q_var_cnt = unbound_vars.num_elems(); - unsigned max_var = m_var_counter.get_max_var(*r); + unsigned max_var = m_counter.get_max_rule_var(*r); expr_ref_vector subst(m); @@ -787,6 +707,27 @@ namespace datalog { r->set_accounting_parent_object(m_ctx, old_r); } + void rule_manager::mk_rule_rewrite_proof(rule& old_rule, rule& new_rule) { + if (&old_rule != &new_rule && + !new_rule.get_proof() && + old_rule.get_proof()) { + expr_ref fml(m); + new_rule.to_formula(fml); + scoped_proof _sc(m); + proof* p = m.mk_rewrite(m.get_fact(old_rule.get_proof()), fml); + new_rule.set_proof(m, m.mk_modus_ponens(old_rule.get_proof(), p)); + } + } + + void rule_manager::mk_rule_asserted_proof(rule& r) { + if (m_ctx.generate_proof_trace()) { + scoped_proof _scp(m); + expr_ref fml(m); + r.to_formula(fml); + r.set_proof(m, m.mk_asserted(fml)); + } + } + void rule_manager::substitute(rule_ref& r, unsigned sz, expr*const* es) { expr_ref tmp(m); app_ref new_head(m); @@ -845,10 +786,23 @@ namespace datalog { for (unsigned i = 0; i < n; i++) { m.dec_ref(get_tail(i)); } + if (m_proof) { + m.dec_ref(m_proof); + } this->~rule(); m.get_allocator().deallocate(get_obj_size(n), this); } + void rule::set_proof(ast_manager& m, proof* p) { + if (p) { + m.inc_ref(p); + } + if (m_proof) { + m.dec_ref(m_proof); + } + m_proof = p; + } + bool rule::is_in_tail(const func_decl * p, bool only_positive) const { unsigned len = only_positive ? get_positive_tail_size() : get_uninterpreted_tail_size(); for (unsigned i = 0; i < len; i++) { @@ -927,6 +881,15 @@ namespace datalog { return exist || univ; } + bool rule::has_negation() const { + for (unsigned i = 0; i < get_uninterpreted_tail_size(); ++i) { + if (is_neg_tail(i)) { + return true; + } + } + return false; + } + void rule::get_used_vars(used_vars& used) const { used.process(get_head()); unsigned sz = get_tail_size(); @@ -1017,6 +980,9 @@ namespace datalog { out << '}'; } out << '\n'; + if (m_proof) { + out << mk_pp(m_proof, m) << '\n'; + } } void rule::to_formula(expr_ref& fml) const { @@ -1106,3 +1072,4 @@ namespace datalog { }; + diff --git a/src/muz_qe/dl_rule.h b/src/muz_qe/dl_rule.h index a9e360344..3f535125f 100644 --- a/src/muz_qe/dl_rule.h +++ b/src/muz_qe/dl_rule.h @@ -24,6 +24,9 @@ Revision History: #include"dl_costs.h" #include"dl_util.h" #include"used_vars.h" +#include"proof_converter.h" +#include"model_converter.h" +#include"ast_counter.h" namespace datalog { @@ -43,11 +46,9 @@ namespace datalog { */ class rule_manager { - ast_manager& m; - context& m_ctx; - var_counter m_var_counter; - obj_map m_memoize_disj; - expr_ref_vector m_refs; + ast_manager& m; + context& m_ctx; + rule_counter m_counter; // only the context can create a rule_manager friend class context; @@ -57,19 +58,13 @@ namespace datalog { /** \brief Move functions from predicate tails into the interpreted tail by introducing new variables. */ + void hoist_compound_predicates(unsigned num_bound, app_ref& head, app_ref_vector& body); + void hoist_compound(unsigned& num_bound, app_ref& fml, app_ref_vector& body); void flatten_body(app_ref_vector& body); - void remove_labels(expr_ref& fml); - - void eliminate_disjunctions(app_ref_vector::element_ref& body, rule_ref_vector& rules, symbol const& name); - - void eliminate_disjunctions(app_ref_vector& body, rule_ref_vector& rules, symbol const& name); - - void eliminate_quantifier_body(app_ref_vector::element_ref& body, rule_ref_vector& rules, symbol const& name); - - void eliminate_quantifier_body(app_ref_vector& body, rule_ref_vector& rules, symbol const& name); + void remove_labels(expr_ref& fml, proof_ref& pr); app_ref ensure_app(expr* e); @@ -81,7 +76,15 @@ namespace datalog { void mk_rule_core(expr* fml, rule_ref_vector& rules, symbol const& name); - unsigned hoist_quantifier(bool is_forall, expr_ref& fml, svector* names); + void mk_negations(app_ref_vector& body, svector& is_negated); + + void mk_rule_core_new(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name); + + void mk_rule_core2(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name); + + static expr_ref mk_implies(app_ref_vector const& body, expr* head); + + unsigned extract_horn(expr* fml, app_ref_vector& body, app_ref& head); /** \brief Perform cheap quantifier elimination to reduce the number of variables in the interpreted tail. @@ -101,7 +104,7 @@ namespace datalog { The formula is of the form (forall (...) (forall (...) (=> (and ...) head))) */ - void mk_rule(expr* fml, rule_ref_vector& rules, symbol const& name = symbol::null); + void mk_rule(expr* fml, proof* p, rule_ref_vector& rules, symbol const& name = symbol::null); /** \brief Create a Datalog query from an expression. @@ -131,7 +134,15 @@ namespace datalog { /** make sure there are not non-quantified variables that occur only in interpreted predicates */ void fix_unbound_vars(rule_ref& r, bool try_quantifier_elimination); + /** + \brief add proof that new rule is obtained by rewriting old rule. + */ + void mk_rule_rewrite_proof(rule& old_rule, rule& new_rule); + /** + \brief tag rule as asserted. + */ + void mk_rule_asserted_proof(rule& r); /** \brief apply substitution to variables of rule. @@ -162,7 +173,7 @@ namespace datalog { static bool is_forall(ast_manager& m, expr* e, quantifier*& q); - var_counter& get_var_counter() { return m_var_counter; } + rule_counter& get_counter() { return m_counter; } }; @@ -170,6 +181,7 @@ namespace datalog { friend class rule_manager; app * m_head; + proof* m_proof; unsigned m_tail_size:20; // unsigned m_reserve:12; unsigned m_ref_cnt; @@ -201,6 +213,10 @@ namespace datalog { void get_used_vars(used_vars& uv) const; public: + + proof * get_proof() const { return m_proof; } + + void set_proof(ast_manager& m, proof* p); app * get_head() const { return m_head; } @@ -237,6 +253,7 @@ namespace datalog { bool has_uninterpreted_non_predicates(func_decl*& f) const; void has_quantifiers(bool& existential, bool& universal) const; bool has_quantifiers() const; + bool has_negation() const; /** \brief Store in d the (direct) dependencies of the given rule. diff --git a/src/muz_qe/dl_rule_set.cpp b/src/muz_qe/dl_rule_set.cpp index caecc76ef..f9ae3620d 100644 --- a/src/muz_qe/dl_rule_set.cpp +++ b/src/muz_qe/dl_rule_set.cpp @@ -30,17 +30,17 @@ namespace datalog { rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed): m_context(o.m_context) { - if(reversed) { + if (reversed) { iterator oit = o.begin(); iterator oend = o.end(); - for(; oit!=oend; ++oit) { + for (; oit!=oend; ++oit) { func_decl * pred = oit->m_key; item_set & orig_items = *oit->get_value(); ensure_key(pred); item_set::iterator dit = orig_items.begin(); item_set::iterator dend = orig_items.end(); - for(; dit!=dend; ++dit) { + for (; dit!=dend; ++dit) { func_decl * master_pred = *dit; insert(master_pred, pred); } @@ -49,7 +49,7 @@ namespace datalog { else { iterator oit = o.begin(); iterator oend = o.end(); - for(; oit!=oend; ++oit) { + for (; oit!=oend; ++oit) { func_decl * pred = oit->m_key; item_set & orig_items = *oit->get_value(); m_data.insert(pred, alloc(item_set, orig_items)); @@ -73,7 +73,7 @@ namespace datalog { rule_dependencies::item_set & rule_dependencies::ensure_key(func_decl * pred) { deps_type::obj_map_entry * e = m_data.insert_if_not_there2(pred, 0); - if(!e->get_data().m_value) { + if (!e->get_data().m_value) { e->get_data().m_value = alloc(item_set); } return *e->get_data().m_value; @@ -101,12 +101,13 @@ namespace datalog { void rule_dependencies::populate(unsigned n, rule * const * rules) { SASSERT(m_data.empty()); - for(unsigned i=0; iget_decl()->get_name() << "\n";); m_visited.reset(); func_decl * d = r->get_head()->get_decl(); func_decl_set & s = ensure_key(d); @@ -141,7 +142,7 @@ namespace datalog { const rule_dependencies::item_set & rule_dependencies::get_deps(func_decl * f) const { deps_type::obj_map_entry * e = m_data.find_core(f); - if(!e) { + if (!e) { return m_empty_item_set; } SASSERT(e->get_data().get_value()); @@ -152,9 +153,9 @@ namespace datalog { ptr_vector to_remove; iterator pit = begin(); iterator pend = end(); - for(; pit!=pend; ++pit) { + for (; pit!=pend; ++pit) { func_decl * pred = pit->m_key; - if(!allowed.contains(pred)) { + if (!allowed.contains(pred)) { to_remove.insert(pred); continue; } @@ -163,7 +164,7 @@ namespace datalog { } ptr_vector::iterator rit = to_remove.begin(); ptr_vector::iterator rend = to_remove.end(); - for(; rit!=rend; ++rit) { + for (; rit!=rend; ++rit) { remove_m_data_entry(*rit); } } @@ -172,7 +173,7 @@ namespace datalog { remove_m_data_entry(itm); iterator pit = begin(); iterator pend = end(); - for(; pit!=pend; ++pit) { + for (; pit!=pend; ++pit) { item_set & itms = *pit->get_value(); itms.remove(itm); } @@ -181,12 +182,12 @@ namespace datalog { void rule_dependencies::remove(const item_set & to_remove) { item_set::iterator rit = to_remove.begin(); item_set::iterator rend = to_remove.end(); - for(; rit!=rend; ++rit) { + for (; rit!=rend; ++rit) { remove_m_data_entry(*rit); } iterator pit = begin(); iterator pend = end(); - for(; pit!=pend; ++pit) { + for (; pit!=pend; ++pit) { item_set * itms = pit->get_value(); set_difference(*itms, to_remove); } @@ -196,9 +197,9 @@ namespace datalog { unsigned res = 0; iterator pit = begin(); iterator pend = end(); - for(; pit!=pend; ++pit) { + for (; pit!=pend; ++pit) { item_set & itms = *pit->get_value(); - if(itms.contains(f)) { + if (itms.contains(f)) { res++; } } @@ -214,10 +215,10 @@ namespace datalog { iterator pit = begin(); iterator pend = end(); - for(; pit!=pend; ++pit) { + for (; pit!=pend; ++pit) { func_decl * pred = pit->m_key; unsigned deg = in_degree(pred); - if(deg==0) { + if (deg==0) { res.push_back(pred); } else { @@ -225,25 +226,25 @@ namespace datalog { } } - while(curr_indexget_data().m_value; SASSERT(child_deg>0); child_deg--; - if(child_deg==0) { + if (child_deg==0) { res.push_back(child); } } curr_index++; } - if(res.size()m_key; const item_set & deps = *pit->m_value; item_set::iterator dit=deps.begin(); item_set::iterator dend=deps.end(); - if(dit==dend) { + if (dit == dend) { out<get_name()<<" - \n"; } - for(; dit!=dend; ++dit) { + for (; dit != dend; ++dit) { func_decl * dep = *dit; - out<get_name()<<" -> "<get_name()<<"\n"; + out << pred->get_name() << " -> " << dep->get_name() << "\n"; } } } @@ -292,8 +293,8 @@ namespace datalog { m_deps(rs.m_context), m_stratifier(0) { add_rules(rs); - if(rs.m_stratifier) { - TRUSTME(close()); + if (rs.m_stratifier) { + VERIFY(close()); } } @@ -302,7 +303,7 @@ namespace datalog { } void rule_set::reset() { - if(m_stratifier) { + if (m_stratifier) { m_stratifier = 0; } reset_dealloc_values(m_head2rules); @@ -346,18 +347,19 @@ namespace datalog { void rule_set::ensure_closed() { - if(!is_closed()) { - TRUSTME(close()); + if (!is_closed()) { + VERIFY(close()); } } bool rule_set::close() { SASSERT(!is_closed()); //the rule_set is not already closed + m_deps.populate(*this); m_stratifier = alloc(rule_stratifier, m_deps); - if(!stratified_negation()) { + if (!stratified_negation()) { m_stratifier = 0; m_deps.reset(); return false; @@ -391,7 +393,7 @@ namespace datalog { unsigned head_strat = get_predicate_strat(head_decl); SASSERT(head_strat>=neg_strat); //head strat can never be lower than that of a tail - if(head_strat==neg_strat) { + if (head_strat == neg_strat) { return false; } } @@ -415,7 +417,7 @@ namespace datalog { const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const { decl2rules::obj_map_entry * e = m_head2rules.find_core(pred); - if(!e) { + if (!e) { return m_empty_rule_vector; } return *e->get_data().m_value; @@ -443,7 +445,7 @@ namespace datalog { ptr_vector::iterator end2 = rules->end(); for (; it2 != end2; ++it2) { rule * r = *it2; - if(!r->passes_output_thresholds(m_context)) { + if (!r->passes_output_thresholds(m_context)) { continue; } r->display(m_context, out); @@ -468,23 +470,23 @@ namespace datalog { const pred_set_vector & strats = get_strats(); pred_set_vector::const_iterator sit = strats.begin(); pred_set_vector::const_iterator send = strats.end(); - for(; sit!=send; ++sit) { + for (; sit!=send; ++sit) { func_decl_set & strat = **sit; func_decl_set::iterator fit=strat.begin(); func_decl_set::iterator fend=strat.end(); bool non_empty = false; - for(; fit!=fend; ++fit) { + for (; fit!=fend; ++fit) { func_decl * first = *fit; const func_decl_set & deps = m_deps.get_deps(first); func_decl_set::iterator dit=deps.begin(); func_decl_set::iterator dend=deps.end(); - for(; dit!=dend; ++dit) { + for (; dit!=dend; ++dit) { non_empty = true; func_decl * dep = *dit; out<get_name()<<" -> "<get_name()<<"\n"; } } - if(non_empty && sit!=send) { + if (non_empty && sit!=send) { out << "\n"; } } @@ -499,7 +501,7 @@ namespace datalog { rule_stratifier::~rule_stratifier() { comp_vector::iterator it = m_strats.begin(); comp_vector::iterator end = m_strats.end(); - for(; it!=end; ++it) { + for (; it!=end; ++it) { SASSERT(*it); dealloc(*it); } @@ -507,7 +509,7 @@ namespace datalog { unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const { unsigned num; - if(!m_pred_strat_nums.find(pred, num)) { + if (!m_pred_strat_nums.find(pred, num)) { //the number of the predicate is not stored, therefore it did not appear //in the algorithm and therefore it does not depend on anything and nothing //depends on it. So it is safe to assign zero strate to it, although it is @@ -520,19 +522,19 @@ namespace datalog { void rule_stratifier::traverse(T* el) { unsigned p_num; - if(m_preorder_nums.find(el, p_num)) { - if(p_numinsert(s_el); m_component_nums.insert(s_el, comp_num); - } while(s_el!=el); + } while (s_el!=el); m_stack_P.pop_back(); } } } void rule_stratifier::process() { - if(m_deps.empty()) { + if (m_deps.empty()) { return; } //detect strong components rule_dependencies::iterator it = m_deps.begin(); rule_dependencies::iterator end = m_deps.end(); - for(; it!=end; ++it) { + for (; it!=end; ++it) { T * el = it->m_key; //we take a note of the preorder number with which this sweep started m_first_preorder = m_next_preorder; @@ -593,32 +595,32 @@ namespace datalog { //init in_degrees it = m_deps.begin(); end = m_deps.end(); - for(; it!=end; ++it) { + for (; it != end; ++it) { T * el = it->m_key; item_set * out_edges = it->m_value; unsigned el_comp; - TRUSTME( m_component_nums.find(el, el_comp) ); + VERIFY( m_component_nums.find(el, el_comp) ); - item_set::iterator eit=out_edges->begin(); - item_set::iterator eend=out_edges->end(); - for(; eit!=eend; ++eit) { + item_set::iterator eit = out_edges->begin(); + item_set::iterator eend = out_edges->end(); + for (; eit!=eend; ++eit) { T * tgt = *eit; unsigned tgt_comp = m_component_nums.find(tgt); - if(el_comp!=tgt_comp) { + if (el_comp != tgt_comp) { in_degrees[tgt_comp]++; } } } - //We put components whose indegree is zero to m_strats and assign its - //m_components entry to zero. + // We put components whose indegree is zero to m_strats and assign its + // m_components entry to zero. unsigned comp_cnt = m_components.size(); - for(unsigned i=0; ibegin(); item_set::iterator cend=comp->end(); - for(; cit!=cend; ++cit) { + for (; cit!=cend; ++cit) { T * el = *cit; const item_set & deps = m_deps.get_deps(el); item_set::iterator eit=deps.begin(); item_set::iterator eend=deps.end(); - for(; eit!=eend; ++eit) { + for (; eit!=eend; ++eit) { T * tgt = *eit; unsigned tgt_comp; - TRUSTME( m_component_nums.find(tgt, tgt_comp) ); + VERIFY( m_component_nums.find(tgt, tgt_comp) ); //m_components[tgt_comp]==0 means the edge is intra-component. //Otherwise it would go to another component, but it is not possible, since //as m_components[tgt_comp]==0, its indegree has already reached zero. - if(m_components[tgt_comp]) { + if (m_components[tgt_comp]) { SASSERT(in_degrees[tgt_comp]>0); in_degrees[tgt_comp]--; - if(in_degrees[tgt_comp]==0) { + if (in_degrees[tgt_comp]==0) { m_strats.push_back(m_components[tgt_comp]); m_components[tgt_comp] = 0; } } - - traverse(*cit); } } @@ -669,13 +669,12 @@ namespace datalog { SASSERT(m_pred_strat_nums.empty()); unsigned strat_cnt = m_strats.size(); - for(unsigned strat_index=0; strat_indexbegin(); item_set::iterator cend=comp->end(); - for(; cit!=cend; ++cit) { + for (; cit != cend; ++cit) { T * el = *cit; - m_pred_strat_nums.insert(el, strat_index); } } @@ -686,6 +685,21 @@ namespace datalog { m_stack_P.finalize(); m_component_nums.finalize(); m_components.finalize(); + + } + + void rule_stratifier::display(std::ostream& out) const { + m_deps.display(out << "dependencies\n"); + out << "strata\n"; + for (unsigned i = 0; i < m_strats.size(); ++i) { + item_set::iterator it = m_strats[i]->begin(); + item_set::iterator end = m_strats[i]->end(); + for (; it != end; ++it) { + out << (*it)->get_name() << " "; + } + out << "\n"; + } + } }; diff --git a/src/muz_qe/dl_rule_set.h b/src/muz_qe/dl_rule_set.h index fdbbf7626..58427ca87 100644 --- a/src/muz_qe/dl_rule_set.h +++ b/src/muz_qe/dl_rule_set.h @@ -20,7 +20,6 @@ Revision History: #define _DL_RULE_SET_H_ #include"obj_hashtable.h" - #include"dl_rule.h" namespace datalog { @@ -46,7 +45,7 @@ namespace datalog { ast_mark m_visited; - //we need to take care with removing to aviod memory leaks + //we need to take care with removing to avoid memory leaks void remove_m_data_entry(func_decl * key); //sometimes we need to return reference to an empty set, @@ -146,6 +145,8 @@ namespace datalog { const comp_vector & get_strats() const { return m_strats; } unsigned get_predicate_strat(func_decl * pred) const; + + void display( std::ostream & out ) const; }; /** diff --git a/src/muz_qe/dl_rule_transformer.cpp b/src/muz_qe/dl_rule_transformer.cpp index 5f52e0a39..5cc686052 100644 --- a/src/muz_qe/dl_rule_transformer.cpp +++ b/src/muz_qe/dl_rule_transformer.cpp @@ -16,11 +16,11 @@ Author: Revision History: --*/ + #include #include #include"dl_context.h" - #include"dl_rule_transformer.h" namespace datalog { @@ -31,25 +31,38 @@ namespace datalog { rule_transformer::~rule_transformer() { - plugin_vector::iterator it=m_plugins.begin(); - plugin_vector::iterator end=m_plugins.end(); + reset(); + } + + void rule_transformer::reset() { + plugin_vector::iterator it = m_plugins.begin(); + plugin_vector::iterator end = m_plugins.end(); for(; it!=end; ++it) { dealloc(*it); } + m_plugins.reset(); + m_dirty = false; + } + + void rule_transformer::cancel() { + plugin_vector::iterator it = m_plugins.begin(); + plugin_vector::iterator end = m_plugins.end(); + for(; it!=end; ++it) { + (*it)->cancel(); + } } struct rule_transformer::plugin_comparator { bool operator()(rule_transformer::plugin * p1, rule_transformer::plugin * p2) { - return p1->get_priority()>p2->get_priority(); + return p1->get_priority() > p2->get_priority(); } }; void rule_transformer::ensure_ordered() { - if (!m_dirty) { - return; + if (m_dirty) { + std::sort(m_plugins.begin(), m_plugins.end(), plugin_comparator()); + m_dirty = false; } - std::sort(m_plugins.begin(), m_plugins.end(), plugin_comparator()); - m_dirty=false; } void rule_transformer::register_plugin(plugin * p) { @@ -58,7 +71,7 @@ namespace datalog { m_dirty=true; } - bool rule_transformer::operator()(rule_set & rules, model_converter_ref& mc, proof_converter_ref& pc) { + bool rule_transformer::operator()(rule_set & rules) { ensure_ordered(); bool modified = false; @@ -67,12 +80,12 @@ namespace datalog { tout<<"init:\n"; rules.display(tout); ); - plugin_vector::iterator it=m_plugins.begin(); - plugin_vector::iterator end=m_plugins.end(); - for(; it!=end; ++it) { + plugin_vector::iterator it = m_plugins.begin(); + plugin_vector::iterator end = m_plugins.end(); + for(; it!=end && !m_context.canceled(); ++it) { plugin & p = **it; - rule_set * new_rules = p(rules, mc, pc); + rule_set * new_rules = p(rules); if (!new_rules) { continue; } diff --git a/src/muz_qe/dl_rule_transformer.h b/src/muz_qe/dl_rule_transformer.h index 1cc41a146..3f60e9aea 100644 --- a/src/muz_qe/dl_rule_transformer.h +++ b/src/muz_qe/dl_rule_transformer.h @@ -23,11 +23,11 @@ Revision History: #include"vector.h" #include"dl_rule.h" #include"dl_rule_set.h" -#include"model_converter.h" -#include"proof_converter.h" namespace datalog { + class context; + class rule_transformer { public: class plugin; @@ -37,9 +37,9 @@ namespace datalog { typedef svector plugin_vector; struct plugin_comparator; - context & m_context; - rule_manager & m_rule_manager; - bool m_dirty; + context & m_context; + rule_manager & m_rule_manager; + bool m_dirty; svector m_plugins; void ensure_ordered(); @@ -47,6 +47,13 @@ namespace datalog { rule_transformer(context & ctx); ~rule_transformer(); + + /** + \brief Reset all registered transformers. + */ + void reset(); + + void cancel(); /** \brief Add a plugin for rule transformation. @@ -59,7 +66,7 @@ namespace datalog { \brief Transform the rule set using the registered transformation plugins. If the rule set has changed, return true; otherwise return false. */ - bool operator()(rule_set & rules, model_converter_ref& mc, proof_converter_ref& pc); + bool operator()(rule_set & rules); }; class rule_transformer::plugin { @@ -72,6 +79,7 @@ namespace datalog { void attach(rule_transformer & transformer) { m_transformer = &transformer; } protected: + /** \brief Create a plugin object for rule_transformer. @@ -80,24 +88,25 @@ namespace datalog { */ plugin(unsigned priority, bool can_destratify_negation = false) : m_priority(priority), m_can_destratify_negation(can_destratify_negation), m_transformer(0) {} + public: virtual ~plugin() {} unsigned get_priority() { return m_priority; } bool can_destratify_negation() const { return m_can_destratify_negation; } + virtual void cancel() {} + /** \brief Return \c rule_set object with containing transformed rules or 0 if no transformation was done. The caller takes ownership of the returned \c rule_set object. */ - virtual rule_set * operator()(rule_set const & source, - model_converter_ref& mc, - proof_converter_ref& pc) = 0; + virtual rule_set * operator()(rule_set const & source) = 0; /** - Removes duplicate tails. + Removes duplicate tails. */ static void remove_duplicate_tails(app_ref_vector& tail, svector& tail_neg); diff --git a/src/muz_qe/dl_util.cpp b/src/muz_qe/dl_util.cpp index 4a406578c..95d268510 100644 --- a/src/muz_qe/dl_util.cpp +++ b/src/muz_qe/dl_util.cpp @@ -153,35 +153,6 @@ namespace datalog { flatten_or(result); } - bool push_toplevel_junction_negation_inside(expr_ref& e) - { - ast_manager& m = e.get_manager(); - bool_rewriter brwr(m); - - expr * arg; - if(!m.is_not(e, arg)) { return false; } - bool is_and = m.is_and(arg); - if(!is_and && !m.is_or(arg)) { return false; } - - //now we know we have formula we need to transform - app * junction = to_app(arg); - expr_ref_vector neg_j_args(m); - unsigned num_args = junction->get_num_args(); - for(unsigned i=0; iget_arg(i), neg_j_arg); - neg_j_args.push_back(neg_j_arg); - } - if(is_and) { - brwr.mk_or(neg_j_args.size(), neg_j_args.c_ptr(), e); - } - else { - brwr.mk_and(neg_j_args.size(), neg_j_args.c_ptr(), e); - } - return true; - } - - bool contains_var(expr * trm, unsigned var_idx) { ptr_vector vars; ::get_free_vars(trm, vars); @@ -431,89 +402,8 @@ namespace datalog { } } - void counter::update(unsigned el, int delta) { - int & counter = get(el); - SASSERT(!m_stay_non_negative || counter>=0); - SASSERT(!m_stay_non_negative || static_cast(counter)>=-delta); - counter += delta; - } - - int & counter::get(unsigned el) { - return m_data.insert_if_not_there2(el, 0)->get_data().m_value; - } - - counter & counter::count(unsigned sz, const unsigned * els, int delta) { - for(unsigned i=0; im_value>0 ) { - cnt++; - } - } - return cnt; - } - - void counter::collect_positive(idx_set & acc) const { - iterator eit = begin(); - iterator eend = end(); - for(; eit!=eend; ++eit) { - if(eit->m_value>0) { acc.insert(eit->m_key); } - } - } - - bool counter::get_max_positive(unsigned & res) const { - bool found = false; - iterator eit = begin(); - iterator eend = end(); - for(; eit!=eend; ++eit) { - if( eit->m_value>0 && (!found || eit->m_key>res) ) { - found = true; - res = eit->m_key; - } - } - return found; - } - - unsigned counter::get_max_positive() const { - unsigned max_pos; - VERIFY(get_max_positive(max_pos)); - return max_pos; - } - - int counter::get_max_counter_value() const { - int res = 0; - iterator eit = begin(); - iterator eend = end(); - for (; eit!=eend; ++eit) { - if( eit->m_value>res ) { - res = eit->m_value; - } - } - return res; - } - - void var_counter::count_vars(ast_manager & m, const app * pred, int coef) { - unsigned n = pred->get_num_args(); - for (unsigned i = 0; i < n; i++) { - m_sorts.reset(); - ::get_free_vars(pred->get_arg(i), m_sorts); - for (unsigned j = 0; j < m_sorts.size(); ++j) { - if (m_sorts[j]) { - update(j, coef); - } - } - } - } - - void var_counter::count_vars(ast_manager & m, const rule * r, int coef) { + + void rule_counter::count_rule_vars(ast_manager & m, const rule * r, int coef) { count_vars(m, r->get_head(), 1); unsigned n = r->get_tail_size(); for (unsigned i = 0; i < n; i++) { @@ -521,50 +411,7 @@ namespace datalog { } } - unsigned var_counter::get_max_var(bool& has_var) { - has_var = false; - unsigned max_var = 0; - while (!m_todo.empty()) { - expr* e = m_todo.back(); - unsigned scope = m_scopes.back(); - m_todo.pop_back(); - m_scopes.pop_back(); - if (m_visited.is_marked(e)) { - continue; - } - m_visited.mark(e, true); - switch(e->get_kind()) { - case AST_QUANTIFIER: { - quantifier* q = to_quantifier(e); - m_todo.push_back(q->get_expr()); - m_scopes.push_back(scope + q->get_num_decls()); - break; - } - case AST_VAR: { - if (to_var(e)->get_idx() >= scope + max_var) { - has_var = true; - max_var = to_var(e)->get_idx() - scope; - } - break; - } - case AST_APP: { - app* a = to_app(e); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - m_todo.push_back(a->get_arg(i)); - m_scopes.push_back(scope); - } - break; - } - default: - UNREACHABLE(); - break; - } - } - m_visited.reset(); - return max_var; - } - - unsigned var_counter::get_max_var(const rule & r) { + unsigned rule_counter::get_max_rule_var(const rule & r) { m_todo.push_back(r.get_head()); m_scopes.push_back(0); unsigned n = r.get_tail_size(); @@ -576,22 +423,6 @@ namespace datalog { return get_max_var(has_var); } - unsigned var_counter::get_max_var(expr* e) { - bool has_var = false; - m_todo.push_back(e); - m_scopes.push_back(0); - return get_max_var(has_var); - } - - unsigned var_counter::get_next_var(expr* e) { - bool has_var = false; - m_todo.push_back(e); - m_scopes.push_back(0); - unsigned mv = get_max_var(has_var); - if (has_var) mv++; - return mv; - } - void del_rule(horn_subsume_model_converter* mc, rule& r) { if (mc) { ast_manager& m = mc->get_manager(); @@ -614,6 +445,7 @@ namespace datalog { } } + void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res) { if (!pc) return; @@ -651,6 +483,44 @@ namespace datalog { pc->insert(pr); } + void resolve_rule(rule const& r1, rule const& r2, unsigned idx, + expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res) { + if (!r1.get_proof()) { + return; + } + SASSERT(r2.get_proof()); + ast_manager& m = s1.get_manager(); + expr_ref fml(m); + res.to_formula(fml); + vector substs; + svector > positions; + substs.push_back(s1); + substs.push_back(s2); + + scoped_proof _sc(m); + proof_ref pr(m); + proof_ref_vector premises(m); + premises.push_back(r1.get_proof()); + premises.push_back(r2.get_proof()); + positions.push_back(std::make_pair(idx+1, 0)); + + TRACE("dl", + tout << premises[0]->get_id() << " " << mk_pp(premises[0].get(), m) << "\n"; + for (unsigned i = 0; i < s1.size(); ++i) { + tout << mk_pp(s1[i], m) << " "; + } + tout << "\n"; + tout << premises[1]->get_id() << " " << mk_pp(premises[1].get(), m) << "\n"; + for (unsigned i = 0; i < s2.size(); ++i) { + tout << mk_pp(s2[i], m) << " "; + } + tout << "\n"; + ); + + pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml, positions, substs); + res.set_proof(m, pr); + } + class skip_model_converter : public model_converter { public: skip_model_converter() {} @@ -678,10 +548,6 @@ namespace datalog { proof_converter* mk_skip_proof_converter() { return alloc(skip_proof_converter); } - unsigned get_max_var(const rule & r, ast_manager & m) { - var_counter ctr; - return ctr.get_max_var(r); - } void reverse_renaming(ast_manager & m, const expr_ref_vector & src, expr_ref_vector & tgt) { SASSERT(tgt.empty()); diff --git a/src/muz_qe/dl_util.h b/src/muz_qe/dl_util.h index 69be7e9ac..b313f0ae3 100644 --- a/src/muz_qe/dl_util.h +++ b/src/muz_qe/dl_util.h @@ -27,6 +27,7 @@ Revision History: #include"replace_proof_converter.h" #include"substitution.h" #include"fixedpoint_params.hpp" +#include"ast_counter.h" namespace datalog { @@ -80,19 +81,6 @@ namespace datalog { void flatten_or(expr* fml, expr_ref_vector& result); - /** - Transform - ~(a1 | ... | aN) - into - ~a1 | ... | ~aN - and - ~(a1 & ... & aN) - into - ~a1 | ... | ~aN - - Return true if something was done. - */ - bool push_toplevel_junction_negation_inside(expr_ref& e); bool contains_var(expr * trm, unsigned var_idx); @@ -411,80 +399,11 @@ namespace datalog { } - class counter { - protected: - typedef u_map map_impl; - map_impl m_data; - const bool m_stay_non_negative; + class rule_counter : public var_counter { public: - typedef map_impl::iterator iterator; - - counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} - - iterator begin() const { return m_data.begin(); } - iterator end() const { return m_data.end(); } - - void update(unsigned el, int delta); - int & get(unsigned el); - /** - \brief Increase values of elements in \c els by \c delta. - - The function returns a reference to \c *this to allow for expressions like - counter().count(sz, arr).get_positive_count() - */ - counter & count(unsigned sz, const unsigned * els, int delta = 1); - counter & count(const unsigned_vector & els, int delta = 1) { - return count(els.size(), els.c_ptr(), delta); - } - - void collect_positive(idx_set & acc) const; - unsigned get_positive_count() const; - bool get_max_positive(unsigned & res) const; - unsigned get_max_positive() const; - /** - Since the default counter value of a counter is zero, the result is never negative. - */ - int get_max_counter_value() const; - }; - - class var_counter : public counter { - ptr_vector m_sorts; - expr_fast_mark1 m_visited; - ptr_vector m_todo; - unsigned_vector m_scopes; - unsigned get_max_var(bool & has_var); - - public: - var_counter(bool stay_non_negative = true) : counter(stay_non_negative) {} - void count_vars(ast_manager & m, const app * t, int coef = 1); - void count_vars(ast_manager & m, const rule * r, int coef = 1); - unsigned get_max_var(const rule& r); - unsigned get_max_var(expr* e); - unsigned get_next_var(expr* e); - }; - - class ast_counter { - typedef obj_map map_impl; - map_impl m_data; - bool m_stay_non_negative; - public: - typedef map_impl::iterator iterator; - - ast_counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {} - - iterator begin() const { return m_data.begin(); } - iterator end() const { return m_data.end(); } - - int & get(ast * el) { - return m_data.insert_if_not_there2(el, 0)->get_data().m_value; - } - void update(ast * el, int delta){ - get(el)+=delta; - SASSERT(!m_stay_non_negative || get(el)>=0); - } - - void inc(ast * el) { update(el, 1); } - void dec(ast * el) { update(el, -1); } + rule_counter(bool stay_non_negative = true): var_counter(stay_non_negative) {} + void count_rule_vars(ast_manager & m, const rule * r, int coef = 1); + unsigned get_max_rule_var(const rule& r); }; void del_rule(horn_subsume_model_converter* mc, rule& r); @@ -492,16 +411,14 @@ namespace datalog { void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx, expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res); + void resolve_rule(rule const& r1, rule const& r2, unsigned idx, + expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res); + model_converter* mk_skip_model_converter(); proof_converter* mk_skip_proof_converter(); - /** - Return maximal variable number, or zero is there isn't any - */ - // unsigned get_max_var(const rule & r, ast_manager & m); - void reverse_renaming(ast_manager & m, const expr_ref_vector & src, expr_ref_vector & tgt); /** diff --git a/src/muz_qe/equiv_proof_converter.cpp b/src/muz_qe/equiv_proof_converter.cpp index 53db01900..98ea88044 100644 --- a/src/muz_qe/equiv_proof_converter.cpp +++ b/src/muz_qe/equiv_proof_converter.cpp @@ -22,12 +22,14 @@ Revision History: #include "dl_util.h" void equiv_proof_converter::insert(expr* fml1, expr* fml2) { - datalog::scoped_proof _sp(m); - proof_ref p1(m), p2(m), p3(m); - p1 = m.mk_asserted(fml1); - p2 = m.mk_rewrite(fml1, fml2); - p3 = m.mk_modus_ponens(p1, p2); - TRACE("proof_converter", tout << mk_pp(p3.get(), m) << "\n";); - SASSERT(m.has_fact(p3)); - m_replace.insert(p3); + if (fml1 != fml2) { + datalog::scoped_proof _sp(m); + proof_ref p1(m), p2(m), p3(m); + p1 = m.mk_asserted(fml1); + p2 = m.mk_rewrite(fml1, fml2); + p3 = m.mk_modus_ponens(p1, p2); + TRACE("proof_converter", tout << mk_pp(p3.get(), m) << "\n";); + SASSERT(m.has_fact(p3)); + m_replace.insert(p3); + } } diff --git a/src/muz_qe/fdd.cpp b/src/muz_qe/fdd.cpp new file mode 100644 index 000000000..9ca5f758a --- /dev/null +++ b/src/muz_qe/fdd.cpp @@ -0,0 +1,311 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + fdd.cpp + +Abstract: + + Finite decision diagram trie. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-07-03. + +Revision History: + + +--*/ + +#include "fdd.h" +#include "hash.h" +#include "bit_vector.h" +#include "trace.h" + +#define OFFSET_OF(th, ty, field) (unsigned char*)(&((ty*)(th))->field) - (unsigned char*)(ty*)(th) + +using namespace fdd; + +unsigned node::get_hash() const { + return string_hash((char*)this, static_cast(OFFSET_OF(this, node, m_ref_count)), 11); +} + +bool node::operator==(node const& other) const { + return + m_var == other.m_var && + m_lo == other.m_lo && + m_hi == other.m_hi; +} + + +// ------------------------------------------ +// manager + +manager::manager() : + m_alloc_node(2), + m_false(0), + m_true(1), + m_root(m_false) +{ + m_nodes.push_back(node()); // false + m_nodes.push_back(node()); // true + inc_ref(m_false); + inc_ref(m_true); + alloc_node(); // pre-allocate a node. +} + +manager::~manager() { +} + +void manager::alloc_node() { + unsigned index; + while (!m_free.empty()) { + index = m_free.back(); + node& n = m_nodes[index]; + m_free.pop_back(); + if (n.get_ref_count() == 0) { + if (!is_leaf(n.lo())) { + m_free.push_back(n.lo()); + } + if (!is_leaf(n.hi())) { + m_free.push_back(n.hi()); + } + m_alloc_node = index; + m_table.erase(n); + return; + } + } + index = m_nodes.size(); + m_nodes.push_back(node()); + m_alloc_node = index; +} + +node_id manager::mk_node(unsigned var, node_id lo, node_id hi) { + if (lo == hi) { + return lo; + } + node n(var, lo, hi); + unsigned index = m_alloc_node; + + node_id result = m_table.insert_if_not_there(n, index).m_value; + + if (result == index) { + alloc_node(); + m_nodes[result] = n; + inc_ref(lo); + inc_ref(hi); + } + + TRACE("fdd", tout << "mk_node: " << var << " " << lo << " " << hi << " -> " << result << "\n";); + + return result; +} + + +void manager::inc_ref(node_id n) { + TRACE("fdd", tout << "incref: " << n << "\n";); + if (!is_leaf(n)) { + m_nodes[n].inc_ref(); + } +} + +void manager::dec_ref(node_id n) { + if (!is_leaf(n) && 0 == m_nodes[n].dec_ref()) { + m_free.push_back(n); + } +} + +void manager::setup_keys(Key const* keys) { + for (unsigned i = 0; i < m_num_keys; ++i) { + m_keys[i] = (uint64)keys[i]; + m_sign[i] = keys[i] < 0; + } + +} + +void manager::insert(Key const* keys) { + setup_keys(keys); + m_insert_cache.reset(); + node_id result = insert_sign(m_num_idx + m_num_keys, m_root); + inc_ref(result); + dec_ref(m_root); + m_root = result; +} + +node_id manager::insert_sign(unsigned idx, node_id n) { + if (idx > m_num_idx) { + --idx; + bool s = idx2sign(idx); + node nd = m_nodes[n]; + if (!is_leaf(n) && nd.var() == idx) { + if (s) { + return mk_node(idx, insert_sign(idx, nd.lo()), nd.hi()); + } + else { + return mk_node(idx, nd.lo(), insert_sign(idx, nd.hi())); + } + } + else { + if (s) { + return mk_node(idx, insert_sign(idx, n), n); + } + else { + return mk_node(idx, n, insert_sign(idx, n)); + } + } + } + SASSERT(m_num_idx == idx); + return insert(idx, n); +} + +node_id manager::insert(unsigned idx, node_id n) { + node_id result; + SASSERT(0 <= idx && idx <= m_num_idx); + TRACE("fdd", tout << "insert: " << idx << " " << n << "\n";); + if (is_leaf(n)) { + while (idx > 0) { + --idx; + if (idx2bit(idx) && !is_dont_care(idx2key(idx))) { + return mk_node(idx, n, insert(idx, n)); + } + } + return m_true; + } + + SASSERT(0 < idx); + --idx; + + config c(m_dont_cares, idx, n); + if (m_insert_cache.find(c, result)) { + return result; + } + + node nd = m_nodes[n]; + SASSERT(idx >= nd.var()); + while (idx > nd.var()) { + if (idx2bit(idx) && !is_dont_care(idx2key(idx))) { + return mk_node(idx, n, insert(idx, n)); + } + --idx; + } + SASSERT(nd.var() == idx); + unsigned key = idx2key(idx); + if (is_dont_care(key)) { + result = mk_node(idx, insert(idx, nd.lo()), insert(idx, nd.hi())); + } + else { + bool bit = idx2bit(idx); + node_id lo, hi; + if (bit) { + hi = insert(idx, nd.hi()); + lo = nd.lo(); + } + else { + lo = insert(idx, nd.lo()); + scoped_dont_cares _set(*this, key); + hi = insert(idx, nd.hi()); + } + result = mk_node(idx, lo, hi); + } + m_insert_cache.insert(c, result); + return result; +} + +void manager::set_dont_care(unsigned key) { + SASSERT(!is_dont_care(key)); + m_dont_cares |= (1ull << key); +} + +void manager::unset_dont_care(unsigned key) { + m_dont_cares &= ~(1ull << key); +} + +bool manager::is_dont_care(unsigned key) const { + return 0 != (m_dont_cares & (1ull << key)); +} + +void manager::collect_statistics(statistics& st) const { + st.update("fdd.num_nodes", m_nodes.size()); +} + + +void manager::reset(unsigned num_keys) { + m_num_keys = num_keys; + m_num_idx = m_num_keys * m_num_bits; + m_dont_cares = 0; + m_sign.resize(num_keys); + m_keys.resize(num_keys); + SASSERT(num_keys <= 8*sizeof(m_dont_cares)); +} + + + +bool manager::find_le(Key const* keys) { + setup_keys(keys); + unsigned idx = m_num_idx + m_num_keys; + node_id n = m_root; + node nc = m_nodes[n]; + while (n > 1 && idx > m_num_idx) { + --idx; + if (nc.var() == idx) { + if (idx2sign(idx)) { + n = nc.lo(); + } + else { + n = nc.hi(); + } + nc = m_nodes[n]; + } + } + while (n > 1) { + SASSERT(idx > 0); + --idx; + while (nc.var() < idx) { + if (idx2bit(idx) && is_dont_care(idx2key(idx))) { + set_dont_care(idx2key(idx)); + } + --idx; + } + SASSERT(nc.var() == idx); + if (is_dont_care(idx2key(idx)) || idx2bit(idx)) { + n = nc.hi(); + } + else { + n = nc.lo(); + } + nc = m_nodes[n]; + } + m_dont_cares = 0; + return n == 1; +} + + +std::ostream& manager::display(std::ostream& out, node_id n) const{ + svector mark; + svector nodes; + nodes.push_back(n); + while (!nodes.empty()) { + n = nodes.back(); + nodes.pop_back(); + if (mark.size() <= n) { + mark.resize(n+1, false); + } + node const& nc = m_nodes[n]; + if (is_leaf(n) || mark[n]) { + continue; + } + nodes.push_back(nc.lo()); + nodes.push_back(nc.hi()); + mark[n] = true; + + if (nc.var() >= m_num_idx) { + out << n << " if " << idx2key(nc.var()) << " then " << nc.hi() << " else " << nc.lo() << "\n"; + } + else { + out << n << " if " << idx2key(nc.var()) << ":" << idx2bitnum(nc.var()) << " then " << nc.hi() << " else " << nc.lo() << "\n"; + } + } + return out; +} + diff --git a/src/muz_qe/fdd.h b/src/muz_qe/fdd.h new file mode 100644 index 000000000..59d5a78f5 --- /dev/null +++ b/src/muz_qe/fdd.h @@ -0,0 +1,173 @@ +/*++ +Copyright (c) 2007 Microsoft Corporation + +Module Name: + + fdd.h + +Abstract: + + Finite decision diagram. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-07-03. + +Revision History: + + +--*/ + +#ifndef __FDD_H__ +#define __FDD_H__ + +#include "hashtable.h" +#include "hash.h" +#include "map.h" +#include "vector.h" +#include "statistics.h" + +namespace fdd { + + + typedef unsigned node_id; + + class node { + unsigned m_var; + node_id m_lo; + node_id m_hi; + unsigned m_ref_count; + void reset(); + public: + node() : m_var(0), m_lo(0), m_hi(0), m_ref_count(0) {} + node(unsigned var, node_id l, node_id h): m_var(var), m_lo(l), m_hi(h), m_ref_count(0) {} + + unsigned get_hash() const; + bool operator==(node const& other) const; + + void inc_ref() { ++m_ref_count; } + unsigned dec_ref() { return --m_ref_count; } + unsigned get_ref_count() const { return m_ref_count; } + node_id lo() const { return m_lo; } + node_id hi() const { return m_hi; } + unsigned var() const { return m_var; } + + struct hash { unsigned operator()(node const& n) const { return n.get_hash(); } }; + struct eq { bool operator()(node const& l, node const& r) const { return l == r; } }; + std::ostream& display(std::ostream& out) const { return out << m_var << " " << m_lo << " " << m_hi << ""; } + }; + + inline std::ostream& operator<<(std::ostream& out, node const& n) { return n.display(out); } + + class config { + uint64 m_dont_cares; + unsigned m_idx; + node_id m_node; + public: + + config(): m_dont_cares(0), m_idx(0), m_node(0) {} + + config(uint64 dont_cares, unsigned idx, node_id n): + m_dont_cares(dont_cares), + m_idx(idx), + m_node(n) + {} + + struct hash { + unsigned operator()(config const& c) const { + return string_hash((char*)&c, sizeof(c), 12); + }; + }; + + struct eq { + bool operator()(config const& a, config const& b) const { + return + a.m_dont_cares == b.m_dont_cares && + a.m_idx == b.m_idx && + a.m_node == b.m_node; + } + }; + }; + + + class manager { + public: + typedef int64 Key; + typedef node::hash node_hash; + typedef node::eq node_eq; + typedef config::hash config_hash; + typedef config::eq config_eq; + private: + typedef map node_table; + typedef map insert_cache; + node_table m_table; + insert_cache m_insert_cache; + svector m_nodes; + unsigned_vector m_free; + unsigned m_alloc_node; + node_id m_false; + node_id m_true; + node_id m_root; + + static const unsigned m_num_bits = 64; + unsigned m_num_keys; + unsigned m_num_idx; // = m_num_keys * m_num_bits + + // state associated with insert. + svector m_keys; + svector m_sign; + + uint64 m_dont_cares; + + public: + manager(); + ~manager(); + + void reset(unsigned num_keys); + + void insert(Key const* keys); + + bool find_le(Key const* keys); + + void collect_statistics(statistics& st) const; + void reset_statistics() {} + unsigned size() const { return m_nodes.size(); } + + void display(std::ostream& out) const { display(out, m_root); } + + private: + void dec_ref(node_id n); + void inc_ref(node_id n); + node_id mk_node(unsigned var, node_id lo, node_id hi); + inline unsigned get_ref_count(node_id n) { return m_nodes[n].get_ref_count(); } + + std::ostream& display(std::ostream& out, node_id n) const; + + void setup_keys(Key const* keys); + node_id insert(unsigned idx, node_id n); + node_id insert_sign(unsigned idx, node_id n); + bool is_dont_care(unsigned idx) const; + + void set_dont_care(unsigned key); + void unset_dont_care(unsigned key); + + struct scoped_dont_cares { + manager& m; + unsigned m_key; + scoped_dont_cares(manager& m, unsigned key):m(m), m_key(key) { m.set_dont_care(key); } + ~scoped_dont_cares() { m.unset_dont_care(m_key); } + }; + + void alloc_node(); + + unsigned idx2key(unsigned i) const { return i % m_num_keys; } + unsigned idx2bitnum(unsigned i) const { SASSERT(i < m_num_idx); return (i / m_num_keys); } + bool idx2bit(unsigned i) const { return 0 != (m_keys[idx2key(i)] & (1LL << idx2bitnum(i))); } + bool idx2sign(unsigned i) const { return m_sign[idx2key(i)]; } + + bool is_leaf(node_id n) const { return n <= 1; } + + }; +}; + +#endif diff --git a/src/muz_qe/fixedpoint_params.pyg b/src/muz_qe/fixedpoint_params.pyg index c2cfadd14..774559cdb 100644 --- a/src/muz_qe/fixedpoint_params.pyg +++ b/src/muz_qe/fixedpoint_params.pyg @@ -41,6 +41,9 @@ def_module_params('fixedpoint', ('simplify_formulas_pre', BOOL, False, "PDR: simplify derived formulas before inductive propagation"), ('simplify_formulas_post', BOOL, False, "PDR: simplify derived formulas after inductive propagation"), ('slice', BOOL, True, "PDR: simplify clause set using slicing"), + ('karr', BOOL, False, "Add linear invariants to clauses using Karr's method"), + ('quantify_arrays', BOOL, False, "create quantified Horn clauses from clauses with arrays"), + ('instantiate_quantifiers', BOOL, False, "instantiate quantified Horn clauses using E-matching heuristic"), ('coalesce_rules', BOOL, False, "BMC: coalesce rules"), ('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"), ('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"), diff --git a/src/muz_qe/heap_trie.h b/src/muz_qe/heap_trie.h index de07423b6..c2a1c52d1 100644 --- a/src/muz_qe/heap_trie.h +++ b/src/muz_qe/heap_trie.h @@ -44,7 +44,7 @@ Notes: #include "small_object_allocator.h" -template +template class heap_trie { struct stats { @@ -123,9 +123,9 @@ class heap_trie { } // push nodes whose keys are <= key into vector. - void find_le(Key key, ptr_vector& nodes) { + void find_le(KeyLE& le, Key key, ptr_vector& nodes) { for (unsigned i = 0; i < m_nodes.size(); ++i) { - if (KeyLE::le(m_nodes[i].first, key)) { + if (le.le(m_nodes[i].first, key)) { node* n = m_nodes[i].second; if (n->ref_count() > 0){ nodes.push_back(n); @@ -179,7 +179,10 @@ class heap_trie { }; small_object_allocator m_alloc; + KeyLE& m_le; unsigned m_num_keys; + unsigned_vector m_keys; + unsigned m_do_reshuffle; node* m_root; stats m_stats; node* m_spare_leaf; @@ -187,9 +190,11 @@ class heap_trie { public: - heap_trie(): + heap_trie(KeyLE& le): m_alloc("heap_trie"), + m_le(le), m_num_keys(0), + m_do_reshuffle(4), m_root(0), m_spare_leaf(0), m_spare_trie(0) @@ -202,7 +207,7 @@ public: } unsigned size() const { - return m_root->num_leaves(); + return m_root?m_root->num_leaves():0; } void reset(unsigned num_keys) { @@ -210,6 +215,10 @@ public: del_node(m_spare_leaf); del_node(m_spare_trie); m_num_keys = num_keys; + m_keys.resize(num_keys); + for (unsigned i = 0; i < num_keys; ++i) { + m_keys[i] = i; + } m_root = mk_trie(); m_spare_trie = mk_trie(); m_spare_leaf = mk_leaf(); @@ -217,7 +226,13 @@ public: void insert(Key const* keys, Value const& val) { ++m_stats.m_num_inserts; - insert(m_root, num_keys(), keys, val); + insert(m_root, num_keys(), keys, m_keys.c_ptr(), val); +#if 0 + if (m_stats.m_num_inserts == (1 << m_do_reshuffle)) { + m_do_reshuffle++; + reorder_keys(); + } +#endif } bool find_eq(Key const* keys, Value& value) { @@ -225,7 +240,7 @@ public: node* n = m_root; node* m; for (unsigned i = 0; i < num_keys(); ++i) { - if (!to_trie(n)->find(keys[i], m)) { + if (!to_trie(n)->find(get_key(keys, i), m)) { return false; } n = m; @@ -242,7 +257,7 @@ public: for (unsigned i = 0; i < num_keys(); ++i) { for (unsigned j = 0; j < todo[index].size(); ++j) { ++m_stats.m_num_find_le_nodes; - to_trie(todo[index][j])->find_le(keys[i], todo[!index]); + to_trie(todo[index][j])->find_le(m_le, get_key(keys, i), todo[!index]); } todo[index].reset(); index = !index; @@ -271,7 +286,7 @@ public: node* m; for (unsigned i = 0; i < num_keys(); ++i) { n->dec_ref(); - VERIFY (to_trie(n)->find(keys[i], m)); + VERIFY (to_trie(n)->find(get_key(keys, i), m)); n = m; } n->dec_ref(); @@ -287,14 +302,14 @@ public: st.update("heap_trie.num_find_eq", m_stats.m_num_find_eq); st.update("heap_trie.num_find_le", m_stats.m_num_find_le); st.update("heap_trie.num_find_le_nodes", m_stats.m_num_find_le_nodes); - st.update("heap_trie.num_nodes", m_root->num_nodes()); + if (m_root) st.update("heap_trie.num_nodes", m_root->num_nodes()); unsigned_vector nums; ptr_vector todo; - todo.push_back(m_root); + if (m_root) todo.push_back(m_root); while (!todo.empty()) { node* n = todo.back(); todo.pop_back(); - if (n->type() == trie_t) { + if (is_trie(n)) { trie* t = to_trie(n); unsigned sz = t->nodes().size(); if (nums.size() <= sz) { @@ -334,38 +349,252 @@ public: out << "\n"; } + class iterator { + ptr_vector m_path; + unsigned_vector m_idx; + vector m_keys; + unsigned m_count; + public: + iterator(node* n) { + if (!n) { + m_count = UINT_MAX; + } + else { + m_count = 0; + first(n); + } + } + Key const* keys() { + return m_keys.c_ptr(); + } + + Value const& value() const { + return to_leaf(m_path.back())->get_value(); + } + iterator& operator++() { fwd(); return *this; } + iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } + bool operator==(iterator const& it) const {return m_count == it.m_count; } + bool operator!=(iterator const& it) const {return m_count != it.m_count; } + + private: + void first(node* r) { + SASSERT(r->ref_count() > 0); + while (is_trie(r)) { + trie* t = to_trie(r); + m_path.push_back(r); + unsigned sz = t->nodes().size(); + for (unsigned i = 0; i < sz; ++i) { + r = t->nodes()[i].second; + if (r->ref_count() > 0) { + m_idx.push_back(i); + m_keys.push_back(t->nodes()[i].first); + break; + } + } + } + SASSERT(is_leaf(r)); + m_path.push_back(r); + } + + void fwd() { + if (m_path.empty()) { + m_count = UINT_MAX; + return; + } + m_path.pop_back(); + while (!m_path.empty()) { + trie* t = to_trie(m_path.back()); + unsigned idx = m_idx.back(); + unsigned sz = t->nodes().size(); + m_idx.pop_back(); + m_keys.pop_back(); + for (unsigned i = idx+1; i < sz; ++i) { + node* r = t->nodes()[i].second; + if (r->ref_count() > 0) { + m_idx.push_back(i); + m_keys.push_back(t->nodes()[i].first); + first(r); + ++m_count; + return; + } + } + m_path.pop_back(); + } + m_count = UINT_MAX; + } + }; + + iterator begin() const { + return iterator(m_root); + } + + iterator end() const { + return iterator(0); + } + + private: - unsigned num_keys() const { + inline unsigned num_keys() const { return m_num_keys; } + inline Key const& get_key(Key const* keys, unsigned i) const { + return keys[m_keys[i]]; + } + + struct KeyEq { + bool operator()(Key const& k1, Key const& k2) const { + return k1 == k2; + } + }; + + + typedef hashtable key_set; + + struct key_info { + unsigned m_index; + unsigned m_index_size; + key_info(unsigned i, unsigned sz): + m_index(i), + m_index_size(sz) + {} + + bool operator<(key_info const& other) const { + return + (m_index_size < other.m_index_size) || + ((m_index_size == other.m_index_size) && + (m_index < other.m_index)); + } + }; + + void reorder_keys() { + vector weights; + weights.resize(num_keys()); + unsigned_vector depth; + ptr_vector nodes; + depth.push_back(0); + nodes.push_back(m_root); + while (!nodes.empty()) { + node* n = nodes.back(); + unsigned d = depth.back(); + nodes.pop_back(); + depth.pop_back(); + if (is_trie(n)) { + trie* t = to_trie(n); + unsigned sz = t->nodes().size(); + for (unsigned i = 0; i < sz; ++i) { + nodes.push_back(t->nodes()[i].second); + depth.push_back(d+1); + weights[d].insert(t->nodes()[i].first); + } + } + } + SASSERT(weights.size() == num_keys()); + svector infos; + unsigned sz = 0; + bool is_sorted = true; + for (unsigned i = 0; i < weights.size(); ++i) { + unsigned sz2 = weights[i].size(); + if (sz > sz2) { + is_sorted = false; + } + sz = sz2; + infos.push_back(key_info(i, sz)); + } + if (is_sorted) { + return; + } + std::sort(infos.begin(), infos.end()); + unsigned_vector sorted_keys, new_keys; + for (unsigned i = 0; i < num_keys(); ++i) { + unsigned j = infos[i].m_index; + sorted_keys.push_back(j); + new_keys.push_back(m_keys[j]); + } + // m_keys: i |-> key_index + // new_keys: i |-> new_key_index + // permutation: key_index |-> new_key_index + SASSERT(sorted_keys.size() == num_keys()); + SASSERT(new_keys.size() == num_keys()); + SASSERT(m_keys.size() == num_keys()); + iterator it = begin(); + trie* new_root = mk_trie(); + IF_VERBOSE(2, verbose_stream() << "before reshuffle: " << m_root->num_nodes() << " nodes\n";); + for (; it != end(); ++it) { + IF_VERBOSE(2, + for (unsigned i = 0; i < num_keys(); ++i) { + for (unsigned j = 0; j < num_keys(); ++j) { + if (m_keys[j] == i) { + verbose_stream() << it.keys()[j] << " "; + break; + } + } + } + verbose_stream() << " |-> " << it.value() << "\n";); + + insert(new_root, num_keys(), it.keys(), sorted_keys.c_ptr(), it.value()); + } + del_node(m_root); + m_root = new_root; + for (unsigned i = 0; i < m_keys.size(); ++i) { + m_keys[i] = new_keys[i]; + } + + IF_VERBOSE(2, verbose_stream() << "after reshuffle: " << new_root->num_nodes() << " nodes\n";); + IF_VERBOSE(2, + it = begin(); + for (; it != end(); ++it) { + for (unsigned i = 0; i < num_keys(); ++i) { + for (unsigned j = 0; j < num_keys(); ++j) { + if (m_keys[j] == i) { + verbose_stream() << it.keys()[j] << " "; + break; + } + } + } + verbose_stream() << " |-> " << it.value() << "\n"; + }); + } + bool find_le(node* n, unsigned index, Key const* keys, check_value& check) { if (index == num_keys()) { SASSERT(n->ref_count() > 0); - return check(to_leaf(n)->get_value()); + bool r = check(to_leaf(n)->get_value()); + IF_VERBOSE(2, + for (unsigned j = 0; j < index; ++j) { + verbose_stream() << " "; + } + verbose_stream() << to_leaf(n)->get_value() << (r?" hit\n":" miss\n");); + return r; } else { - Key key = keys[index]; - children_t const& nodes = to_trie(n)->nodes(); + Key const& key = get_key(keys, index); + children_t& nodes = to_trie(n)->nodes(); for (unsigned i = 0; i < nodes.size(); ++i) { ++m_stats.m_num_find_le_nodes; - if (KeyLE::le(nodes[i].first, key)) { - node* m = nodes[i].second; - if (m->ref_count() > 0 && find_le(m, index+1, keys, check)) { - return true; + node* m = nodes[i].second; + IF_VERBOSE(2, + for (unsigned j = 0; j < index; ++j) { + verbose_stream() << " "; + } + verbose_stream() << nodes[i].first << " <=? " << key << " rc:" << m->ref_count() << "\n";); + if (m->ref_count() > 0 && m_le.le(nodes[i].first, key) && find_le(m, index+1, keys, check)) { + if (i > 0) { + std::swap(nodes[i], nodes[0]); } + return true; } } return false; } } - void insert(node* n, unsigned num_keys, Key const* keys, Value const& val) { + void insert(node* n, unsigned num_keys, Key const* keys, unsigned const* permutation, Value const& val) { // assumption: key is not in table. for (unsigned i = 0; i < num_keys; ++i) { n->inc_ref(); - n = insert_key(to_trie(n), (i + 1 == num_keys), keys[i]); + n = insert_key(to_trie(n), (i + 1 == num_keys), keys[permutation[i]]); } n->inc_ref(); to_leaf(n)->set_value(val); @@ -400,7 +629,7 @@ private: if (!n) { return; } - if (n->type() == trie_t) { + if (is_trie(n)) { trie* t = to_trie(n); for (unsigned i = 0; i < t->nodes().size(); ++i) { del_node(t->nodes()[i].second); @@ -415,15 +644,23 @@ private: } } - trie* to_trie(node* n) const { - SASSERT(n->type() == trie_t); + static trie* to_trie(node* n) { + SASSERT(is_trie(n)); return static_cast(n); } - leaf* to_leaf(node* n) const { - SASSERT(n->type() == leaf_t); + static leaf* to_leaf(node* n) { + SASSERT(is_leaf(n)); return static_cast(n); } + + static bool is_leaf(node* n) { + return n->type() == leaf_t; + } + + static bool is_trie(node* n) { + return n->type() == trie_t; + } }; #endif diff --git a/src/muz_qe/hilbert_basis.cpp b/src/muz_qe/hilbert_basis.cpp index 7d15a1958..62f091dbc 100644 --- a/src/muz_qe/hilbert_basis.cpp +++ b/src/muz_qe/hilbert_basis.cpp @@ -21,9 +21,11 @@ Revision History: #include "heap.h" #include "map.h" #include "heap_trie.h" +#include "fdd.h" +#include "stopwatch.h" -template -class rational_map : public map {}; + +typedef int_hashtable > int_table; class hilbert_basis::value_index1 { @@ -36,7 +38,6 @@ class hilbert_basis::value_index1 { void reset() { memset(this, 0, sizeof(*this)); } }; - typedef int_hashtable > int_table; hilbert_basis& hb; int_table m_table; stats m_stats; @@ -58,14 +59,13 @@ public: m_table.reset(); } - bool find(offset_t idx, values const& vs, offset_t& found_idx) { + bool find(offset_t idx, values const& vs) { // display_profile(idx, std::cout); int_table::iterator it = m_table.begin(), end = m_table.end(); for (; it != end; ++it) { offset_t offs(*it); ++m_stats.m_num_comparisons; if (*it != static_cast(idx.m_offset) && hb.is_subsumed(idx, offs)) { - found_idx = offs; ++m_stats.m_num_hit; return true; } @@ -163,18 +163,21 @@ private: class hilbert_basis::value_index2 { struct key_le { - static bool le(numeral const& n1, numeral const& n2) { - return hilbert_basis::is_abs_geq(n2, n1); + hilbert_basis& hb; + key_le(hilbert_basis& hb): hb(hb) {} + bool le(numeral const& n1, numeral const& n2) const { + return hb.is_abs_geq(n2, n1); } }; - struct checker : public heap_trie::check_value { + + typedef heap_trie ht; + + struct checker : public ht::check_value { hilbert_basis* hb; offset_t m_value; - offset_t* m_found; - checker(): hb(0), m_found(0) {} - virtual bool operator()(unsigned const& v) { - if (m_value.m_offset != v && hb->is_subsumed(m_value, offset_t(v))) { - *m_found = offset_t(v); + checker(): hb(0) {} + virtual bool operator()(unsigned const& v) { + if (m_value.m_offset != v) { // && hb->is_subsumed(m_value, offset_t(v))) { return true; } else { @@ -183,33 +186,25 @@ class hilbert_basis::value_index2 { } }; hilbert_basis& hb; - heap_trie m_trie; - vector m_found; - bool m_init; + key_le m_le; + ht m_trie; checker m_checker; - vector m_keys; + unsigned m_offset; -#if 1 numeral const* get_keys(values const& vs) { - return vs()-1; + return vs()-m_offset; } -#else - numeral const* get_keys(values const& vs) { - unsigned sz = m_keys.size(); - for (unsigned i = 0; i < sz; ++i) { - m_keys[sz-i-1] = vs()[i-1]; - } - return m_keys.c_ptr(); - } -#endif public: - value_index2(hilbert_basis& hb): hb(hb), m_init(false) { + value_index2(hilbert_basis& hb): + hb(hb), + m_le(hb), + m_trie(m_le), + m_offset(1) { m_checker.hb = &hb; } void insert(offset_t idx, values const& vs) { - init(); m_trie.insert(get_keys(vs), idx.m_offset); } @@ -217,15 +212,13 @@ public: m_trie.remove(get_keys(vs)); } - void reset() { - m_trie.reset(hb.get_num_vars()+1); - m_keys.resize(hb.get_num_vars()+1); + void reset(unsigned offset) { + m_offset = offset; + m_trie.reset(hb.get_num_vars()+m_offset); } - bool find(offset_t idx, values const& vs, offset_t& found_idx) { - init(); + bool find(offset_t idx, values const& vs) { m_checker.m_value = idx; - m_checker.m_found = &found_idx; return m_trie.find_le(get_keys(vs), m_checker); } @@ -245,15 +238,63 @@ public: // m_trie.display(out); } -private: - void init() { - if (!m_init) { - reset(); - m_init = true; - } - } + }; +class hilbert_basis::value_index3 { + hilbert_basis& hb; + fdd::manager m_fdd; + unsigned m_offset; + svector m_keys; + + int64 const* get_keys(values const& vs) { + numeral const* nums = vs()-m_offset; + for (unsigned i = 0; i < m_keys.size(); ++i) { + m_keys[i] = nums[i].get_int64(); + } + return m_keys.c_ptr(); + } + +public: + + value_index3(hilbert_basis & hb): hb(hb), m_offset(1) {} + + void insert(offset_t, values const& vs) { + m_fdd.insert(get_keys(vs)); + } + + bool find(offset_t, values const& vs) { + return m_fdd.find_le(get_keys(vs)); + } + + void reset(unsigned offset) { + m_offset = offset; + m_fdd.reset(hb.get_num_vars()+m_offset); + m_keys.resize(hb.get_num_vars()+m_offset); + } + + void collect_statistics(statistics& st) const { + m_fdd.collect_statistics(st); + } + + void reset_statistics() { + m_fdd.reset_statistics(); + } + + unsigned size() const { + return m_fdd.size(); + } + + void remove(offset_t idx, values const& vs) { + UNREACHABLE(); + } + + void display(std::ostream& out) const { + m_fdd.display(out); + } + + +}; class hilbert_basis::index { @@ -262,6 +303,7 @@ class hilbert_basis::index { // typedef value_index1 value_index; typedef value_index2 value_index; + // typedef value_index3 value_index; struct stats { unsigned m_num_find; @@ -270,15 +312,19 @@ class hilbert_basis::index { void reset() { memset(this, 0, sizeof(*this)); } }; - typedef rational_map value_map; + template + class numeral_map : public map {}; + + typedef numeral_map value_map; hilbert_basis& hb; value_map m_neg; value_index m_pos; value_index m_zero; stats m_stats; + unsigned m_num_ineqs; public: - index(hilbert_basis& hb): hb(hb), m_pos(hb), m_zero(hb) {} + index(hilbert_basis& hb): hb(hb), m_pos(hb), m_zero(hb), m_num_ineqs(0) {} void insert(offset_t idx, values const& vs) { ++m_stats.m_num_insert; @@ -292,6 +338,7 @@ public: value_index* map = 0; if (!m_neg.find(vs.weight(), map)) { map = alloc(value_index, hb); + map->reset(m_num_ineqs); m_neg.insert(vs.weight(), map); } map->insert(idx, vs); @@ -310,29 +357,30 @@ public: } } - bool find(offset_t idx, values const& vs, offset_t& found_idx) { + bool find(offset_t idx, values const& vs) { ++m_stats.m_num_find; if (vs.weight().is_pos()) { - return m_pos.find(idx, vs, found_idx); + return m_pos.find(idx, vs); } else if (vs.weight().is_zero()) { - return m_zero.find(idx, vs, found_idx); + return m_zero.find(idx, vs); } else { value_index* map; return m_neg.find(vs.weight(), map) && - map->find(idx, vs, found_idx); + map->find(idx, vs); } } - void reset() { + void reset(unsigned num_ineqs) { value_map::iterator it = m_neg.begin(), end = m_neg.end(); for (; it != end; ++it) { - it->m_value->reset(); + dealloc(it->m_value); } - m_pos.reset(); - m_zero.reset(); + m_pos.reset(num_ineqs); + m_zero.reset(num_ineqs); + m_num_ineqs = num_ineqs; m_neg.reset(); } @@ -483,16 +531,194 @@ public: } }; + +class hilbert_basis::vector_lt_t { + hilbert_basis& hb; +public: + vector_lt_t(hilbert_basis& hb): hb(hb) {} + bool operator()(offset_t idx1, offset_t idx2) const { + return hb.vector_lt(idx1, idx2); + } +}; + + +class hilbert_basis::passive2 { + struct lt { + passive2** p; + lt(passive2** p): p(p) {} + bool operator()(int v1, int v2) const { + return (**p)(v1, v2); + } + }; + hilbert_basis& hb; + svector m_pos_sos; + svector m_neg_sos; + vector m_pos_sos_sum; + vector m_neg_sos_sum; + vector m_sum_abs; + unsigned_vector m_psos; + svector m_pas; + vector m_weight; + unsigned_vector m_free_list; + passive2* m_this; + lt m_lt; + heap m_heap; // binary heap over weights + + numeral sum_abs(offset_t idx) const { + numeral w(0); + unsigned nv = hb.get_num_vars(); + for (unsigned i = 0; i < nv; ++i) { + w += abs(hb.vec(idx)[i]); + } + return w; + } + +public: + passive2(hilbert_basis& hb): + hb(hb), + m_lt(&m_this), + m_heap(10, m_lt) + { + m_this = this; + } + + void init(svector const& I) { + for (unsigned i = 0; i < I.size(); ++i) { + numeral const& w = hb.vec(I[i]).weight(); + if (w.is_pos()) { + m_pos_sos.push_back(I[i]); + m_pos_sos_sum.push_back(sum_abs(I[i])); + } + else { + m_neg_sos.push_back(I[i]); + m_neg_sos_sum.push_back(sum_abs(I[i])); + } + } + } + + void reset() { + m_heap.reset(); + m_free_list.reset(); + m_psos.reset(); + m_pas.reset(); + m_sum_abs.reset(); + m_pos_sos.reset(); + m_neg_sos.reset(); + m_pos_sos_sum.reset(); + m_neg_sos_sum.reset(); + m_weight.reset(); + } + + void insert(offset_t idx, unsigned offset) { + SASSERT(!m_pos_sos.empty()); + unsigned v; + if (m_free_list.empty()) { + v = m_pas.size(); + m_pas.push_back(idx); + m_psos.push_back(offset); + m_weight.push_back(numeral(0)); + m_heap.set_bounds(v+1); + m_sum_abs.push_back(sum_abs(idx)); + } + else { + v = m_free_list.back(); + m_free_list.pop_back(); + m_pas[v] = idx; + m_psos[v] = offset; + m_weight[v] = numeral(0); + m_sum_abs[v] = sum_abs(idx); + } + next_resolvable(hb.vec(idx).weight().is_pos(), v); + } + + bool empty() const { + return m_heap.empty(); + } + + unsigned pop(offset_t& sos, offset_t& pas) { + SASSERT (!empty()); + unsigned val = static_cast(m_heap.erase_min()); + pas = m_pas[val]; + numeral old_weight = hb.vec(pas).weight(); + bool is_positive = old_weight.is_pos(); + unsigned psos = m_psos[val]; + sos = is_positive?m_neg_sos[psos]:m_pos_sos[psos]; + m_psos[val]++; + next_resolvable(is_positive, val); + numeral new_weight = hb.vec(sos).weight() + old_weight; + if (new_weight.is_pos() != old_weight.is_pos()) { + psos = 0; + } + return psos; + } + + bool operator()(int v1, int v2) const { + return m_weight[v1] < m_weight[v2]; + } + + class iterator { + passive2& p; + unsigned m_idx; + void fwd() { + while (m_idx < p.m_pas.size() && + is_invalid_offset(p.m_pas[m_idx])) { + ++m_idx; + } + } + public: + iterator(passive2& p, unsigned i): p(p), m_idx(i) { fwd(); } + offset_t pas() const { return p.m_pas[m_idx]; } + offset_t sos() const { return (p.hb.vec(pas()).weight().is_pos()?p.m_neg_sos:p.m_pos_sos)[p.m_psos[m_idx]]; } + iterator& operator++() { ++m_idx; fwd(); return *this; } + iterator operator++(int) { iterator tmp = *this; ++*this; return tmp; } + bool operator==(iterator const& it) const {return m_idx == it.m_idx; } + bool operator!=(iterator const& it) const {return m_idx != it.m_idx; } + }; + + iterator begin() { + return iterator(*this, 0); + } + + iterator end() { + return iterator(*this, m_pas.size()); + } +private: + void next_resolvable(bool is_positive, unsigned v) { + offset_t pas = m_pas[v]; + svector const& soss = is_positive?m_neg_sos:m_pos_sos; + while (m_psos[v] < soss.size()) { + unsigned psos = m_psos[v]; + offset_t sos = soss[psos]; + if (hb.can_resolve(sos, pas, false)) { + m_weight[v] = m_sum_abs[v] + (is_positive?m_neg_sos_sum[psos]:m_pos_sos_sum[psos]); + m_heap.insert(v); + return; + } + ++m_psos[v]; + } + // add pas to free-list for hb if it is not in sos. + m_free_list.push_back(v); + m_psos[v] = UINT_MAX; + m_pas[v] = mk_invalid_offset(); + } +}; + + hilbert_basis::hilbert_basis(): - m_cancel(false) + m_cancel(false), + m_use_support(true), + m_use_ordered_support(true), + m_use_ordered_subsumption(true) { m_index = alloc(index, *this); m_passive = alloc(passive, *this); + m_passive2 = alloc(passive2, *this); } hilbert_basis::~hilbert_basis() { dealloc(m_index); dealloc(m_passive); + dealloc(m_passive2); } hilbert_basis::offset_t hilbert_basis::mk_invalid_offset() { @@ -505,14 +731,25 @@ bool hilbert_basis::is_invalid_offset(offset_t offs) { void hilbert_basis::reset() { m_ineqs.reset(); - m_basis.reset(); + m_iseq.reset(); m_store.reset(); + m_basis.reset(); m_free_list.reset(); - m_active.reset(); - m_passive->reset(); + m_sos.reset(); m_zero.reset(); - m_index->reset(); - m_cancel = false; + m_active.reset(); + if (m_passive) { + m_passive->reset(); + } + if (m_passive2) { + m_passive2->reset(); + } + if (m_index) { + m_index->reset(1); + } + m_ints.reset(); + m_current_ineq = 0; + } void hilbert_basis::collect_statistics(statistics& st) const { @@ -528,42 +765,46 @@ void hilbert_basis::reset_statistics() { m_index->reset_statistics(); } -void hilbert_basis::add_ge(num_vector const& v, numeral const& b) { +void hilbert_basis::add_ge(rational_vector const& v, rational const& b) { SASSERT(m_ineqs.empty() || v.size() + 1 == m_ineqs.back().size()); num_vector w; - w.push_back(-b); - w.append(v); + w.push_back(to_numeral(-b)); + for (unsigned i = 0; i < v.size(); ++i) { + w.push_back(to_numeral(v[i])); + } m_ineqs.push_back(w); m_iseq.push_back(false); } -void hilbert_basis::add_le(num_vector const& v, numeral const& b) { - num_vector w(v); +void hilbert_basis::add_le(rational_vector const& v, rational const& b) { + rational_vector w(v); for (unsigned i = 0; i < w.size(); ++i) { w[i].neg(); } add_ge(w, -b); } -void hilbert_basis::add_eq(num_vector const& v, numeral const& b) { +void hilbert_basis::add_eq(rational_vector const& v, rational const& b) { SASSERT(m_ineqs.empty() || v.size() + 1 == m_ineqs.back().size()); num_vector w; - w.push_back(-b); - w.append(v); + w.push_back(to_numeral(-b)); + for (unsigned i = 0; i < v.size(); ++i) { + w.push_back(to_numeral(v[i])); + } m_ineqs.push_back(w); m_iseq.push_back(true); } -void hilbert_basis::add_ge(num_vector const& v) { - add_ge(v, numeral(0)); +void hilbert_basis::add_ge(rational_vector const& v) { + add_ge(v, rational(0)); } -void hilbert_basis::add_le(num_vector const& v) { - add_le(v, numeral(0)); +void hilbert_basis::add_le(rational_vector const& v) { + add_le(v, rational(0)); } -void hilbert_basis::add_eq(num_vector const& v) { - add_eq(v, numeral(0)); +void hilbert_basis::add_eq(rational_vector const& v) { + add_eq(v, rational(0)); } void hilbert_basis::set_is_int(unsigned var_index) { @@ -589,7 +830,7 @@ unsigned hilbert_basis::get_num_vars() const { } hilbert_basis::values hilbert_basis::vec(offset_t offs) const { - return values(m_store.c_ptr() + (get_num_vars() + 1)*offs.m_offset); + return values(m_ineqs.size(), m_store.c_ptr() + offs.m_offset); } void hilbert_basis::init_basis() { @@ -622,7 +863,17 @@ lbool hilbert_basis::saturate() { m_current_ineq = 0; while (!m_cancel && m_current_ineq < m_ineqs.size()) { select_inequality(); + stopwatch sw; + sw.start(); lbool r = saturate(m_ineqs[m_current_ineq], m_iseq[m_current_ineq]); + IF_VERBOSE(3, + { statistics st; + collect_statistics(st); + st.display(verbose_stream()); + sw.stop(); + verbose_stream() << "time: " << sw.get_seconds() << "\n"; + }); + ++m_stats.m_num_saturations; if (r != l_true) { return r; @@ -635,27 +886,29 @@ lbool hilbert_basis::saturate() { return l_true; } -lbool hilbert_basis::saturate(num_vector const& ineq, bool is_eq) { +lbool hilbert_basis::saturate_orig(num_vector const& ineq, bool is_eq) { m_active.reset(); m_passive->reset(); m_zero.reset(); - m_index->reset(); + m_index->reset(m_current_ineq+1); + int_table support; TRACE("hilbert_basis", display_ineq(tout, ineq, is_eq);); - bool has_non_negative = false; iterator it = begin(); for (; it != end(); ++it) { - values v = vec(*it); + offset_t idx = *it; + values v = vec(idx); v.weight() = get_weight(v, ineq); - add_goal(*it); - if (v.weight().is_nonneg()) { - has_non_negative = true; + for (unsigned k = 0; k < m_current_ineq; ++k) { + v.weight(k) = get_weight(v, m_ineqs[k]); + } + add_goal(idx); + if (m_use_support) { + support.insert(idx.m_offset); } } TRACE("hilbert_basis", display(tout);); - if (!has_non_negative) { - return l_false; - } // resolve passive into active + offset_t j = alloc_vector(); while (!m_passive->empty()) { if (m_cancel) { return l_undef; @@ -667,14 +920,16 @@ lbool hilbert_basis::saturate(num_vector const& ineq, bool is_eq) { continue; } for (unsigned i = 0; !m_cancel && i < m_active.size(); ++i) { - if (can_resolve(idx, m_active[i])) { - offset_t j = alloc_vector(); + if ((!m_use_support || support.contains(m_active[i].m_offset)) && can_resolve(idx, m_active[i], true)) { resolve(idx, m_active[i], j); - add_goal(j); + if (add_goal(j)) { + j = alloc_vector(); + } } } m_active.push_back(idx); } + m_free_list.push_back(j); // Move positive from active and zeros to new basis. m_basis.reset(); m_basis.append(m_zero); @@ -691,22 +946,116 @@ lbool hilbert_basis::saturate(num_vector const& ineq, bool is_eq) { m_passive->reset(); m_zero.reset(); TRACE("hilbert_basis", display(tout);); - return l_true; + return m_basis.empty()?l_false:l_true; } -void hilbert_basis::get_basis_solution(unsigned i, num_vector& v, bool& is_initial) { +bool hilbert_basis::vector_lt(offset_t idx1, offset_t idx2) const { + values v = vec(idx1); + values w = vec(idx2); + numeral a(0), b(0); + for (unsigned i = 0; i < get_num_vars(); ++i) { + a += abs(v[i]); + b += abs(w[i]); + } + return a < b; +} + +lbool hilbert_basis::saturate(num_vector const& ineq, bool is_eq) { + m_zero.reset(); + m_index->reset(m_current_ineq+1); + m_passive2->reset(); + m_sos.reset(); + TRACE("hilbert_basis", display_ineq(tout, ineq, is_eq);); + unsigned init_basis_size = 0; + for (unsigned i = 0; i < m_basis.size(); ++i) { + offset_t idx = m_basis[i]; + values v = vec(idx); + v.weight() = get_weight(v, ineq); + for (unsigned k = 0; k < m_current_ineq; ++k) { + v.weight(k) = get_weight(v, m_ineqs[k]); + } + m_index->insert(idx, v); + if (v.weight().is_zero()) { + m_zero.push_back(idx); + } + else { + if (v.weight().is_pos()) { + m_basis[init_basis_size++] = idx; + } + m_sos.push_back(idx); + } + } + m_basis.resize(init_basis_size); + m_passive2->init(m_sos); + // ASSERT basis is sorted by weight. + + // initialize passive + for (unsigned i = 0; (init_basis_size > 0) && i < m_sos.size(); ++i) { + if (vec(m_sos[i]).weight().is_neg()) { + m_passive2->insert(m_sos[i], 0); + } + } + + TRACE("hilbert_basis", display(tout);); + // resolve passive into active + offset_t idx = alloc_vector(); + while (!m_cancel && !m_passive2->empty()) { + offset_t sos, pas; + TRACE("hilbert_basis", display(tout); ); + unsigned offset = m_passive2->pop(sos, pas); + SASSERT(can_resolve(sos, pas, true)); + resolve(sos, pas, idx); + if (is_subsumed(idx)) { + continue; + } + values v = vec(idx); + m_index->insert(idx, v); + if (v.weight().is_zero()) { + m_zero.push_back(idx); + } + else { + if (!m_use_ordered_support) { + offset = 0; + } + m_passive2->insert(idx, offset); + if (v.weight().is_pos()) { + m_basis.push_back(idx); + } + } + idx = alloc_vector(); + } + if (m_cancel) { + return l_undef; + } + + m_free_list.push_back(idx); + // remove positive values from basis if we are looking for an equality. + while (is_eq && !m_basis.empty()) { + m_free_list.push_back(m_basis.back()); + m_basis.pop_back(); + } + m_basis.append(m_zero); + std::sort(m_basis.begin(), m_basis.end(), vector_lt_t(*this)); + m_zero.reset(); + TRACE("hilbert_basis", display(tout);); + return m_basis.empty()?l_false:l_true; +} + +void hilbert_basis::get_basis_solution(unsigned i, rational_vector& v, bool& is_initial) { offset_t offs = m_basis[i]; v.reset(); for (unsigned i = 1; i < get_num_vars(); ++i) { - v.push_back(vec(offs)[i]); + v.push_back(to_rational(vec(offs)[i])); } is_initial = !vec(offs)[0].is_zero(); } -void hilbert_basis::get_ge(unsigned i, num_vector& v, numeral& b, bool& is_eq) { +void hilbert_basis::get_ge(unsigned i, rational_vector& v, rational& b, bool& is_eq) { v.reset(); - v.append(m_ineqs[i].size() - 1, m_ineqs[i].c_ptr() + 1); - b = -m_ineqs[i][0]; + for (unsigned j = 1; j < m_ineqs[i].size(); ++j) { + v.push_back(to_rational(m_ineqs[i][j])); + } + b = to_rational(-m_ineqs[i][0]); is_eq = m_iseq[i]; } @@ -719,6 +1068,12 @@ void hilbert_basis::select_inequality() { for (unsigned j = best+1; prod != 0 && j < m_ineqs.size(); ++j) { unsigned non_zeros2 = get_num_nonzeros(m_ineqs[j]); unsigned prod2 = get_ineq_product(m_ineqs[j]); + if (prod2 == 0) { + prod = prod2; + non_zeros = non_zeros2; + best = j; + break; + } if (non_zeros2 < non_zeros || (non_zeros2 == non_zeros && prod2 < prod)) { prod = prod2; non_zeros = non_zeros2; @@ -757,6 +1112,22 @@ unsigned hilbert_basis::get_ineq_product(num_vector const& ineq) { return num_pos * num_neg; } +hilbert_basis::numeral hilbert_basis::get_ineq_diff(num_vector const& ineq) { + numeral max_pos(0), min_neg(0); + iterator it = begin(); + for (; it != end(); ++it) { + values v = vec(*it); + numeral w = get_weight(v, ineq); + if (w > max_pos) { + max_pos = w; + } + else if (w < min_neg) { + min_neg = w; + } + } + return max_pos - min_neg; +} + void hilbert_basis::recycle(offset_t idx) { m_index->remove(idx, vec(idx)); m_free_list.push_back(idx); @@ -772,6 +1143,9 @@ void hilbert_basis::resolve(offset_t i, offset_t j, offset_t r) { u[k] = v[k] + w[k]; } u.weight() = v.weight() + w.weight(); + for (unsigned k = 0; k < m_current_ineq; ++k) { + u.weight(k) = v.weight(k) + w.weight(k); + } TRACE("hilbert_basis_verbose", display(tout, i); display(tout, j); @@ -782,10 +1156,11 @@ void hilbert_basis::resolve(offset_t i, offset_t j, offset_t r) { hilbert_basis::offset_t hilbert_basis::alloc_vector() { if (m_free_list.empty()) { - unsigned num_vars = get_num_vars(); - unsigned idx = m_store.size(); - m_store.resize(idx + 1 + num_vars); - return offset_t(idx/(1+num_vars)); + unsigned sz = m_ineqs.size() + get_num_vars(); + unsigned idx = m_store.size(); + m_store.resize(idx + sz); + // std::cout << "alloc vector: " << idx << " " << sz << " " << m_store.c_ptr() + idx << " " << m_ineqs.size() << "\n"; + return offset_t(idx); } else { offset_t result = m_free_list.back(); @@ -794,11 +1169,11 @@ hilbert_basis::offset_t hilbert_basis::alloc_vector() { } } -void hilbert_basis::add_goal(offset_t idx) { +bool hilbert_basis::add_goal(offset_t idx) { TRACE("hilbert_basis", display(tout, idx);); values v = vec(idx); if (is_subsumed(idx)) { - return; + return false; } m_index->insert(idx, v); if (v.weight().is_zero()) { @@ -807,22 +1182,23 @@ void hilbert_basis::add_goal(offset_t idx) { else { m_passive->insert(idx); } + return true; } bool hilbert_basis::is_subsumed(offset_t idx) { - offset_t found_idx; - if (m_index->find(idx, vec(idx), found_idx)) { + if (m_index->find(idx, vec(idx))) { ++m_stats.m_num_subsumptions; return true; } return false; } -bool hilbert_basis::can_resolve(offset_t i, offset_t j) const { - if (get_sign(i) == get_sign(j)) { +bool hilbert_basis::can_resolve(offset_t i, offset_t j, bool check_sign) const { + if (check_sign && get_sign(i) == get_sign(j)) { return false; } + SASSERT(get_sign(i) != get_sign(j)); values const& v1 = vec(i); values const& v2 = vec(j); if (v1[0].is_one() && v2[0].is_one()) { @@ -841,7 +1217,7 @@ bool hilbert_basis::can_resolve(offset_t i, offset_t j) const { } hilbert_basis::sign_t hilbert_basis::get_sign(offset_t idx) const { - numeral val = vec(idx).weight(); + numeral const& val = vec(idx).weight(); if (val.is_pos()) { return pos; } @@ -885,6 +1261,15 @@ void hilbert_basis::display(std::ostream& out) const { display(out, *it); } } + if (!m_passive2->empty()) { + passive2::iterator it = m_passive2->begin(); + passive2::iterator end = m_passive2->end(); + out << "passive:\n"; + for (; it != end; ++it) { + display(out << "sos:", it.sos()); + display(out << "pas:", it.pas()); + } + } if (!m_zero.empty()) { out << "zero:\n"; for (unsigned i = 0; i < m_zero.size(); ++i) { @@ -976,7 +1361,7 @@ bool hilbert_basis::is_subsumed(offset_t i, offset_t j) const { n >= m && (!m.is_neg() || n == m) && is_geq(v, w); for (unsigned k = 0; r && k < m_current_ineq; ++k) { - r = get_weight(vec(i), m_ineqs[k]) >= get_weight(vec(j), m_ineqs[k]); + r = v.weight(k) >= w.weight(k); } CTRACE("hilbert_basis", r, display(tout, i); @@ -996,7 +1381,7 @@ bool hilbert_basis::is_geq(values const& v, values const& w) const { return true; } -bool hilbert_basis::is_abs_geq(numeral const& v, numeral const& w) { +bool hilbert_basis::is_abs_geq(numeral const& v, numeral const& w) const { if (w.is_neg()) { return v <= w; } diff --git a/src/muz_qe/hilbert_basis.h b/src/muz_qe/hilbert_basis.h index 567d72fbc..733b3a2f0 100644 --- a/src/muz_qe/hilbert_basis.h +++ b/src/muz_qe/hilbert_basis.h @@ -18,6 +18,11 @@ Author: Revision History: + Hilbert basis can be templatized + based on traits that define numeral: + as rational, mpz, checked_int64 + (checked or unchecked). + --*/ #ifndef _HILBERT_BASIS_H_ @@ -26,16 +31,31 @@ Revision History: #include "rational.h" #include "lbool.h" #include "statistics.h" +#include "checked_int64.h" + +typedef vector rational_vector; class hilbert_basis { -public: - typedef rational numeral; + + static const bool check = true; + typedef checked_int64 numeral; typedef vector num_vector; -private: + static checked_int64 to_numeral(rational const& r) { + if (!r.is_int64()) { + throw checked_int64::overflow_exception(); + } + return checked_int64(r.get_int64()); + } + static rational to_rational(checked_int64 const& i) { + return rational(i.get_int64(), rational::i64()); + } + class value_index1; class value_index2; + class value_index3; class index; class passive; + class passive2; struct offset_t { unsigned m_offset; offset_t(unsigned o) : m_offset(o) {} @@ -55,12 +75,14 @@ private: class values { numeral* m_values; public: - values(numeral* v):m_values(v) {} - numeral& weight() { return m_values[0]; } // value of a*x - numeral& operator[](unsigned i) { return m_values[i+1]; } // value of x_i - numeral const& weight() const { return m_values[0]; } // value of a*x - numeral const& operator[](unsigned i) const { return m_values[i+1]; } // value of x_i - numeral const* operator()() const { return m_values + 1; } + values(unsigned offset, numeral* v): m_values(v+offset) { } + numeral& weight() { return m_values[-1]; } // value of a*x + numeral const& weight() const { return m_values[-1]; } // value of a*x + numeral& weight(int i) { return m_values[-2-i]; } // value of b_i*x for 0 <= i < current inequality. + numeral const& weight(int i) const { return m_values[-2-i]; } // value of b_i*x + numeral& operator[](unsigned i) { return m_values[i]; } // value of x_i + numeral const& operator[](unsigned i) const { return m_values[i]; } // value of x_i + numeral const* operator()() const { return m_values; } }; vector m_ineqs; // set of asserted inequalities @@ -69,13 +91,20 @@ private: svector m_basis; // vector of current basis svector m_free_list; // free list of unused storage svector m_active; // active set + svector m_sos; // set of support svector m_zero; // zeros passive* m_passive; // passive set + passive2* m_passive2; // passive set volatile bool m_cancel; stats m_stats; index* m_index; // index of generated vectors unsigned_vector m_ints; // indices that can be both positive and negative unsigned m_current_ineq; + + bool m_use_support; // parameter: (associativity) resolve only against vectors that are initially in basis. + bool m_use_ordered_support; // parameter: (commutativity) resolve in order + bool m_use_ordered_subsumption; // parameter + class iterator { hilbert_basis const& hb; unsigned m_idx; @@ -91,27 +120,32 @@ private: static offset_t mk_invalid_offset(); static bool is_invalid_offset(offset_t offs); lbool saturate(num_vector const& ineq, bool is_eq); + lbool saturate_orig(num_vector const& ineq, bool is_eq); void init_basis(); void select_inequality(); unsigned get_num_nonzeros(num_vector const& ineq); unsigned get_ineq_product(num_vector const& ineq); + numeral get_ineq_diff(num_vector const& ineq); void add_unit_vector(unsigned i, numeral const& e); unsigned get_num_vars() const; numeral get_weight(values const & val, num_vector const& ineq) const; bool is_geq(values const& v, values const& w) const; - static bool is_abs_geq(numeral const& v, numeral const& w); + bool is_abs_geq(numeral const& v, numeral const& w) const; bool is_subsumed(offset_t idx); bool is_subsumed(offset_t i, offset_t j) const; void recycle(offset_t idx); - bool can_resolve(offset_t i, offset_t j) const; + bool can_resolve(offset_t i, offset_t j, bool check_sign) const; sign_t get_sign(offset_t idx) const; - void add_goal(offset_t idx); + bool add_goal(offset_t idx); offset_t alloc_vector(); void resolve(offset_t i, offset_t j, offset_t r); iterator begin() const { return iterator(*this,0); } iterator end() const { return iterator(*this, m_basis.size()); } + class vector_lt_t; + bool vector_lt(offset_t i, offset_t j) const; + values vec(offset_t offs) const; void display(std::ostream& out, offset_t o) const; @@ -125,19 +159,23 @@ public: void reset(); + void set_use_support(bool b) { m_use_support = b; } + void set_use_ordered_support(bool b) { m_use_ordered_support = b; } + void set_use_ordered_subsumption(bool b) { m_use_ordered_subsumption = b; } + // add inequality v*x >= 0 // add inequality v*x <= 0 // add equality v*x = 0 - void add_ge(num_vector const& v); - void add_le(num_vector const& v); - void add_eq(num_vector const& v); + void add_ge(rational_vector const& v); + void add_le(rational_vector const& v); + void add_eq(rational_vector const& v); // add inequality v*x >= b // add inequality v*x <= b // add equality v*x = b - void add_ge(num_vector const& v, numeral const& b); - void add_le(num_vector const& v, numeral const& b); - void add_eq(num_vector const& v, numeral const& b); + void add_ge(rational_vector const& v, rational const& b); + void add_le(rational_vector const& v, rational const& b); + void add_eq(rational_vector const& v, rational const& b); void set_is_int(unsigned var_index); bool get_is_int(unsigned var_index) const; @@ -145,10 +183,10 @@ public: lbool saturate(); unsigned get_basis_size() const { return m_basis.size(); } - void get_basis_solution(unsigned i, num_vector& v, bool& is_initial); + void get_basis_solution(unsigned i, rational_vector& v, bool& is_initial); unsigned get_num_ineqs() const { return m_ineqs.size(); } - void get_ge(unsigned i, num_vector& v, numeral& b, bool& is_eq); + void get_ge(unsigned i, rational_vector& v, rational& b, bool& is_eq); void set_cancel(bool f) { m_cancel = f; } diff --git a/src/muz_qe/hnf.cpp b/src/muz_qe/hnf.cpp new file mode 100644 index 000000000..5a7d1c4ba --- /dev/null +++ b/src/muz_qe/hnf.cpp @@ -0,0 +1,489 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + hnf.cpp + +Abstract: + + Horn normal form conversion. + +Author: + + Nikolaj Bjorner (nbjorner) 3-20-2013 + +Notes: + + Convert formula + + (forall x f(x)) + + into conjunction + + (f1 xy) (f2 xy) (f3 xy) + + such that + + (forall x f(x)) ~ /\ (forall xy (f_i xy)) + + modulo definitions that are introduced. + + + Convert proof with + asserted (forall xy (f' xy)) + + To: + (forall xy (f' xy)) by mp~ 1, 2 + 1. asserted/def-intro (forall xy (f xy)) + 2. (forall xy (f xy)) ~ (forall xy (f' xy)) by trans, 3, 4 + 3. (forall xy (f xy)) ~ (forall xy (f1 xy)) by pull quantifiers (rewrite) + 4. (forall xy (f1 xy)) ~ (forall xy (f' xy)) by oeq_quant_intro 5 + 5. f1 xy ~ f' xy by sub-proof. + + +--*/ +#include"hnf.h" +#include"warning.h" +#include"used_vars.h" +#include"well_sorted.h" +#include"var_subst.h" +#include"name_exprs.h" +#include"act_cache.h" +#include"cooperate.h" +#include"ast_pp.h" +#include"quant_hoist.h" +#include"dl_util.h" +#include"for_each_ast.h" +#include"for_each_expr.h" + +class hnf::imp { + ast_manager& m; + bool m_produce_proofs; + volatile bool m_cancel; + expr_ref_vector m_todo; + proof_ref_vector m_proofs; + expr_ref_vector m_refs; + symbol m_name; + svector m_names; + ptr_vector m_sorts; + quantifier_hoister m_qh; + obj_map m_memoize_disj; + obj_map m_memoize_proof; + func_decl_ref_vector m_fresh_predicates; + +public: + imp(ast_manager & m): + m(m), + m_produce_proofs(false), + m_cancel(false), + m_todo(m), + m_proofs(m), + m_refs(m), + m_name("P"), + m_qh(m), + m_fresh_predicates(m) { + } + + void operator()(expr * n, + proof* p, + expr_ref_vector& result, + proof_ref_vector& ps) { + expr_ref fml(m); + proof_ref pr(m); + m_todo.reset(); + m_proofs.reset(); + m_refs.reset(); + m_memoize_disj.reset(); + m_memoize_proof.reset(); + m_fresh_predicates.reset(); + m_todo.push_back(n); + m_proofs.push_back(p); + m_produce_proofs = p != 0; + while (!m_todo.empty() && !m_cancel) { + fml = m_todo.back(); + pr = m_proofs.back(); + m_todo.pop_back(); + m_proofs.pop_back(); + mk_horn(fml, pr); + if (fml) { + result.push_back(fml); + ps.push_back(pr); + } + } + TRACE("hnf", + tout << mk_pp(n, m) << "\n==>\n"; + for (unsigned i = 0; i < result.size(); ++i) { + tout << mk_pp(result[i].get(), m) << "\n"; + }); + } + + void set_cancel(bool f) { + m_cancel = f; + } + + void set_name(symbol const& n) { + m_name = n; + } + + func_decl_ref_vector const& get_fresh_predicates() { + return m_fresh_predicates; + } + + void reset() { + m_cancel = false; + m_todo.reset(); + m_proofs.reset(); + m_refs.reset(); + m_memoize_disj.reset(); + m_memoize_proof.reset(); + m_fresh_predicates.reset(); + } + + ast_manager& get_manager() { return m; } + +private: + + bool produce_proofs() const { + return m_produce_proofs; + } + + bool is_predicate(expr* p) const { + return is_app(p) && is_predicate(to_app(p)->get_decl()); + } + + bool is_predicate(func_decl* f) const { + return m.is_bool(f->get_range()) && f->get_family_id() == null_family_id; + } + + class contains_predicate_proc { + imp const& m; + public: + struct found {}; + contains_predicate_proc(imp const& m): m(m) {} + void operator()(var * n) {} + void operator()(quantifier * n) {} + void operator()(app* n) { + if (m.is_predicate(n)) throw found(); + } + }; + + bool contains_predicate(expr* fml) const { + contains_predicate_proc proc(*this); + try { + quick_for_each_expr(proc, fml); + } + catch (contains_predicate_proc::found) { + return true; + } + return false; + } + + + void mk_horn(expr_ref& fml, proof_ref& premise) { + expr* e1, *e2; + expr_ref_vector body(m); + proof_ref_vector defs(m); + expr_ref fml0(m), fml1(m), fml2(m), head(m); + proof_ref p(m); + fml0 = fml; + m_names.reset(); + m_sorts.reset(); + m_qh.pull_quantifier(true, fml0, &m_sorts, &m_names); + if (premise){ + fml1 = bind_variables(fml0); + if (!m_sorts.empty()) { + proof* p1 = m.mk_pull_quant(fml, to_quantifier(fml1)); + premise = mk_modus_ponens(premise, p1); + } + } + head = fml0; + while (m.is_implies(head, e1, e2)) { + body.push_back(e1); + head = e2; + } + datalog::flatten_and(body); + if (premise) { + p = m.mk_rewrite(fml0, mk_implies(body, head)); + } + + // + // Case: + // A \/ B -> C + // => + // A -> C + // B -> C + // + if (body.size() == 1 && m.is_or(body[0].get()) && contains_predicate(body[0].get())) { + app* _or = to_app(body[0].get()); + unsigned sz = _or->get_num_args(); + expr* const* args = _or->get_args(); + for (unsigned i = 0; i < sz; ++i) { + m_todo.push_back(bind_variables(m.mk_implies(args[i], head))); + m_proofs.push_back(0); + } + + if (premise) { + expr_ref f1 = bind_variables(mk_implies(body, head)); + expr* f2 = m.mk_and(sz, m_todo.c_ptr()+m_todo.size()-sz); + proof_ref p2(m), p3(m); + p2 = m.mk_def_axiom(m.mk_iff(f1, f2)); + p3 = mk_quant_intro(fml, f1, p); + p2 = mk_transitivity(p3, p2); + p2 = mk_modus_ponens(premise, p2); + for (unsigned i = 0; i < sz; ++i) { + m_proofs[m_proofs.size()-sz+i] = m.mk_and_elim(p2, i); + } + } + fml = 0; + return; + } + + + eliminate_disjunctions(body, defs); + p = mk_congruence(p, body, head, defs); + + eliminate_quantifier_body(body, defs); + p = mk_congruence(p, body, head, defs); + + fml2 = mk_implies(body, head); + + fml = bind_variables(fml2); + + if (premise) { + SASSERT(p); + p = mk_quant_intro(fml1, fml, p); + premise = mk_modus_ponens(premise, p); + } + } + + proof* mk_quant_intro(expr* e1, expr* e2, proof* p) { + if (m_sorts.empty()) { + return p; + } + quantifier* q1 = to_quantifier(e1); + quantifier* q2 = to_quantifier(e2); + if (m.is_iff(m.get_fact(p))) { + return m.mk_quant_intro(q1, q2, p); + } + if (m.is_oeq(m.get_fact(p))) { + return m.mk_oeq_quant_intro(q1, q2, p); + } + UNREACHABLE(); + return p; + } + + + void eliminate_disjunctions(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) { + expr* b = body.get(); + expr* e1; + bool negate_args = false; + bool is_disj = false; + unsigned num_disj = 0; + expr* const* disjs = 0; + if (!contains_predicate(b)) { + return; + } + TRACE("hnf", tout << mk_pp(b, m) << "\n";); + if (m.is_or(b)) { + is_disj = true; + negate_args = false; + num_disj = to_app(b)->get_num_args(); + disjs = to_app(b)->get_args(); + } + if (m.is_not(b, e1) && m.is_and(e1)) { + is_disj = true; + negate_args = true; + num_disj = to_app(e1)->get_num_args(); + disjs = to_app(e1)->get_args(); + } + if (is_disj) { + app* old_head = 0; + if (m_memoize_disj.find(b, old_head)) { + body = old_head; + } + else { + app_ref head = mk_fresh_head(b); + proof_ref_vector defs(m); + for (unsigned i = 0; i < num_disj; ++i) { + expr* e = disjs[i]; + if (negate_args) { + e = m.mk_not(e); + } + m_todo.push_back(bind_variables(m.mk_implies(e, head))); + m_proofs.push_back(0); + if (produce_proofs()) { + defs.push_back(m.mk_def_intro(m_todo.back())); + m_proofs[m_proofs.size()-1] = defs.back(); + } + } + if (produce_proofs()) { + proof* p = m.mk_apply_defs(body.get(), head, defs.size(), defs.c_ptr()); + m_refs.push_back(p); + m_memoize_proof.insert(b, p); + } + m_memoize_disj.insert(b, head); + m_refs.push_back(b); + m_refs.push_back(head); + // update the body to be the newly introduced head relation + body = head; + } + + if (produce_proofs()) { + proofs.push_back(m_memoize_proof.find(b)); + } + } + } + + app_ref mk_fresh_head(expr* e) { + ptr_vector sorts0, sorts1; + get_free_vars(e, sorts0); + expr_ref_vector args(m); + for (unsigned i = 0; i < sorts0.size(); ++i) { + if (sorts0[i]) { + args.push_back(m.mk_var(i, sorts0[i])); + sorts1.push_back(sorts0[i]); + } + } + func_decl_ref f(m); + f = m.mk_fresh_func_decl(m_name.str().c_str(), "", sorts1.size(), sorts1.c_ptr(), m.mk_bool_sort()); + m_fresh_predicates.push_back(f); + return app_ref(m.mk_app(f, args.size(), args.c_ptr()), m); + } + + void eliminate_disjunctions(expr_ref_vector& body, proof_ref_vector& proofs) { + for (unsigned i = 0; i < body.size(); ++i) { + expr_ref_vector::element_ref r = body[i]; + eliminate_disjunctions(r, proofs); + } + } + + void eliminate_quantifier_body(expr_ref_vector::element_ref& body, proof_ref_vector& proofs) { + if (is_forall(body.get()) && contains_predicate(body.get())) { + quantifier* q = to_quantifier(body.get()); + expr* e = q->get_expr(); + if (!is_predicate(e)) { + app_ref head = mk_fresh_head(e); + m_todo.push_back(bind_variables(m.mk_implies(e, head))); + m_proofs.push_back(0); + body = m.update_quantifier(q, head); + if (produce_proofs()) { + proof* def_intro = m.mk_def_intro(m_todo.back()); + proof* def_proof = m.mk_apply_def(e, head, def_intro); + proofs.push_back(m.mk_nnf_neg(q, body.get(), 1, &def_proof)); + m_proofs[m_proofs.size()-1] = def_intro; + } + } + } + } + + void eliminate_quantifier_body(expr_ref_vector& body, proof_ref_vector& proofs) { + for (unsigned i = 0; i < body.size(); ++i) { + expr_ref_vector::element_ref r = body[i]; + eliminate_quantifier_body(r, proofs); + } + } + + app_ref mk_implies(expr_ref_vector const& body, expr* head) { + switch (body.size()) { + case 0: + return app_ref(to_app(head), m); + case 1: + return app_ref(m.mk_implies(body[0], head), m); + default: + return app_ref(m.mk_implies(m.mk_and(body.size(), body.c_ptr()), head), m); + } + } + + + proof_ref mk_congruence(proof* p1, expr_ref_vector const& body, expr* head, proof_ref_vector& defs) { + if (defs.empty()) { + return proof_ref(p1, m); + } + else { + SASSERT(p1); + proof_ref p2(m), p3(m); + app_ref fml = mk_implies(body, head); + expr* fact = m.get_fact(p1); + if (m.is_iff(fact)) { + p1 = m.mk_iff_oeq(p1); + fact = m.get_fact(p1); + } + VERIFY (m.is_oeq(fact) || m.is_eq(fact)); + app* e2 = to_app(to_app(fact)->get_arg(1)); + p2 = m.mk_oeq_congruence(e2, fml, defs.size(), defs.c_ptr()); + p3 = mk_transitivity(p1, p2); + defs.reset(); + return proof_ref(p3, m); + } + } + + proof_ref mk_modus_ponens(proof* premise, proof* eq) { + proof_ref result(m); + result = m.mk_modus_ponens(premise, eq); + if (m.get_fact(premise) == m.get_fact(result)) { + result = premise; + } + return result; + } + + proof* mk_transitivity(proof* p1, proof* p2) { + if (p1) { + app* f = to_app(m.get_fact(p1)); + if (f->get_arg(0) == f->get_arg(1)) { + return p2; + } + } + if (p2) { + app* f = to_app(m.get_fact(p2)); + if (f->get_arg(0) == f->get_arg(1)) { + return p1; + } + } + return m.mk_transitivity(p1, p2); + } + + expr_ref bind_variables(expr* e) { + SASSERT(m_sorts.size() == m_names.size()); + if (m_sorts.empty()) { + return expr_ref(e, m); + } + return expr_ref(m.mk_forall(m_sorts.size(), m_sorts.c_ptr(), m_names.c_ptr(), e), m); + } + +}; + +hnf::hnf(ast_manager & m) { + m_imp = alloc(imp, m); +} + +hnf::~hnf() { + dealloc(m_imp); +} + +void hnf::operator()(expr * n, proof* p, expr_ref_vector & rs, proof_ref_vector& ps) { + m_imp->operator()(n, p, rs, ps); + TRACE("hnf", + ast_manager& m = rs.get_manager(); + tout << mk_ismt2_pp(n, m) << "\nHNF result:\n"; + for (unsigned i = 0; i < rs.size(); ++i) { + tout << mk_pp(rs[i].get(), m) << "\n"; + } + ); +} + +void hnf::set_cancel(bool f) { + m_imp->set_cancel(f); +} + +void hnf::set_name(symbol const& n) { + m_imp->set_name(n); +} + +void hnf::reset() { + m_imp->reset(); +} + +func_decl_ref_vector const& hnf::get_fresh_predicates() { + return m_imp->get_fresh_predicates(); +} diff --git a/src/muz_qe/hnf.h b/src/muz_qe/hnf.h new file mode 100644 index 000000000..37339540b --- /dev/null +++ b/src/muz_qe/hnf.h @@ -0,0 +1,49 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + hnf.h + +Abstract: + + Horn normal form convertion. +Author: + + +Notes: + + Very similar to NNF. + +--*/ + +#ifndef _HNF_H_ +#define _HNF_H_ + +#include"ast.h" +#include"params.h" +#include"defined_names.h" +#include"proof_converter.h" + +class hnf { + class imp; + imp * m_imp; +public: + hnf(ast_manager & m); + ~hnf(); + + void operator()(expr * n, // [IN] expression that should be put into Horn NF + proof* p, // [IN] proof of n + expr_ref_vector & rs, // [OUT] resultant (conjunction) of expressions + proof_ref_vector& ps // [OUT] proofs of rs + ); + + void cancel() { set_cancel(true); } + void reset_cancel() { set_cancel(false); } + void set_cancel(bool f); + void set_name(symbol const& name); + void reset(); + func_decl_ref_vector const& get_fresh_predicates(); +}; + +#endif /* _HNF_H_ */ diff --git a/src/muz_qe/horn_tactic.cpp b/src/muz_qe/horn_tactic.cpp index 0637148d6..1a8f562d9 100644 --- a/src/muz_qe/horn_tactic.cpp +++ b/src/muz_qe/horn_tactic.cpp @@ -21,15 +21,20 @@ Revision History: #include"proof_converter.h" #include"horn_tactic.h" #include"dl_context.h" +#include"expr_replacer.h" +#include"dl_rule_transformer.h" +#include"dl_mk_slice.h" class horn_tactic : public tactic { struct imp { ast_manager& m; + bool m_is_simplify; datalog::context m_ctx; smt_params m_fparams; - imp(ast_manager & m, params_ref const & p): + imp(bool t, ast_manager & m, params_ref const & p): m(m), + m_is_simplify(t), m_ctx(m, m_fparams) { updt_params(p); } @@ -87,7 +92,7 @@ class horn_tactic : public tactic { void register_predicate(expr* a) { SASSERT(is_predicate(a)); - m_ctx.register_predicate(to_app(a)->get_decl(), true); + m_ctx.register_predicate(to_app(a)->get_decl(), false); } void check_predicate(ast_mark& mark, expr* a) { @@ -108,8 +113,8 @@ class horn_tactic : public tactic { todo.append(to_app(a)->get_num_args(), to_app(a)->get_args()); } else if (m.is_ite(a)) { - todo.append(to_app(a)->get_arg(1)); - todo.append(to_app(a)->get_arg(2)); + todo.push_back(to_app(a)->get_arg(1)); + todo.push_back(to_app(a)->get_arg(2)); } else if (is_predicate(a)) { register_predicate(a); @@ -164,7 +169,6 @@ class horn_tactic : public tactic { SASSERT(g->is_well_sorted()); mc = 0; pc = 0; core = 0; tactic_report report("horn", *g); - bool produce_models = g->models_enabled(); bool produce_proofs = g->proofs_enabled(); if (produce_proofs) { @@ -180,6 +184,9 @@ class horn_tactic : public tactic { expr_ref_vector queries(m); std::stringstream msg; + m_ctx.reset(); + m_ctx.ensure_opened(); + for (unsigned i = 0; i < sz; i++) { f = g->form(i); formula_kind k = get_formula_kind(f); @@ -196,7 +203,7 @@ class horn_tactic : public tactic { } } - if (queries.size() != 1) { + if (queries.size() != 1 || m_is_simplify) { q = m.mk_fresh_const("query", m.mk_bool_sort()); register_predicate(q); for (unsigned i = 0; i < queries.size(); ++i) { @@ -208,8 +215,26 @@ class horn_tactic : public tactic { } SASSERT(queries.size() == 1); q = queries[0].get(); + if (m_is_simplify) { + simplify(q, g, result, mc, pc); + } + else { + verify(q, g, result, mc, pc); + } + } + + void verify(expr* q, + goal_ref const& g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc) { + lbool is_reachable = m_ctx.query(q); g->inc_depth(); + + bool produce_models = g->models_enabled(); + bool produce_proofs = g->proofs_enabled(); + result.push_back(g.get()); switch (is_reachable) { case l_true: { @@ -237,19 +262,60 @@ class horn_tactic : public tactic { TRACE("horn", g->display(tout);); SASSERT(g->is_well_sorted()); } + + void simplify(expr* q, + goal_ref const& g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc) { + + expr_ref fml(m); + + + func_decl* query_pred = to_app(q)->get_decl(); + m_ctx.set_output_predicate(query_pred); + m_ctx.get_rules(); // flush adding rules. + m_ctx.apply_default_transformation(); + + if (m_ctx.get_params().slice()) { + datalog::rule_transformer transformer(m_ctx); + datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); + transformer.register_plugin(slice); + m_ctx.transform_rules(transformer); + } + + expr_substitution sub(m); + sub.insert(q, m.mk_false()); + scoped_ptr rep = mk_default_expr_replacer(m); + rep->set_substitution(&sub); + g->inc_depth(); + g->reset(); + result.push_back(g.get()); + datalog::rule_set const& rules = m_ctx.get_rules(); + datalog::rule_set::iterator it = rules.begin(), end = rules.end(); + for (; it != end; ++it) { + datalog::rule* r = *it; + r->to_formula(fml); + (*rep)(fml); + g->assert_expr(fml); + } + } + }; - + + bool m_is_simplify; params_ref m_params; statistics m_stats; imp * m_imp; public: - horn_tactic(ast_manager & m, params_ref const & p): + horn_tactic(bool t, ast_manager & m, params_ref const & p): + m_is_simplify(t), m_params(p) { - m_imp = alloc(imp, m, p); + m_imp = alloc(imp, t, m, p); } virtual tactic * translate(ast_manager & m) { - return alloc(horn_tactic, m, m_params); + return alloc(horn_tactic, m_is_simplify, m, m_params); } virtual ~horn_tactic() { @@ -293,7 +359,7 @@ public: m_imp = 0; } dealloc(d); - d = alloc(imp, m, m_params); + d = alloc(imp, m_is_simplify, m, m_params); #pragma omp critical (tactic_cancel) { m_imp = d; @@ -308,6 +374,10 @@ protected: }; tactic * mk_horn_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(horn_tactic, m, p)); + return clean(alloc(horn_tactic, false, m, p)); +} + +tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p) { + return clean(alloc(horn_tactic, true, m, p)); } diff --git a/src/muz_qe/horn_tactic.h b/src/muz_qe/horn_tactic.h index 7f56a77ba..f041321ea 100644 --- a/src/muz_qe/horn_tactic.h +++ b/src/muz_qe/horn_tactic.h @@ -27,4 +27,9 @@ tactic * mk_horn_tactic(ast_manager & m, params_ref const & p = params_ref()); /* ADD_TACTIC("horn", "apply tactic for horn clauses.", "mk_horn_tactic(m, p)") */ + +tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); +/* + ADD_TACTIC("horn-simplify", "simplify horn clauses.", "mk_horn_simplify_tactic(m, p)") +*/ #endif diff --git a/src/muz_qe/pdr_context.cpp b/src/muz_qe/pdr_context.cpp index 73bffd4e4..d4027d73d 100644 --- a/src/muz_qe/pdr_context.cpp +++ b/src/muz_qe/pdr_context.cpp @@ -410,6 +410,15 @@ namespace pdr { add_property(result, level); } + void pred_transformer::propagate_to_infinity(unsigned invariant_level) { + expr_ref inv = get_formulas(invariant_level, false); + add_property(inv, infty_level); + // cleanup + for (unsigned i = invariant_level; i < m_levels.size(); ++i) { + m_levels[i].reset(); + } + } + lbool pred_transformer::is_reachable(model_node& n, expr_ref_vector* core, bool& uses_level) { TRACE("pdr", tout << "is-reachable: " << head()->get_name() << " level: " << n.level() << "\n"; @@ -723,26 +732,6 @@ namespace pdr { m_closed = true; } - expr_ref model_node::get_trace() const { - pred_transformer& p = pt(); - ast_manager& m = p.get_manager(); - manager& pm = p.get_pdr_manager(); - TRACE("pdr", model_smt2_pp(tout, m, get_model(), 0);); - func_decl* f = p.head(); - unsigned arity = f->get_arity(); - model_ref model = get_model_ptr(); - expr_ref_vector args(m); - expr_ref v(m); - model_evaluator mev(m); - - for (unsigned i = 0; i < arity; ++i) { - v = m.mk_const(pm.o2n(p.sig(i),0)); - expr_ref e = mev.eval(model, v); - args.push_back(e); - } - return expr_ref(m.mk_app(f, args.size(), args.c_ptr()), m); - } - static bool is_ini(datalog::rule const& r) { return r.get_uninterpreted_tail_size() == 0; } @@ -961,24 +950,114 @@ namespace pdr { return out; } - expr_ref model_search::get_trace() const { + /** + \brief Ensure that all nodes in the tree have associated models. + get_trace and get_proof_trace rely on models to extract rules. + */ + void model_search::update_models() { + obj_map models; + ptr_vector todo; + todo.push_back(m_root); + while (!todo.empty()) { + model_node* n = todo.back(); + if (n->get_model_ptr()) { + models.insert(n->state(), n->get_model_ptr()); + } + todo.pop_back(); + todo.append(n->children().size(), n->children().c_ptr()); + } + + todo.push_back(m_root); + while (!todo.empty()) { + model_node* n = todo.back(); + model* md = 0; + if (!n->get_model_ptr() && models.find(n->state(), md)) { + model_ref mr(md); + n->set_model(mr); + } + todo.pop_back(); + todo.append(n->children().size(), n->children().c_ptr()); + } + } + + /** + Extract trace comprising of constraints + and predicates that are satisfied from facts to the query. + The resulting trace + */ + expr_ref model_search::get_trace(context const& ctx) { pred_transformer& pt = get_root().pt(); ast_manager& m = pt.get_manager(); manager& pm = pt.get_pdr_manager(); - expr_ref result(m.mk_true(),m); - expr_ref_vector rules(m); - ptr_vector nodes; - nodes.push_back(m_root); - while (!nodes.empty()) { - model_node* current = nodes.back(); - nodes.pop_back(); - rules.push_back(current->get_trace()); - nodes.append(current->children()); - } - return pm.mk_and(rules); + datalog::context& dctx = ctx.get_context(); + datalog::rule_manager& rm = dctx.get_rule_manager(); + expr_ref_vector constraints(m), predicates(m); + expr_ref tmp(m); + ptr_vector children; + unsigned deltas[2]; + datalog::rule_ref rule(rm), r0(rm); + model_node* n = m_root; + datalog::rule_counter& vc = rm.get_counter(); + substitution subst(m); + unifier unif(m); + rule = n->get_rule(); + unsigned max_var = vc.get_max_rule_var(*rule); + predicates.push_back(rule->get_head()); + children.push_back(n); + bool first = true; + update_models(); + while (!children.empty()) { + SASSERT(children.size() == predicates.size()); + expr_ref_vector binding(m); + n = children.back(); + children.pop_back(); + TRACE("pdr", n->display(tout, 0);); + n->mk_instantiate(r0, rule, binding); + + max_var = std::max(max_var, vc.get_max_rule_var(*rule)); + subst.reset(); + subst.reserve(2, max_var+1); + deltas[0] = 0; + deltas[1] = max_var+1; + + VERIFY(unif(predicates.back(), rule->get_head(), subst)); + for (unsigned i = 0; i < constraints.size(); ++i) { + subst.apply(2, deltas, expr_offset(constraints[i].get(), 0), tmp); + dctx.get_rewriter()(tmp); + constraints[i] = tmp; + } + for (unsigned i = 0; i < predicates.size(); ++i) { + subst.apply(2, deltas, expr_offset(predicates[i].get(), 0), tmp); + predicates[i] = tmp; + } + if (!first) { + constraints.push_back(predicates.back()); + } + first = false; + predicates.pop_back(); + for (unsigned i = 0; i < rule->get_uninterpreted_tail_size(); ++i) { + subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); + predicates.push_back(tmp); + } + for (unsigned i = rule->get_uninterpreted_tail_size(); i < rule->get_tail_size(); ++i) { + subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); + dctx.get_rewriter()(tmp); + if (!m.is_true(tmp)) { + constraints.push_back(tmp); + } + } + for (unsigned i = 0; i < constraints.size(); ++i) { + max_var = std::max(vc.get_max_var(constraints[i].get()), max_var); + } + for (unsigned i = 0; i < predicates.size(); ++i) { + max_var = std::max(vc.get_max_var(predicates[i].get()), max_var); + } + children.append(n->children()); + } + return pm.mk_and(constraints); } - proof_ref model_search::get_proof_trace(context const& ctx) const { + proof_ref model_search::get_proof_trace(context const& ctx) { pred_transformer& pt = get_root().pt(); ast_manager& m = pt.get_manager(); datalog::context& dctx = ctx.get_context(); @@ -994,8 +1073,10 @@ namespace pdr { proof* pr = 0; unifier.set_normalize(false); todo.push_back(m_root); + update_models(); while (!todo.empty()) { model_node* n = todo.back(); + TRACE("pdr", n->display(tout, 0);); if (cache.find(n->state(), pr)) { todo.pop_back(); continue; @@ -1018,12 +1099,14 @@ namespace pdr { continue; } proof_ref rl(m); - expr_ref fml0(m); expr_ref_vector binding(m); n->mk_instantiate(r0, r1, binding); - r0->to_formula(fml0); proof_ref p1(m), p2(m); - p1 = m.mk_asserted(fml0); + p1 = r0->get_proof(); + if (!p1) { + r0->display(dctx, std::cout); + } + SASSERT(p1); pfs[0] = p1; rls[0] = r1; TRACE("pdr", @@ -1367,6 +1450,8 @@ namespace pdr { if (!m_params.validate_result()) { return; } + std::stringstream msg; + switch(m_last_result) { case l_true: { proof_ref pr = get_proof(); @@ -1374,7 +1459,9 @@ namespace pdr { expr_ref_vector side_conditions(m); bool ok = checker.check(pr, side_conditions); if (!ok) { - IF_VERBOSE(0, verbose_stream() << "proof validation failed\n";); + msg << "proof validation failed"; + IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); + throw default_exception(msg.str()); } for (unsigned i = 0; i < side_conditions.size(); ++i) { expr* cond = side_conditions[i].get(); @@ -1385,9 +1472,9 @@ namespace pdr { solver.assert_expr(tmp); lbool res = solver.check(); if (res != l_false) { - IF_VERBOSE(0, verbose_stream() << "rule validation failed\n"; - verbose_stream() << mk_pp(cond, m) << "\n"; - ); + msg << "rule validation failed when checking: " << mk_pp(cond, m); + IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); + throw default_exception(msg.str()); } } break; @@ -1430,14 +1517,16 @@ namespace pdr { names.push_back(symbol(i)); } sorts.reverse(); - tmp = m.mk_exists(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp); + if (!sorts.empty()) { + tmp = m.mk_exists(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp); + } smt::kernel solver(m, get_fparams()); solver.assert_expr(tmp); lbool res = solver.check(); if (res != l_false) { - IF_VERBOSE(0, verbose_stream() << "rule validation failed\n"; - verbose_stream() << mk_pp(tmp, m) << "\n"; - ); + msg << "rule validation failed when checking: " << mk_pp(tmp, m); + IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); + throw default_exception(msg.str()); } } } @@ -1530,12 +1619,21 @@ namespace pdr { inductive_property ex(m, mc, rs); verbose_stream() << ex.to_string(); }); + + // upgrade invariants that are known to be inductive. + decl2rel::iterator it = m_rels.begin (), end = m_rels.end (); + for (; m_inductive_lvl > 0 && it != end; ++it) { + if (it->m_value->head() != m_query_pred) { + it->m_value->propagate_to_infinity (m_inductive_lvl); + } + } validate(); return l_false; } catch (unknown_exception) { return l_undef; } + UNREACHABLE(); return l_undef; } @@ -1594,7 +1692,7 @@ namespace pdr { proof_ref pr = get_proof(); return expr_ref(pr.get(), m); } - return m_search.get_trace(); + return m_search.get_trace(*this); } expr_ref context::mk_unsat_answer() const { @@ -1939,6 +2037,7 @@ namespace pdr { } st.update("PDR num unfoldings", m_stats.m_num_nodes); st.update("PDR max depth", m_stats.m_max_depth); + st.update("PDR inductive level", m_inductive_lvl); m_pm.collect_statistics(st); for (unsigned i = 0; i < m_core_generalizers.size(); ++i) { diff --git a/src/muz_qe/pdr_context.h b/src/muz_qe/pdr_context.h index 7491327dd..6aa02ef10 100644 --- a/src/muz_qe/pdr_context.h +++ b/src/muz_qe/pdr_context.h @@ -138,6 +138,7 @@ namespace pdr { ptr_vector& get_aux_vars(datalog::rule const& r) { return m_rule2vars.find(&r); } bool propagate_to_next_level(unsigned level); + void propagate_to_infinity(unsigned level); void add_property(expr * lemma, unsigned lvl); // add property 'p' to state at level. lbool is_reachable(model_node& n, expr_ref_vector* core, bool& uses_level); @@ -223,7 +224,6 @@ namespace pdr { void set_rule(datalog::rule const* r) { m_rule = r; } datalog::rule* get_rule(); - expr_ref get_trace() const; void mk_instantiate(datalog::rule_ref& r0, datalog::rule_ref& r1, expr_ref_vector& binding); std::ostream& display(std::ostream& out, unsigned indent); @@ -240,6 +240,7 @@ namespace pdr { void erase_leaf(model_node& n); void remove_node(model_node& n); void enqueue_leaf(model_node& n); // add leaf to priority queue. + void update_models(); public: model_search(bool bfs): m_bfs(bfs), m_root(0) {} ~model_search(); @@ -253,8 +254,8 @@ namespace pdr { void set_root(model_node* n); model_node& get_root() const { return *m_root; } std::ostream& display(std::ostream& out) const; - expr_ref get_trace() const; - proof_ref get_proof_trace(context const& ctx) const; + expr_ref get_trace(context const& ctx); + proof_ref get_proof_trace(context const& ctx); void backtrack_level(bool uses_level, model_node& n); }; @@ -299,7 +300,7 @@ namespace pdr { decl2rel m_rels; // Map from relation predicate to fp-operator. func_decl_ref m_query_pred; pred_transformer* m_query; - model_search m_search; + mutable model_search m_search; lbool m_last_result; unsigned m_inductive_lvl; ptr_vector m_core_generalizers; diff --git a/src/muz_qe/pdr_dl_interface.cpp b/src/muz_qe/pdr_dl_interface.cpp index 54a40f8b8..e2232dafe 100644 --- a/src/muz_qe/pdr_dl_interface.cpp +++ b/src/muz_qe/pdr_dl_interface.cpp @@ -85,6 +85,7 @@ lbool dl_interface::query(expr * query) { m_pred2slice.reset(); ast_manager& m = m_ctx.get_manager(); datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); + datalog::rule_set old_rules(m_ctx.get_rules()); func_decl_ref query_pred(m); datalog::rule_ref_vector query_rules(rule_manager); @@ -94,7 +95,7 @@ lbool dl_interface::query(expr * query) { expr_ref bg_assertion = m_ctx.get_background_assertion(); check_reset(); - + TRACE("pdr", if (!m.is_true(bg_assertion)) { tout << "axioms:\n"; @@ -105,19 +106,15 @@ lbool dl_interface::query(expr * query) { m_ctx.display_rules(tout); ); - model_converter_ref mc = datalog::mk_skip_model_converter(); - proof_converter_ref pc; - if (m_ctx.get_params().generate_proof_trace()) { - pc = datalog::mk_skip_proof_converter(); - } + m_ctx.set_output_predicate(query_pred); - m_ctx.apply_default_transformation(mc, pc); + m_ctx.apply_default_transformation(); if (m_ctx.get_params().slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); transformer.register_plugin(slice); - m_ctx.transform_rules(transformer, mc, pc); + m_ctx.transform_rules(transformer); query_pred = slice->get_predicate(query_pred.get()); m_ctx.set_output_predicate(query_pred); @@ -134,22 +131,25 @@ lbool dl_interface::query(expr * query) { if (m_ctx.get_params().unfold_rules() > 0) { unsigned num_unfolds = m_ctx.get_params().unfold_rules(); - datalog::rule_transformer transformer1(m_ctx), transformer2(m_ctx); + datalog::rule_transformer transf1(m_ctx), transf2(m_ctx); + transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); + transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); if (m_ctx.get_params().coalesce_rules()) { - transformer1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); - m_ctx.transform_rules(transformer1, mc, pc); + m_ctx.transform_rules(transf1); } - transformer2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); while (num_unfolds > 0) { - m_ctx.transform_rules(transformer2, mc, pc); + m_ctx.transform_rules(transf2); --num_unfolds; } } // remove universal quantifiers from body. + + + datalog::mk_extract_quantifiers* extract_quantifiers = alloc(datalog::mk_extract_quantifiers, m_ctx); datalog::rule_transformer extract_q_tr(m_ctx); extract_q_tr.register_plugin(extract_quantifiers); - m_ctx.transform_rules(extract_q_tr, mc, pc); + m_ctx.transform_rules(extract_q_tr); IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); @@ -162,8 +162,8 @@ lbool dl_interface::query(expr * query) { datalog::scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode. - m_context->set_proof_converter(pc); - m_context->set_model_converter(mc); + m_context->set_proof_converter(m_ctx.get_proof_converter()); + m_context->set_model_converter(m_ctx.get_model_converter()); m_context->set_query(query_pred); m_context->set_axioms(bg_assertion); m_context->update_rules(m_pdr_rules); diff --git a/src/muz_qe/pdr_generalizers.cpp b/src/muz_qe/pdr_generalizers.cpp index 4cdfb186e..1b8ea22f9 100644 --- a/src/muz_qe/pdr_generalizers.cpp +++ b/src/muz_qe/pdr_generalizers.cpp @@ -228,11 +228,13 @@ namespace pdr { is_lower = !is_lower; } + vector bound; + bound.push_back(std::make_pair(x, i)); if (is_lower) { - m_lb.insert(abs(r), std::make_pair(x, i)); + m_lb.insert(abs(r), bound); } else { - m_ub.insert(abs(r), std::make_pair(x, i)); + m_ub.insert(abs(r), bound); } } diff --git a/src/muz_qe/pdr_quantifiers.cpp b/src/muz_qe/pdr_quantifiers.cpp index 5cc97893a..4a7b4b995 100644 --- a/src/muz_qe/pdr_quantifiers.cpp +++ b/src/muz_qe/pdr_quantifiers.cpp @@ -612,8 +612,8 @@ namespace pdr { datalog::rule_set::iterator it = m_rules.begin(), end = m_rules.end(); for (; it != end; ++it) { datalog::rule* r = *it; - datalog::var_counter vc(true); - unsigned max_var = vc.get_max_var(*r); + datalog::rule_counter vc(true); + unsigned max_var = vc.get_max_rule_var(*r); app_ref_vector body(m); for (unsigned i = 0; i < m_instantiations.size(); ++i) { if (r == m_instantiated_rules[i]) { diff --git a/src/muz_qe/pdr_util.cpp b/src/muz_qe/pdr_util.cpp index 1ea705e6e..237cf9415 100644 --- a/src/muz_qe/pdr_util.cpp +++ b/src/muz_qe/pdr_util.cpp @@ -32,7 +32,6 @@ Notes: #include "for_each_expr.h" #include "smt_params.h" #include "model.h" -#include "model_v2_pp.h" #include "ref_vector.h" #include "rewriter.h" #include "rewriter_def.h" @@ -42,6 +41,7 @@ Notes: #include "pdr_util.h" #include "arith_decl_plugin.h" #include "expr_replacer.h" +#include "model_smt2_pp.h" namespace pdr { @@ -510,13 +510,24 @@ namespace pdr { set_x(e); } } + + void model_evaluator::eval_exprs(expr_ref_vector& es) { + model_ref mr(m_model); + for (unsigned j = 0; j < es.size(); ++j) { + if (m_array.is_as_array(es[j].get())) { + es[j] = eval(mr, es[j].get()); + } + } + } bool model_evaluator::extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case) { SASSERT(m_array.is_array(a)); + TRACE("pdr", tout << mk_pp(a, m) << "\n";); while (m_array.is_store(a)) { expr_ref_vector store(m); store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1); + eval_exprs(store); stores.push_back(store); a = to_app(a)->get_arg(0); } @@ -526,7 +537,7 @@ namespace pdr { return true; } - if (m_array.is_as_array(a)) { + while (m_array.is_as_array(a)) { func_decl* f = m_array.get_as_array_func_decl(to_app(a)); func_interp* g = m_model->get_func_interp(f); unsigned sz = g->num_entries(); @@ -538,20 +549,30 @@ namespace pdr { store.push_back(fe->get_result()); for (unsigned j = 0; j < store.size(); ++j) { if (!is_ground(store[j].get())) { + TRACE("pdr", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";); return false; } } + eval_exprs(store); stores.push_back(store); } else_case = g->get_else(); if (!else_case) { + TRACE("pdr", tout << "no else case " << mk_pp(a, m) << "\n";); return false; } if (!is_ground(else_case)) { + TRACE("pdr", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";); return false; } + if (m_array.is_as_array(else_case)) { + model_ref mr(m_model); + else_case = eval(mr, else_case); + } + TRACE("pdr", tout << "else case: " << mk_pp(else_case, m) << "\n";); return true; } + TRACE("pdr", tout << "no translation: " << mk_pp(a, m) << "\n";); return false; } @@ -570,7 +591,8 @@ namespace pdr { } sort* s = m.get_sort(arg1); sort* r = get_array_range(s); - if (!r->is_infinite() && !r->is_very_big()) { + // give up evaluating finite domain/range arrays + if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); return; @@ -591,6 +613,9 @@ namespace pdr { << mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";); set_false(e); } + else if (m_array.is_array(else1)) { + eval_array_eq(e, else1, else2); + } else { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); @@ -614,18 +639,23 @@ namespace pdr { if (w1 == w2) { continue; } - else if (m.is_value(w1) && m.is_value(w2)) { + if (m.is_value(w1) && m.is_value(w2)) { TRACE("pdr", tout << "Equality evaluation: " << mk_pp(e, m) << "\n"; tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n"; tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";); set_false(e); - return; + } + else if (m_array.is_array(w1)) { + eval_array_eq(e, w1, w2); + if (is_true(e)) { + continue; + } } else { TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";); set_x(e); - return; } + return; } set_true(e); } @@ -869,6 +899,7 @@ namespace pdr { } if (is_x(form)) { IF_VERBOSE(0, verbose_stream() << "formula undetermined in model: " << mk_pp(form, m) << "\n";); + TRACE("pdr", model_smt2_pp(tout, m, *m_model, 0);); has_x = true; } } diff --git a/src/muz_qe/pdr_util.h b/src/muz_qe/pdr_util.h index 220e56b3c..ddbf0d122 100644 --- a/src/muz_qe/pdr_util.h +++ b/src/muz_qe/pdr_util.h @@ -104,6 +104,8 @@ namespace pdr { bool check_model(ptr_vector const & formulas); bool extract_array_func_interp(expr* a, vector& stores, expr_ref& else_case); + + void eval_exprs(expr_ref_vector& es); public: model_evaluator(ast_manager& m) : m(m), m_arith(m), m_array(m), m_refs(m) {} diff --git a/src/muz_qe/qe_lite.cpp b/src/muz_qe/qe_lite.cpp index 5f018895c..ff49584ff 100644 --- a/src/muz_qe/qe_lite.cpp +++ b/src/muz_qe/qe_lite.cpp @@ -201,9 +201,15 @@ namespace eq { return (*m_is_variable)(e); } - bool is_neg_var(ast_manager & m, expr * e) { + bool is_neg_var(ast_manager & m, expr * e, var*& v) { expr* e1; - return m.is_not(e, e1) && is_variable(e1); + if (m.is_not(e, e1) && is_variable(e1)) { + v = to_var(e1); + return true; + } + else { + return false; + } } @@ -328,18 +334,19 @@ namespace eq { bool is_var_eq(expr * e, ptr_vector& vs, expr_ref_vector & ts) { expr* lhs, *rhs; + var* v; // (= VAR t), (iff VAR t), (iff (not VAR) t), (iff t (not VAR)) cases if (m.is_eq(e, lhs, rhs) || m.is_iff(e, lhs, rhs)) { // (iff (not VAR) t) (iff t (not VAR)) cases if (!is_variable(lhs) && !is_variable(rhs) && m.is_bool(lhs)) { - if (!is_neg_var(m, lhs)) { + if (!is_neg_var(m, lhs, v)) { std::swap(lhs, rhs); } - if (!is_neg_var(m, lhs)) { + if (!is_neg_var(m, lhs, v)) { return false; } - vs.push_back(to_var(lhs)); + vs.push_back(v); ts.push_back(m.mk_not(rhs)); TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); return true; @@ -378,9 +385,9 @@ namespace eq { } // VAR = false case - if (is_neg_var(m, e)) { + if (is_neg_var(m, e, v)) { ts.push_back(m.mk_false()); - vs.push_back(to_var(to_app(e)->get_arg(0))); + vs.push_back(v); TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); return true; } @@ -2492,7 +2499,13 @@ class qe_lite_tactic : public tactic { new_f = f; m_qe(new_f, new_pr); if (produce_proofs) { - new_pr = m.mk_modus_ponens(g->pr(i), new_pr); + expr* fact = m.get_fact(new_pr); + if (to_app(fact)->get_arg(0) != to_app(fact)->get_arg(1)) { + new_pr = m.mk_modus_ponens(g->pr(i), new_pr); + } + else { + new_pr = g->pr(i); + } } g->update(i, new_f, new_pr, g->dep(i)); } diff --git a/src/muz_qe/qe_sat_tactic.h b/src/muz_qe/qe_sat_tactic.h index c539216be..15228c534 100644 --- a/src/muz_qe/qe_sat_tactic.h +++ b/src/muz_qe/qe_sat_tactic.h @@ -26,7 +26,7 @@ Revision History: namespace qe { - tactic * mk_sat_tactic(ast_manager& m, params_ref const& p); + tactic * mk_sat_tactic(ast_manager& m, params_ref const& p = params_ref()); }; /* diff --git a/src/muz_qe/rel_context.cpp b/src/muz_qe/rel_context.cpp index 1b4042ab9..58263b9d0 100644 --- a/src/muz_qe/rel_context.cpp +++ b/src/muz_qe/rel_context.cpp @@ -27,13 +27,11 @@ Revision History: #include"dl_product_relation.h" #include"dl_bound_relation.h" #include"dl_interval_relation.h" +#include"dl_mk_karr_invariants.h" #include"dl_finite_product_relation.h" #include"dl_sparse_table.h" #include"dl_table.h" #include"dl_table_relation.h" -#ifndef _EXTERNAL_RELEASE -#include"dl_skip_table.h" -#endif namespace datalog { @@ -42,21 +40,23 @@ namespace datalog { m(ctx.get_manager()), m_rmanager(ctx), m_answer(m), - m_cancel(false), - m_last_result_relation(0) { + m_last_result_relation(0), + m_ectx(ctx) { + + // register plugins for builtin tables + get_rmanager().register_plugin(alloc(sparse_table_plugin, get_rmanager())); get_rmanager().register_plugin(alloc(hashtable_table_plugin, get_rmanager())); get_rmanager().register_plugin(alloc(bitvector_table_plugin, get_rmanager())); get_rmanager().register_plugin(alloc(equivalence_table_plugin, get_rmanager())); -#ifndef _EXTERNAL_RELEASE - get_rmanager().register_plugin(alloc(skip_table_plugin, get_rmanager())); -#endif - //register plugins for builtin relations + // register plugins for builtin relations get_rmanager().register_plugin(alloc(bound_relation_plugin, get_rmanager())); get_rmanager().register_plugin(alloc(interval_relation_plugin, get_rmanager())); + get_rmanager().register_plugin(alloc(karr_relation_plugin, get_rmanager())); + } @@ -97,21 +97,23 @@ namespace datalog { decl_set original_predicates; m_context.collect_predicates(original_predicates); - instruction_block rules_code; + m_code.reset(); instruction_block termination_code; - execution_context ex_ctx(m_context); + m_ectx.reset(); lbool result; TRACE("dl", m_context.display(tout);); while (true) { - model_converter_ref mc; // Ignored in Datalog mode - proof_converter_ref pc; // Ignored in Datalog mode - m_context.transform_rules(mc, pc); - compiler::compile(m_context, m_context.get_rules(), rules_code, termination_code); + m_context.transform_rules(); + if (m_context.canceled()) { + result = l_undef; + break; + } + compiler::compile(m_context, m_context.get_rules(), m_code, termination_code); - TRACE("dl", rules_code.display(*this, tout); ); + TRACE("dl", m_code.display(*this, tout); ); bool timeout_after_this_round = time_limit && (restart_time==0 || remaining_time_limit<=restart_time); @@ -119,29 +121,32 @@ namespace datalog { unsigned timeout = time_limit ? (restart_time!=0) ? std::min(remaining_time_limit, restart_time) : remaining_time_limit : restart_time; - ex_ctx.set_timelimit(timeout); + m_ectx.set_timelimit(timeout); } - bool early_termination = !rules_code.perform(ex_ctx); - ex_ctx.reset_timelimit(); - VERIFY( termination_code.perform(ex_ctx) ); + bool early_termination = !m_code.perform(m_ectx); + m_ectx.reset_timelimit(); + VERIFY( termination_code.perform(m_ectx) || m_context.canceled()); - rules_code.process_all_costs(); + m_code.process_all_costs(); - IF_VERBOSE(10, ex_ctx.report_big_relations(1000, verbose_stream());); - + IF_VERBOSE(10, m_ectx.report_big_relations(1000, verbose_stream());); + + if (m_context.canceled()) { + result = l_undef; + break; + } if (!early_termination) { m_context.set_status(OK); result = l_true; break; } - if (memory::above_high_watermark()) { m_context.set_status(MEMOUT); result = l_undef; break; } - if (timeout_after_this_round || m_cancel) { + if (timeout_after_this_round) { m_context.set_status(TIMEOUT); result = l_undef; break; @@ -159,9 +164,7 @@ namespace datalog { restart_time = static_cast(new_restart_time); } - rules_code.reset(); - termination_code.reset(); - ex_ctx.reset(); + termination_code.reset(); m_context.reopen(); restrict_predicates(original_predicates); m_context.replace_rules(original_rules); @@ -169,30 +172,32 @@ namespace datalog { } m_context.reopen(); restrict_predicates(original_predicates); + m_context.record_transformed_rules(); m_context.replace_rules(original_rules); m_context.close(); - TRACE("dl", ex_ctx.report_big_relations(100, tout);); - m_cancel = false; + TRACE("dl", m_ectx.report_big_relations(100, tout);); + m_code.process_all_costs(); + m_code.make_annotations(m_ectx); return result; } -#define BEGIN_QUERY() \ +#define BEGIN_QUERY() \ rule_set original_rules(m_context.get_rules()); \ - decl_set original_preds; \ - m_context.collect_predicates(original_preds); \ - bool was_closed = m_context.is_closed(); \ - if (was_closed) { \ + decl_set original_preds; \ + m_context.collect_predicates(original_preds); \ + bool was_closed = m_context.is_closed(); \ + if (was_closed) { \ m_context.reopen(); \ - } \ - -#define END_QUERY() \ + } \ + +#define END_QUERY() \ m_context.reopen(); \ m_context.replace_rules(original_rules); \ - restrict_predicates(original_preds); \ - \ - if (was_closed) { \ + restrict_predicates(original_preds); \ + \ + if (was_closed) { \ m_context.close(); \ - } \ + } \ lbool rel_context::query(unsigned num_rels, func_decl * const* rels) { get_rmanager().reset_saturated_marks(); @@ -266,14 +271,12 @@ namespace datalog { reset_negated_tables(); if (m_context.generate_explanations()) { - model_converter_ref mc; // ignored in Datalog mode - proof_converter_ref pc; // ignored in Datalog mode rule_transformer transformer(m_context); //expl_plugin is deallocated when transformer goes out of scope mk_explanations * expl_plugin = alloc(mk_explanations, m_context, m_context.explanations_on_relation_level()); transformer.register_plugin(expl_plugin); - m_context.transform_rules(transformer, mc, pc); + m_context.transform_rules(transformer); //we will retrieve the predicate with explanations instead of the original query predicate query_pred = expl_plugin->get_e_decl(query_pred); @@ -283,11 +286,9 @@ namespace datalog { } if (m_context.magic_sets_for_queries()) { - model_converter_ref mc; // Ignored in Datalog mode - proof_converter_ref pc; // Ignored in Datalog mode rule_transformer transformer(m_context); transformer.register_plugin(alloc(mk_magic_sets, m_context, qrule.get())); - m_context.transform_rules(transformer, mc, pc); + m_context.transform_rules(transformer); } lbool res = saturate(); @@ -429,6 +430,10 @@ namespace datalog { get_rmanager().set_predicate_kind(pred, target_kind); } + void rel_context::set_cancel(bool f) { + get_rmanager().set_cancel(f); + } + relation_plugin & rel_context::get_ordinary_relation_plugin(symbol relation_name) { relation_plugin * plugin = get_rmanager().get_relation_plugin(relation_name); if (!plugin) { @@ -492,6 +497,11 @@ namespace datalog { } } + bool rel_context::has_facts(func_decl * pred) const { + relation_base* r = try_get_relation(pred); + return r && !r->empty(); + } + void rel_context::store_relation(func_decl * pred, relation_base * rel) { get_rmanager().store_relation(pred, rel); } @@ -513,5 +523,17 @@ namespace datalog { get_rmanager().display(out); } + void rel_context::display_profile(std::ostream& out) const { + out << "\n--------------\n"; + out << "Instructions\n"; + m_code.display(*this, out); + + out << "\n--------------\n"; + out << "Big relations\n"; + m_ectx.report_big_relations(1000, out); + + get_rmanager().display_relation_sizes(out); + } + }; diff --git a/src/muz_qe/rel_context.h b/src/muz_qe/rel_context.h index b05532d14..d2f6973da 100644 --- a/src/muz_qe/rel_context.h +++ b/src/muz_qe/rel_context.h @@ -22,6 +22,7 @@ Revision History: #define _REL_CONTEXT_H_ #include "ast.h" #include "dl_relation_manager.h" +#include "dl_instruction.h" #include "lbool.h" namespace datalog { @@ -35,10 +36,11 @@ namespace datalog { ast_manager& m; relation_manager m_rmanager; expr_ref m_answer; - volatile bool m_cancel; relation_base * m_last_result_relation; decl_set m_output_preds; fact_vector m_table_facts; + execution_context m_ectx; + instruction_block m_code; void reset_negated_tables(); @@ -53,15 +55,14 @@ namespace datalog { relation_manager & get_rmanager(); const relation_manager & get_rmanager() const; - ast_manager& get_manager() { return m; } - context& get_context() { return m_context; } + ast_manager& get_manager() const { return m; } + context& get_context() const { return m_context; } relation_base & get_relation(func_decl * pred); relation_base * try_get_relation(func_decl * pred) const; expr_ref get_last_answer() { return m_answer; } bool output_profile() const; - lbool query(expr* q); lbool query(unsigned num_rels, func_decl * const* rels); @@ -70,10 +71,7 @@ namespace datalog { void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred); - void cancel() { m_cancel = true; } - - void cleanup() { m_cancel = false; } - + void set_cancel(bool f); /** \brief Restrict the set of used predicates to \c res. @@ -94,9 +92,14 @@ namespace datalog { */ bool result_contains_fact(relation_fact const& f); + /** \brief add facts to relation + */ void add_fact(func_decl* pred, relation_fact const& fact); - void add_fact(func_decl* pred, table_fact const& fact); + + /** \brief check if facts were added to relation + */ + bool has_facts(func_decl * pred) const; /** \brief Store the relation \c rel under the predicate \c pred. The \c context object @@ -107,6 +110,8 @@ namespace datalog { void display_output_facts(std::ostream & out) const; void display_facts(std::ostream & out) const; + void display_profile(std::ostream& out) const; + lbool saturate(); }; diff --git a/src/muz_qe/tab_context.cpp b/src/muz_qe/tab_context.cpp index 72727bea8..9ee059f44 100644 --- a/src/muz_qe/tab_context.cpp +++ b/src/muz_qe/tab_context.cpp @@ -317,7 +317,7 @@ namespace tb { for (unsigned i = utsz; i < tsz; ++i) { fmls.push_back(r->get_tail(i)); } - m_num_vars = 1 + r.get_manager().get_var_counter().get_max_var(*r); + m_num_vars = 1 + r.get_manager().get_counter().get_max_rule_var(*r); m_head = r->get_head(); m_predicates.reset(); for (unsigned i = 0; i < utsz; ++i) { @@ -1644,9 +1644,7 @@ namespace datalog { void resolve_rule(replace_proof_converter& pc, tb::clause const& r1, tb::clause const& r2, expr_ref_vector const& s1, expr_ref_vector const& s2, tb::clause const& res) const { unsigned idx = r1.get_predicate_index(); - expr_ref fml1 = r1.to_formula(); - expr_ref fml2 = r2.to_formula(); - expr_ref fml3 = res.to_formula(); + expr_ref fml = res.to_formula(); vector substs; svector > positions; substs.push_back(s1); @@ -1654,13 +1652,12 @@ namespace datalog { scoped_proof _sc(m); proof_ref pr(m); proof_ref_vector premises(m); - premises.push_back(m.mk_asserted(fml1)); - premises.push_back(m.mk_asserted(fml2)); + premises.push_back(m.mk_asserted(r1.to_formula())); + premises.push_back(m.mk_asserted(r2.to_formula())); positions.push_back(std::make_pair(idx+1, 0)); - pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml3, positions, substs); + pr = m.mk_hyper_resolve(2, premises.c_ptr(), fml, positions, substs); pc.insert(pr); - } - + } }; tab::tab(context& ctx): diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 399fb2c61..3e9b60260 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1745,6 +1745,12 @@ namespace sat { mark_lit(m_lemma[i]); } + literal l0 = m_lemma[0]; + // l0 is the FUIP, and we never remove the FUIP. + // + // In the following loop, we use unmark_lit(l) to remove a + // literal from m_lemma. + for (unsigned i = 0; i < sz; i++) { literal l = m_lemma[i]; if (!is_marked_lit(l)) @@ -1754,9 +1760,15 @@ namespace sat { watch_list::const_iterator it = wlist.begin(); watch_list::const_iterator end = wlist.end(); for (; it != end; ++it) { + // In this for-loop, the conditions l0 != ~l2 and l0 != ~l3 + // are not really needed if the solver does not miss unit propagations. + // However, we add them anyway because we don't want to rely on this + // property of the propagator. + // For example, if this property is relaxed in the future, then the code + // without the conditions l0 != ~l2 and l0 != ~l3 may remove the FUIP if (it->is_binary_clause()) { literal l2 = it->get_literal(); - if (is_marked_lit(~l2)) { + if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } @@ -1764,11 +1776,11 @@ namespace sat { else if (it->is_ternary_clause()) { literal l2 = it->get_literal1(); literal l3 = it->get_literal2(); - if (is_marked_lit(l2) && is_marked_lit(~l3)) { + if (is_marked_lit(l2) && is_marked_lit(~l3) && l0 != ~l3) { // eliminate ~l3 from lemma because we have the clause l \/ l2 \/ l3 unmark_lit(~l3); } - else if (is_marked_lit(~l2) && is_marked_lit(l3)) { + else if (is_marked_lit(~l2) && is_marked_lit(l3) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 \/ l3 unmark_lit(~l2); } @@ -1786,7 +1798,41 @@ namespace sat { literal_vector::iterator end = implied_lits->end(); for (; it != end; ++it) { literal l2 = *it; - if (is_marked_lit(~l2)) { + // Here, we must check l0 != ~l2. + // l \/ l2 is an implied binary clause. + // However, it may have been deduced using a lemma that has been deleted. + // For example, consider the following sequence of events: + // + // 1. Initial clause database: + // + // l \/ ~p1 + // p1 \/ ~p2 + // p2 \/ ~p3 + // p3 \/ ~p4 + // q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // ~q1 \/ q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // ~q1 \/ ~q2 \/ p1 \/ p2 \/ p3 \/ p4 \/ l2 + // ... + // + // 2. Now suppose we learned the lemma + // + // p1 \/ p2 \/ p3 \/ p4 \/ l2 (*) + // + // 3. Probing is executed and we notice hat (~l => l2) when we assign l to false. + // That is, l \/ l2 is an implied clause. Note that probing does not add + // this clause to the clause database (there are too many). + // + // 4. Lemma (*) is deleted (garbage collected). + // + // 5. l is decided to be false, p1, p2, p3 and p4 are propagated using BCP, + // but l2 is not since the lemma (*) was deleted. + // + // Probing module still "knows" that l \/ l2 is valid binary clause + // + // 6. A new lemma is created where ~l2 is the FUIP and the lemma also contains l. + // If we remove l0 != ~l2 may try to delete the FUIP. + if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index 44a9d9b66..fe2699504 100644 --- a/src/shell/datalog_frontend.cpp +++ b/src/shell/datalog_frontend.cpp @@ -200,9 +200,7 @@ unsigned read_datalog(char const * file) { timeout = UINT_MAX; } do { - model_converter_ref mc; // ignored - proof_converter_ref pc; // ignored - ctx.transform_rules(mc, pc); + ctx.transform_rules(); datalog::compiler::compile(ctx, ctx.get_rules(), rules_code, termination_code); @@ -211,7 +209,6 @@ unsigned read_datalog(char const * file) { rules_code.make_annotations(ex_ctx); ex_ctx.set_timelimit(timeout); - SASSERT(!ex_ctx.should_terminate()); early_termination = !rules_code.perform(ex_ctx); if(early_termination) { diff --git a/src/shell/resource.h b/src/shell/resource.h deleted file mode 100644 index dde3ba323..000000000 --- a/src/shell/resource.h +++ /dev/null @@ -1,14 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by shell.rc - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 101 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index 3155d9c58..c5d3c08cf 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -653,7 +653,7 @@ void asserted_formulas::propagate_values() { // will be (silently) eliminated, and models produced by Z3 will not contain them. flush_cache(); } - TRACE("propagate_values", tout << "afer:\n"; display(tout);); + TRACE("propagate_values", tout << "after:\n"; display(tout);); } void asserted_formulas::propagate_booleans() { diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 43dd1b586..21b5c6281 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -29,6 +29,7 @@ def_module_params(module_name='smt', ('qi.cost', STRING, '(+ weight generation)', 'expression specifying what is the cost of a given quantifier instantiation'), ('qi.max_multi_patterns', UINT, 0, 'specify the number of extra multi patterns'), ('bv.reflect', BOOL, True, 'create enode for every bit-vector term'), + ('bv.enable_int2bv', BOOL, False, 'enable support for int2bv and bv2int operators'), ('arith.random_initial_value', BOOL, False, 'use random initial values in the simplex-based procedure for linear arithmetic'), ('arith.solver', UINT, 2, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination'), ('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation'), diff --git a/src/smt/params/theory_bv_params.cpp b/src/smt/params/theory_bv_params.cpp index c2a31c59d..d3f386ab4 100644 --- a/src/smt/params/theory_bv_params.cpp +++ b/src/smt/params/theory_bv_params.cpp @@ -22,4 +22,5 @@ Revision History: void theory_bv_params::updt_params(params_ref const & _p) { smt_params_helper p(_p); m_bv_reflect = p.bv_reflect(); + m_bv_enable_int2bv2int = p.bv_enable_int2bv(); } diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index e69b7a1b6..f3d0ca3bb 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -1535,8 +1535,23 @@ namespace smt { n1->insert_exception(m_t); } - virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { - // do nothing... + virtual void populate_inst_sets(quantifier * q, auf_solver & slv, context * ctx) { + unsigned num_vars = q->get_num_decls(); + ast_manager & m = ctx->get_manager(); + sort * s = q->get_decl_sort(num_vars - m_var_i - 1); + if (m.is_uninterp(s)) { + // For uninterpreted sorst, we add all terms in the context. + // See Section 4.1 in the paper "Complete Quantifier Instantiation" + node * S_q_i = slv.get_uvar(q, m_var_i); + ptr_vector::const_iterator it = ctx->begin_enodes(); + ptr_vector::const_iterator end = ctx->end_enodes(); + for (; it != end; ++it) { + enode * n = *it; + if (ctx->is_relevant(n) && get_sort(n->get_owner()) == s) { + S_q_i->insert(n->get_owner(), n->get_generation()); + } + } + } } }; @@ -1924,7 +1939,8 @@ namespace smt { m_mutil.mk_add(t1, t2, r); } - bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t) const { + bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t, bool & inv) const { + inv = false; // true if invert the sign TRACE("is_var_and_ground", tout << "is_var_and_ground: " << mk_ismt2_pp(lhs, m_manager) << " " << mk_ismt2_pp(rhs, m_manager) << "\n";); if (is_var(lhs) && is_ground(rhs)) { v = to_var(lhs); @@ -1939,7 +1955,6 @@ namespace smt { return true; } else { - bool inv = false; // true if invert the sign expr_ref tmp(m_manager); if (is_var_plus_ground(lhs, inv, v, tmp) && is_ground(rhs)) { if (inv) @@ -1959,6 +1974,11 @@ namespace smt { return false; } + bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t) const { + bool inv; + return is_var_and_ground(lhs, rhs, v, t, inv); + } + bool is_x_eq_t_atom(expr * n, var * & v, expr_ref & t) const { if (!is_app(n)) return false; @@ -2011,22 +2031,28 @@ namespace smt { if (sign) { bool r = is_le_ge(atom) && is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, t); CTRACE("is_x_gle_t", r, tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m_manager) << "\n--->\n" - << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n";); + << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n"; + tout << "sign: " << sign << "\n";); return r; } else { if (is_le_ge(atom)) { expr_ref tmp(m_manager); - if (is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, tmp)) { + bool le = is_le(atom); + bool inv = false; + if (is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, tmp, inv)) { + if (inv) + le = !le; sort * s = m_manager.get_sort(tmp); expr_ref one(m_manager); one = mk_one(s); - if (is_le(atom)) + if (le) mk_add(tmp, one, t); else mk_sub(tmp, one, t); TRACE("is_x_gle_t", tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m_manager) << "\n--->\n" - << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n";); + << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n"; + tout << "sign: " << sign << "\n";); return true; } } diff --git a/src/smt/tactic/ctx_solver_simplify_tactic.cpp b/src/smt/tactic/ctx_solver_simplify_tactic.cpp index 25c41c2a2..5668ca455 100644 --- a/src/smt/tactic/ctx_solver_simplify_tactic.cpp +++ b/src/smt/tactic/ctx_solver_simplify_tactic.cpp @@ -33,10 +33,14 @@ class ctx_solver_simplify_tactic : public tactic { arith_util m_arith; mk_simplified_app m_mk_app; func_decl_ref m_fn; + obj_map m_fns; unsigned m_num_steps; + volatile bool m_cancel; public: ctx_solver_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()): - m(m), m_params(p), m_solver(m, m_front_p), m_arith(m), m_mk_app(m), m_fn(m), m_num_steps(0) { + m(m), m_params(p), m_solver(m, m_front_p), + m_arith(m), m_mk_app(m), m_fn(m), m_num_steps(0), + m_cancel(false) { sort* i_sort = m_arith.mk_int(); m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort()); } @@ -45,7 +49,13 @@ public: return alloc(ctx_solver_simplify_tactic, m, m_params); } - virtual ~ctx_solver_simplify_tactic() {} + virtual ~ctx_solver_simplify_tactic() { + obj_map::iterator it = m_fns.begin(), end = m_fns.end(); + for (; it != end; ++it) { + m.dec_ref(it->m_value); + } + m_fns.reset(); + } virtual void updt_params(params_ref const & p) { m_solver.updt_params(p); @@ -76,15 +86,18 @@ public: virtual void cleanup() { reset_statistics(); m_solver.reset(); + m_cancel = false; } + protected: + virtual void set_cancel(bool f) { m_solver.set_cancel(f); + m_cancel = false; } void reduce(goal& g) { SASSERT(g.is_well_sorted()); - m_num_steps = 0; expr_ref fml(m); tactic_report report("ctx-solver-simplify", g); if (g.inconsistent()) @@ -92,7 +105,34 @@ protected: ptr_vector fmls; g.get_formulas(fmls); fml = m.mk_and(fmls.size(), fmls.c_ptr()); + m_solver.push(); reduce(fml); + m_solver.pop(1); + SASSERT(m_solver.get_scope_level() == 0); + TRACE("ctx_solver_simplify_tactic", + for (unsigned i = 0; i < fmls.size(); ++i) { + tout << mk_pp(fmls[i], m) << "\n"; + } + tout << "=>\n"; + tout << mk_pp(fml, m) << "\n";); + DEBUG_CODE( + { + m_solver.push(); + expr_ref fml1(m); + fml1 = m.mk_and(fmls.size(), fmls.c_ptr()); + fml1 = m.mk_iff(fml, fml1); + fml1 = m.mk_not(fml1); + m_solver.assert_expr(fml1); + lbool is_sat = m_solver.check(); + TRACE("ctx_solver_simplify_tactic", tout << "is non-equivalence sat?: " << is_sat << "\n";); + if (is_sat != l_false) { + TRACE("ctx_solver_simplify_tactic", + tout << "result is not equivalent to input\n"; + tout << mk_pp(fml1, m) << "\n";); + UNREACHABLE(); + } + m_solver.pop(1); + }); g.reset(); g.assert_expr(fml, 0, 0); IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(ctx-solver-simplify :num-steps " << m_num_steps << ")\n";); @@ -106,21 +146,23 @@ protected: svector is_checked; svector parent_ids, self_ids; expr_ref_vector fresh_vars(m), trail(m); - expr_ref res(m); - obj_map > cache; + expr_ref res(m), tmp(m); + obj_map > cache; unsigned id = 1; - expr* n = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); - expr* n2, *fml; + expr_ref n2(m), fml(m); unsigned path_id = 0, self_pos = 0; app * a; unsigned sz; std::pair path_r; ptr_vector found; + expr_ref_vector args(m); + expr_ref n = mk_fresh(id, m.mk_bool_sort()); + trail.push_back(n); fml = result.get(); - m_solver.assert_expr(m.mk_not(m.mk_iff(fml, n))); + tmp = m.mk_not(m.mk_iff(fml, n)); + m_solver.assert_expr(tmp); - trail.push_back(n); todo.push_back(fml); names.push_back(n); is_checked.push_back(false); @@ -128,9 +170,9 @@ protected: self_ids.push_back(0); m_solver.push(); - while (!todo.empty()) { + while (!todo.empty() && !m_cancel) { expr_ref res(m); - ptr_buffer args; + args.reset(); expr* e = todo.back(); unsigned pos = parent_ids.back(); n = names.back(); @@ -139,11 +181,8 @@ protected: if (cache.contains(e)) { goto done; } - if (!m.is_bool(e)) { - res = e; - goto done; - } if (m.is_bool(e) && !checked && simplify_bool(n, res)) { + TRACE("ctx_solver_simplify_tactic", tout << "simplified: " << mk_pp(e, m) << " |-> " << mk_pp(res, m) << "\n";); goto done; } if (!is_app(e)) { @@ -164,35 +203,35 @@ protected: found.reset(); // arguments already simplified. for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); - if (!m.is_bool(arg)) { - args.push_back(arg); - } - else if (cache.find(arg, path_r) && !found.contains(arg)) { + if (cache.find(arg, path_r) && !found.contains(arg)) { // // This is a single traversal version of the context // simplifier. It simplifies only the first occurrence of - // a formula with respect to the context. + // a sub-term with respect to the context. // found.push_back(arg); if (path_r.first == self_pos) { - TRACE("ctx_solver_simplify_tactic", tout << "cached " << mk_pp(arg, m) << "\n";); + TRACE("ctx_solver_simplify_tactic", tout << "cached " << mk_pp(arg, m) << " |-> " << mk_pp(path_r.second, m) << "\n";); args.push_back(path_r.second); } - else { + else if (m.is_bool(arg)) { res = local_simplify(a, n, id, i); TRACE("ctx_solver_simplify_tactic", tout << "Already cached: " << path_r.first << " " << mk_pp(res, m) << "\n";); + args.push_back(res); + } + else { args.push_back(arg); } } else if (!n2 && !found.contains(arg)) { - n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); + n2 = mk_fresh(id, m.get_sort(arg)); + trail.push_back(n2); todo.push_back(arg); parent_ids.push_back(self_pos); self_ids.push_back(0); names.push_back(n2); - trail.push_back(n2); args.push_back(n2); is_checked.push_back(false); } @@ -205,7 +244,8 @@ protected: // child needs to be visited. if (n2) { m_solver.push(); - m_solver.assert_expr(m.mk_eq(res, n)); + tmp = m.mk_eq(res, n); + m_solver.assert_expr(tmp); continue; } @@ -224,12 +264,14 @@ protected: is_checked.pop_back(); m_solver.pop(1); } - VERIFY(cache.find(fml, path_r)); - result = path_r.second; + if (!m_cancel) { + VERIFY(cache.find(fml, path_r)); + result = path_r.second; + } } bool simplify_bool(expr* n, expr_ref& res) { - + expr_ref tmp(m); m_solver.push(); m_solver.assert_expr(n); lbool is_sat = m_solver.check(); @@ -240,7 +282,8 @@ protected: } m_solver.push(); - m_solver.assert_expr(m.mk_not(n)); + tmp = m.mk_not(n); + m_solver.assert_expr(tmp); is_sat = m_solver.check(); m_solver.pop(1); if (is_sat == l_false) { @@ -251,11 +294,25 @@ protected: return false; } + expr_ref mk_fresh(unsigned& id, sort* s) { + func_decl* fn; + if (m.is_bool(s)) { + fn = m_fn; + } + else if (!m_fns.find(s, fn)) { + fn = m.mk_func_decl(symbol(0xbeef101 + id), m_arith.mk_int(), s); + m.inc_ref(fn); + m_fns.insert(s, fn); + } + return expr_ref(m.mk_app(fn, m_arith.mk_numeral(rational(id++), true)), m); + } + + expr_ref local_simplify(app* a, expr* n, unsigned& id, unsigned index) { SASSERT(index < a->get_num_args()); SASSERT(m.is_bool(a->get_arg(index))); - expr_ref n2(m), result(m); - n2 = m.mk_app(m_fn, m_arith.mk_numeral(rational(id++), true)); + expr_ref n2(m), result(m), tmp(m); + n2 = mk_fresh(id, m.get_sort(a->get_arg(index))); ptr_buffer args; for (unsigned i = 0; i < a->get_num_args(); ++i) { if (i == index) { @@ -267,7 +324,8 @@ protected: } m_mk_app(a->get_decl(), args.size(), args.c_ptr(), result); m_solver.push(); - m_solver.assert_expr(m.mk_eq(result, n)); + tmp = m.mk_eq(result, n); + m_solver.assert_expr(tmp); if (!simplify_bool(n2, result)) { result = a; } diff --git a/src/tactic/arith/probe_arith.cpp b/src/tactic/arith/probe_arith.cpp index dcb64e6d3..45fc868ac 100644 --- a/src/tactic/arith/probe_arith.cpp +++ b/src/tactic/arith/probe_arith.cpp @@ -313,23 +313,28 @@ struct is_non_nira_functor { bool m_int; bool m_real; bool m_quant; + bool m_linear; - is_non_nira_functor(ast_manager & _m, bool _int, bool _real, bool _quant):m(_m), u(m), m_int(_int), m_real(_real), m_quant(_quant) {} + is_non_nira_functor(ast_manager & _m, bool _int, bool _real, bool _quant, bool linear):m(_m), u(m), m_int(_int), m_real(_real), m_quant(_quant), m_linear(linear) {} + + void throw_found() { + throw found(); + } void operator()(var * x) { if (!m_quant) - throw found(); + throw_found(); sort * s = x->get_sort(); if (m_int && u.is_int(s)) return; if (m_real && u.is_real(s)) return; - throw found(); + throw_found(); } void operator()(quantifier *) { if (!m_quant) - throw found(); + throw_found(); } bool compatible_sort(app * n) const { @@ -344,35 +349,92 @@ struct is_non_nira_functor { void operator()(app * n) { if (!compatible_sort(n)) - throw found(); + throw_found(); family_id fid = n->get_family_id(); if (fid == m.get_basic_family_id()) return; - if (fid == u.get_family_id()) + if (fid == u.get_family_id()) { + switch (n->get_decl_kind()) { + case OP_LE: case OP_GE: case OP_LT: case OP_GT: + case OP_ADD: case OP_UMINUS: case OP_SUB: case OP_ABS: + case OP_NUM: + return; + case OP_MUL: + if (m_linear) { + if (n->get_num_args() != 2) + throw_found(); + if (!u.is_numeral(n->get_arg(0))) + throw_found(); + } + return; + case OP_IDIV: case OP_DIV: case OP_REM: case OP_MOD: + if (m_linear && !u.is_numeral(n->get_arg(1))) + throw_found(); + return; + case OP_IS_INT: + if (m_real) + throw_found(); + return; + case OP_TO_INT: + case OP_TO_REAL: + return; + case OP_POWER: + if (m_linear) + throw_found(); + return; + case OP_IRRATIONAL_ALGEBRAIC_NUM: + if (m_linear || !m_real) + throw_found(); + return; + default: + throw_found(); + } return; + } + if (is_uninterp_const(n)) return; - throw found(); + throw_found(); } }; static bool is_qfnia(goal const & g) { - is_non_nira_functor p(g.m(), true, false, false); + is_non_nira_functor p(g.m(), true, false, false, false); return !test(g, p); } static bool is_qfnra(goal const & g) { - is_non_nira_functor p(g.m(), false, true, false); + is_non_nira_functor p(g.m(), false, true, false, false); return !test(g, p); } static bool is_nia(goal const & g) { - is_non_nira_functor p(g.m(), true, false, true); + is_non_nira_functor p(g.m(), true, false, true, false); return !test(g, p); } static bool is_nra(goal const & g) { - is_non_nira_functor p(g.m(), false, true, true); + is_non_nira_functor p(g.m(), false, true, true, false); + return !test(g, p); +} + +static bool is_nira(goal const & g) { + is_non_nira_functor p(g.m(), true, true, true, false); + return !test(g, p); +} + +static bool is_lra(goal const & g) { + is_non_nira_functor p(g.m(), false, true, true, true); + return !test(g, p); +} + +static bool is_lia(goal const & g) { + is_non_nira_functor p(g.m(), true, false, true, true); + return !test(g, p); +} + +static bool is_lira(goal const & g) { + is_non_nira_functor p(g.m(), true, true, true, true); return !test(g, p); } @@ -404,6 +466,34 @@ public: } }; +class is_nira_probe : public probe { +public: + virtual result operator()(goal const & g) { + return is_nira(g); + } +}; + +class is_lia_probe : public probe { +public: + virtual result operator()(goal const & g) { + return is_lia(g); + } +}; + +class is_lra_probe : public probe { +public: + virtual result operator()(goal const & g) { + return is_lra(g); + } +}; + +class is_lira_probe : public probe { +public: + virtual result operator()(goal const & g) { + return is_lira(g); + } +}; + probe * mk_is_qfnia_probe() { return alloc(is_qfnia_probe); } @@ -419,3 +509,19 @@ probe * mk_is_nia_probe() { probe * mk_is_nra_probe() { return alloc(is_nra_probe); } + +probe * mk_is_nira_probe() { + return alloc(is_nira_probe); +} + +probe * mk_is_lia_probe() { + return alloc(is_lia_probe); +} + +probe * mk_is_lra_probe() { + return alloc(is_lra_probe); +} + +probe * mk_is_lira_probe() { + return alloc(is_lira_probe); +} diff --git a/src/tactic/arith/probe_arith.h b/src/tactic/arith/probe_arith.h index 829bcfab8..17e9efb28 100644 --- a/src/tactic/arith/probe_arith.h +++ b/src/tactic/arith/probe_arith.h @@ -49,11 +49,19 @@ probe * mk_is_qfnia_probe(); probe * mk_is_qfnra_probe(); probe * mk_is_nia_probe(); probe * mk_is_nra_probe(); +probe * mk_is_nira_probe(); +probe * mk_is_lia_probe(); +probe * mk_is_lra_probe(); +probe * mk_is_lira_probe(); /* ADD_PROBE("is-qfnia", "true if the goal is in QF_NIA (quantifier-free nonlinear integer arithmetic).", "mk_is_qfnia_probe()") ADD_PROBE("is-qfnra", "true if the goal is in QF_NRA (quantifier-free nonlinear real arithmetic).", "mk_is_qfnra_probe()") ADD_PROBE("is-nia", "true if the goal is in NIA (nonlinear integer arithmetic, formula may have quantifiers).", "mk_is_nia_probe()") ADD_PROBE("is-nra", "true if the goal is in NRA (nonlinear real arithmetic, formula may have quantifiers).", "mk_is_nra_probe()") + ADD_PROBE("is-nira", "true if the goal is in NIRA (nonlinear integer and real arithmetic, formula may have quantifiers).", "mk_is_nira_probe()") + ADD_PROBE("is-lia", "true if the goal is in LIA (linear integer arithmetic, formula may have quantifiers).", "mk_is_lia_probe()") + ADD_PROBE("is-lra", "true if the goal is in LRA (linear real arithmetic, formula may have quantifiers).", "mk_is_lra_probe()") + ADD_PROBE("is-lira", "true if the goal is in LIRA (linear integer and real arithmetic, formula may have quantifiers).", "mk_is_lira_probe()") */ #endif diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index 169e27927..f2ddd86ce 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -29,6 +29,7 @@ Revision History: #include"th_rewriter.h" #include"filter_model_converter.h" #include"ast_smt2_pp.h" +#include"expr_replacer.h" /* ---- @@ -131,18 +132,16 @@ struct purify_arith_proc { proof_ref_vector m_new_cnstr_prs; expr_ref m_subst; proof_ref m_subst_pr; - bool m_in_q; - unsigned m_var_idx; + expr_ref_vector m_new_vars; - rw_cfg(purify_arith_proc & o, bool in_q): + rw_cfg(purify_arith_proc & o): m_owner(o), m_pinned(o.m()), m_new_cnstrs(o.m()), m_new_cnstr_prs(o.m()), m_subst(o.m()), m_subst_pr(o.m()), - m_in_q(in_q), - m_var_idx(0) { + m_new_vars(o.m()) { } ast_manager & m() { return m_owner.m(); } @@ -155,14 +154,9 @@ struct purify_arith_proc { bool elim_inverses() const { return m_owner.m_elim_inverses; } expr * mk_fresh_var(bool is_int) { - if (m_in_q) { - unsigned idx = m_var_idx; - m_var_idx++; - return m().mk_var(idx, is_int ? u().mk_int() : u().mk_real()); - } - else { - return m().mk_fresh_const(0, is_int ? u().mk_int() : u().mk_real()); - } + expr * r = m().mk_fresh_const(0, is_int ? u().mk_int() : u().mk_real()); + m_new_vars.push_back(r); + return r; } expr * mk_fresh_real_var() { return mk_fresh_var(false); } @@ -596,105 +590,51 @@ struct purify_arith_proc { struct rw : public rewriter_tpl { rw_cfg m_cfg; - rw(purify_arith_proc & o, bool in_q): + rw(purify_arith_proc & o): rewriter_tpl(o.m(), o.m_produce_proofs, m_cfg), - m_cfg(o, in_q) { - } - }; - - /** - \brief Return the number of (auxiliary) variables needed for converting an expression. - */ - struct num_vars_proc { - arith_util & m_util; - expr_fast_mark1 m_visited; - ptr_vector m_todo; - unsigned m_num_vars; - bool m_elim_root_objs; - - num_vars_proc(arith_util & u, bool elim_root_objs): - m_util(u), - m_elim_root_objs(elim_root_objs) { - } - - void visit(expr * t) { - if (m_visited.is_marked(t)) - return; - m_visited.mark(t); - m_todo.push_back(t); - } - - void process(app * t) { - if (t->get_family_id() == m_util.get_family_id()) { - if (m_util.is_power(t)) { - rational k; - if (m_util.is_numeral(t->get_arg(1), k) && (k.is_zero() || !k.is_int())) { - m_num_vars++; - } - } - else if (m_util.is_div(t) || - m_util.is_idiv(t) || - m_util.is_mod(t) || - m_util.is_to_int(t) || - (m_util.is_irrational_algebraic_numeral(t) && m_elim_root_objs)) { - m_num_vars++; - } - } - unsigned num_args = t->get_num_args(); - for (unsigned i = 0; i < num_args; i++) - visit(t->get_arg(i)); - } - - unsigned operator()(expr * t) { - m_num_vars = 0; - visit(t); - while (!m_todo.empty()) { - expr * t = m_todo.back(); - m_todo.pop_back(); - if (is_app(t)) - process(to_app(t)); - } - m_visited.reset(); - return m_num_vars; + m_cfg(o) { } }; void process_quantifier(quantifier * q, expr_ref & result, proof_ref & result_pr) { result_pr = 0; - num_vars_proc p(u(), m_elim_root_objs); - expr_ref body(m()); - unsigned num_vars = p(q->get_expr()); - if (num_vars > 0) { - // open space for aux vars - var_shifter shifter(m()); - shifter(q->get_expr(), num_vars, body); - } - else { - body = q->get_expr(); - } - - rw r(*this, true); + rw r(*this); expr_ref new_body(m()); proof_ref new_body_pr(m()); - r(body, new_body, new_body_pr); + r(q->get_expr(), new_body, new_body_pr); + unsigned num_vars = r.cfg().m_new_vars.size(); TRACE("purify_arith", tout << "num_vars: " << num_vars << "\n"; - tout << "body: " << mk_ismt2_pp(body, m()) << "\nnew_body: " << mk_ismt2_pp(new_body, m()) << "\n";); + tout << "body: " << mk_ismt2_pp(q->get_expr(), m()) << "\nnew_body: " << mk_ismt2_pp(new_body, m()) << "\n";); if (num_vars == 0) { + SASSERT(r.cfg().m_new_cnstrs.empty()); result = m().update_quantifier(q, new_body); if (m_produce_proofs) result_pr = m().mk_quant_intro(q, to_quantifier(result.get()), result_pr); } else { + // Add new constraints expr_ref_vector & cnstrs = r.cfg().m_new_cnstrs; cnstrs.push_back(new_body); new_body = m().mk_and(cnstrs.size(), cnstrs.c_ptr()); + // Open space for new variables + var_shifter shifter(m()); + shifter(new_body, num_vars, new_body); + // Rename fresh constants in r.cfg().m_new_vars to variables ptr_buffer sorts; buffer names; + expr_substitution subst(m(), false, false); for (unsigned i = 0; i < num_vars; i++) { - sorts.push_back(u().mk_real()); + expr * c = r.cfg().m_new_vars.get(i); + sort * s = get_sort(c); + sorts.push_back(s); names.push_back(m().mk_fresh_var_name("x")); + unsigned idx = num_vars - i - 1; + subst.insert(c, m().mk_var(idx, s)); } + scoped_ptr replacer = mk_default_expr_replacer(m()); + replacer->set_substitution(&subst); + (*replacer)(new_body, new_body); new_body = m().mk_exists(num_vars, sorts.c_ptr(), names.c_ptr(), new_body); result = m().update_quantifier(q, new_body); if (m_produce_proofs) { @@ -708,7 +648,7 @@ struct purify_arith_proc { } void operator()(goal & g, model_converter_ref & mc, bool produce_models) { - rw r(*this, false); + rw r(*this); // purify expr_ref new_curr(m()); proof_ref new_pr(m()); diff --git a/src/tactic/bv/max_bv_sharing_tactic.cpp b/src/tactic/bv/max_bv_sharing_tactic.cpp index 251e2b754..f60487d60 100644 --- a/src/tactic/bv/max_bv_sharing_tactic.cpp +++ b/src/tactic/bv/max_bv_sharing_tactic.cpp @@ -269,7 +269,7 @@ class max_bv_sharing_tactic : public tactic { m_rw.cfg().cleanup(); g->inc_depth(); result.push_back(g.get()); - TRACE("qe", g->display(tout);); + TRACE("max_bv_sharing", g->display(tout);); SASSERT(g->is_well_sorted()); } }; diff --git a/src/tactic/core/propagate_values_tactic.cpp b/src/tactic/core/propagate_values_tactic.cpp index 6d9e6ccbd..1e358177f 100644 --- a/src/tactic/core/propagate_values_tactic.cpp +++ b/src/tactic/core/propagate_values_tactic.cpp @@ -165,7 +165,7 @@ class propagate_values_tactic : public tactic { m_occs(*m_goal); while (true) { - TRACE("propagate_values", m_goal->display(tout);); + TRACE("propagate_values", tout << "while(true) loop\n"; m_goal->display(tout);); if (forward) { for (; m_idx < size; m_idx++) { process_current(); @@ -201,14 +201,14 @@ class propagate_values_tactic : public tactic { if (round >= m_max_rounds) break; IF_VERBOSE(100, verbose_stream() << "starting new round, goal size: " << m_goal->num_exprs() << std::endl;); - TRACE("propgate_values", tout << "round finished\n"; m_goal->display(tout); tout << "\n";); + TRACE("propagate_values", tout << "round finished\n"; m_goal->display(tout); tout << "\n";); } end: m_goal->elim_redundancies(); m_goal->inc_depth(); result.push_back(m_goal); SASSERT(m_goal->is_well_sorted()); - TRACE("propagate_values", m_goal->display(tout);); + TRACE("propagate_values", tout << "end\n"; m_goal->display(tout);); TRACE("propagate_values_core", m_goal->display_with_dependencies(tout);); m_goal = 0; } diff --git a/src/tactic/fpa/fpa2bv_converter.cpp b/src/tactic/fpa/fpa2bv_converter.cpp index 4afdc501f..84a1efaff 100644 --- a/src/tactic/fpa/fpa2bv_converter.cpp +++ b/src/tactic/fpa/fpa2bv_converter.cpp @@ -21,15 +21,15 @@ Notes: #include"fpa2bv_converter.h" -#define BVULT(X,Y,R) { expr_ref bvult_eq(m), bvult_not(m); m_simp.mk_eq(X, Y, bvult_eq); m_simp.mk_not(bvult_eq, bvult_not); m_simp.mk_and(m_bv_util.mk_ule(X,Y), bvult_not, R); } -#define BVSLT(X,Y,R) { expr_ref bvslt_eq(m), bvslt_not(m); m_simp.mk_eq(X, Y, bvslt_eq); m_simp.mk_not(bvslt_eq, bvslt_not); m_simp.mk_and(m_bv_util.mk_sle(X,Y), bvslt_not, R); } +#define BVULT(X,Y,R) { expr_ref bvult_eq(m), bvult_not(m); m_simp.mk_eq(X, Y, bvult_eq); m_simp.mk_not(bvult_eq, bvult_not); expr_ref t(m); t = m_bv_util.mk_ule(X,Y); m_simp.mk_and(t, bvult_not, R); } +#define BVSLT(X,Y,R) { expr_ref bvslt_eq(m), bvslt_not(m); m_simp.mk_eq(X, Y, bvslt_eq); m_simp.mk_not(bvslt_eq, bvslt_not); expr_ref t(m); t = m_bv_util.mk_sle(X,Y); m_simp.mk_and(t, bvslt_not, R); } fpa2bv_converter::fpa2bv_converter(ast_manager & m) : m(m), m_simp(m), m_util(m), - m_mpf_manager(m_util.fm()), - m_mpz_manager(m_mpf_manager.mpz_manager()), + m_mpf_manager(m_util.fm()), + m_mpz_manager(m_mpf_manager.mpz_manager()), m_bv_util(m), extra_assertions(m) { m_plugin = static_cast(m.get_plugin(m.mk_family_id("float"))); @@ -37,7 +37,7 @@ fpa2bv_converter::fpa2bv_converter(ast_manager & m) : fpa2bv_converter::~fpa2bv_converter() { dec_ref_map_key_values(m, m_const2bv); - dec_ref_map_key_values(m, m_rm_const2bv); + dec_ref_map_key_values(m, m_rm_const2bv); } void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { @@ -123,17 +123,23 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { unsigned ebits = m_util.get_ebits(srt); unsigned sbits = m_util.get_sbits(srt); + expr_ref sgn(m), s(m), e(m); + sort_ref s_sgn(m), s_sig(m), s_exp(m); + s_sgn = m_bv_util.mk_sort(1); + s_sig = m_bv_util.mk_sort(sbits-1); + s_exp = m_bv_util.mk_sort(ebits); + #ifdef _DEBUG std::string p("fpa2bv"); std::string name = f->get_name().str(); - expr * sgn = m.mk_fresh_const((p + "_sgn_" + name).c_str(), m_bv_util.mk_sort(1)); - expr * s = m.mk_fresh_const((p + "_sig_" + name).c_str(), m_bv_util.mk_sort(sbits-1)); - expr * e = m.mk_fresh_const((p + "_exp_" + name).c_str(), m_bv_util.mk_sort(ebits)); + sgn = m.mk_fresh_const((p + "_sgn_" + name).c_str(), s_sgn); + s = m.mk_fresh_const((p + "_sig_" + name).c_str(), s_sig); + e = m.mk_fresh_const((p + "_exp_" + name).c_str(), s_exp); #else - expr * sgn = m.mk_fresh_const(0, m_bv_util.mk_sort(1)); - expr * s = m.mk_fresh_const(0, m_bv_util.mk_sort(sbits-1)); - expr * e = m.mk_fresh_const(0, m_bv_util.mk_sort(ebits)); + sgn = m.mk_fresh_const(0, s_sgn); + s = m.mk_fresh_const(0, s_sig); + e = m.mk_fresh_const(0, s_exp); #endif mk_triple(sgn, s, e, result); @@ -153,7 +159,7 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { result = r; } else { - SASSERT(is_rm_sort(f->get_range())); + SASSERT(is_rm_sort(f->get_range())); result = m.mk_fresh_const( #ifdef _DEBUG @@ -245,9 +251,10 @@ void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits, expr_ref & rm, dbg_decouple("fpa2bv_add_exp_delta", exp_delta); // cap the delta - expr_ref cap(m); + expr_ref cap(m), cap_le_delta(m); cap = m_bv_util.mk_numeral(sbits+2, ebits); - m_simp.mk_ite(m_bv_util.mk_ule(cap, exp_delta), cap, exp_delta, exp_delta); + cap_le_delta = m_bv_util.mk_ule(cap, exp_delta); + m_simp.mk_ite(cap_le_delta, cap, exp_delta, exp_delta); dbg_decouple("fpa2bv_add_exp_delta_capped", exp_delta); @@ -270,20 +277,22 @@ void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits, expr_ref & rm, SASSERT(is_well_sorted(m, shifted_d_sig)); sticky_raw = m_bv_util.mk_extract(sbits+2, 0, shifted_big); - expr_ref sticky_eq(m); - m_simp.mk_eq(sticky_raw, m_bv_util.mk_numeral(0, sbits+3), sticky_eq); - m_simp.mk_ite(sticky_eq, - m_bv_util.mk_numeral(0, sbits+3), - m_bv_util.mk_numeral(1, sbits+3), - sticky); + expr_ref sticky_eq(m), nil_sbit3(m), one_sbit3(m); + nil_sbit3 = m_bv_util.mk_numeral(0, sbits+3); + one_sbit3 = m_bv_util.mk_numeral(1, sbits+3); + m_simp.mk_eq(sticky_raw, nil_sbit3, sticky_eq); + m_simp.mk_ite(sticky_eq, nil_sbit3, one_sbit3, sticky); SASSERT(is_well_sorted(m, sticky)); - expr * or_args[2] = { shifted_d_sig.get(), sticky.get() }; + expr * or_args[2] = { shifted_d_sig, sticky }; shifted_d_sig = m_bv_util.mk_bv_or(2, or_args); SASSERT(is_well_sorted(m, shifted_d_sig)); - expr_ref eq_sgn(m), neq_sgn(m); - m_simp.mk_eq(c_sgn, d_sgn, eq_sgn); + expr_ref eq_sgn(m); + m_simp.mk_eq(c_sgn, d_sgn, eq_sgn); + + // dbg_decouple("fpa2bv_add_eq_sgn", eq_sgn); + TRACE("fpa2bv_add_core", tout << "EQ_SGN = " << mk_ismt2_pp(eq_sgn, m) << std::endl; ); // two extra bits for catching the overflow. c_sig = m_bv_util.mk_zero_extend(2, c_sig); @@ -311,19 +320,24 @@ void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits, expr_ref & rm, dbg_decouple("fpa2bv_add_sign_bv", sign_bv); dbg_decouple("fpa2bv_add_n_sum", n_sum); - - family_id bvfid = m_bv_util.get_fid(); + + family_id bvfid = m_bv_util.get_fid(); expr_ref res_sgn_c1(m), res_sgn_c2(m), res_sgn_c3(m); - res_sgn_c1 = m.mk_app(bvfid, OP_BAND, m_bv_util.mk_bv_not(c_sgn), d_sgn, sign_bv); - res_sgn_c2 = m.mk_app(bvfid, OP_BAND, c_sgn, m_bv_util.mk_bv_not(d_sgn), m_bv_util.mk_bv_not(sign_bv)); + expr_ref not_c_sgn(m), not_d_sgn(m), not_sign_bv(m); + not_c_sgn = m_bv_util.mk_bv_not(c_sgn); + not_d_sgn = m_bv_util.mk_bv_not(d_sgn); + not_sign_bv = m_bv_util.mk_bv_not(sign_bv); + res_sgn_c1 = m.mk_app(bvfid, OP_BAND, not_c_sgn, d_sgn, sign_bv); + res_sgn_c2 = m.mk_app(bvfid, OP_BAND, c_sgn, not_d_sgn, not_sign_bv); res_sgn_c3 = m.mk_app(bvfid, OP_BAND, c_sgn, d_sgn); - expr * res_sgn_or_args[3] = { res_sgn_c1.get(), res_sgn_c2.get(), res_sgn_c3.get() }; + expr * res_sgn_or_args[3] = { res_sgn_c1, res_sgn_c2, res_sgn_c3 }; res_sgn = m_bv_util.mk_bv_or(3, res_sgn_or_args); - expr_ref res_sig_eq(m), sig_abs(m); - m_simp.mk_eq(sign_bv, m_bv_util.mk_numeral(1, 1), res_sig_eq); - m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); + expr_ref res_sig_eq(m), sig_abs(m), one_1(m); + one_1 = m_bv_util.mk_numeral(1, 1); + m_simp.mk_eq(sign_bv, one_1, res_sig_eq); + m_simp.mk_ite(res_sig_eq, n_sum, sum, sig_abs); dbg_decouple("fpa2bv_add_sig_abs", sig_abs); @@ -333,9 +347,9 @@ void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits, expr_ref & rm, void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); - - expr_ref rm(m), x(m), y(m); - rm = args[0]; + + expr_ref rm(m), x(m), y(m); + rm = args[0]; x = args[1]; y = args[2]; @@ -388,10 +402,14 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, m_simp.mk_and(x_is_inf, xy_is_neg, v3_and); mk_ite(v3_and, nan, y, v3); - expr_ref rm_is_to_neg(m), v4_and(m); + expr_ref rm_is_to_neg(m), signs_and(m), signs_xor(m), v4_and(m), rm_and_xor(m), neg_cond(m); m_simp.mk_and(x_is_zero, y_is_zero, c4); + m_simp.mk_and(x_is_neg, y_is_neg, signs_and); + m_simp.mk_xor(x_is_neg, y_is_neg, signs_xor); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); - mk_ite(rm_is_to_neg, nzero, pzero, v4); + m_simp.mk_and(rm_is_to_neg, signs_xor, rm_and_xor); + m_simp.mk_or(signs_and, rm_and_xor, neg_cond); + mk_ite(neg_cond, nzero, pzero, v4); m_simp.mk_and(x_is_neg, y_is_neg, v4_and); mk_ite(v4_and, x, v4, v4); @@ -405,9 +423,9 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); - expr_ref a_sgn(m), a_sig(m), a_exp(m), b_sgn(m), b_sig(m), b_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); - unpack(y, b_sgn, b_sig, b_exp, false); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, false); + unpack(y, b_sgn, b_sig, b_exp, b_lz, false); dbg_decouple("fpa2bv_add_unpack_a_sgn", a_sgn); dbg_decouple("fpa2bv_add_unpack_a_sig", a_sig); @@ -432,8 +450,9 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, c_sgn, c_sig, c_exp, d_sgn, d_sig, d_exp, res_sgn, res_sig, res_exp); - expr_ref is_zero_sig(m); - m_simp.mk_eq(res_sig, m_bv_util.mk_numeral(0, sbits+4), is_zero_sig); + expr_ref is_zero_sig(m), nil_sbit4(m); + nil_sbit4 = m_bv_util.mk_numeral(0, sbits+4); + m_simp.mk_eq(res_sig, nil_sbit4, is_zero_sig); SASSERT(is_well_sorted(m, is_zero_sig)); @@ -463,7 +482,7 @@ void fpa2bv_converter::mk_sub(func_decl * f, unsigned num, expr * const * args, SASSERT(num == 3); expr_ref t(m); mk_uminus(f, 1, &args[2], t); - expr * nargs[3] = { args[0], args[1], t.get() }; + expr * nargs[3] = { args[0], args[1], t }; mk_add(f, 3, nargs, result); } @@ -481,9 +500,9 @@ void fpa2bv_converter::mk_uminus(func_decl * f, unsigned num, expr * const * arg void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); - - expr_ref rm(m), x(m), y(m); - rm = args[0]; + + expr_ref rm(m), x(m), y(m); + rm = args[0]; x = args[1]; y = args[2]; @@ -492,7 +511,7 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, mk_nzero(f, nzero); mk_pzero(f, pzero); mk_minus_inf(f, ninf); - mk_plus_inf(f, pinf); + mk_plus_inf(f, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); @@ -556,13 +575,21 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, unsigned sbits = m_util.get_sbits(f->get_range()); SASSERT(ebits <= sbits); - expr_ref a_sgn(m), a_sig(m), a_exp(m), b_sgn(m), b_sig(m), b_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); - unpack(y, b_sgn, b_sig, b_exp, true); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); - expr_ref lz_a(m), lz_b(m); - mk_leading_zeros(a_sig, ebits+2, lz_a); - mk_leading_zeros(b_sig, ebits+2, lz_b); + dbg_decouple("fpa2bv_mul_a_sig", a_sig); + dbg_decouple("fpa2bv_mul_a_exp", a_exp); + dbg_decouple("fpa2bv_mul_b_sig", b_sig); + dbg_decouple("fpa2bv_mul_b_exp", b_exp); + + expr_ref a_lz_ext(m), b_lz_ext(m); + a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); + b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); + + dbg_decouple("fpa2bv_mul_lz_a", a_lz); + dbg_decouple("fpa2bv_mul_lz_b", b_lz); expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); @@ -578,9 +605,9 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, dbg_decouple("fpa2bv_mul_res_sgn", res_sgn); - res_exp = m_bv_util.mk_bv_sub( - m_bv_util.mk_bv_add(a_exp_ext, b_exp_ext), - m_bv_util.mk_bv_add(lz_a, lz_b)); + res_exp = m_bv_util.mk_bv_add( + m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref product(m); product = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); @@ -589,9 +616,20 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, SASSERT(m_bv_util.get_bv_size(product) == 2*sbits); - expr_ref sticky(m); - sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sbits-4, 0, product)); - res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(2*sbits-1, sbits-3, product), sticky); + expr_ref h_p(m), l_p(m), rbits(m); + h_p = m_bv_util.mk_extract(2*sbits-1, sbits, product); + l_p = m_bv_util.mk_extract(sbits-1, 0, product); + + if (sbits >= 4) { + expr_ref sticky(m); + sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sbits-4, 0, product)); + rbits = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits-1, sbits-3, product), sticky); + } + else + rbits = m_bv_util.mk_concat(l_p, m_bv_util.mk_numeral(0, 4 - sbits)); + + SASSERT(m_bv_util.get_bv_size(rbits) == 4); + res_sig = m_bv_util.mk_concat(h_p, rbits); round(f->get_range(), rm, res_sgn, res_sig, res_exp, v7); @@ -610,9 +648,9 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); - - expr_ref rm(m), x(m), y(m); - rm = args[0]; + + expr_ref rm(m), x(m), y(m); + rm = args[0]; x = args[1]; y = args[2]; @@ -643,8 +681,8 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, dbg_decouple("fpa2bv_div_y_is_pos", y_is_pos); dbg_decouple("fpa2bv_div_y_is_inf", y_is_inf); - expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m); - expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m); + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m), c6(m), c7(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m), v7(m), v8(m); // (x is NaN) || (y is NaN) -> NaN m_simp.mk_or(x_is_nan, y_is_nan, c1); @@ -673,20 +711,25 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, mk_is_ninf(y, c5); mk_ite(x_is_inf, nan, xy_zero, v5); - // (y is 0) -> if (x is 0) then NaN else inf with x's sign. + // (y is 0) -> if (x is 0) then NaN else inf with xor sign. c6 = y_is_zero; - expr_ref x_sgn_inf(m); - mk_ite(x_is_pos, pinf, ninf, x_sgn_inf); - mk_ite(x_is_zero, nan, x_sgn_inf, v6); + expr_ref sgn_inf(m); + mk_ite(signs_xor, ninf, pinf, sgn_inf); + mk_ite(x_is_zero, nan, sgn_inf, v6); + + // (x is 0) -> result is zero with sgn = x.sgn^y.sgn + // This is a special case to avoid problems with the unpacking of zero. + c7 = x_is_zero; + mk_ite(signs_xor, nzero, pzero, v7); // else comes the actual division. unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); SASSERT(ebits <= sbits); - expr_ref a_sgn(m), a_sig(m), a_exp(m), b_sgn(m), b_sig(m), b_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); - unpack(y, b_sgn, b_sig, b_exp, true); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); unsigned extra_bits = sbits+2; expr_ref a_sig_ext(m), b_sig_ext(m); @@ -701,7 +744,13 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, expr * signs[2] = { a_sgn, b_sgn }; res_sgn = m_bv_util.mk_bv_xor(2, signs); - res_exp = m_bv_util.mk_bv_sub(a_exp_ext, b_exp_ext); + expr_ref a_lz_ext(m), b_lz_ext(m); + a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); + b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); + + res_exp = m_bv_util.mk_bv_sub( + m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref quotient(m); quotient = m.mk_app(m_bv_util.get_fid(), OP_BUDIV, a_sig_ext, b_sig_ext); @@ -716,10 +765,11 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, SASSERT(m_bv_util.get_bv_size(res_sig) == (sbits + 4)); - round(f->get_range(), rm, res_sgn, res_sig, res_exp, v7); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, v8); // And finally, we tie them together. - mk_ite(c6, v6, v7, result); + mk_ite(c7, v7, v8, result); + mk_ite(c6, v6, result, result); mk_ite(c5, v5, result, result); mk_ite(c4, v4, result, result); mk_ite(c3, v3, result, result); @@ -733,9 +783,9 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_remainder(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); - + // Remainder is always exact, so there is no rounding mode. - expr_ref x(m), y(m); + expr_ref x(m), y(m); x = args[0]; y = args[1]; @@ -783,7 +833,7 @@ void fpa2bv_converter::mk_remainder(func_decl * f, unsigned num, expr * const * // (x is 0) -> x c4 = x_is_zero; - v4 = x; + v4 = pzero; // (y is 0) -> NaN. c5 = y_is_zero; @@ -793,10 +843,10 @@ void fpa2bv_converter::mk_remainder(func_decl * f, unsigned num, expr * const * unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); - expr_ref a_sgn(m), a_sig(m), a_exp(m); - expr_ref b_sgn(m), b_sig(m), b_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); - unpack(y, b_sgn, b_sig, b_exp, true); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); + expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); BVSLT(a_exp, b_exp, c6); v6 = x; @@ -937,10 +987,10 @@ void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 4); - + // fusedma means (x * y) + z - expr_ref rm(m), x(m), y(m), z(m); - rm = args[0]; + expr_ref rm(m), x(m), y(m), z(m); + rm = args[0]; x = args[1]; y = args[2]; z = args[3]; @@ -1026,16 +1076,16 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar expr_ref rm_is_to_neg(m); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); - expr_ref a_sgn(m), a_sig(m), a_exp(m); - expr_ref b_sgn(m), b_sig(m), b_exp(m); - expr_ref c_sgn(m), c_sig(m), c_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); - unpack(y, b_sgn, b_sig, b_exp, true); - unpack(z, c_sgn, c_sig, c_exp, false); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); + expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); + expr_ref c_sgn(m), c_sig(m), c_exp(m), c_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); + unpack(y, b_sgn, b_sig, b_exp, b_lz, true); + unpack(z, c_sgn, c_sig, c_exp, c_lz, false); - expr_ref lz_a(m), lz_b(m); - mk_leading_zeros(a_sig, ebits+2, lz_a); - mk_leading_zeros(b_sig, ebits+2, lz_b); + expr_ref a_lz_ext(m), b_lz_ext(m); + a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); + b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); expr_ref a_sig_ext(m), b_sig_ext(m); a_sig_ext = m_bv_util.mk_zero_extend(sbits, a_sig); @@ -1053,7 +1103,7 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar mul_exp = m_bv_util.mk_bv_sub( m_bv_util.mk_bv_add(a_exp_ext, b_exp_ext), - m_bv_util.mk_bv_add(lz_a, lz_b)); + m_bv_util.mk_bv_add(a_lz_ext, b_lz_ext)); mul_sig = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); @@ -1108,8 +1158,9 @@ void fpa2bv_converter::mk_fusedma(func_decl * f, unsigned num, expr * const * ar sig_size = m_bv_util.get_bv_size(res_sig); SASSERT(sig_size == sbits+4); - expr_ref is_zero_sig(m); - m_simp.mk_eq(res_sig, m_bv_util.mk_numeral(0, sbits+4), is_zero_sig); + expr_ref is_zero_sig(m), nil_sbits4(m); + nil_sbits4 = m_bv_util.mk_numeral(0, sbits+4); + m_simp.mk_eq(res_sig, nil_sbits4, is_zero_sig); SASSERT(is_well_sorted(m, is_zero_sig)); @@ -1142,9 +1193,9 @@ void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); - - expr_ref rm(m), x(m); - rm = args[0]; + + expr_ref rm(m), x(m); + rm = args[0]; x = args[1]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); @@ -1172,13 +1223,16 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * unsigned sbits = m_util.get_sbits(f->get_range()); SASSERT(ebits < sbits); - expr_ref a_sgn(m), a_sig(m), a_exp(m); - unpack(x, a_sgn, a_sig, a_exp, true); + expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); + unpack(x, a_sgn, a_sig, a_exp, a_lz, true); - expr_ref exp_is_small(m); - m_simp.mk_eq(m_bv_util.mk_extract(ebits-1, ebits-1, a_exp), - m_bv_util.mk_numeral(1, 1), - exp_is_small); + dbg_decouple("fpa2bv_r2i_unpacked_sig", a_sig); + dbg_decouple("fpa2bv_r2i_unpacked_exp", a_exp); + + expr_ref exp_is_small(m), exp_h(m), one_1(m); + exp_h = m_bv_util.mk_extract(ebits-1, ebits-1, a_exp); + one_1 = m_bv_util.mk_numeral(1, 1); + m_simp.mk_eq(exp_h, one_1, exp_is_small); dbg_decouple("fpa2bv_r2i_exp_is_small", exp_is_small); c3 = exp_is_small; mk_ite(x_is_pos, pzero, nzero, v3); @@ -1226,6 +1280,9 @@ void fpa2bv_converter::mk_float_eq(func_decl * f, unsigned num, expr * const * a expr * x = args[0], * y = args[1]; + TRACE("fpa2bv_float_eq", tout << "X = " << mk_ismt2_pp(x, m) << std::endl; + tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); + expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m); mk_is_nan(x, x_is_nan); mk_is_nan(y, y_is_nan); @@ -1253,6 +1310,8 @@ void fpa2bv_converter::mk_float_eq(func_decl * f, unsigned num, expr * const * a m_simp.mk_ite(c2, m.mk_true(), c3t4, c2else); m_simp.mk_ite(c1, m.mk_false(), c2else, result); + + TRACE("fpa2bv_float_eq", tout << "FLOAT_EQ = " << mk_ismt2_pp(result, m) << std::endl; ); } void fpa2bv_converter::mk_float_lt(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -1273,12 +1332,13 @@ void fpa2bv_converter::mk_float_lt(func_decl * f, unsigned num, expr * const * a split(x, x_sgn, x_sig, x_exp); split(y, y_sgn, y_sig, y_exp); - expr_ref c3(m), t3(m), t4(m); - - m_simp.mk_eq(x_sgn, m_bv_util.mk_numeral(1, 1), c3); + expr_ref c3(m), t3(m), t4(m), one_1(m), nil_1(m); + one_1 = m_bv_util.mk_numeral(1, 1); + nil_1 = m_bv_util.mk_numeral(0, 1); + m_simp.mk_eq(x_sgn, one_1, c3); expr_ref y_sgn_eq_0(m), y_lt_x_exp(m), y_lt_x_sig(m), y_eq_x_exp(m), y_le_x_sig_exp(m), t3_or(m); - m_simp.mk_eq(y_sgn, m_bv_util.mk_numeral(0, 1), y_sgn_eq_0); + m_simp.mk_eq(y_sgn, nil_1, y_sgn_eq_0); BVULT(y_exp, x_exp, y_lt_x_exp); BVULT(y_sig, x_sig, y_lt_x_sig); m_simp.mk_eq(y_exp, x_exp, y_eq_x_exp); @@ -1287,7 +1347,7 @@ void fpa2bv_converter::mk_float_lt(func_decl * f, unsigned num, expr * const * a m_simp.mk_ite(y_sgn_eq_0, m.mk_true(), t3_or, t3); expr_ref y_sgn_eq_1(m), x_lt_y_exp(m), x_eq_y_exp(m), x_lt_y_sig(m), x_le_y_sig_exp(m), t4_or(m); - m_simp.mk_eq(y_sgn, m_bv_util.mk_numeral(1, 1), y_sgn_eq_1); + m_simp.mk_eq(y_sgn, one_1, y_sgn_eq_1); BVULT(x_exp, y_exp, x_lt_y_exp); m_simp.mk_eq(x_exp, y_exp, x_eq_y_exp); BVULT(x_sig, y_sig, x_lt_y_sig); @@ -1365,23 +1425,171 @@ void fpa2bv_converter::mk_is_sign_minus(func_decl * f, unsigned num, expr * cons } void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { - if (num == 3 && m_bv_util.is_bv(args[0]) && - m_bv_util.is_bv(args[1]) && m_bv_util.is_bv(args[2])) { + TRACE("fpa2bv_to_float", for (unsigned i=0; i < num; i++) + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl; ); + + if (num == 3 && + m_bv_util.is_bv(args[0]) && + m_bv_util.is_bv(args[1]) && + m_bv_util.is_bv(args[2])) { // Theoretically, the user could have thrown in it's own triple of bit-vectors. // Just keep it here, as there will be something else that uses it. mk_triple(args[0], args[1], args[2], result); } + else if (num == 2 && is_app(args[1]) && m_util.is_float(m.get_sort(args[1]))) { + // We also support float to float conversion + expr_ref rm(m), x(m); + rm = args[0]; + x = args[1]; + + expr_ref c1(m), c2(m), c3(m), c4(m), c5(m); + expr_ref v1(m), v2(m), v3(m), v4(m), v5(m), v6(m); + expr_ref one1(m); + + one1 = m_bv_util.mk_numeral(1, 1); + expr_ref ninf(m), pinf(m); + mk_plus_inf(f, pinf); + mk_minus_inf(f, ninf); + + // NaN -> NaN + mk_is_nan(x, c1); + mk_nan(f, v1); + + // +0 -> +0 + mk_is_pzero(x, c2); + mk_pzero(f, v2); + + // -0 -> -0 + mk_is_nzero(x, c3); + mk_nzero(f, v3); + + // +oo -> +oo + mk_is_pinf(x, c4); + v4 = pinf; + + // -oo -> -oo + mk_is_ninf(x, c5); + v5 = ninf; + + // otherwise: the actual conversion with rounding. + sort * s = f->get_range(); + expr_ref sgn(m), sig(m), exp(m), lz(m); + unpack(x, sgn, sig, exp, lz, true); + + expr_ref res_sgn(m), res_sig(m), res_exp(m); + + res_sgn = sgn; + + unsigned from_sbits = m_util.get_sbits(m.get_sort(args[1])); + unsigned from_ebits = m_util.get_ebits(m.get_sort(args[1])); + unsigned to_sbits = m_util.get_sbits(s); + unsigned to_ebits = m_util.get_ebits(s); + + SASSERT(m_bv_util.get_bv_size(sgn) == 1); + SASSERT(m_bv_util.get_bv_size(sig) == from_sbits); + SASSERT(m_bv_util.get_bv_size(exp) == from_ebits); + + if (from_sbits < (to_sbits + 3)) + // make sure that sig has at least to_sbits + 3 + res_sig = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, to_sbits+3-from_sbits)); + else if (from_sbits > (to_sbits + 3)) + { + // collapse the extra bits into a sticky bit. + expr_ref sticky(m), low(m), high(m); + low = m_bv_util.mk_extract(from_sbits - to_sbits - 3, 0, sig); + high = m_bv_util.mk_extract(from_sbits - 1, from_sbits - to_sbits - 2, sig); + sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, low.get()); + res_sig = m_bv_util.mk_concat(high, sticky); + } + else + res_sig = sig; + + res_sig = m_bv_util.mk_zero_extend(1, res_sig); // extra zero in the front for the rounder. + unsigned sig_sz = m_bv_util.get_bv_size(res_sig); + SASSERT(sig_sz == to_sbits+4); + + expr_ref exponent_overflow(m); + exponent_overflow = m.mk_false(); + + if (from_ebits < (to_ebits + 2)) + res_exp = m_bv_util.mk_sign_extend(to_ebits+2-from_ebits, exp); + else if (from_ebits > (to_ebits + 2)) + { + expr_ref high(m), low(m), lows(m), high_red_or(m), high_red_and(m), h_or_eq(m), h_and_eq(m); + expr_ref no_ovf(m), zero1(m), s_is_one(m), s_is_zero(m); + high = m_bv_util.mk_extract(from_ebits - 1, to_ebits + 2, exp); + low = m_bv_util.mk_extract(to_ebits+1, 0, exp); + lows = m_bv_util.mk_extract(to_ebits+1, to_ebits+1, low); + + high_red_or = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, high.get()); + high_red_and = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, high.get()); + + zero1 = m_bv_util.mk_numeral(0, 1); + m_simp.mk_eq(high_red_and, one1, h_and_eq); + m_simp.mk_eq(high_red_or, zero1, h_or_eq); + m_simp.mk_eq(lows, zero1, s_is_zero); + m_simp.mk_eq(lows, one1, s_is_one); + + expr_ref c2(m); + m_simp.mk_ite(h_or_eq, s_is_one, m.mk_false(), c2); + m_simp.mk_ite(h_and_eq, s_is_zero, c2, exponent_overflow); + + // Note: Upon overflow, we _could_ try to shift the significand around... + + res_exp = low; + } + else + res_exp = exp; + + SASSERT(m_bv_util.get_bv_size(res_exp) == to_ebits+2); + + expr_ref rounded(m); + round(s, rm, res_sgn, res_sig, res_exp, rounded); + + expr_ref is_neg(m), sig_inf(m); + m_simp.mk_eq(sgn, one1, is_neg); + mk_ite(is_neg, ninf, pinf, sig_inf); + + dbg_decouple("fpa2bv_to_float_exp_ovf", exponent_overflow); + + mk_ite(exponent_overflow, sig_inf, rounded, v6); + + // And finally, we tie them together. + mk_ite(c5, v5, v6, result); + mk_ite(c4, v4, result, result); + mk_ite(c3, v3, result, result); + mk_ite(c2, v2, result, result); + mk_ite(c1, v1, result, result); + } else { + // .. other than that, we only support rationals for asFloat SASSERT(num == 2); SASSERT(m_util.is_float(f->get_range())); unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); - - SASSERT(m_util.is_rm(to_app(args[0])->get_decl()->get_range())); - mpf_rounding_mode rm = static_cast(to_app(args[1])->get_decl_kind()); - + unsigned sbits = m_util.get_sbits(f->get_range()); + + SASSERT(m_bv_util.is_numeral(args[0])); + rational tmp_rat; unsigned sz; + m_bv_util.is_numeral(to_expr(args[0]), tmp_rat, sz); + SASSERT(tmp_rat.is_int32()); + SASSERT(sz == 3); + BV_RM_VAL bv_rm = (BV_RM_VAL) tmp_rat.get_unsigned(); + + mpf_rounding_mode rm; + switch(bv_rm) + { + case BV_RM_TIES_TO_AWAY: rm = MPF_ROUND_NEAREST_TAWAY; break; + case BV_RM_TIES_TO_EVEN: rm = MPF_ROUND_NEAREST_TEVEN; break; + case BV_RM_TO_NEGATIVE: rm = MPF_ROUND_TOWARD_NEGATIVE; break; + case BV_RM_TO_POSITIVE: rm = MPF_ROUND_TOWARD_POSITIVE; break; + case BV_RM_TO_ZERO: rm = MPF_ROUND_TOWARD_ZERO; break; + default: UNREACHABLE(); + } + + SASSERT(m_util.au().is_numeral(args[1])); + rational q; - SASSERT(m_util.au().is_numeral(args[1])); + SASSERT(m_util.au().is_numeral(args[1])); m_util.au().is_numeral(args[1], q); mpf v; @@ -1395,6 +1603,8 @@ void fpa2bv_converter::mk_to_float(func_decl * f, unsigned num, expr * const * a m_util.fm().del(v); } + + SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -1418,10 +1628,11 @@ void fpa2bv_converter::mk_is_nan(expr * e, expr_ref & result) { split(e, sgn, sig, exp); // exp == 1^n , sig != 0 - expr_ref sig_is_zero(m), sig_is_not_zero(m), exp_is_top(m), top_exp(m); + expr_ref sig_is_zero(m), sig_is_not_zero(m), exp_is_top(m), top_exp(m), zero(m); mk_top_exp(m_bv_util.get_bv_size(exp), top_exp); - m_simp.mk_eq(sig, m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)), sig_is_zero); + zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)); + m_simp.mk_eq(sig, zero, sig_is_zero); m_simp.mk_not(sig_is_zero, sig_is_not_zero); m_simp.mk_eq(exp, top_exp, exp_is_top); m_simp.mk_and(exp_is_top, sig_is_not_zero, result); @@ -1430,9 +1641,10 @@ void fpa2bv_converter::mk_is_nan(expr * e, expr_ref & result) { void fpa2bv_converter::mk_is_inf(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split(e, sgn, sig, exp); - expr_ref eq1(m), eq2(m), top_exp(m); + expr_ref eq1(m), eq2(m), top_exp(m), zero(m); mk_top_exp(m_bv_util.get_bv_size(exp), top_exp); - m_simp.mk_eq(sig, m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)), eq1); + zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)); + m_simp.mk_eq(sig, zero, eq1); m_simp.mk_eq(exp, top_exp, eq2); m_simp.mk_and(eq1, eq2, result); } @@ -1455,22 +1667,27 @@ void fpa2bv_converter::mk_is_pos(expr * e, expr_ref & result) { SASSERT(is_app_of(e, m_plugin->get_family_id(), OP_TO_FLOAT)); SASSERT(to_app(e)->get_num_args() == 3); expr * a0 = to_app(e)->get_arg(0); - m_simp.mk_eq(a0, m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(a0)), result); + expr_ref zero(m); + zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(a0)); + m_simp.mk_eq(a0, zero, result); } void fpa2bv_converter::mk_is_neg(expr * e, expr_ref & result) { SASSERT(is_app_of(e, m_plugin->get_family_id(), OP_TO_FLOAT)); SASSERT(to_app(e)->get_num_args() == 3); expr * a0 = to_app(e)->get_arg(0); - m_simp.mk_eq(a0, m_bv_util.mk_numeral(1, m_bv_util.get_bv_size(a0)), result); + expr_ref one(m); + one = m_bv_util.mk_numeral(1, m_bv_util.get_bv_size(a0)); + m_simp.mk_eq(a0, one, result); } void fpa2bv_converter::mk_is_zero(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split(e, sgn, sig, exp); - expr_ref eq1(m), eq2(m), bot_exp(m); + expr_ref eq1(m), eq2(m), bot_exp(m), zero(m); mk_bot_exp(m_bv_util.get_bv_size(exp), bot_exp); - m_simp.mk_eq(sig, m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)), eq1); + zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(sig)); + m_simp.mk_eq(sig, zero, eq1); m_simp.mk_eq(exp, bot_exp, eq2); m_simp.mk_and(eq1, eq2, result); } @@ -1478,35 +1695,40 @@ void fpa2bv_converter::mk_is_zero(expr * e, expr_ref & result) { void fpa2bv_converter::mk_is_nzero(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split(e, sgn, sig, exp); - expr_ref e_is_zero(m), eq(m); + expr_ref e_is_zero(m), eq(m), one_1(m); mk_is_zero(e, e_is_zero); - m_simp.mk_eq(sgn, m_bv_util.mk_numeral(1, 1), eq); + one_1 = m_bv_util.mk_numeral(1, 1); + m_simp.mk_eq(sgn, one_1, eq); m_simp.mk_and(eq, e_is_zero, result); } void fpa2bv_converter::mk_is_pzero(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split(e, sgn, sig, exp); - expr_ref e_is_zero(m), eq(m); + expr_ref e_is_zero(m), eq(m), nil_1(m); mk_is_zero(e, e_is_zero); - m_simp.mk_eq(sgn, m_bv_util.mk_numeral(0, 1), eq); + nil_1 = m_bv_util.mk_numeral(0, 1); + m_simp.mk_eq(sgn, nil_1, eq); m_simp.mk_and(eq, e_is_zero, result); } void fpa2bv_converter::mk_is_denormal(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split(e, sgn, sig, exp); - m_simp.mk_eq(exp, m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(exp)), result); + expr_ref zero(m); + zero = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(exp)); + m_simp.mk_eq(exp, zero, result); } void fpa2bv_converter::mk_is_normal(expr * e, expr_ref & result) { expr * sgn, * sig, * exp; split(e, sgn, sig, exp); - expr_ref is_special(m), is_denormal(m); + expr_ref is_special(m), is_denormal(m), p(m); mk_is_denormal(e, is_denormal); unsigned ebits = m_bv_util.get_bv_size(exp); - m_simp.mk_eq(exp, m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits), ebits), is_special); + p = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits), ebits); + m_simp.mk_eq(exp, p, is_special); expr_ref or_ex(m); m_simp.mk_or(is_special, is_denormal, or_ex); @@ -1514,21 +1736,21 @@ void fpa2bv_converter::mk_is_normal(expr * e, expr_ref & result) { } void fpa2bv_converter::mk_is_rm(expr * e, BV_RM_VAL rm, expr_ref & result) { - SASSERT(m_bv_util.is_bv(e) && m_bv_util.get_bv_size(e) == 3); + SASSERT(m_bv_util.is_bv(e) && m_bv_util.get_bv_size(e) == 3); expr_ref rm_num(m); rm_num = m_bv_util.mk_numeral(rm, 3); - switch(rm) - { - case BV_RM_TIES_TO_AWAY: - case BV_RM_TIES_TO_EVEN: - case BV_RM_TO_NEGATIVE: - case BV_RM_TO_POSITIVE: return m_simp.mk_eq(e, rm_num, result); - case BV_RM_TO_ZERO: - default: + switch(rm) + { + case BV_RM_TIES_TO_AWAY: + case BV_RM_TIES_TO_EVEN: + case BV_RM_TO_NEGATIVE: + case BV_RM_TO_POSITIVE: return m_simp.mk_eq(e, rm_num, result); + case BV_RM_TO_ZERO: + default: rm_num = m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3); expr_ref r(m); r = m_bv_util.mk_ule(e, rm_num); return m_simp.mk_not(r, result); - } + } } void fpa2bv_converter::mk_top_exp(unsigned sz, expr_ref & result) { @@ -1546,7 +1768,7 @@ void fpa2bv_converter::mk_min_exp(unsigned ebits, expr_ref & result) { } void fpa2bv_converter::mk_max_exp(unsigned ebits, expr_ref & result) { - SASSERT(ebits > 0); + SASSERT(ebits > 0); result = m_bv_util.mk_numeral(m_mpf_manager.m_powers2.m1(ebits-1, false), ebits); } @@ -1557,9 +1779,12 @@ void fpa2bv_converter::mk_leading_zeros(expr * e, unsigned max_bits, expr_ref & if (bv_sz == 0) result = m_bv_util.mk_numeral(0, max_bits); else if (bv_sz == 1) { - expr_ref eq(m); - m_simp.mk_eq(e, m_bv_util.mk_numeral(0, 1), eq); - m_simp.mk_ite(eq, m_bv_util.mk_numeral(1, max_bits), m_bv_util.mk_numeral(0, max_bits), result); + expr_ref eq(m), nil_1(m), one_m(m), nil_m(m); + nil_1 = m_bv_util.mk_numeral(0, 1); + one_m = m_bv_util.mk_numeral(1, max_bits); + nil_m = m_bv_util.mk_numeral(0, max_bits); + m_simp.mk_eq(e, nil_1, eq); + m_simp.mk_ite(eq, one_m, nil_m, result); } else { expr_ref H(m), L(m); @@ -1573,11 +1798,13 @@ void fpa2bv_converter::mk_leading_zeros(expr * e, unsigned max_bits, expr_ref & mk_leading_zeros(H, max_bits, lzH); /* recursive! */ mk_leading_zeros(L, max_bits, lzL); - expr_ref H_is_zero(m); - m_simp.mk_eq(H, m_bv_util.mk_numeral(0, H_size), H_is_zero); + expr_ref H_is_zero(m), nil_h(m); + nil_h = m_bv_util.mk_numeral(0, H_size); + m_simp.mk_eq(H, nil_h, H_is_zero); - expr_ref sum(m); - sum = m_bv_util.mk_bv_add(m_bv_util.mk_numeral(H_size, max_bits), lzL); + expr_ref sum(m), h_m(m); + h_m = m_bv_util.mk_numeral(H_size, max_bits); + sum = m_bv_util.mk_bv_add(h_m, lzL); m_simp.mk_ite(H_is_zero, sum, lzH, result); } @@ -1608,7 +1835,7 @@ void fpa2bv_converter::mk_unbias(expr * e, expr_ref & result) { result = m_bv_util.mk_concat(n_leading, rest); } -void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, bool normalize) { +void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & lz, bool normalize) { SASSERT(is_app_of(e, m_plugin->get_family_id(), OP_TO_FLOAT)); SASSERT(to_app(e)->get_num_args() == 3); @@ -1627,32 +1854,59 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref expr_ref normal_sig(m), normal_exp(m); normal_sig = m_bv_util.mk_concat(m_bv_util.mk_numeral(1, 1), sig); mk_unbias(exp, normal_exp); + dbg_decouple("fpa2bv_unpack_normal_exp", normal_exp); expr_ref denormal_sig(m), denormal_exp(m); - denormal_sig = m_bv_util.mk_concat(m_bv_util.mk_numeral(0, 1), sig); + denormal_sig = m_bv_util.mk_zero_extend(1, sig); denormal_exp = m_bv_util.mk_numeral(1, ebits); mk_unbias(denormal_exp, denormal_exp); + dbg_decouple("fpa2bv_unpack_denormal_exp", denormal_exp); + + expr_ref zero_e(m); + zero_e = m_bv_util.mk_numeral(0, ebits); if (normalize) { - expr_ref is_sig_zero(m), shift(m), lz(m); - m_simp.mk_eq(m_bv_util.mk_numeral(0, sbits-1), sig, is_sig_zero); - mk_leading_zeros(sig, ebits, lz); - m_simp.mk_ite(is_sig_zero, m_bv_util.mk_numeral(0, ebits), lz, shift); - SASSERT(is_well_sorted(m, is_sig_zero)); - SASSERT(is_well_sorted(m, lz)); + expr_ref lz_d(m); + mk_leading_zeros(denormal_sig, ebits, lz_d); + m_simp.mk_ite(is_normal, zero_e, lz_d, lz); + dbg_decouple("fpa2bv_unpack_lz", lz); + + expr_ref is_sig_zero(m), shift(m), zero_s(m); + zero_s = m_bv_util.mk_numeral(0, sbits); + m_simp.mk_eq(zero_s, denormal_sig, is_sig_zero); + m_simp.mk_ite(is_sig_zero, zero_e, lz, shift); + dbg_decouple("fpa2bv_unpack_shift", shift); + SASSERT(is_well_sorted(m, is_sig_zero)); SASSERT(is_well_sorted(m, shift)); - if (ebits < sbits) { + SASSERT(m_bv_util.get_bv_size(shift) == ebits); + if (ebits <= sbits) { expr_ref q(m); - q = m_bv_util.mk_zero_extend(sbits-ebits, shift); - denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, q); + q = m_bv_util.mk_zero_extend(sbits-ebits, shift); + denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, q); } else { - denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, shift); - } + // the maximum shift is `sbits', because after that the mantissa + // would be zero anyways. So we can safely cut the shift variable down, + // as long as we check the higher bits. + expr_ref sh(m), is_sh_zero(m), sl(m), zero_s(m), sbits_s(m), short_shift(m); + zero_s = m_bv_util.mk_numeral(0, sbits-1); + sbits_s = m_bv_util.mk_numeral(sbits, sbits); + sh = m_bv_util.mk_extract(ebits-1, sbits, shift); + m_simp.mk_eq(zero_s, sh, is_sh_zero); + short_shift = m_bv_util.mk_extract(sbits-1, 0, shift); + m_simp.mk_ite(is_sh_zero, short_shift, sbits_s, sl); + denormal_sig = m_bv_util.mk_bv_shl(denormal_sig, sl); + } } + else + lz = zero_e; SASSERT(is_well_sorted(m, normal_sig)); SASSERT(is_well_sorted(m, denormal_sig)); + SASSERT(is_well_sorted(m, normal_exp)); + SASSERT(is_well_sorted(m, denormal_exp)); + + dbg_decouple("fpa2bv_unpack_is_normal", is_normal); m_simp.mk_ite(is_normal, normal_sig, denormal_sig, sig); m_simp.mk_ite(is_normal, normal_exp, denormal_exp, exp); @@ -1672,19 +1926,20 @@ void fpa2bv_converter::unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) { - switch(f->get_decl_kind()) - { - case OP_RM_NEAREST_TIES_TO_AWAY: result = m_bv_util.mk_numeral(BV_RM_TIES_TO_AWAY, 3); break; + switch(f->get_decl_kind()) + { + case OP_RM_NEAREST_TIES_TO_AWAY: result = m_bv_util.mk_numeral(BV_RM_TIES_TO_AWAY, 3); break; case OP_RM_NEAREST_TIES_TO_EVEN: result = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); break; case OP_RM_TOWARD_NEGATIVE: result = m_bv_util.mk_numeral(BV_RM_TO_NEGATIVE, 3); break; case OP_RM_TOWARD_POSITIVE: result = m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3); break; - case OP_RM_TOWARD_ZERO: result = m_bv_util.mk_numeral(BV_RM_TO_ZERO, 3); break; - default: UNREACHABLE(); - } + case OP_RM_TOWARD_ZERO: result = m_bv_util.mk_numeral(BV_RM_TO_ZERO, 3); break; + default: UNREACHABLE(); + } } -void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { +void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { #ifdef _DEBUG + // return; expr_ref new_e(m); new_e = m.mk_fresh_const(prefix, m.get_sort(e)); extra_assertions.push_back(m.mk_eq(new_e, e)); @@ -1701,144 +1956,163 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & dbg_decouple("fpa2bv_rnd_sig", sig); dbg_decouple("fpa2bv_rnd_exp", exp); - SASSERT(is_well_sorted(m, rm)); - SASSERT(is_well_sorted(m, sgn)); - SASSERT(is_well_sorted(m, sig)); - SASSERT(is_well_sorted(m, exp)); + SASSERT(is_well_sorted(m, rm)); + SASSERT(is_well_sorted(m, sgn)); + SASSERT(is_well_sorted(m, sig)); + SASSERT(is_well_sorted(m, exp)); - TRACE("fpa2bv_dbg", tout << "RND: " << std::endl << - "ebits = " << ebits << std::endl << - "sbits = " << sbits << std::endl << - "sgn = " << mk_ismt2_pp(sgn, m) << std::endl << - "sig = " << mk_ismt2_pp(sig, m) << std::endl << - "exp = " << mk_ismt2_pp(exp, m) << std::endl; ); + TRACE("fpa2bv_dbg", tout << "RND: " << std::endl << + "ebits = " << ebits << std::endl << + "sbits = " << sbits << std::endl << + "sgn = " << mk_ismt2_pp(sgn, m) << std::endl << + "sig = " << mk_ismt2_pp(sig, m) << std::endl << + "exp = " << mk_ismt2_pp(exp, m) << std::endl; ); - // Assumptions: sig is of the form f[-1:0] . f[1:sbits-1] [guard,round,sticky], + // Assumptions: sig is of the form f[-1:0] . f[1:sbits-1] [guard,round,sticky], // i.e., it has 2 + (sbits-1) + 3 = sbits + 4 bits, where the first one is in sgn. - // Furthermore, note that sig is an unsigned bit-vector, while exp is signed. + // Furthermore, note that sig is an unsigned bit-vector, while exp is signed. - SASSERT(ebits <= sbits); - SASSERT(m_bv_util.is_bv(rm) && m_bv_util.get_bv_size(rm) == 3); - SASSERT(m_bv_util.is_bv(sgn) && m_bv_util.get_bv_size(sgn) == 1); - SASSERT(m_bv_util.is_bv(sig) && m_bv_util.get_bv_size(sig) >= 5); - SASSERT(m_bv_util.is_bv(exp) && m_bv_util.get_bv_size(exp) >= 4); + SASSERT(ebits <= sbits); + SASSERT(m_bv_util.is_bv(rm) && m_bv_util.get_bv_size(rm) == 3); + SASSERT(m_bv_util.is_bv(sgn) && m_bv_util.get_bv_size(sgn) == 1); + SASSERT(m_bv_util.is_bv(sig) && m_bv_util.get_bv_size(sig) >= 5); + SASSERT(m_bv_util.is_bv(exp) && m_bv_util.get_bv_size(exp) >= 4); SASSERT(m_bv_util.get_bv_size(sig) == sbits+4); SASSERT(m_bv_util.get_bv_size(exp) == ebits+2); - // bool UNFen = false; + // bool UNFen = false; // bool OVFen = false; - expr_ref e_min(m), e_max(m); - mk_min_exp(ebits, e_min); - mk_max_exp(ebits, e_max); + expr_ref e_min(m), e_max(m); + mk_min_exp(ebits, e_min); + mk_max_exp(ebits, e_max); - TRACE("fpa2bv_dbg", tout << "e_min = " << mk_ismt2_pp(e_min, m) << std::endl << - "e_max = " << mk_ismt2_pp(e_max, m) << std::endl;); + TRACE("fpa2bv_dbg", tout << "e_min = " << mk_ismt2_pp(e_min, m) << std::endl << + "e_max = " << mk_ismt2_pp(e_max, m) << std::endl;); - expr_ref OVF1(m), e_top_three(m), sigm1(m), e_eq_emax_and_sigm1(m), e_eq_emax(m); - expr_ref e3(m), ne3(m), e2(m), e1(m), e21(m); - m_simp.mk_eq(m_bv_util.mk_extract(ebits+1, ebits+1, exp), m_bv_util.mk_numeral(1, 1), e3); - m_simp.mk_eq(m_bv_util.mk_extract(ebits, ebits, exp), m_bv_util.mk_numeral(1, 1), e2); - m_simp.mk_eq(m_bv_util.mk_extract(ebits-1, ebits-1, exp), m_bv_util.mk_numeral(1, 1), e1); + expr_ref OVF1(m), e_top_three(m), sigm1(m), e_eq_emax_and_sigm1(m), e_eq_emax(m); + expr_ref e3(m), ne3(m), e2(m), e1(m), e21(m), one_1(m), h_exp(m), sh_exp(m), th_exp(m); + one_1 = m_bv_util.mk_numeral(1, 1); + h_exp = m_bv_util.mk_extract(ebits+1, ebits+1, exp); + sh_exp = m_bv_util.mk_extract(ebits, ebits, exp); + th_exp = m_bv_util.mk_extract(ebits-1, ebits-1, exp); + m_simp.mk_eq(h_exp, one_1, e3); + m_simp.mk_eq(sh_exp, one_1, e2); + m_simp.mk_eq(th_exp, one_1, e1); m_simp.mk_or(e2, e1, e21); m_simp.mk_not(e3, ne3); m_simp.mk_and(ne3, e21, e_top_three); - m_simp.mk_eq(m_bv_util.mk_zero_extend(2, e_max), exp, e_eq_emax); - m_simp.mk_eq(m_bv_util.mk_extract(sbits+3, sbits+3, sig), m_bv_util.mk_numeral(1, 1), sigm1); + + expr_ref ext_emax(m), t_sig(m); + ext_emax = m_bv_util.mk_zero_extend(2, e_max); + t_sig = m_bv_util.mk_extract(sbits+3, sbits+3, sig); + m_simp.mk_eq(ext_emax, exp, e_eq_emax); + m_simp.mk_eq(t_sig, one_1, sigm1); m_simp.mk_and(e_eq_emax, sigm1, e_eq_emax_and_sigm1); m_simp.mk_or(e_top_three, e_eq_emax_and_sigm1, OVF1); dbg_decouple("fpa2bv_rnd_OVF1", OVF1); - TRACE("fpa2bv_dbg", tout << "OVF1 = " << mk_ismt2_pp(OVF1, m) << std::endl;); - SASSERT(is_well_sorted(m, OVF1)); + TRACE("fpa2bv_dbg", tout << "OVF1 = " << mk_ismt2_pp(OVF1, m) << std::endl;); + SASSERT(is_well_sorted(m, OVF1)); - expr_ref lz(m); - mk_leading_zeros(sig, ebits+2, lz); // CMW: is this always large enough? + expr_ref lz(m); + mk_leading_zeros(sig, ebits+2, lz); // CMW: is this always large enough? dbg_decouple("fpa2bv_rnd_lz", lz); - TRACE("fpa2bv_dbg", tout << "LZ = " << mk_ismt2_pp(lz, m) << std::endl;); + TRACE("fpa2bv_dbg", tout << "LZ = " << mk_ismt2_pp(lz, m) << std::endl;); - expr_ref t(m); - t = m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(1, ebits+2)); + expr_ref t(m); + t = m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(1, ebits+2)); t = m_bv_util.mk_bv_sub(t, lz); - t = m_bv_util.mk_bv_sub(t, m_bv_util.mk_sign_extend(2, e_min)); + t = m_bv_util.mk_bv_sub(t, m_bv_util.mk_sign_extend(2, e_min)); expr_ref TINY(m); - TINY = m_bv_util.mk_sle(t, m_bv_util.mk_numeral(-1, ebits+2)); + TINY = m_bv_util.mk_sle(t, m_bv_util.mk_numeral(-1, ebits+2)); - TRACE("fpa2bv_dbg", tout << "TINY = " << mk_ismt2_pp(TINY, m) << std::endl;); - SASSERT(is_well_sorted(m, TINY)); + TRACE("fpa2bv_dbg", tout << "TINY = " << mk_ismt2_pp(TINY, m) << std::endl;); + SASSERT(is_well_sorted(m, TINY)); dbg_decouple("fpa2bv_rnd_TINY", TINY); - expr_ref beta(m); + expr_ref beta(m); beta = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(exp, lz), m_bv_util.mk_numeral(1, ebits+2)); - TRACE("fpa2bv_dbg", tout << "beta = " << mk_ismt2_pp(beta, m)<< std::endl; ); - SASSERT(is_well_sorted(m, beta)); + TRACE("fpa2bv_dbg", tout << "beta = " << mk_ismt2_pp(beta, m)<< std::endl; ); + SASSERT(is_well_sorted(m, beta)); dbg_decouple("fpa2bv_rnd_beta", beta); - expr_ref sigma(m), sigma_add(m); - sigma_add = m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits+2)); - m_simp.mk_ite(TINY, sigma_add, lz, sigma); + dbg_decouple("fpa2bv_rnd_e_min", e_min); + dbg_decouple("fpa2bv_rnd_e_max", e_max); + + expr_ref sigma(m), sigma_add(m), e_min_p2(m); + sigma_add = m_bv_util.mk_bv_sub(exp, m_bv_util.mk_sign_extend(2, e_min)); + sigma_add = m_bv_util.mk_bv_add(sigma_add, m_bv_util.mk_numeral(1, ebits+2)); + m_simp.mk_ite(TINY, sigma_add, lz, sigma); dbg_decouple("fpa2bv_rnd_sigma", sigma); - TRACE("fpa2bv_dbg", tout << "Shift distance: " << mk_ismt2_pp(sigma, m) << std::endl;); + TRACE("fpa2bv_dbg", tout << "Shift distance: " << mk_ismt2_pp(sigma, m) << std::endl;); SASSERT(is_well_sorted(m, sigma)); - // Normalization shift + // Normalization shift dbg_decouple("fpa2bv_rnd_sig_before_shift", sig); - unsigned sig_size = m_bv_util.get_bv_size(sig); + unsigned sig_size = m_bv_util.get_bv_size(sig); SASSERT(sig_size == sbits+4); - unsigned sigma_size = m_bv_util.get_bv_size(sigma); + SASSERT(m_bv_util.get_bv_size(sigma) == ebits+2); + unsigned sigma_size = ebits+2; - expr_ref sigma_neg(m), sigma_cap(m), sigma_neg_capped(m), sigma_lt_zero(m), sig_ext(m), rs_sig(m), ls_sig(m), big_sh_sig(m); - sigma_neg = m_bv_util.mk_bv_neg(sigma); + expr_ref sigma_neg(m), sigma_cap(m), sigma_neg_capped(m), sigma_lt_zero(m), sig_ext(m), + rs_sig(m), ls_sig(m), big_sh_sig(m), sigma_le_cap(m); + sigma_neg = m_bv_util.mk_bv_neg(sigma); sigma_cap = m_bv_util.mk_numeral(sbits+2, sigma_size); - m_simp.mk_ite(m_bv_util.mk_sle(sigma_neg, sigma_cap), sigma_neg, sigma_cap, sigma_neg_capped); + sigma_le_cap = m_bv_util.mk_sle(sigma_neg, sigma_cap); + m_simp.mk_ite(sigma_le_cap, sigma_neg, sigma_cap, sigma_neg_capped); dbg_decouple("fpa2bv_rnd_sigma_neg", sigma_neg); dbg_decouple("fpa2bv_rnd_sigma_neg_capped", sigma_neg_capped); - sigma_lt_zero = m_bv_util.mk_sle(sigma, m_bv_util.mk_numeral(-1, sigma_size)); + sigma_lt_zero = m_bv_util.mk_sle(sigma, m_bv_util.mk_numeral(-1, sigma_size)); dbg_decouple("fpa2bv_rnd_sigma_lt_zero", sigma_lt_zero); - sig_ext = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, sig_size)); - rs_sig = m_bv_util.mk_bv_lshr(sig_ext, m_bv_util.mk_zero_extend(2*sig_size - sigma_size, sigma_neg_capped)); - ls_sig = m_bv_util.mk_bv_shl(sig_ext, m_bv_util.mk_zero_extend(2*sig_size - sigma_size, sigma)); - m_simp.mk_ite(sigma_lt_zero, rs_sig, ls_sig, big_sh_sig); + sig_ext = m_bv_util.mk_concat(sig, m_bv_util.mk_numeral(0, sig_size)); + rs_sig = m_bv_util.mk_bv_lshr(sig_ext, m_bv_util.mk_zero_extend(2*sig_size - sigma_size, sigma_neg_capped)); + ls_sig = m_bv_util.mk_bv_shl(sig_ext, m_bv_util.mk_zero_extend(2*sig_size - sigma_size, sigma)); + m_simp.mk_ite(sigma_lt_zero, rs_sig, ls_sig, big_sh_sig); SASSERT(m_bv_util.get_bv_size(big_sh_sig) == 2*sig_size); dbg_decouple("fpa2bv_rnd_big_sh_sig", big_sh_sig); unsigned sig_extract_low_bit = (2*sig_size-1)-(sbits+2)+1; - sig = m_bv_util.mk_extract(2*sig_size-1, sig_extract_low_bit, big_sh_sig); + sig = m_bv_util.mk_extract(2*sig_size-1, sig_extract_low_bit, big_sh_sig); SASSERT(m_bv_util.get_bv_size(sig) == sbits+2); dbg_decouple("fpa2bv_rnd_shifted_sig", sig); - expr_ref sticky(m); - sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sig_extract_low_bit-1, 0, big_sh_sig)); - SASSERT(is_well_sorted(m, sticky)); - SASSERT(is_well_sorted(m, sig)); - - // put the sticky bit into the significand. - expr * tmp[] = { sig, m_bv_util.mk_zero_extend(sbits+1, sticky) }; - sig = m_bv_util.mk_bv_or(2, tmp); + expr_ref sticky(m); + sticky = m.mk_app(m_bv_util.get_fid(), OP_BREDOR, m_bv_util.mk_extract(sig_extract_low_bit-1, 0, big_sh_sig)); + SASSERT(is_well_sorted(m, sticky)); SASSERT(is_well_sorted(m, sig)); - SASSERT(m_bv_util.get_bv_size(sig) == sbits+2); - - // CMW: The (OVF1 && OVFen) and (TINY && UNFen) cases are never taken. - m_simp.mk_ite(TINY, m_bv_util.mk_zero_extend(2, e_min), beta, exp); - SASSERT(is_well_sorted(m, exp)); - // Significand rounding + // put the sticky bit into the significand. + expr_ref ext_sticky(m); + ext_sticky = m_bv_util.mk_zero_extend(sbits+1, sticky); + expr * tmp[] = { sig, ext_sticky }; + sig = m_bv_util.mk_bv_or(2, tmp); + SASSERT(is_well_sorted(m, sig)); + SASSERT(m_bv_util.get_bv_size(sig) == sbits+2); + + // CMW: The (OVF1 && OVFen) and (TINY && UNFen) cases are never taken. + expr_ref ext_emin(m); + ext_emin = m_bv_util.mk_zero_extend(2, e_min); + m_simp.mk_ite(TINY, ext_emin, beta, exp); + SASSERT(is_well_sorted(m, exp)); + + // Significand rounding expr_ref round(m), last(m); - sticky = m_bv_util.mk_extract(0, 0, sig); // new sticky bit! - round = m_bv_util.mk_extract(1, 1, sig); + sticky = m_bv_util.mk_extract(0, 0, sig); // new sticky bit! + round = m_bv_util.mk_extract(1, 1, sig); last = m_bv_util.mk_extract(2, 2, sig); TRACE("fpa2bv_dbg", tout << "sticky = " << mk_ismt2_pp(sticky, m) << std::endl;); @@ -1847,70 +2121,76 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & dbg_decouple("fpa2bv_rnd_round", round); dbg_decouple("fpa2bv_rnd_last", last); - sig = m_bv_util.mk_extract(sbits+1, 2, sig); + sig = m_bv_util.mk_extract(sbits+1, 2, sig); - expr * last_sticky[2] = { last, sticky }; - expr * round_sticky[2] = { round, sticky }; - expr * last_or_sticky = m_bv_util.mk_bv_or(2, last_sticky); - expr * round_or_sticky = m_bv_util.mk_bv_or(2, round_sticky); - expr * round_lors[2] = { m_bv_util.mk_bv_not(round), m_bv_util.mk_bv_not(last_or_sticky) }; - expr * pos_args[2] = { sgn, m_bv_util.mk_bv_not(round_or_sticky) }; - expr * neg_args[2] = { m_bv_util.mk_bv_not(sgn), m_bv_util.mk_bv_not(round_or_sticky) }; + expr_ref last_or_sticky(m), round_or_sticky(m), not_round(m), not_lors(m), not_rors(m), not_sgn(m); + expr * last_sticky[2] = { last, sticky }; + expr * round_sticky[2] = { round, sticky }; + last_or_sticky = m_bv_util.mk_bv_or(2, last_sticky); + round_or_sticky = m_bv_util.mk_bv_or(2, round_sticky); + not_round = m_bv_util.mk_bv_not(round); + not_lors = m_bv_util.mk_bv_not(last_or_sticky); + not_rors = m_bv_util.mk_bv_not(round_or_sticky); + not_sgn = m_bv_util.mk_bv_not(sgn); + expr * round_lors[2] = { not_round, not_lors}; + expr * pos_args[2] = { sgn, not_rors }; + expr * neg_args[2] = { not_sgn, not_rors }; expr_ref inc_teven(m), inc_taway(m), inc_pos(m), inc_neg(m); - inc_teven = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, round_lors)); - inc_taway = round; - inc_pos = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, pos_args)); - inc_neg = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, neg_args)); + inc_teven = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, round_lors)); + inc_taway = round; + inc_pos = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, pos_args)); + inc_neg = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, neg_args)); expr_ref inc(m), inc_c2(m), inc_c3(m), inc_c4(m); - expr_ref rm_is_to_neg(m), rm_is_to_pos(m), rm_is_away(m), rm_is_even(m); + expr_ref rm_is_to_neg(m), rm_is_to_pos(m), rm_is_away(m), rm_is_even(m), nil_1(m); + nil_1 = m_bv_util.mk_numeral(0, 1); mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_to_pos); mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_is_away); mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_is_even); - m_simp.mk_ite(rm_is_to_neg, inc_neg, m_bv_util.mk_numeral(0, 1), inc_c4); + m_simp.mk_ite(rm_is_to_neg, inc_neg, nil_1, inc_c4); m_simp.mk_ite(rm_is_to_pos, inc_pos, inc_c4, inc_c3); m_simp.mk_ite(rm_is_away, inc_taway, inc_c3, inc_c2); - m_simp.mk_ite(rm_is_even, inc_teven, inc_c2, inc); + m_simp.mk_ite(rm_is_even, inc_teven, inc_c2, inc); - SASSERT(m_bv_util.get_bv_size(inc) == 1 && is_well_sorted(m, inc)); + SASSERT(m_bv_util.get_bv_size(inc) == 1 && is_well_sorted(m, inc)); dbg_decouple("fpa2bv_rnd_inc", inc); - sig = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(1, sig), - m_bv_util.mk_zero_extend(sbits, inc)); - SASSERT(is_well_sorted(m, sig)); + sig = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(1, sig), + m_bv_util.mk_zero_extend(sbits, inc)); + SASSERT(is_well_sorted(m, sig)); dbg_decouple("fpa2bv_rnd_sig_plus_inc", sig); - // Post normalization - SASSERT(m_bv_util.get_bv_size(sig) == sbits + 1); + // Post normalization + SASSERT(m_bv_util.get_bv_size(sig) == sbits + 1); expr_ref SIGovf(m); - m_simp.mk_eq(m_bv_util.mk_extract(sbits, sbits, sig), m_bv_util.mk_numeral(1, 1), SIGovf); + t_sig = m_bv_util.mk_extract(sbits, sbits, sig); + m_simp.mk_eq(t_sig, one_1, SIGovf); SASSERT(is_well_sorted(m, SIGovf)); dbg_decouple("fpa2bv_rnd_SIGovf", SIGovf); - m_simp.mk_ite(SIGovf, - m_bv_util.mk_extract(sbits, 1, sig), - m_bv_util.mk_extract(sbits-1, 0, sig), - sig); + expr_ref hallbut1_sig(m), lallbut1_sig(m); + hallbut1_sig = m_bv_util.mk_extract(sbits, 1, sig); + lallbut1_sig = m_bv_util.mk_extract(sbits-1, 0, sig); + m_simp.mk_ite(SIGovf, hallbut1_sig, lallbut1_sig, sig); - SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); + SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); - m_simp.mk_ite(SIGovf, - m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(1, ebits+2)), - exp, - exp); + expr_ref exp_p1(m); + exp_p1 = m_bv_util.mk_bv_add(exp, m_bv_util.mk_numeral(1, ebits+2)); + m_simp.mk_ite(SIGovf, exp_p1, exp, exp); - SASSERT(is_well_sorted(m, sig)); - SASSERT(is_well_sorted(m, exp)); + SASSERT(is_well_sorted(m, sig)); + SASSERT(is_well_sorted(m, exp)); dbg_decouple("fpa2bv_rnd_sig_postnormalized", sig); dbg_decouple("fpa2bv_rnd_exp_postnormalized", exp); - - SASSERT(m_bv_util.get_bv_size(sig) == sbits); - SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); - SASSERT(m_bv_util.get_bv_size(e_max) == ebits); + + SASSERT(m_bv_util.get_bv_size(sig) == sbits); + SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); + SASSERT(m_bv_util.get_bv_size(e_max) == ebits); - // Exponent adjustment and rounding + // Exponent adjustment and rounding expr_ref biased_exp(m); mk_bias(m_bv_util.mk_extract(ebits-1, 0, exp), biased_exp); dbg_decouple("fpa2bv_rnd_unbiased_exp", exp); @@ -1920,12 +2200,12 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & SASSERT(is_well_sorted(m, OVF1)); SASSERT(m.is_bool(OVF1)); - expr_ref preOVF2(m), OVF2(m), OVF(m); - m_simp.mk_eq(m.mk_app(m_bv_util.get_fid(), OP_BREDAND, biased_exp.get()), m_bv_util.mk_numeral(1, 1), preOVF2); + expr_ref preOVF2(m), OVF2(m), OVF(m), exp_redand(m), pem2m1(m); + exp_redand = m.mk_app(m_bv_util.get_fid(), OP_BREDAND, biased_exp.get()); + m_simp.mk_eq(exp_redand, one_1, preOVF2); m_simp.mk_and(SIGovf, preOVF2, OVF2); - m_simp.mk_ite(OVF2, m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-2), ebits), - biased_exp, - biased_exp); + pem2m1 = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-2), ebits); + m_simp.mk_ite(OVF2, pem2m1, biased_exp, biased_exp); m_simp.mk_or(OVF1, OVF2, OVF); SASSERT(is_well_sorted(m, OVF2)); @@ -1950,11 +2230,11 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & m_simp.mk_eq(sgn, m_bv_util.mk_numeral(0, 1), sgn_is_zero); expr_ref max_sig(m), max_exp(m), inf_sig(m), inf_exp(m); - max_sig = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(sbits-1, false), sbits-1); - max_exp = m_bv_util.mk_concat(m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1, false), ebits-1), + max_sig = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(sbits-1, false), sbits-1); + max_exp = m_bv_util.mk_concat(m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1, false), ebits-1), m_bv_util.mk_numeral(0, 1)); - inf_sig = m_bv_util.mk_numeral(0, sbits-1); - inf_exp = top_exp; + inf_sig = m_bv_util.mk_numeral(0, sbits-1); + inf_exp = top_exp; dbg_decouple("fpa2bv_rnd_max_exp", max_exp); @@ -1962,15 +2242,17 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & m_simp.mk_ite(rm_zero_or_neg, max_exp, inf_exp, max_inf_exp_neg); m_simp.mk_ite(rm_zero_or_pos, max_exp, inf_exp, max_inf_exp_pos); m_simp.mk_ite(sgn_is_zero, max_inf_exp_neg, max_inf_exp_pos, ovfl_exp); - m_simp.mk_eq(m_bv_util.mk_extract(sbits-1, sbits-1, sig), m_bv_util.mk_numeral(0, 1), n_d_check); + t_sig = m_bv_util.mk_extract(sbits-1, sbits-1, sig); + m_simp.mk_eq(t_sig, nil_1, n_d_check); m_simp.mk_ite(n_d_check, bot_exp /* denormal */, biased_exp, n_d_exp); m_simp.mk_ite(OVF, ovfl_exp, n_d_exp, exp); - expr_ref max_inf_sig_neg(m), max_inf_sig_pos(m), ovfl_sig(m); + expr_ref max_inf_sig_neg(m), max_inf_sig_pos(m), ovfl_sig(m), rest_sig(m); m_simp.mk_ite(rm_zero_or_neg, max_sig, inf_sig, max_inf_sig_neg); m_simp.mk_ite(rm_zero_or_pos, max_sig, inf_sig, max_inf_sig_pos); m_simp.mk_ite(sgn_is_zero, max_inf_sig_neg, max_inf_sig_pos, ovfl_sig); - m_simp.mk_ite(OVF, ovfl_sig, m_bv_util.mk_extract(sbits-2, 0, sig), sig); + rest_sig = m_bv_util.mk_extract(sbits-2, 0, sig); + m_simp.mk_ite(OVF, ovfl_sig, rest_sig, sig); dbg_decouple("fpa2bv_rnd_sgn_final", sgn); dbg_decouple("fpa2bv_rnd_sig_final", sig); @@ -1978,26 +2260,21 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = sgn; - res_sig = sig; - res_exp = exp; - - SASSERT(m_bv_util.get_bv_size(res_sgn) == 1); - SASSERT(is_well_sorted(m, res_sgn)); + res_sig = sig; + res_exp = exp; + + SASSERT(m_bv_util.get_bv_size(res_sgn) == 1); + SASSERT(is_well_sorted(m, res_sgn)); SASSERT(m_bv_util.get_bv_size(res_sig) == sbits-1); - SASSERT(is_well_sorted(m, res_sig)); + SASSERT(is_well_sorted(m, res_sig)); SASSERT(m_bv_util.get_bv_size(res_exp) == ebits); - SASSERT(is_well_sorted(m, res_exp)); + SASSERT(is_well_sorted(m, res_exp)); - mk_triple(res_sgn, res_sig, res_exp, result); + mk_triple(res_sgn, res_sig, res_exp, result); TRACE("fpa2bv_round", tout << "ROUND = " << mk_ismt2_pp(result, m) << std::endl; ); } - -fpa2bv_model_converter * fpa2bv_converter::mk_model_converter() { - return alloc(fpa2bv_model_converter, m, m_const2bv, m_rm_const2bv); -} - void fpa2bv_model_converter::display(std::ostream & out) { out << "(fpa2bv-model-converter"; for (obj_map::iterator it = m_const2bv.begin(); @@ -2008,7 +2285,7 @@ void fpa2bv_model_converter::display(std::ostream & out) { unsigned indent = n.size() + 4; out << mk_ismt2_pp(it->m_value, m, indent) << ")"; } - for (obj_map::iterator it = m_rm_const2bv.begin(); + for (obj_map::iterator it = m_rm_const2bv.begin(); it != m_rm_const2bv.end(); it++) { const symbol & n = it->m_key->get_name(); @@ -2024,11 +2301,23 @@ model_converter * fpa2bv_model_converter::translate(ast_translation & translator for (obj_map::iterator it = m_const2bv.begin(); it != m_const2bv.end(); it++) - res->m_const2bv.insert(translator(it->m_key), translator(it->m_value)); - for (obj_map::iterator it = m_rm_const2bv.begin(); + { + func_decl * k = translator(it->m_key); + expr * v = translator(it->m_value); + res->m_const2bv.insert(k, v); + translator.to().inc_ref(k); + translator.to().inc_ref(v); + } + for (obj_map::iterator it = m_rm_const2bv.begin(); it != m_rm_const2bv.end(); it++) - res->m_rm_const2bv.insert(translator(it->m_key), translator(it->m_value)); + { + func_decl * k = translator(it->m_key); + expr * v = translator(it->m_value); + res->m_rm_const2bv.insert(k, v); + translator.to().inc_ref(k); + translator.to().inc_ref(v); + } return res; } @@ -2059,9 +2348,10 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { unsigned ebits = fu.get_ebits(var->get_range()); unsigned sbits = fu.get_sbits(var->get_range()); - expr * sgn = bv_mdl->get_const_interp(to_app(a->get_arg(0))->get_decl()); - expr * sig = bv_mdl->get_const_interp(to_app(a->get_arg(1))->get_decl()); - expr * exp = bv_mdl->get_const_interp(to_app(a->get_arg(2))->get_decl()); + expr_ref sgn(m), sig(m), exp(m); + sgn = bv_mdl->get_const_interp(to_app(a->get_arg(0))->get_decl()); + sig = bv_mdl->get_const_interp(to_app(a->get_arg(1))->get_decl()); + exp = bv_mdl->get_const_interp(to_app(a->get_arg(2))->get_decl()); seen.insert(to_app(a->get_arg(0))->get_decl()); seen.insert(to_app(a->get_arg(1))->get_decl()); @@ -2098,30 +2388,30 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { mpzm.del(sig_z); } - for (obj_map::iterator it = m_rm_const2bv.begin(); + for (obj_map::iterator it = m_rm_const2bv.begin(); it != m_rm_const2bv.end(); it++) { - func_decl * var = it->m_key; + func_decl * var = it->m_key; app * a = to_app(it->m_value); SASSERT(fu.is_rm(var->get_range())); - rational val(0); - unsigned sz = 0; - if (a && bu.is_numeral(a, val, sz)) { - TRACE("fpa2bv_mc", tout << var->get_name() << " == " << val.to_string() << std::endl; ); - SASSERT(val.is_uint64()); - switch (val.get_uint64()) - { - case BV_RM_TIES_TO_AWAY: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_away()); break; - case BV_RM_TIES_TO_EVEN: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_even()); break; - case BV_RM_TO_NEGATIVE: float_mdl->register_decl(var, fu.mk_round_toward_negative()); break; - case BV_RM_TO_POSITIVE: float_mdl->register_decl(var, fu.mk_round_toward_positive()); break; - case BV_RM_TO_ZERO: - default: float_mdl->register_decl(var, fu.mk_round_toward_zero()); - } + rational val(0); + unsigned sz = 0; + if (a && bu.is_numeral(a, val, sz)) { + TRACE("fpa2bv_mc", tout << var->get_name() << " == " << val.to_string() << std::endl; ); + SASSERT(val.is_uint64()); + switch (val.get_uint64()) + { + case BV_RM_TIES_TO_AWAY: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_away()); break; + case BV_RM_TIES_TO_EVEN: float_mdl->register_decl(var, fu.mk_round_nearest_ties_to_even()); break; + case BV_RM_TO_NEGATIVE: float_mdl->register_decl(var, fu.mk_round_toward_negative()); break; + case BV_RM_TO_POSITIVE: float_mdl->register_decl(var, fu.mk_round_toward_positive()); break; + case BV_RM_TO_ZERO: + default: float_mdl->register_decl(var, fu.mk_round_toward_zero()); + } seen.insert(var); - } - } + } + } fu.fm().del(fp_val); @@ -2130,9 +2420,8 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { for (unsigned i = 0; i < sz; i++) { func_decl * c = bv_mdl->get_constant(i); - if (seen.contains(c)) - continue; - float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); + if (!seen.contains(c)) + float_mdl->register_decl(c, bv_mdl->get_const_interp(c)); } // And keep everything else @@ -2151,3 +2440,9 @@ void fpa2bv_model_converter::convert(model * bv_mdl, model * float_mdl) { float_mdl->register_usort(s, u.size(), u.c_ptr()); } } + +model_converter * mk_fpa2bv_model_converter(ast_manager & m, + obj_map const & const2bv, + obj_map const & rm_const2bv) { + return alloc(fpa2bv_model_converter, m, const2bv, rm_const2bv); +} diff --git a/src/tactic/fpa/fpa2bv_converter.h b/src/tactic/fpa/fpa2bv_converter.h index 1ee374941..e5d546763 100644 --- a/src/tactic/fpa/fpa2bv_converter.h +++ b/src/tactic/fpa/fpa2bv_converter.h @@ -35,13 +35,13 @@ class fpa2bv_converter { ast_manager & m; basic_simplifier_plugin m_simp; float_util m_util; - mpf_manager & m_mpf_manager; - unsynch_mpz_manager & m_mpz_manager; + mpf_manager & m_mpf_manager; + unsynch_mpz_manager & m_mpz_manager; bv_util m_bv_util; float_decl_plugin * m_plugin; obj_map m_const2bv; - obj_map m_rm_const2bv; + obj_map m_rm_const2bv; public: fpa2bv_converter(ast_manager & m); @@ -52,22 +52,22 @@ public: bool is_float(sort * s) { return m_util.is_float(s); } bool is_float(expr * e) { return is_app(e) && m_util.is_float(to_app(e)->get_decl()->get_range()); } bool is_float_family(func_decl * f) { return f->get_family_id() == m_util.get_family_id(); } - bool is_rm_sort(sort * s) { return m_util.is_rm(s); } + bool is_rm_sort(sort * s) { return m_util.is_rm(s); } void mk_triple(expr * sign, expr * significand, expr * exponent, expr_ref & result) { - SASSERT(m_bv_util.is_bv(sign) && m_bv_util.get_bv_size(sign) == 1); - SASSERT(m_bv_util.is_bv(significand)); - SASSERT(m_bv_util.is_bv(exponent)); + SASSERT(m_bv_util.is_bv(sign) && m_bv_util.get_bv_size(sign) == 1); + SASSERT(m_bv_util.is_bv(significand)); + SASSERT(m_bv_util.is_bv(exponent)); result = m.mk_app(m_util.get_family_id(), OP_TO_FLOAT, sign, significand, exponent); } void mk_eq(expr * a, expr * b, expr_ref & result); void mk_ite(expr * c, expr * t, expr * f, expr_ref & result); - void mk_rounding_mode(func_decl * f, expr_ref & result); + void mk_rounding_mode(func_decl * f, expr_ref & result); void mk_value(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_const(func_decl * f, expr_ref & result); - void mk_rm_const(func_decl * f, expr_ref & result); + void mk_rm_const(func_decl * f, expr_ref & result); void mk_plus_inf(func_decl * f, expr_ref & result); void mk_minus_inf(func_decl * f, expr_ref & result); @@ -102,7 +102,8 @@ public: void mk_to_float(func_decl * f, unsigned num, expr * const * args, expr_ref & result); void mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * args, expr_ref & result); - fpa2bv_model_converter * mk_model_converter(); + obj_map const & const2bv() const { return m_const2bv; } + obj_map const & rm_const2bv() const { return m_rm_const2bv; } void dbg_decouple(const char * prefix, expr_ref & e); expr_ref_vector extra_assertions; @@ -122,11 +123,11 @@ protected: void mk_is_denormal(expr * e, expr_ref & result); void mk_is_normal(expr * e, expr_ref & result); - void mk_is_rm(expr * e, BV_RM_VAL rm, expr_ref & result); + void mk_is_rm(expr * e, BV_RM_VAL rm, expr_ref & result); void mk_top_exp(unsigned sz, expr_ref & result); void mk_bot_exp(unsigned sz, expr_ref & result); - void mk_min_exp(unsigned ebits, expr_ref & result); + void mk_min_exp(unsigned ebits, expr_ref & result); void mk_max_exp(unsigned ebits, expr_ref & result); void mk_leading_zeros(expr * e, unsigned max_bits, expr_ref & result); @@ -134,8 +135,8 @@ protected: void mk_bias(expr * e, expr_ref & result); void mk_unbias(expr * e, expr_ref & result); - void unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, bool normalize); - void round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & result); + void unpack(expr * e, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & lz, bool normalize); + void round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & result); void add_core(unsigned sbits, unsigned ebits, expr_ref & rm, expr_ref & c_sgn, expr_ref & c_sig, expr_ref & c_exp, expr_ref & d_sgn, expr_ref & d_sig, expr_ref & d_exp, @@ -146,11 +147,11 @@ protected: class fpa2bv_model_converter : public model_converter { ast_manager & m; obj_map m_const2bv; - obj_map m_rm_const2bv; + obj_map m_rm_const2bv; public: - fpa2bv_model_converter(ast_manager & m, obj_map & const2bv, - obj_map & rm_const2bv) : + fpa2bv_model_converter(ast_manager & m, obj_map const & const2bv, + obj_map const & rm_const2bv) : m(m) { // Just create a copy? for (obj_map::iterator it = const2bv.begin(); @@ -161,7 +162,7 @@ public: m.inc_ref(it->m_key); m.inc_ref(it->m_value); } - for (obj_map::iterator it = rm_const2bv.begin(); + for (obj_map::iterator it = rm_const2bv.begin(); it != rm_const2bv.end(); it++) { @@ -173,7 +174,7 @@ public: virtual ~fpa2bv_model_converter() { dec_ref_map_key_values(m, m_const2bv); - dec_ref_map_key_values(m, m_rm_const2bv); + dec_ref_map_key_values(m, m_rm_const2bv); } virtual void operator()(model_ref & md, unsigned goal_idx) { @@ -198,4 +199,9 @@ protected: void convert(model * bv_mdl, model * float_mdl); }; + +model_converter * mk_fpa2bv_model_converter(ast_manager & m, + obj_map const & const2bv, + obj_map const & rm_const2bv); + #endif diff --git a/src/tactic/fpa/fpa2bv_rewriter.h b/src/tactic/fpa/fpa2bv_rewriter.h index 94694867a..4b3525a32 100644 --- a/src/tactic/fpa/fpa2bv_rewriter.h +++ b/src/tactic/fpa/fpa2bv_rewriter.h @@ -73,7 +73,7 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { return BR_DONE; } - if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_rm_sort(f->get_range())) { + if (num == 0 && f->get_family_id() == null_family_id && m_conv.is_rm_sort(f->get_range())) { m_conv.mk_rm_const(f, result); return BR_DONE; } @@ -102,7 +102,7 @@ struct fpa2bv_rewriter_cfg : public default_rewriter_cfg { case OP_RM_NEAREST_TIES_TO_EVEN: case OP_RM_TOWARD_NEGATIVE: case OP_RM_TOWARD_POSITIVE: - case OP_RM_TOWARD_ZERO: m_conv.mk_rounding_mode(f, result); return BR_DONE; + case OP_RM_TOWARD_ZERO: m_conv.mk_rounding_mode(f, result); return BR_DONE; case OP_FLOAT_VALUE: m_conv.mk_value(f, num, args, result); return BR_DONE; case OP_FLOAT_PLUS_INF: m_conv.mk_plus_inf(f, result); return BR_DONE; case OP_FLOAT_MINUS_INF: m_conv.mk_minus_inf(f, result); return BR_DONE; diff --git a/src/tactic/fpa/fpa2bv_tactic.cpp b/src/tactic/fpa/fpa2bv_tactic.cpp index 79f41518e..a90ff9317 100644 --- a/src/tactic/fpa/fpa2bv_tactic.cpp +++ b/src/tactic/fpa/fpa2bv_tactic.cpp @@ -90,7 +90,7 @@ class fpa2bv_tactic : public tactic { } if (g->models_enabled()) - mc = m_conv.mk_model_converter(); + mc = mk_fpa2bv_model_converter(m, m_conv.const2bv(), m_conv.rm_const2bv()); g->inc_depth(); result.push_back(g.get()); diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index c6dad42b2..03a13aba5 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -111,19 +111,29 @@ void goal::push_back(expr * f, proof * pr, expr_dependency * d) { if (m().is_true(f)) return; if (m().is_false(f)) { + // Make sure pr and d are not deleted by the m().del(...) statements. + proof_ref saved_pr(m()); + expr_dependency_ref saved_d(m()); + saved_pr = pr; + saved_d = d; m().del(m_forms); m().del(m_proofs); m().del(m_dependencies); m_inconsistent = true; + m().push_back(m_forms, m().mk_false()); + if (proofs_enabled()) + m().push_back(m_proofs, saved_pr); + if (unsat_core_enabled()) + m().push_back(m_dependencies, saved_d); } else { SASSERT(!m_inconsistent); + m().push_back(m_forms, f); + if (proofs_enabled()) + m().push_back(m_proofs, pr); + if (unsat_core_enabled()) + m().push_back(m_dependencies, d); } - m().push_back(m_forms, f); - if (proofs_enabled()) - m().push_back(m_proofs, pr); - if (unsat_core_enabled()) - m().push_back(m_dependencies, d); } void goal::quick_process(bool save_first, expr * & f, expr_dependency * d) { diff --git a/src/tactic/portfolio/default_tactic.cpp b/src/tactic/portfolio/default_tactic.cpp index c5687cd1a..d65ba8d35 100644 --- a/src/tactic/portfolio/default_tactic.cpp +++ b/src/tactic/portfolio/default_tactic.cpp @@ -26,22 +26,18 @@ Notes: #include"qfnra_tactic.h" #include"nra_tactic.h" #include"probe_arith.h" +#include"quant_tactics.h" tactic * mk_default_tactic(ast_manager & m, params_ref const & p) { tactic * st = using_params(and_then(mk_simplify_tactic(m), - cond(mk_is_qfbv_probe(), - mk_qfbv_tactic(m), - cond(mk_is_qflia_probe(), - mk_qflia_tactic(m), - cond(mk_is_qflra_probe(), - mk_qflra_tactic(m), - cond(mk_is_qfnra_probe(), - mk_qfnra_tactic(m), - cond(mk_is_qfnia_probe(), - mk_qfnia_tactic(m), - cond(mk_is_nra_probe(), - mk_nra_tactic(m), - mk_smt_tactic()))))))), + cond(mk_is_qfbv_probe(), mk_qfbv_tactic(m), + cond(mk_is_qflia_probe(), mk_qflia_tactic(m), + cond(mk_is_qflra_probe(), mk_qflra_tactic(m), + cond(mk_is_qfnra_probe(), mk_qfnra_tactic(m), + cond(mk_is_qfnia_probe(), mk_qfnia_tactic(m), + cond(mk_is_nra_probe(), mk_nra_tactic(m), + cond(mk_is_lira_probe(), mk_lira_tactic(m, p), + mk_smt_tactic())))))))), p); return st; } diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 0e63255ca..55512f677 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -72,6 +72,8 @@ tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const return mk_uflra_tactic(m, p); else if (logic=="LRA") return mk_lra_tactic(m, p); + else if (logic=="LIA") + return mk_lia_tactic(m, p); else if (logic=="UFBV") return mk_ufbv_tactic(m, p); else if (logic=="BV") diff --git a/src/tactic/smtlogics/quant_tactics.cpp b/src/tactic/smtlogics/quant_tactics.cpp index 6b5ede813..937b0229e 100644 --- a/src/tactic/smtlogics/quant_tactics.cpp +++ b/src/tactic/smtlogics/quant_tactics.cpp @@ -22,6 +22,7 @@ Revision History: #include"solve_eqs_tactic.h" #include"elim_uncnstr_tactic.h" #include"qe_tactic.h" +#include"qe_sat_tactic.h" #include"ctx_simplify_tactic.h" #include"smt_tactic.h" @@ -98,9 +99,19 @@ tactic * mk_aufnira_tactic(ast_manager & m, params_ref const & p) { tactic * mk_lra_tactic(ast_manager & m, params_ref const & p) { tactic * st = and_then(mk_quant_preprocessor(m), - mk_qe_tactic(m), - mk_smt_tactic()); + or_else(try_for(mk_smt_tactic(), 100), + try_for(qe::mk_sat_tactic(m), 1000), + try_for(mk_smt_tactic(), 1000), + and_then(mk_qe_tactic(m), mk_smt_tactic()))); + st->updt_params(p); return st; } +tactic * mk_lia_tactic(ast_manager & m, params_ref const & p) { + return mk_lra_tactic(m, p); +} + +tactic * mk_lira_tactic(ast_manager & m, params_ref const & p) { + return mk_lra_tactic(m, p); +} diff --git a/src/tactic/smtlogics/quant_tactics.h b/src/tactic/smtlogics/quant_tactics.h index dc8c458c4..5cf27cde4 100644 --- a/src/tactic/smtlogics/quant_tactics.h +++ b/src/tactic/smtlogics/quant_tactics.h @@ -29,5 +29,7 @@ tactic * mk_auflia_tactic(ast_manager & m, params_ref const & p); tactic * mk_auflira_tactic(ast_manager & m, params_ref const & p); tactic * mk_aufnira_tactic(ast_manager & m, params_ref const & p); tactic * mk_lra_tactic(ast_manager & m, params_ref const & p); +tactic * mk_lia_tactic(ast_manager & m, params_ref const & p); +tactic * mk_lira_tactic(ast_manager & m, params_ref const & p); #endif diff --git a/src/test/bit_vector.cpp b/src/test/bit_vector.cpp index 2b67c3a71..7d3f96ae4 100644 --- a/src/test/bit_vector.cpp +++ b/src/test/bit_vector.cpp @@ -276,12 +276,35 @@ static void tst_bv_reset() { } } +static void tst_eq() { + bit_vector b1, b2, b3; + b1.resize(32); + b2.resize(32); + b3.resize(32); + + b1.set(3, true); + SASSERT(b1 != b2); + SASSERT(!(b1 == b2)); + SASSERT(b2 == b3); + + b3.set(3, true); + SASSERT(b1 == b3); + SASSERT(!(b1 != b3)); + + b2.set(31, true); + b3.set(31); + b3.unset(3); + SASSERT(b2 == b3); + SASSERT(!(b2 != b3)); +} + void tst_bit_vector() { tst_crash(); tst_shift(); tst_or(); tst_and(); tst_bv_reset(); + tst_eq(); return; tst2(); for (unsigned i = 0; i < 20; i++) { diff --git a/src/test/fdd.cpp b/src/test/fdd.cpp new file mode 100644 index 000000000..2a0079456 --- /dev/null +++ b/src/test/fdd.cpp @@ -0,0 +1,87 @@ +#include "fdd.h" + +static void test1() { + fdd::manager m; + + m.reset(2); + int64 keys1[2] = { 1, 2 }; + m.insert(keys1); + m.display(std::cout << "test1\n"); +} + +static void test2() { + fdd::manager m; + + m.reset(2); + int64 keys2[2] = { 2, 1 }; + m.insert(keys2); + m.display(std::cout << "test2\n"); + +} + +static void test3() { + fdd::manager m; + + m.reset(2); + int64 keys1[2] = { 1, 2 }; + int64 keys2[2] = { 2, 1 }; + m.insert(keys1); + m.insert(keys2); + m.display(std::cout << "test3\n"); +} + +static void test4() { + fdd::manager m; + + std::cout << "test4\n"; + + m.reset(2); + int64 keys1[2] = { 1, 2 }; + int64 keys2[2] = { 2, 1 }; + int64 keys3[2] = { 1, 1 }; + int64 keys4[2] = { 2, 2 }; + int64 keys5[2] = { 2, 3 }; + int64 keys6[2] = { 3, 1 }; + int64 keys7[2] = { 3, 4 }; + m.insert(keys1); + m.insert(keys2); + std::cout << m.find_le(keys1) << "\n"; + std::cout << m.find_le(keys2) << "\n"; + std::cout << m.find_le(keys3) << "\n"; + std::cout << m.find_le(keys4) << "\n"; + std::cout << m.find_le(keys5) << "\n"; + std::cout << m.find_le(keys6) << "\n"; + std::cout << m.find_le(keys7) << "\n"; + + SASSERT(m.find_le(keys1)); + SASSERT(m.find_le(keys2)); + SASSERT(!m.find_le(keys3)); + SASSERT(m.find_le(keys4)); + SASSERT(m.find_le(keys5)); + SASSERT(m.find_le(keys6)); + SASSERT(m.find_le(keys7)); +} + +static void test5() { + fdd::manager m; + + std::cout << "test5\n"; + + m.reset(2); + int64 keys1[2] = { 1, 2 }; + int64 keys2[2] = { 2, 1 }; + m.insert(keys1); + m.insert(keys2); + m.insert(keys2); + + m.display(std::cout); + +} + +void tst_fdd() { + test1(); + test2(); + test3(); + test4(); + test5(); +} diff --git a/src/test/heap_trie.cpp b/src/test/heap_trie.cpp index ad3cd0a8d..92ef97f72 100644 --- a/src/test/heap_trie.cpp +++ b/src/test/heap_trie.cpp @@ -4,7 +4,8 @@ struct unsigned_le { static bool le(unsigned i, unsigned j) { return i <= j; } }; -typedef heap_trie heap_trie_t; + +typedef heap_trie heap_trie_t; static void find_le(heap_trie_t& ht, unsigned num_keys, unsigned const* keys) { statistics st; @@ -24,8 +25,10 @@ static void find_le(heap_trie_t& ht, unsigned num_keys, unsigned const* keys) { } + void tst_heap_trie() { - heap_trie_t ht; + unsigned_le le; + heap_trie_t ht(le); ht.reset(3); unsigned keys1[3] = { 1, 2, 3}; diff --git a/src/test/hilbert_basis.cpp b/src/test/hilbert_basis.cpp index b02c8c90c..4752dd78d 100644 --- a/src/test/hilbert_basis.cpp +++ b/src/test/hilbert_basis.cpp @@ -10,6 +10,9 @@ #include #include +static bool g_use_ordered_support = false; +static bool g_use_ordered_subsumption = false; +static bool g_use_support = false; class hilbert_basis_validate { ast_manager& m; @@ -217,6 +220,7 @@ static void on_ctrl_c(int) { raise(SIGINT); } +#if 0 static void validate_sat(hilbert_basis& hb) { ast_manager m; reg_decl_plugins(m); @@ -236,18 +240,22 @@ static void validate_sat(hilbert_basis& hb) { lbool r = sol->check_sat(0,0); std::cout << r << "\n"; } +#endif static void saturate_basis(hilbert_basis& hb) { signal(SIGINT, on_ctrl_c); g_hb = &hb; g_start_time = static_cast(clock()); + hb.set_use_ordered_support(g_use_ordered_support); + hb.set_use_support(g_use_support); + hb.set_use_ordered_subsumption(g_use_ordered_subsumption); lbool is_sat = hb.saturate(); switch(is_sat) { case l_true: std::cout << "sat\n"; hb.display(std::cout); - validate_sat(hb); + //validate_sat(hb); break; case l_false: std::cout << "unsat\n"; @@ -502,15 +510,60 @@ static void tst15() { saturate_basis(hb); } +static void tst16() { + hilbert_basis hb; + hb.add_le(vec(1, 0), R(100)); + saturate_basis(hb); +} + +static void tst17() { + hilbert_basis hb; + hb.add_eq(vec(1, 0), R(0)); + hb.add_eq(vec(-1, 0), R(0)); + hb.add_eq(vec(0, 2), R(0)); + hb.add_eq(vec(0, -2), R(0)); + saturate_basis(hb); + +} + +static void tst18() { + hilbert_basis hb; + hb.add_eq(vec(0, 1), R(0)); + hb.add_eq(vec(1, -1), R(2)); + saturate_basis(hb); +} + +static void tst19() { + hilbert_basis hb; + hb.add_eq(vec(0, 1, 0), R(0)); + hb.add_eq(vec(1, -1, 0), R(2)); + saturate_basis(hb); +} void tst_hilbert_basis() { std::cout << "hilbert basis test\n"; +// tst3(); +// return; + + g_use_ordered_support = true; + + tst18(); + return; + + tst19(); + return; + tst17(); if (true) { tst1(); tst2(); tst3(); tst4(); + tst4(); + tst4(); + tst4(); + tst4(); + tst4(); tst5(); tst6(); tst7(); @@ -522,6 +575,7 @@ void tst_hilbert_basis() { tst13(); tst14(); tst15(); + tst16(); gorrila_test(0, 4, 3, 20, 5); gorrila_test(1, 4, 3, 20, 5); //gorrila_test(2, 4, 3, 20, 5); @@ -531,4 +585,14 @@ void tst_hilbert_basis() { else { gorrila_test(0, 10, 7, 20, 11); } + + return; + std::cout << "ordered support\n"; + g_use_ordered_support = true; + tst4(); + + std::cout << "non-ordered support\n"; + g_use_ordered_support = false; + tst4(); + } diff --git a/src/test/karr.cpp b/src/test/karr.cpp new file mode 100644 index 000000000..87debf662 --- /dev/null +++ b/src/test/karr.cpp @@ -0,0 +1,294 @@ +#include "hilbert_basis.h" + +/* + Test generation of linear congruences a la Karr. + + */ +namespace karr { + + struct matrix { + vector > A; + vector b; + + unsigned size() const { return A.size(); } + + void reset() { + A.reset(); + b.reset(); + } + + matrix& operator=(matrix const& other) { + reset(); + append(other); + return *this; + } + + void append(matrix const& other) { + A.append(other.A); + b.append(other.b); + } + + void display(std::ostream& out) { + for (unsigned i = 0; i < A.size(); ++i) { + for (unsigned j = 0; j < A[i].size(); ++j) { + out << A[i][j] << " "; + } + out << " = " << -b[i] << "\n"; + } + } + }; + + // treat src as a homogeneous matrix. + void dualizeH(matrix& dst, matrix const& src) { + hilbert_basis hb; + for (unsigned i = 0; i < src.size(); ++i) { + vector v(src.A[i]); + v.push_back(src.b[i]); + hb.add_eq(v, rational(0)); + } + for (unsigned i = 0; i < 1 + src.A[0].size(); ++i) { + hb.set_is_int(i); + } + lbool is_sat = hb.saturate(); + hb.display(std::cout); + SASSERT(is_sat == l_true); + dst.reset(); + unsigned basis_size = hb.get_basis_size(); + for (unsigned i = 0; i < basis_size; ++i) { + bool is_initial; + vector soln; + hb.get_basis_solution(i, soln, is_initial); + if (!is_initial) { + dst.b.push_back(soln.back()); + soln.pop_back(); + dst.A.push_back(soln); + } + } + } + + // treat src as an inhomegeneous matrix. + void dualizeI(matrix& dst, matrix const& src) { + hilbert_basis hb; + for (unsigned i = 0; i < src.size(); ++i) { + hb.add_eq(src.A[i], -src.b[i]); + } + for (unsigned i = 0; i < src.A[0].size(); ++i) { + hb.set_is_int(i); + } + lbool is_sat = hb.saturate(); + hb.display(std::cout); + SASSERT(is_sat == l_true); + dst.reset(); + unsigned basis_size = hb.get_basis_size(); + bool first_initial = true; + for (unsigned i = 0; i < basis_size; ++i) { + bool is_initial; + vector soln; + hb.get_basis_solution(i, soln, is_initial); + if (is_initial && first_initial) { + dst.A.push_back(soln); + dst.b.push_back(rational(1)); + first_initial = false; + } + else if (!is_initial) { + dst.A.push_back(soln); + dst.b.push_back(rational(0)); + } + } + } + + void juxtapose(matrix& dst, matrix const& M, matrix const& N) { + dst = M; + dst.append(N); + } + + void join(matrix& dst, matrix const& M, matrix const& N) { + matrix MD, ND, dstD; + dualizeI(MD, M); + dualizeI(ND, N); + juxtapose(dstD, MD, ND); + dualizeH(dst, dstD); + } + + void joinD(matrix& dst, matrix const& MD, matrix const& ND) { + matrix dstD; + juxtapose(dstD, MD, ND); + dualizeH(dst, dstD); + } + + void transition( + matrix& dst, + matrix const& src, + matrix const& Ab) { + matrix T; + // length of rows in Ab are twice as long as + // length of rows in src. + SASSERT(2*src.A[0].size() == Ab.A[0].size()); + vector zeros; + for (unsigned i = 0; i < src.A[0].size(); ++i) { + zeros.push_back(rational(0)); + } + for (unsigned i = 0; i < src.size(); ++i) { + T.A.push_back(src.A[i]); + T.A.back().append(zeros); + } + T.b.append(src.b); + T.append(Ab); + + T.display(std::cout << "T:\n"); + matrix TD; + dualizeI(TD, T); + TD.display(std::cout << "TD:\n"); + for (unsigned i = 0; i < TD.size(); ++i) { + vector v; + v.append(src.size(), TD.A[i].c_ptr() + src.size()); + dst.A.push_back(v); + dst.b.push_back(TD.b[i]); + } + dst.display(std::cout << "dst\n"); + } + + static vector V(int i, int j) { + vector v; + v.push_back(rational(i)); + v.push_back(rational(j)); + return v; + } + + static vector V(int i, int j, int k, int l) { + vector v; + v.push_back(rational(i)); + v.push_back(rational(j)); + v.push_back(rational(k)); + v.push_back(rational(l)); + return v; + } + +#if 0 + static vector V(int i, int j, int k, int l, int m) { + vector v; + v.push_back(rational(i)); + v.push_back(rational(j)); + v.push_back(rational(k)); + v.push_back(rational(l)); + v.push_back(rational(m)); + return v; + } +#endif + + static vector V(int i, int j, int k, int l, int x, int y, int z) { + vector v; + v.push_back(rational(i)); + v.push_back(rational(j)); + v.push_back(rational(k)); + v.push_back(rational(l)); + v.push_back(rational(x)); + v.push_back(rational(y)); + v.push_back(rational(z)); + return v; + } + +#define R(_x_) rational(_x_) + + + static void tst1() { + matrix Theta; + matrix Ab; + + // + Theta.A.push_back(V(1, 0)); + Theta.b.push_back(R(0)); + Theta.A.push_back(V(0, 1)); + Theta.b.push_back(R(-2)); + + Theta.display(std::cout << "Theta\n"); + + Ab.A.push_back(V(-1, 0, 1, 0)); + Ab.b.push_back(R(1)); + Ab.A.push_back(V(-1, -2, 0, 1)); + Ab.b.push_back(R(1)); + + Ab.display(std::cout << "Ab\n"); + + matrix ThetaD; + dualizeI(ThetaD, Theta); + ThetaD.display(std::cout); + + matrix t1D, e1; + transition(t1D, Theta, Ab); + joinD(e1, t1D, ThetaD); + + t1D.display(std::cout << "t1D\n"); + e1.display(std::cout << "e1\n"); + + matrix t2D, e2; + transition(t2D, e1, Ab); + joinD(e2, t2D, ThetaD); + + t2D.display(std::cout << "t2D\n"); + e2.display(std::cout << "e2\n"); + } + + void tst2() { + /** + 0 0 0 0 0 0 0 = 0 + 0 0 0 0 0 0 0 = 0 + 0 0 0 0 0 0 0 = 0 + 0 0 0 0 0 0 0 = 0 + 0 0 0 0 1 0 0 = 0 + 0 0 0 0 -1 0 0 = 0 + 0 1 0 0 0 0 0 = 0 + 0 -1 0 0 0 0 0 = 0 + 0 0 0 2 0 0 0 = 0 + 0 0 0 -2 0 0 0 = 0 + */ + + matrix ND; + ND.A.push_back(V(0,0,0,0,1,0,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,0,0,0,-1,0,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,1,0,0,0,0,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,-1,0,0,0,0,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,0,0,2,0,0,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,0,0,-2,0,0,0)); ND.b.push_back(R(0)); + + ND.display(std::cout << "ND\n"); + + matrix N; + dualizeH(N, ND); + + N.display(std::cout << "N\n"); + + + } + + void tst3() { + /** + 0 0 0 0 1 0 0 = 0 + 0 0 0 0 -1 0 0 = 0 + 0 1 0 0 0 0 0 = 0 + 0 -1 0 0 0 0 0 = 0 + 0 0 0 2 0 0 0 = 0 + 0 0 0 -2 0 0 0 = 0 + */ + + matrix ND; + ND.A.push_back(V(1,0)); ND.b.push_back(R(0)); + ND.A.push_back(V(0,2)); ND.b.push_back(R(0)); + + ND.display(std::cout << "ND\n"); + + matrix N; + dualizeH(N, ND); + + N.display(std::cout << "N\n"); + + + } + +}; + +void tst_karr() { + karr::tst3(); + return; + karr::tst1(); +} diff --git a/src/test/main.cpp b/src/test/main.cpp index 9dc12be1c..6ecfd6d4f 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -209,6 +209,8 @@ int main(int argc, char ** argv) { TST(rcf); TST(hilbert_basis); TST(heap_trie); + TST(karr); + TST(fdd); } void initialize_mam() {} diff --git a/src/test/rational.cpp b/src/test/rational.cpp index 9cbcdb00e..834aab92f 100644 --- a/src/test/rational.cpp +++ b/src/test/rational.cpp @@ -171,7 +171,7 @@ static void tst2() { rational int64_max("9223372036854775807"); - rational int64_min(-int64_max - rational(1)); + rational int64_min((-int64_max) - rational(1)); // is_int64 SASSERT(int64_max.is_int64()); SASSERT(int64_min.is_int64()); diff --git a/src/util/bit_vector.cpp b/src/util/bit_vector.cpp index 210d230bc..e3a2bc331 100644 --- a/src/util/bit_vector.cpp +++ b/src/util/bit_vector.cpp @@ -16,7 +16,7 @@ Author: Revision History: --*/ - +#include #include"bit_vector.h" #include"trace.h" @@ -116,7 +116,7 @@ void bit_vector::shift_right(unsigned k) { } } -bool bit_vector::operator==(bit_vector const & source) { +bool bit_vector::operator==(bit_vector const & source) const { if (m_num_bits != source.m_num_bits) return false; unsigned n = num_words(); @@ -129,6 +129,7 @@ bool bit_vector::operator==(bit_vector const & source) { } unsigned bit_rest = source.m_num_bits % 32; unsigned mask = (1 << bit_rest) - 1; + if (mask == 0) mask = UINT_MAX; return (m_data[i] & mask) == (source.m_data[i] & mask); } diff --git a/src/util/bit_vector.h b/src/util/bit_vector.h index f451ae70f..1d6083717 100644 --- a/src/util/bit_vector.h +++ b/src/util/bit_vector.h @@ -37,7 +37,8 @@ class bit_vector { } static unsigned num_words(unsigned num_bits) { - return (num_bits % 32) == 0 ? (num_bits / 32) : ((num_bits / 32) + 1); + // return (num_bits % 32) == 0 ? (num_bits / 32) : ((num_bits / 32) + 1); + return (num_bits + 31) / 32; } void expand_to(unsigned new_capacity); @@ -106,7 +107,7 @@ public: } bool empty() const { - return m_num_bits != 0; + return m_num_bits == 0; } unsigned num_words() const { @@ -171,9 +172,9 @@ public: resize(sz, val); } - bool operator==(bit_vector const & other); + bool operator==(bit_vector const & other) const; - bool operator!=(bit_vector const & other) { return !operator==(other); } + bool operator!=(bit_vector const & other) const { return !operator==(other); } bit_vector & operator=(bit_vector const & source) { m_num_bits = source.m_num_bits; diff --git a/src/util/checked_int64.h b/src/util/checked_int64.h new file mode 100644 index 000000000..3772e5ab0 --- /dev/null +++ b/src/util/checked_int64.h @@ -0,0 +1,231 @@ +/*++ +Copyright (c) 2013 Microsoft Corporation + +Module Name: + + checked_int64.h + +Abstract: + + A class for wrapping checked (and unchecked) int64 operations. + Note: the mpfx class defines a more general class of fixed-point operations. + A tradeoff is that it relies on a manager. + This class several of the most common operations from rational, so + it can be swapped for rational. + +Author: + + Nikolaj Bjorner (nbjorner) 2013-03-25. + +Revision History: + +--*/ + +#ifndef __CHECKED_INT64_H_ +#define __CHECKED_INT64_H_ + +#include"z3_exception.h" +#include"rational.h" + +template +class checked_int64 { + int64 m_value; + typedef checked_int64 ci; + + rational r64(int64 i) { return rational(i, rational::i64()); } + +public: + + checked_int64(): m_value(0) {} + checked_int64(int64 v): m_value(v) {} + checked_int64(checked_int64 const& other) { m_value = other.m_value; } + + class overflow_exception : public z3_exception { + virtual char const * msg() const { return "checked_int64 overflow/underflow";} + }; + + bool is_zero() const { return m_value == 0; } + bool is_pos() const { return m_value > 0; } + bool is_neg() const { return m_value < 0; } + bool is_one() const { return m_value == 1; } + bool is_minus_one() const { return m_value == -1; } + bool is_nonneg() const { return m_value >= 0; } + bool is_nonpos() const { return m_value <= 0; } + bool is_even() const { return 0 == (m_value ^ 0x1); } + + static checked_int64 zero() { return ci(0); } + static checked_int64 one() { return ci(1); } + static checked_int64 minus_one() { return ci(-1);} + + int64 get_int64() const { return m_value; } + + checked_int64 abs() const { + if (m_value >= 0) { + return *this; + } + if (CHECK && m_value == INT64_MIN) { + throw overflow_exception(); + } + return ci(-m_value); + } + + checked_int64& neg() { + if (CHECK && m_value == INT64_MIN) { + throw overflow_exception(); + } + m_value = -m_value; + return *this; + } + + unsigned hash() const { return static_cast(m_value); } + + struct hash_proc { unsigned operator()(checked_int64 const& r) const { return r.hash(); } }; + + struct eq_proc { bool operator()(checked_int64 const& r1, checked_int64 const& r2) const { return r1 == r2; } }; + + friend inline std::ostream& operator<<(std::ostream& out, checked_int64 const& i) { + return out << i.m_value; + } + + friend inline bool operator==(checked_int64 const& a, checked_int64 const& b) { + return a.m_value == b.m_value; + } + + friend inline bool operator<(checked_int64 const& a, checked_int64 const& b) { + return a.m_value < b.m_value; + } + + checked_int64 & operator++() { + if (CHECK && INT64_MAX == m_value) { + throw overflow_exception(); + } + ++m_value; + return *this; + } + + const checked_int64 operator++(int) { checked_int64 tmp(*this); ++(*this); return tmp; } + + checked_int64 & operator--() { + if (CHECK && m_value == INT64_MIN) { + throw overflow_exception(); + } + --m_value; + return *this; + } + + const checked_int64 operator--(int) { checked_int64 tmp(*this); --(*this); return tmp; } + + checked_int64& operator+=(checked_int64 const& other) { + if (CHECK && m_value > 0 && other.m_value > 0 && + (m_value > INT_MAX || other.m_value > INT_MAX)) { + rational r(r64(m_value) + r64(other.m_value)); + if (!r.is_int64()) { + throw overflow_exception(); + } + m_value = r.get_int64(); + return *this; + } + if (CHECK && m_value < 0 && other.m_value < 0 && + (m_value < INT_MIN || other.m_value < INT_MIN)) { + rational r(r64(m_value) + r64(other.m_value)); + if (!r.is_int64()) { + throw overflow_exception(); + } + m_value = r.get_int64(); + return *this; + } + m_value += other.m_value; + return *this; + } + + checked_int64& operator-=(checked_int64 const& other) { + if (CHECK && m_value > 0 && other.m_value < 0 && + (m_value > INT_MAX || other.m_value < INT_MIN)) { + rational r(r64(m_value) - r64(other.m_value)); + if (!r.is_int64()) { + throw overflow_exception(); + } + m_value = r.get_int64(); + return *this; + } + if (CHECK && m_value < 0 && other.m_value > 0 && + (m_value < INT_MIN || other.m_value > INT_MAX)) { + rational r(r64(m_value) - r64(other.m_value)); + if (!r.is_int64()) { + throw overflow_exception(); + } + m_value = r.get_int64(); + return *this; + } + m_value -= other.m_value; + return *this; + } + + checked_int64& operator*=(checked_int64 const& other) { + if (CHECK) { + rational r(r64(m_value) * r64(other.m_value)); + if (!r.is_int64()) { + throw overflow_exception(); + } + m_value = r.get_int64(); + } + else { + m_value *= other.m_value; + } + return *this; + } + + friend inline checked_int64 abs(checked_int64 const& i) { + return i.abs(); + } + +}; + +template +inline bool operator!=(checked_int64 const & i1, checked_int64 const & i2) { + return !operator==(i1, i2); +} + +template +inline bool operator>(checked_int64 const & i1, checked_int64 const & i2) { + return operator<(i2, i1); +} + +template +inline bool operator<=(checked_int64 const & i1, checked_int64 const & i2) { + return !operator>(i1, i2); +} + +template +inline bool operator>=(checked_int64 const & i1, checked_int64 const & i2) { + return !operator<(i1, i2); +} + +template +inline checked_int64 operator-(checked_int64 const& i) { + checked_int64 result(i); + return result.neg(); +} + +template +inline checked_int64 operator+(checked_int64 const& a, checked_int64 const& b) { + checked_int64 result(a); + result += b; + return result; +} + +template +inline checked_int64 operator-(checked_int64 const& a, checked_int64 const& b) { + checked_int64 result(a); + result -= b; + return result; +} + +template +inline checked_int64 operator*(checked_int64 const& a, checked_int64 const& b) { + checked_int64 result(a); + result *= b; + return result; +} + +#endif diff --git a/src/util/env_params.cpp b/src/util/env_params.cpp index 28d80d92d..b01a1c250 100644 --- a/src/util/env_params.cpp +++ b/src/util/env_params.cpp @@ -26,7 +26,7 @@ void env_params::updt_params() { params_ref p = gparams::get(); set_verbosity_level(p.get_uint("verbose", get_verbosity_level())); enable_warning_messages(p.get_bool("warning", true)); - memory::set_max_size(p.get_uint("memory_max_size", 0)); + memory::set_max_size(megabytes_to_bytes(p.get_uint("memory_max_size", 0))); memory::set_high_watermark(p.get_uint("memory_high_watermark", 0)); } diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 8b1fbe40e..3b2e8edc1 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -49,6 +49,7 @@ char const * g_params_renames[] = { "restart_factor", "smt.restart_factor", "arith_random_initial_value", "smt.arith.random_initial_value", "bv_reflect", "smt.bv.reflect", + "bv_enable_int2bv_propagation", "smt.bv.enable_int2bv", "qi_cost", "smt.qi.cost", "qi_eager_threshold", "smt.qi.eager_threshold", "nl_arith", "smt.arith.nl", diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index 219172b34..8585e7eda 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -117,14 +117,14 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, char const * value) { std::string v(value); size_t e_pos = v.find('p'); if (e_pos == std::string::npos) e_pos = v.find('P'); - + std::string f, e; - + f = (e_pos != std::string::npos) ? v.substr(0, e_pos) : v; e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0"; - + TRACE("mpf_dbg", tout << " f = " << f << " e = " << e << std::endl;); - + mpq q; m_mpq_manager.set(q, f.c_str()); @@ -132,14 +132,14 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, char const * value) { m_mpz_manager.set(ex, e.c_str()); set(o, rm, q, ex); - + TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mpz const & exponent) { // Assumption: this represents significand * 2^exponent. set_rounding_mode(rm); - + mpq sig; m_mpq_manager.set(sig, significand); int64 exp = m_mpz_manager.get_int64(exponent); @@ -349,7 +349,7 @@ void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) { else o.value = fmod(x.value, y.value); -// Here is an x87 alternative if the above makes problems; this may also be faster. + // Here is an x87 alternative if the above makes problems; this may also be faster. #if 0 double xv = x.value; double yv = y.value; @@ -434,7 +434,7 @@ void hwf_manager::display_smt2(std::ostream & out, hwf const & a, bool decimal) void hwf_manager::to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o) { SASSERT(is_normal(x) || is_denormal(x) || is_zero(x)); scoped_mpz n(qm), d(qm); - + if (is_normal(x)) qm.set(n, sig(x) | 0x0010000000000000ull); else @@ -466,7 +466,7 @@ bool hwf_manager::is_neg(hwf const & x) { bool hwf_manager::is_pos(hwf const & x) { return !sgn(x) && !is_nan(x); } - + bool hwf_manager::is_nzero(hwf const & x) { return RAW(x.value) == 0x8000000000000000ull; } @@ -581,20 +581,20 @@ void hwf_manager::mk_ninf(hwf & o) { #ifdef _WINDOWS #if defined(_AMD64_) || defined(_M_IA64) - #ifdef USE_INTRINSICS - #define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) - #else - #define SETRM(RM) _controlfp_s(&sse2_state, RM, _MCW_RC); - #endif +#ifdef USE_INTRINSICS +#define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) #else - #ifdef USE_INTRINSICS - #define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) - #else - #define SETRM(RM) __control87_2(RM, _MCW_RC, &x86_state, &sse2_state) - #endif +#define SETRM(RM) _controlfp_s(&sse2_state, RM, _MCW_RC); #endif #else - #define SETRM(RM) fesetround(RM) +#ifdef USE_INTRINSICS +#define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) +#else +#define SETRM(RM) __control87_2(RM, _MCW_RC, &x86_state, &sse2_state) +#endif +#endif +#else +#define SETRM(RM) fesetround(RM) #endif unsigned hwf_manager::prev_power_of_two(hwf const & a) { @@ -608,9 +608,28 @@ unsigned hwf_manager::prev_power_of_two(hwf const & a) { void hwf_manager::set_rounding_mode(mpf_rounding_mode rm) { -#ifdef _WINDOWS +#ifdef _WINDOWS +#ifdef USE_INTRINSICS switch (rm) { - case MPF_ROUND_NEAREST_TEVEN: + case MPF_ROUND_NEAREST_TEVEN: + SETRM(_MM_ROUND_NEAREST); + break; + case MPF_ROUND_TOWARD_POSITIVE: + SETRM(_MM_ROUND_UP); + break; + case MPF_ROUND_TOWARD_NEGATIVE: + SETRM(_MM_ROUND_DOWN); + break; + case MPF_ROUND_TOWARD_ZERO: + SETRM(_MM_ROUND_TOWARD_ZERO); + break; + case MPF_ROUND_NEAREST_TAWAY: + default: + UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! + } +#else + switch (rm) { + case MPF_ROUND_NEAREST_TEVEN: SETRM(_RC_NEAR); break; case MPF_ROUND_TOWARD_POSITIVE: @@ -626,6 +645,7 @@ void hwf_manager::set_rounding_mode(mpf_rounding_mode rm) default: UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! } +#endif #else // OSX/Linux switch (rm) { case MPF_ROUND_NEAREST_TEVEN: diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 6c542f1af..dfac97626 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -367,7 +367,7 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode o.ebits = ebits; o.sbits = sbits; - signed ds = sbits - x.sbits; + signed ds = sbits - x.sbits + 4; // plus rounding bits if (ds > 0) { m_mpz_manager.mul2k(o.significand, ds); @@ -520,9 +520,8 @@ void mpf_manager::add_sub(mpf_rounding_mode rm, mpf const & x, mpf const & y, mp } } else if (is_zero(x) && is_zero(y)) { - if (sgn(x) && sgn_y) - set(o, x); - else if (rm == MPF_ROUND_TOWARD_NEGATIVE) + if ((x.sign && sgn_y) || + ((rm == MPF_ROUND_TOWARD_NEGATIVE) && (x.sign != sgn_y))) mk_nzero(x.ebits, x.sbits, o); else mk_pzero(x.ebits, x.sbits, o); @@ -627,29 +626,28 @@ void mpf_manager::mul(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else - mk_inf(x.ebits, x.sbits, sgn(y), o); + mk_inf(x.ebits, x.sbits, y.sign, o); } else if (is_pinf(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else - mk_inf(x.ebits, x.sbits, sgn(x), o); + mk_inf(x.ebits, x.sbits, x.sign, o); } else if (is_ninf(x)) { if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else - mk_inf(x.ebits, x.sbits, !sgn(y), o); + mk_inf(x.ebits, x.sbits, !y.sign, o); } else if (is_ninf(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else - mk_inf(x.ebits, x.sbits, !sgn(x), o); + mk_inf(x.ebits, x.sbits, !x.sign, o); } else if (is_zero(x) || is_zero(y)) { - set(o, x); - o.sign = x.sign ^ y.sign; + mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else { o.ebits = x.ebits; @@ -699,31 +697,35 @@ void mpf_manager::div(mpf_rounding_mode rm, mpf const & x, mpf const & y, mpf & if (is_inf(y)) mk_nan(x.ebits, x.sbits, o); else - mk_inf(x.ebits, x.sbits, sgn(y), o); + mk_inf(x.ebits, x.sbits, y.sign, o); } else if (is_pinf(y)) { if (is_inf(x)) mk_nan(x.ebits, x.sbits, o); else - mk_zero(x.ebits, x.sbits, (x.sign ^ y.sign) == 1, o); + mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else if (is_ninf(x)) { if (is_inf(y)) mk_nan(x.ebits, x.sbits, o); else - mk_inf(x.ebits, x.sbits, !sgn(y), o); + mk_inf(x.ebits, x.sbits, !y.sign, o); } else if (is_ninf(y)) { if (is_inf(x)) mk_nan(x.ebits, x.sbits, o); else - mk_zero(x.ebits, x.sbits, (x.sign ^ y.sign) == 1, o); + mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else if (is_zero(y)) { if (is_zero(x)) mk_nan(x.ebits, x.sbits, o); else - mk_inf(x.ebits, x.sbits, sgn(x), o); + mk_inf(x.ebits, x.sbits, x.sign != y.sign, o); + } + else if (is_zero(x)) { + // Special case to avoid problems with unpacking of zeros. + mk_zero(x.ebits, x.sbits, x.sign != y.sign, o); } else { o.ebits = x.ebits; @@ -837,6 +839,10 @@ void mpf_manager::sqrt(mpf_rounding_mode rm, mpf const & x, mpf & o) { else mk_nzero(x.ebits, x.sbits, o); } + else if (is_pzero(x)) + mk_pzero(x.ebits, x.sbits, o); + else if (is_nzero(x)) + mk_nzero(x.ebits, x.sbits, o); else { o.ebits = x.ebits; o.sbits = x.sbits; @@ -933,7 +939,7 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { else if (is_inf(y)) set(o, x); else if (is_zero(x)) - set(o, x); + mk_pzero(x.ebits, x.sbits, o); else if (is_zero(y)) mk_nan(x.ebits, x.sbits, o); else { @@ -982,9 +988,9 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { void mpf_manager::maximum(mpf const & x, mpf const & y, mpf & o) { if (is_nan(x)) set(o, y); - else if (is_nan(y) || (sgn(y) && is_zero(x) && is_zero(y))) - set(o, x); - else if (gt(x, y)) + else if (is_nan(y)) + set(o, x); + else if (gt(x, y) || (is_zero(x) && is_nzero(y))) set(o, x); else set(o, y); @@ -993,9 +999,9 @@ void mpf_manager::maximum(mpf const & x, mpf const & y, mpf & o) { void mpf_manager::minimum(mpf const & x, mpf const & y, mpf & o) { if (is_nan(x)) set(o, y); - else if (is_nan(y) || (sgn(x) && is_zero(x) && is_zero(y))) + else if (is_nan(y)) set(o, x); - else if (lt(x, y)) + else if (lt(x, y) || (is_nzero(x) && is_zero(y))) set(o, x); else set(o, y); diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index c77978647..bd7f30a76 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -120,6 +120,7 @@ mpz_manager::mpz_manager(): mpz_set_ui(m_tmp, max_l); mpz_add(m_uint64_max, m_uint64_max, m_tmp); mpz_init(m_int64_max); + mpz_init(m_int64_min); max_l = static_cast(INT64_MAX % static_cast(UINT_MAX)); max_h = static_cast(INT64_MAX / static_cast(UINT_MAX)); @@ -128,6 +129,8 @@ mpz_manager::mpz_manager(): mpz_mul(m_int64_max, m_tmp, m_int64_max); mpz_set_ui(m_tmp, max_l); mpz_add(m_int64_max, m_tmp, m_int64_max); + mpz_neg(m_int64_min, m_int64_max); + mpz_sub_ui(m_int64_min, m_int64_min, 1); #endif mpz one(1); @@ -152,6 +155,7 @@ mpz_manager::~mpz_manager() { deallocate(m_arg[1]); mpz_clear(m_uint64_max); mpz_clear(m_int64_max); + mpz_clear(m_int64_min); #endif if (SYNCH) omp_destroy_nest_lock(&m_lock); @@ -1299,9 +1303,9 @@ bool mpz_manager::is_int64(mpz const & a) const { if (is_small(a)) return true; #ifndef _MP_GMP - if (!is_uint64(a)) + if (!is_abs_uint64(a)) return false; - uint64 num = get_uint64(a); + uint64 num = big_abs_to_uint64(a); uint64 msb = static_cast(1) << 63; uint64 msb_val = msb & num; if (a.m_val >= 0) { @@ -1317,7 +1321,7 @@ bool mpz_manager::is_int64(mpz const & a) const { } #else // GMP version - return mpz_cmp(*a.m_ptr, m_int64_max) <= 0; + return mpz_cmp(m_int64_min, *a.m_ptr) <= 0 && mpz_cmp(*a.m_ptr, m_int64_max) <= 0; #endif } @@ -1327,14 +1331,7 @@ uint64 mpz_manager::get_uint64(mpz const & a) const { return static_cast(a.m_val); #ifndef _MP_GMP SASSERT(a.m_ptr->m_size > 0); - if (a.m_ptr->m_size == 1) - return digits(a)[0]; - if (sizeof(digit_t) == sizeof(uint64)) - // 64-bit machine - return digits(a)[0]; - else - // 32-bit machine - return ((static_cast(digits(a)[1]) << 32) | (static_cast(digits(a)[0]))); + return big_abs_to_uint64(a); #else // GMP version if (sizeof(uint64) == sizeof(unsigned long)) { @@ -1359,7 +1356,7 @@ int64 mpz_manager::get_int64(mpz const & a) const { return static_cast(a.m_val); #ifndef _MP_GMP SASSERT(is_int64(a)); - uint64 num = get_uint64(a); + uint64 num = big_abs_to_uint64(a); if (a.m_val < 0) { if (num != 0 && (num << 1) == 0) return INT64_MIN; diff --git a/src/util/mpz.h b/src/util/mpz.h index b5c301d82..923d5b3a7 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -168,6 +168,7 @@ class mpz_manager { mpz_t * m_arg[2]; mpz_t m_uint64_max; mpz_t m_int64_max; + mpz_t m_int64_min; mpz_t * allocate() { mpz_t * cell = reinterpret_cast(m_allocator.allocate(sizeof(mpz_t))); @@ -211,6 +212,30 @@ class mpz_manager { static digit_t * digits(mpz const & c) { return c.m_ptr->m_digits; } + // Return true if the absolute value fits in a UINT64 + static bool is_abs_uint64(mpz const & a) { + if (is_small(a)) + return true; + if (sizeof(digit_t) == sizeof(uint64)) + return size(a) <= 1; + else + return size(a) <= 2; + } + + // CAST the absolute value into a UINT64 + static uint64 big_abs_to_uint64(mpz const & a) { + SASSERT(is_abs_uint64(a)); + SASSERT(!is_small(a)); + if (a.m_ptr->m_size == 1) + return digits(a)[0]; + if (sizeof(digit_t) == sizeof(uint64)) + // 64-bit machine + return digits(a)[0]; + else + // 32-bit machine + return ((static_cast(digits(a)[1]) << 32) | (static_cast(digits(a)[0]))); + } + template void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell) { if (is_small(a)) { diff --git a/src/util/util.h b/src/util/util.h index 3360c2282..0aa8f881d 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -45,7 +45,7 @@ COMPILE_TIME_ASSERT(sizeof(int64) == 8); #define INT64_MIN static_cast(0x8000000000000000ull) #endif #ifndef INT64_MAX -#define INT64_MAX static_cast(0x0fffffffffffffffull) +#define INT64_MAX static_cast(0x7fffffffffffffffull) #endif #ifndef UINT64_MAX #define UINT64_MAX 0xffffffffffffffffull @@ -394,11 +394,14 @@ public: inline std::ostream & operator<<(std::ostream & out, escaped const & s) { s.display(out); return out; } -inline unsigned long long megabytes_to_bytes(unsigned b) { - if (b == UINT_MAX) - return UINT64_MAX; - else - return static_cast(b) * 1024ull * 1024ull; +inline size_t megabytes_to_bytes(unsigned mb) { + if (mb == UINT_MAX) + return SIZE_MAX; + unsigned long long b = static_cast(mb) * 1024ull * 1024ull; + size_t r = static_cast(b); + if (r != b) // overflow + r = SIZE_MAX; + return r; } void z3_bound_num_procs(); diff --git a/src/util/vector.h b/src/util/vector.h index a9d36b202..704452d0f 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -151,23 +151,6 @@ public: } } - vector(T const & e) : - m_data(0) { - push_back(e); - } - - vector(T const & t1, T const & t2) : - m_data(0) { - push_back(t1); - push_back(t2); - } - - vector(T const & t1, T const & t2, T const & t3) : - m_data(0) { - push_back(t1); - push_back(t2); - push_back(t3); - } ~vector() { destroy();