diff --git a/CMakeLists.txt b/CMakeLists.txt index e4732ef67..a086afd71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ endif() # Project version ################################################################################ set(Z3_VERSION_MAJOR 4) -set(Z3_VERSION_MINOR 7) +set(Z3_VERSION_MINOR 8) set(Z3_VERSION_PATCH 0) set(Z3_VERSION_TWEAK 0) set(Z3_VERSION "${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}.${Z3_VERSION_PATCH}.${Z3_VERSION_TWEAK}") @@ -218,12 +218,17 @@ include(${CMAKE_SOURCE_DIR}/cmake/z3_add_cxx_flag.cmake) ################################################################################ # C++ language version ################################################################################ -# FIXME: Use CMake's own mechanism for selecting language version -if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) - z3_add_cxx_flag("-std=c++11" REQUIRED) -else() - message(AUTHOR_WARNING "Not setting C++ language version for compiler") -endif() +if ("${CMAKE_VERSION}" VERSION_LESS "3.1") + # FIXME: Drop this when we upgrade to newer CMake versions. + if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + z3_add_cxx_flag("-std=c++11" REQUIRED) + else() + message(AUTHOR_WARNING "Not setting C++ language version for compiler") + endif() +else () + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif () ################################################################################ # Platform detection diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 86e7039dd..7439342de 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,5 +1,55 @@ RELEASE NOTES +Version 4.8.0 +============= + +- New requirements: + - A breaking change to the API is that parsers for SMT-LIB2 formulas return a vector of + formulas as opposed to a conjunction of formulas. The vector of formulas correspond to + the set of "assert" instructions in the SMT-LIB input. + +- New features + - A parallel mode is available for select theories, including QF_BV. + By setting parallel.enable=true Z3 will spawn a number of worker threads proportional to the + number of available CPU cores to apply cube and conquer solving on the goal. + - The SAT solver by default handle cardinality and PB constraints using a custom plugin + that operates directly on cardinality and PB constraints. + - A "cube" interface is exposed over the solver API. + - Model conversion is first class over the textual API, such that subgoals created from running a + solver can be passed in text files and a model for the original formula can be recreated from the result. + - This has also led to changes in how models are tracked over tactic subgoals. The API for + extracting models from apply_result have been replaced. + - An optional mode handles xor constraints using a custom xor propagator. + It is off by default and its value not demonstrated. + - The SAT solver includes new inprocessing technques that are available during simplification. + It performs asymmetric tautology elimination by default, and one can turn on more powerful inprocessing techniques + (known as ACCE, ABCE, CCE). Asymmetric branching also uses features introduced in Lingeling by exploiting binary implication graphs. + Use sat.acce=true to enable the full repertoire of inprocessing methods. By default, clauses that are "eliminated" by acce are tagged + as lemmas (redundant) and are garbage collected if their glue level is high. + +- Removed features: + - interpolation API + - duality engine for constrained Horn clauses. + - long deprecated API functions have been removed from z3_api.h + + +Version 4.7.1 +============= + +- New requirements: + - uses stdbool and stdint as part of z3. + +- New features: + - none + +- Removed features: + - none + +- Notes: + This is a minor release prior to a set of planned major updates. + It uses minor version 7 to indicate that the use of stdbool and + stdint are breaking changes to consumers of the C-based API. + Version 4.6.0 ============= diff --git a/contrib/cmake/src/test/lp/CMakeLists.txt b/contrib/cmake/src/test/lp/CMakeLists.txt new file mode 100644 index 000000000..6683a1758 --- /dev/null +++ b/contrib/cmake/src/test/lp/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(lp_tst lp_main.cpp lp.cpp $ $ $ $ ) +target_compile_definitions(lp_tst PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) +target_compile_options(lp_tst PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) +target_include_directories(lp_tst PRIVATE ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}) +target_link_libraries(lp_tst PRIVATE ${Z3_DEPENDENT_LIBS}) +z3_append_linker_flag_list_to_target(lp_tst ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index a1a9e64bd..bfe865e06 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -272,7 +272,6 @@ try: cleanup_API(doc_path('../src/api/z3_rcf.h'), temp_path('z3_rcf.h')) cleanup_API(doc_path('../src/api/z3_fixedpoint.h'), temp_path('z3_fixedpoint.h')) cleanup_API(doc_path('../src/api/z3_optimization.h'), temp_path('z3_optimization.h')) - cleanup_API(doc_path('../src/api/z3_interp.h'), temp_path('z3_interp.h')) cleanup_API(doc_path('../src/api/z3_fpa.h'), temp_path('z3_fpa.h')) print("Removed annotations from z3_api.h.") diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index b25507885..640e3a10b 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -470,7 +470,7 @@ void unsat_core_example2() { // The solver s already contains p1 => F // To disable F, we add (not p1) as an additional assumption qs.push_back(!p1); - std::cout << s.check((unsigned)qs.size(), &qs[0]) << "\n"; + std::cout << s.check(static_cast(qs.size()), &qs[0]) << "\n"; expr_vector core2 = s.unsat_core(); std::cout << core2 << "\n"; std::cout << "size: " << core2.size() << "\n"; @@ -707,7 +707,7 @@ void tactic_example7() { std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << "model for subgoal:\n" << m << "\n"; - std::cout << "model for original goal:\n" << r.convert_model(m) << "\n"; + std::cout << "model for original goal:\n" << subgoal.convert_model(m) << "\n"; } void tactic_example8() { @@ -1149,7 +1149,7 @@ static void parse_example() { func_decl_vector decls(c); sort B = c.bool_sort(); decls.push_back(c.function("a", 0, 0, B)); - expr a = c.parse_string("(assert a)", sorts, decls); + expr_vector a = c.parse_string("(assert a)", sorts, decls); std::cout << a << "\n"; // expr b = c.parse_string("(benchmark tst :extrafuns ((x Int) (y Int)) :formula (> x y) :formula (> x 0))"); diff --git a/examples/c/test_capi.c b/examples/c/test_capi.c index c2b1f0af9..a6937f293 100644 --- a/examples/c/test_capi.c +++ b/examples/c/test_capi.c @@ -378,7 +378,7 @@ void assert_comm_axiom(Z3_context ctx, Z3_solver s, Z3_func_decl f) { Z3_sort t; Z3_symbol f_name, t_name; - Z3_ast q; + Z3_ast_vector q; t = Z3_get_range(ctx, f); @@ -394,13 +394,14 @@ void assert_comm_axiom(Z3_context ctx, Z3_solver s, Z3_func_decl f) /* Inside the parser, type t will be referenced using the symbol 'T'. */ t_name = Z3_mk_string_symbol(ctx, "T"); - q = Z3_parse_smtlib2_string(ctx, "(assert (forall ((x T) (y T)) (= (f x y) (f y x))))", 1, &t_name, &t, 1, &f_name, &f); - printf("assert axiom:\n%s\n", Z3_ast_to_string(ctx, q)); - Z3_solver_assert(ctx, s, q); + printf("assert axiom:\n%s\n", Z3_ast_vector_to_string(ctx, q)); + for (unsigned i = 0; i < Z3_ast_vector_size(ctx, q); ++i) { + Z3_solver_assert(ctx, s, Z3_ast_vector_get(ctx, q, i)); + } } /** @@ -1642,7 +1643,7 @@ void parser_example2() Z3_ast x, y; Z3_symbol names[2]; Z3_func_decl decls[2]; - Z3_ast f; + Z3_ast_vector f; printf("\nparser_example2\n"); LOG_MSG("parser_example2"); @@ -1665,8 +1666,11 @@ void parser_example2() 0, 0, 0, /* 'x' and 'y' declarations are inserted as 'a' and 'b' into the parser symbol table. */ 2, names, decls); - printf("formula: %s\n", Z3_ast_to_string(ctx, f)); - Z3_solver_assert(ctx, s, f); + printf("formula: %s\n", Z3_ast_vector_to_string(ctx, f)); + printf("assert axiom:\n%s\n", Z3_ast_vector_to_string(ctx, f)); + for (unsigned i = 0; i < Z3_ast_vector_size(ctx, f); ++i) { + Z3_solver_assert(ctx, s, Z3_ast_vector_get(ctx, f, i)); + } check(ctx, s, Z3_L_TRUE); del_solver(ctx, s); @@ -1685,7 +1689,7 @@ void parser_example3() Z3_symbol g_name; Z3_sort g_domain[2]; Z3_func_decl g; - Z3_ast thm; + Z3_ast_vector thm; printf("\nparser_example3\n"); LOG_MSG("parser_example3"); @@ -1710,8 +1714,8 @@ void parser_example3() "(assert (forall ((x Int) (y Int)) (=> (= x y) (= (g x 0) (g 0 y)))))", 0, 0, 0, 1, &g_name, &g); - printf("formula: %s\n", Z3_ast_to_string(ctx, thm)); - prove(ctx, s, thm, Z3_TRUE); + printf("formula: %s\n", Z3_ast_vector_to_string(ctx, thm)); + prove(ctx, s, Z3_ast_vector_get(ctx, thm, 0), Z3_TRUE); del_solver(ctx, s); Z3_del_context(ctx); @@ -2286,46 +2290,6 @@ void unsat_core_and_proof_example() { Z3_del_context(ctx); } -void interpolation_example() { - Z3_context ctx = mk_context(); - Z3_ast pa = mk_bool_var(ctx, "PredA"); - Z3_ast pb = mk_bool_var(ctx, "PredB"); - Z3_ast pc = mk_bool_var(ctx, "PredC"); - Z3_ast args1[2] = {pa,pb}, args2[2] = {Z3_mk_not(ctx,pb),pc}; - Z3_ast args3[2] = {Z3_mk_interpolant(ctx,Z3_mk_and(ctx,2,args1)),Z3_mk_and(ctx,2,args2)}; - Z3_ast f = Z3_mk_and(ctx,2,args3); - Z3_ast_vector interpolant = 0; - Z3_model m = 0; - Z3_lbool result = Z3_L_UNDEF; - - printf("\ninterpolation_example\n"); - LOG_MSG("interpolation_example"); - - result = Z3_compute_interpolant(ctx,f,0,&interpolant,&m); - - switch (result) { - case Z3_L_FALSE: - printf("unsat\n"); - printf("interpolant: %s\n", Z3_ast_to_string(ctx, Z3_ast_vector_get(ctx, interpolant, 0))); - printf("\n"); - break; - case Z3_L_UNDEF: - printf("unknown\n"); - printf("potential model:\n"); - if (m) Z3_model_inc_ref(ctx, m); - display_model(ctx, stdout, m); - break; - case Z3_L_TRUE: - printf("sat\n"); - if (m) Z3_model_inc_ref(ctx, m); - display_model(ctx, stdout, m); - break; - } - - /* delete logical context */ - if (m) Z3_model_dec_ref(ctx, m); - Z3_del_context(ctx); -} #define MAX_RETRACTABLE_ASSERTIONS 1024 @@ -2576,13 +2540,15 @@ void reference_counter_example() { */ void smt2parser_example() { Z3_context ctx; - Z3_ast fs; + Z3_ast_vector fs; printf("\nsmt2parser_example\n"); LOG_MSG("smt2parser_example"); ctx = mk_context(); fs = Z3_parse_smtlib2_string(ctx, "(declare-fun a () (_ BitVec 8)) (assert (bvuge a #x10)) (assert (bvule a #xf0))", 0, 0, 0, 0, 0, 0); - printf("formulas: %s\n", Z3_ast_to_string(ctx, fs)); + Z3_ast_vector_inc_ref(ctx, fs); + printf("formulas: %s\n", Z3_ast_vector_to_string(ctx, fs)); + Z3_ast_vector_dec_ref(ctx, fs); Z3_del_context(ctx); } @@ -3008,7 +2974,6 @@ int main() { binary_tree_example(); enum_example(); unsat_core_and_proof_example(); - interpolation_example(); incremental_example1(); reference_counter_example(); smt2parser_example(); diff --git a/examples/dotnet/Program.cs b/examples/dotnet/Program.cs index aec3bdcc5..06cc66150 100644 --- a/examples/dotnet/Program.cs +++ b/examples/dotnet/Program.cs @@ -175,7 +175,7 @@ namespace test_mapi string bench = string.Format("(assert (forall ((x {0}) (y {1})) (= ({2} x y) ({3} y x))))", t.Name, t.Name, f.Name, f.Name); - return ctx.ParseSMTLIB2String(bench, new Symbol[] { t.Name }, new Sort[] { t }, new Symbol[] { f.Name }, new FuncDecl[] { f }); + return ctx.ParseSMTLIB2String(bench, new Symbol[] { t.Name }, new Sort[] { t }, new Symbol[] { f.Name }, new FuncDecl[] { f })[0]; } /// @@ -322,7 +322,6 @@ namespace test_mapi Status q = s.Check(); Console.WriteLine("Solver says: " + q); Console.WriteLine("Model: \n" + s.Model); - Console.WriteLine("Converted Model: \n" + ar.ConvertModel(0, s.Model)); if (q != Status.SATISFIABLE) throw new TestFailedException(); } @@ -612,7 +611,6 @@ namespace test_mapi Expr f_x = ctx.MkApp(f, x); Expr f_y = ctx.MkApp(f, y); Expr g_y = ctx.MkApp(g, y); - Pattern[] pats = new Pattern[] { ctx.MkPattern(new Expr[] { f_x, g_y }) }; Expr[] no_pats = new Expr[] { f_y }; Expr[] bound = new Expr[2] { x, y }; Expr body = ctx.MkAnd(ctx.MkEq(f_x, f_y), ctx.MkEq(f_y, g_y)); @@ -629,7 +627,6 @@ namespace test_mapi Expr f_x = ctx.MkApp(f, x); Expr f_y = ctx.MkApp(f, y); Expr g_y = ctx.MkApp(g, y); - Pattern[] pats = new Pattern[] { ctx.MkPattern(new Expr[] { f_x, g_y }) }; Expr[] no_pats = new Expr[] { f_y }; Symbol[] names = new Symbol[] { ctx.MkSymbol("x"), ctx.MkSymbol("y") }; Sort[] sorts = new Sort[] { ctx.IntSort, ctx.IntSort }; @@ -730,7 +727,6 @@ namespace test_mapi { Console.WriteLine("BasicTests"); - Symbol qi = ctx.MkSymbol(1); Symbol fname = ctx.MkSymbol("f"); Symbol x = ctx.MkSymbol("x"); Symbol y = ctx.MkSymbol("y"); @@ -977,7 +973,8 @@ namespace test_mapi using (Context ctx = new Context(new Dictionary() { { "MODEL", "true" } })) { - Expr a = ctx.ParseSMTLIB2File(filename); + BoolExpr[] fmls = ctx.ParseSMTLIB2File(filename); + BoolExpr a = ctx.MkAnd(fmls); Console.WriteLine("SMT2 file read time: " + (System.DateTime.Now - before).TotalSeconds + " sec"); @@ -1319,7 +1316,7 @@ namespace test_mapi new Sort[] { int_type, int_type } // types of projection operators ); FuncDecl first = tuple.FieldDecls[0]; // declarations are for projections - FuncDecl second = tuple.FieldDecls[1]; + // FuncDecl second = tuple.FieldDecls[1]; Expr x = ctx.MkConst("x", int_type); Expr y = ctx.MkConst("y", int_type); Expr n1 = tuple.MkDecl[x, y]; @@ -1383,7 +1380,9 @@ namespace test_mapi { Console.WriteLine("ParserExample1"); - var fml = ctx.ParseSMTLIB2String("(declare-const x Int) (declare-const y Int) (assert (> x y)) (assert (> x 0))"); + var fmls = ctx.ParseSMTLIB2String("(declare-const x Int) (declare-const y Int) (assert (> x y)) (assert (> x 0))"); + var fml = ctx.MkAnd(fmls); + Console.WriteLine("formula {0}", fml); Model m = Check(ctx, fml, Status.SATISFIABLE); @@ -1399,7 +1398,7 @@ namespace test_mapi FuncDecl a = ctx.MkConstDecl(declNames[0], ctx.MkIntSort()); FuncDecl b = ctx.MkConstDecl(declNames[1], ctx.MkIntSort()); FuncDecl[] decls = new FuncDecl[] { a, b }; - BoolExpr f = ctx.ParseSMTLIB2String("(assert (> a b))", null, null, declNames, decls); + BoolExpr f = ctx.ParseSMTLIB2String("(assert (> a b))", null, null, declNames, decls)[0]; Console.WriteLine("formula: {0}", f); Check(ctx, f, Status.SATISFIABLE); } @@ -1420,7 +1419,7 @@ namespace test_mapi BoolExpr thm = ctx.ParseSMTLIB2String("(assert (forall ((x Int) (y Int)) (=> (= x y) (= (gg x 0) (gg 0 y)))))", null, null, new Symbol[] { ctx.MkSymbol("gg") }, - new FuncDecl[] { g }); + new FuncDecl[] { g })[0]; Console.WriteLine("formula: {0}", thm); Prove(ctx, thm, false, ca); diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 7b8902768..0ad9a8d10 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -167,12 +167,12 @@ class JavaExample "function must be binary, and argument types must be equal to return type"); } - String bench = "(benchmark comm :formula (forall (x " + t.getName() + String bench = "(assert (forall (x " + t.getName() + ") (y " + t.getName() + ") (= (" + f.getName() + " x y) (" + f.getName() + " y x))))"; return ctx.parseSMTLIB2String(bench, new Symbol[] { t.getName() }, new Sort[] { t }, new Symbol[] { f.getName() }, - new FuncDecl[] { f }); + new FuncDecl[] { f })[0]; } // / "Hello world" example: create a Z3 logical context, and delete it. @@ -344,8 +344,6 @@ class JavaExample Status q = s.check(); System.out.println("Solver says: " + q); System.out.println("Model: \n" + s.getModel()); - System.out.println("Converted Model: \n" - + ar.convertModel(0, s.getModel())); if (q != Status.SATISFIABLE) throw new TestFailedException(); } @@ -1041,7 +1039,7 @@ class JavaExample HashMap cfg = new HashMap(); cfg.put("model", "true"); Context ctx = new Context(cfg); - Expr a = ctx.parseSMTLIB2File(filename, null, null, null, null); + BoolExpr a = ctx.mkAnd(ctx.parseSMTLIB2File(filename, null, null, null, null)); long t_diff = ((new Date()).getTime() - before.getTime()) / 1000; @@ -1445,7 +1443,7 @@ class JavaExample BoolExpr f = ctx.parseSMTLIB2String( "(declare-const x Int) (declare-const y Int) (assert (and (> x y) (> x 0)))", - null, null, null, null); + null, null, null, null)[0]; System.out.println("formula " + f); @SuppressWarnings("unused") @@ -1465,7 +1463,7 @@ class JavaExample FuncDecl[] decls = new FuncDecl[] { a, b }; BoolExpr f = ctx.parseSMTLIB2String("(assert (> a b))", null, null, - declNames, decls); + declNames, decls)[0]; System.out.println("formula: " + f); check(ctx, f, Status.SATISFIABLE); } @@ -1486,7 +1484,7 @@ class JavaExample BoolExpr thm = ctx.parseSMTLIB2String( "(assert (forall ((x Int) (y Int)) (=> (= x y) (= (gg x 0) (gg 0 y)))))", null, null, new Symbol[] { ctx.mkSymbol("gg") }, - new FuncDecl[] { g }); + new FuncDecl[] { g })[0]; System.out.println("formula: " + thm); prove(ctx, thm, false, ca); } diff --git a/examples/python/all_interval_series.py b/examples/python/all_interval_series.py index 216941451..8269303d3 100644 --- a/examples/python/all_interval_series.py +++ b/examples/python/all_interval_series.py @@ -8,6 +8,8 @@ from __future__ import print_function from z3 import * import time +set_option("sat.gc.burst", False) # disable GC at every search. It is wasteful for these small queries. + def diff_at_j_is_i(xs, j, i): assert(0 <= j and j + 1 < len(xs)) assert(1 <= i and i < len(xs)) diff --git a/examples/python/parallel.py b/examples/python/parallel.py new file mode 100644 index 000000000..42ff50927 --- /dev/null +++ b/examples/python/parallel.py @@ -0,0 +1,36 @@ +from z3 import * +from multiprocessing.pool import ThreadPool +from copy import deepcopy + +pool = ThreadPool(8) +x = Int('x') + +assert x.ctx == main_ctx() + + +def calculate(x, n, ctx): + """ Do a simple computation with a context""" + assert x.ctx == ctx + assert x.ctx != main_ctx() + + # Parallel creation of z3 object + condition = And(x < 2, x > n, ctx) + + # Parallel solving + solver = Solver(ctx=ctx) + solver.add(condition) + solver.check() + + +for i in range(100): + # Create new context for the computation + # Note that we need to do this sequentially, as parallel access to the current context or its objects + # will result in a segfault + i_context = Context() + x_i = deepcopy(x).translate(i_context) + + # Kick off parallel computation + pool.apply_async(calculate, [x_i, i, i_context]) + +pool.close() +pool.join() diff --git a/examples/tptp/tptp5.cpp b/examples/tptp/tptp5.cpp index 772440b42..facbf6c0a 100644 --- a/examples/tptp/tptp5.cpp +++ b/examples/tptp/tptp5.cpp @@ -2203,9 +2203,8 @@ static void check_error(z3::context& ctx) { static void display_tptp(std::ostream& out) { // run SMT2 parser, pretty print TFA format. z3::context ctx; - Z3_ast _fml = Z3_parse_smtlib2_file(ctx, g_input_file, 0, 0, 0, 0, 0, 0); - check_error(ctx); - z3::expr fml(ctx, _fml); + z3::expr_vector fmls = ctx.parse_file(g_input_file); + z3::expr fml = z3::mk_and(fmls); pp_tptp pp(ctx); pp.collect_decls(fml); diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 8f601f4fe..f2720c893 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -9,12 +9,12 @@ from mk_util import * # Z3 Project definition def init_project_def(): - set_version(4, 7, 0, 0) + set_version(4, 8, 0, 0) add_lib('util', []) - add_lib('lp', ['util'], 'util/lp') add_lib('polynomial', ['util'], 'math/polynomial') add_lib('sat', ['util']) add_lib('nlsat', ['polynomial', 'sat']) + add_lib('lp', ['util','nlsat'], 'util/lp') add_lib('hilbert', ['util'], 'math/hilbert') add_lib('simplex', ['util'], 'math/simplex') add_lib('automata', ['util'], 'math/automata') @@ -32,16 +32,15 @@ def init_project_def(): add_lib('grobner', ['ast'], 'math/grobner') add_lib('euclid', ['util'], 'math/euclid') add_lib('core_tactics', ['tactic', 'macros', 'normal_forms', 'rewriter'], 'tactic/core') - add_lib('sat_tactic', ['tactic', 'sat'], 'sat/tactic') + add_lib('proofs', ['rewriter', 'util'], 'ast/proofs') + add_lib('solver', ['model', 'tactic', 'proofs']) + add_lib('sat_tactic', ['tactic', 'sat', 'solver'], 'sat/tactic') add_lib('arith_tactics', ['core_tactics', 'sat'], 'tactic/arith') add_lib('nlsat_tactic', ['nlsat', 'sat_tactic', 'arith_tactics'], 'nlsat/tactic') add_lib('subpaving_tactic', ['core_tactics', 'subpaving'], 'math/subpaving/tactic') add_lib('aig_tactic', ['tactic'], 'tactic/aig') - add_lib('proofs', ['rewriter', 'util'], 'ast/proofs') - add_lib('solver', ['model', 'tactic', 'proofs']) add_lib('ackermannization', ['model', 'rewriter', 'ast', 'solver', 'tactic'], 'ackermannization') - add_lib('interp', ['solver']) - add_lib('cmd_context', ['solver', 'rewriter', 'interp']) + add_lib('cmd_context', ['solver', 'rewriter']) add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'arith_tactics'], 'cmd_context/extra_cmds') add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2') add_lib('fpa', ['ast', 'util', 'rewriter', 'model'], 'ast/fpa') @@ -56,7 +55,6 @@ def init_project_def(): add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') add_lib('qe', ['smt','sat','nlsat','tactic','nlsat_tactic'], 'qe') - add_lib('duality', ['smt', 'interp', 'qe']) add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe'], 'muz/base') add_lib('dataflow', ['muz'], 'muz/dataflow') add_lib('transforms', ['muz', 'hilbert', 'dataflow'], 'muz/transforms') @@ -67,17 +65,15 @@ def init_project_def(): add_lib('tab', ['muz', 'transforms'], 'muz/tab') add_lib('bmc', ['muz', 'transforms'], 'muz/bmc') add_lib('ddnf', ['muz', 'transforms', 'rel'], 'muz/ddnf') - add_lib('duality_intf', ['muz', 'transforms', 'duality'], 'muz/duality') - add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'duality_intf', 'ddnf', 'spacer'], 'muz/fp') - add_lib('nlsat_smt_tactic', ['nlsat_tactic', 'smt_tactic'], 'tactic/nlsat_smt') + add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'ddnf', 'spacer'], 'muz/fp') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('sat_solver', ['solver', 'core_tactics', 'aig_tactic', 'bv_tactics', 'arith_tactics', 'sat_tactic'], 'sat/sat_solver') - add_lib('smtlogic_tactics', ['ackermannization', 'sat_solver', 'arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe','nlsat_smt_tactic'], 'tactic/smtlogics') + add_lib('smtlogic_tactics', ['ackermannization', 'sat_solver', 'arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe'], 'tactic/smtlogics') add_lib('fpa_tactics', ['fpa', 'core_tactics', 'bv_tactics', 'sat_tactic', 'smt_tactic', 'arith_tactics', 'smtlogic_tactics'], 'tactic/fpa') add_lib('portfolio', ['smtlogic_tactics', 'sat_solver', 'ufbv_tactic', 'fpa_tactics', 'aig_tactic', 'fp', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('opt', ['smt', 'smtlogic_tactics', 'sls_tactic', 'sat_solver'], 'opt') - API_files = ['z3_api.h', 'z3_ast_containers.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_fixedpoint.h', 'z3_optimization.h', 'z3_interp.h', 'z3_fpa.h', 'z3_spacer.h'] - add_lib('api', ['portfolio', 'realclosure', 'interp', 'opt'], + API_files = ['z3_api.h', 'z3_ast_containers.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_fixedpoint.h', 'z3_optimization.h', 'z3_fpa.h', 'z3_spacer.h'] + add_lib('api', ['portfolio', 'realclosure', 'opt'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3') add_exe('test', ['api', 'fuzzing', 'simplex'], exe_name='test-z3', install=False) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 99de61703..d51735255 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1932,8 +1932,14 @@ class MLComponent(Component): OCAML_FLAGS = '' if DEBUG_MODE: OCAML_FLAGS += '-g' - OCAMLCF = OCAMLC + ' ' + OCAML_FLAGS - OCAMLOPTF = OCAMLOPT + ' ' + OCAML_FLAGS + + if OCAMLFIND: + # Load Big_int, which is no longer part of the standard library, via the num package: https://github.com/ocaml/num + OCAMLCF = OCAMLFIND + ' ' + 'ocamlc -package num' + ' ' + OCAML_FLAGS + OCAMLOPTF = OCAMLFIND + ' ' + 'ocamlopt -package num' + ' ' + OCAML_FLAGS + else: + OCAMLCF = OCAMLC + ' ' + OCAML_FLAGS + OCAMLOPTF = OCAMLOPT + ' ' + OCAML_FLAGS src_dir = self.to_src_dir mk_dir(os.path.join(BUILD_DIR, self.sub_dir)) @@ -2505,6 +2511,7 @@ def mk_config(): LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS + SLIBEXTRAFLAGS = '%s -Wl,-soname,libz3.so' % SLIBEXTRAFLAGS elif sysname == 'FreeBSD': CXXFLAGS = '%s -D_FREEBSD_' % CXXFLAGS OS_DEFINES = '-D_FREEBSD_' @@ -2803,6 +2810,7 @@ def get_header_files_for_components(component_src_dirs): def mk_install_tactic_cpp(cnames, path): component_src_dirs = [] for cname in cnames: + print("Component %s" % cname) c = get_component(cname) component_src_dirs.append(c.src_dir) h_files_full_path = get_header_files_for_components(component_src_dirs) diff --git a/scripts/vsts-mac.sh b/scripts/vsts-mac.sh index 9be53967f..959dccbbd 100644 --- a/scripts/vsts-mac.sh +++ b/scripts/vsts-mac.sh @@ -15,5 +15,4 @@ make c_example git clone https://github.com/z3prover/z3test.git z3test ls -python z3test/scripts/test_benchmarks.py ./z3 ./z3test/regressions/smt2 - +python z3test/scripts/test_benchmarks.py ./z3 ./z3test/regressions/smt2 \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7cde89ae2..9020c9e4b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,7 +12,6 @@ set(Z3_API_HEADER_FILES_TO_SCAN z3_rcf.h z3_fixedpoint.h z3_optimization.h - z3_interp.h z3_fpa.h z3_spacer.h ) @@ -36,10 +35,10 @@ endforeach() # raised if you try to declare a component is dependent on another component # that has not yet been declared. add_subdirectory(util) -add_subdirectory(util/lp) add_subdirectory(math/polynomial) add_subdirectory(sat) add_subdirectory(nlsat) +add_subdirectory(util/lp) add_subdirectory(math/hilbert) add_subdirectory(math/simplex) add_subdirectory(math/automata) @@ -56,14 +55,13 @@ add_subdirectory(parsers/util) add_subdirectory(math/grobner) add_subdirectory(math/euclid) add_subdirectory(tactic/core) -add_subdirectory(sat/tactic) -add_subdirectory(tactic/arith) -add_subdirectory(nlsat/tactic) add_subdirectory(math/subpaving/tactic) add_subdirectory(tactic/aig) add_subdirectory(solver) +add_subdirectory(sat/tactic) +add_subdirectory(tactic/arith) +add_subdirectory(nlsat/tactic) add_subdirectory(ackermannization) -add_subdirectory(interp) add_subdirectory(cmd_context) add_subdirectory(cmd_context/extra_cmds) add_subdirectory(parsers/smt2) @@ -79,7 +77,6 @@ add_subdirectory(tactic/bv) add_subdirectory(smt/tactic) add_subdirectory(tactic/sls) add_subdirectory(qe) -add_subdirectory(duality) add_subdirectory(muz/base) add_subdirectory(muz/dataflow) add_subdirectory(muz/transforms) @@ -89,10 +86,8 @@ add_subdirectory(muz/clp) add_subdirectory(muz/tab) add_subdirectory(muz/bmc) add_subdirectory(muz/ddnf) -add_subdirectory(muz/duality) add_subdirectory(muz/spacer) add_subdirectory(muz/fp) -add_subdirectory(tactic/nlsat_smt) add_subdirectory(tactic/ufbv) add_subdirectory(sat/sat_solver) add_subdirectory(tactic/smtlogics) @@ -160,7 +155,6 @@ set (libz3_public_headers z3_fpa.h z3.h c++/z3++.h - z3_interp.h z3_macros.h z3_optimization.h z3_polynomial.h diff --git a/src/ackermannization/ackermannize_bv_tactic.cpp b/src/ackermannization/ackermannize_bv_tactic.cpp index 73034622e..3ffee518e 100644 --- a/src/ackermannization/ackermannize_bv_tactic.cpp +++ b/src/ackermannization/ackermannize_bv_tactic.cpp @@ -29,12 +29,7 @@ public: ~ackermannize_bv_tactic() override { } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - mc = nullptr; + void operator()(goal_ref const & g, goal_ref_buffer & result) override { tactic_report report("ackermannize", *g); fail_if_unsat_core_generation("ackermannize", g); fail_if_proof_generation("ackermannize", g); @@ -52,17 +47,14 @@ public: TRACE("ackermannize", tout << "ackermannize not run due to limit" << std::endl;); result.reset(); result.push_back(g.get()); - mc = nullptr; - pc = nullptr; - core = nullptr; return; } result.push_back(resg.get()); // report model if (g->models_enabled()) { - mc = mk_ackermannize_bv_model_converter(m, lackr.get_info()); + resg->add(mk_ackermannize_bv_model_converter(m, lackr.get_info())); } - + resg->inc_depth(); TRACE("ackermannize", resg->display(tout << "out\n");); SASSERT(resg->is_well_sorted()); diff --git a/src/ackermannization/ackr_model_converter.cpp b/src/ackermannization/ackr_model_converter.cpp index f5b90de1b..9e87a1a69 100644 --- a/src/ackermannization/ackr_model_converter.cpp +++ b/src/ackermannization/ackr_model_converter.cpp @@ -39,8 +39,9 @@ public: ~ackr_model_converter() override { } - void operator()(model_ref & md, unsigned goal_idx) override { - SASSERT(goal_idx == 0); + void get_units(obj_map& units) override { units.reset(); } + + void operator()(model_ref & md) override { SASSERT(!fixed_model || md.get() == 0 || (!md->get_num_constants() && !md->get_num_functions())); model_ref& old_model = fixed_model ? abstr_model : md; SASSERT(old_model.get()); @@ -49,8 +50,6 @@ public: md = new_model; } - void operator()(model_ref & md) override { operator()(md, 0); } - model_converter * translate(ast_translation & translator) override { ackr_info_ref retv_info = info->translate(translator); if (fixed_model) { @@ -62,6 +61,10 @@ public: } } + void display(std::ostream & out) override { + out << "(ackr-model-converter)\n"; + } + protected: ast_manager & m; const ackr_info_ref info; @@ -144,6 +147,7 @@ void ackr_model_converter::add_entry(model_evaluator & evaluator, else { TRACE("ackr_model", tout << "entry already present\n";); } + } model_converter * mk_ackr_model_converter(ast_manager & m, const ackr_info_ref& info) { diff --git a/src/ackermannization/lackr_model_converter_lazy.cpp b/src/ackermannization/lackr_model_converter_lazy.cpp index 15673ffb6..a37373aab 100644 --- a/src/ackermannization/lackr_model_converter_lazy.cpp +++ b/src/ackermannization/lackr_model_converter_lazy.cpp @@ -30,8 +30,7 @@ public: ~lackr_model_converter_lazy() override { } - void operator()(model_ref & md, unsigned goal_idx) override { - SASSERT(goal_idx == 0); + void operator()(model_ref & md) override { SASSERT(md.get() == 0 || (!md->get_num_constants() && !md->get_num_functions())); SASSERT(model_constructor.get()); model * new_model = alloc(model, m); @@ -39,15 +38,18 @@ public: model_constructor->make_model(md); } - void operator()(model_ref & md) override { - operator()(md, 0); - } + void get_units(obj_map& units) override { units.reset(); } //void display(std::ostream & out); model_converter * translate(ast_translation & translator) override { NOT_IMPLEMENTED_YET(); } + + void display(std::ostream & out) override { + out << "(lackr-model-converter)\n"; + } + protected: ast_manager& m; const lackr_model_constructor_ref model_constructor; diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index fcdbb1651..4a5514a7b 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -48,7 +48,6 @@ z3_add_component(api api_datatype.cpp api_fpa.cpp api_goal.cpp - api_interp.cpp api_log.cpp api_model.cpp api_numeral.cpp @@ -67,7 +66,6 @@ z3_add_component(api z3_replayer.cpp ${full_path_generated_files} COMPONENT_DEPENDENCIES - interp opt portfolio realclosure diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 1f4a86903..536b94fb2 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -195,7 +195,6 @@ extern "C" { MK_BINARY(Z3_mk_xor, mk_c(c)->get_basic_fid(), OP_XOR, SKIP); MK_NARY(Z3_mk_and, mk_c(c)->get_basic_fid(), OP_AND, SKIP); MK_NARY(Z3_mk_or, mk_c(c)->get_basic_fid(), OP_OR, SKIP); - MK_UNARY(Z3_mk_interpolant, mk_c(c)->get_basic_fid(), OP_INTERP, SKIP); Z3_ast mk_ite_core(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3) { expr * result = mk_c(c)->m().mk_ite(to_expr(t1), to_expr(t2), to_expr(t3)); @@ -900,7 +899,6 @@ extern "C" { case OP_NOT: return Z3_OP_NOT; case OP_IMPLIES: return Z3_OP_IMPLIES; case OP_OEQ: return Z3_OP_OEQ; - case OP_INTERP: return Z3_OP_INTERP; case PR_UNDEF: return Z3_OP_PR_UNDEF; case PR_TRUE: return Z3_OP_PR_TRUE; diff --git a/src/api/api_context.h b/src/api/api_context.h index 72a0e4bbb..50e89113d 100644 --- a/src/api/api_context.h +++ b/src/api/api_context.h @@ -114,7 +114,7 @@ namespace api { ~context(); ast_manager & m() const { return *(m_manager.get()); } - context_params & params() { return m_params; } + context_params & params() { m_params.updt_params(); return m_params; } scoped_ptr& cmd() { return m_cmd; } bool produce_proofs() const { return m().proofs_enabled(); } bool produce_models() const { return m_params.m_model; } diff --git a/src/api/api_goal.cpp b/src/api/api_goal.cpp index 0783af5ab..ae48a3f6f 100644 --- a/src/api/api_goal.cpp +++ b/src/api/api_goal.cpp @@ -21,6 +21,7 @@ Revision History: #include "api/api_context.h" #include "api/api_goal.h" #include "ast/ast_translation.h" +#include "api/api_model.h" extern "C" { @@ -151,6 +152,20 @@ extern "C" { Z3_CATCH_RETURN(Z3_FALSE); } + Z3_model Z3_API Z3_goal_convert_model(Z3_context c, Z3_goal g, Z3_model m) { + Z3_TRY; + LOG_Z3_goal_convert_model(c, g, m); + RESET_ERROR_CODE(); + model_ref new_m; + Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); + mk_c(c)->save_object(m_ref); + if (m) m_ref->m_model = to_model_ref(m)->copy(); + if (to_goal_ref(g)->mc()) + (*to_goal_ref(g)->mc())(m_ref->m_model); + RETURN_Z3(of_model(m_ref)); + Z3_CATCH_RETURN(0); + } + Z3_goal Z3_API Z3_goal_translate(Z3_context c, Z3_goal g, Z3_context target) { Z3_TRY; LOG_Z3_goal_translate(c, g, target); @@ -178,4 +193,18 @@ extern "C" { Z3_CATCH_RETURN(""); } + Z3_string Z3_API Z3_goal_to_dimacs_string(Z3_context c, Z3_goal g) { + Z3_TRY; + LOG_Z3_goal_to_dimacs_string(c, g); + RESET_ERROR_CODE(); + std::ostringstream buffer; + to_goal_ref(g)->display_dimacs(buffer); + // Hack for removing the trailing '\n' + std::string result = buffer.str(); + SASSERT(result.size() > 0); + result.resize(result.size()-1); + return mk_c(c)->mk_external_string(result); + Z3_CATCH_RETURN(""); + } + }; diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp deleted file mode 100644 index 1000a6d3b..000000000 --- a/src/api/api_interp.cpp +++ /dev/null @@ -1,728 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - api_interp.cpp - - Abstract: - API for interpolation - - Author: - - Ken McMillan - - Revision History: - - --*/ -#include -#include -#include "api/z3.h" -#include "api/api_log_macros.h" -#include "api/api_context.h" -#include "api/api_tactic.h" -#include "api/api_solver.h" -#include "api/api_model.h" -#include "api/api_stats.h" -#include "api/api_ast_vector.h" -#include "solver/tactic2solver.h" -#include "util/scoped_ctrl_c.h" -#include "util/cancel_eh.h" -#include "util/scoped_timer.h" -#include "tactic/portfolio/smt_strategic_solver.h" -#include "smt/smt_solver.h" -#include "smt/smt_implied_equalities.h" -#include "interp/iz3interp.h" -#include "interp/iz3profiling.h" -#include "interp/iz3hash.h" -#include "interp/iz3pp.h" -#include "interp/iz3checker.h" -#include "ast/scoped_proof.h" - -using namespace stl_ext; - -// WARNING: don't make a hash_map with this if the range type -// has a destructor: you'll get an address dependency!!! -namespace stl_ext { - template <> - class hash < Z3_ast > { - public: - size_t operator()(const Z3_ast p) const { - return (size_t)p; - } - }; -} - -typedef interpolation_options_struct *Z3_interpolation_options; - -extern "C" { - - Z3_context Z3_mk_interpolation_context(Z3_config cfg){ - if (!cfg) cfg = Z3_mk_config(); - Z3_set_param_value(cfg, "PROOF", "true"); - Z3_set_param_value(cfg, "MODEL", "true"); - // Z3_set_param_value(cfg, "PRE_SIMPLIFIER","false"); - // Z3_set_param_value(cfg, "SIMPLIFY_CLAUSES","false"); - - Z3_context ctx = Z3_mk_context(cfg); - return ctx; - } - - void Z3_interpolate_proof(Z3_context ctx, - Z3_ast proof, - int num, - Z3_ast *cnsts, - unsigned *parents, - Z3_params options, - Z3_ast *interps, - int num_theory, - Z3_ast *theory) - { - - if (num > 1){ // if we have interpolants to compute - - ptr_vector pre_cnsts_vec(num); // get constraints in a vector - for (int i = 0; i < num; i++){ - ast *a = to_ast(cnsts[i]); - pre_cnsts_vec[i] = a; - } - - ::vector pre_parents_vec; // get parents in a vector - if (parents){ - pre_parents_vec.resize(num); - for (int i = 0; i < num; i++) - pre_parents_vec[i] = parents[i]; - } - - ptr_vector theory_vec; // get background theory in a vector - if (theory){ - theory_vec.resize(num_theory); - for (int i = 0; i < num_theory; i++) - theory_vec[i] = to_ast(theory[i]); - } - - ptr_vector interpolants(num - 1); // make space for result - - ast_manager &_m = mk_c(ctx)->m(); - iz3interpolate(_m, - to_ast(proof), - pre_cnsts_vec, - pre_parents_vec, - interpolants, - theory_vec, - nullptr); // ignore params for now FIXME - - // copy result back - for (unsigned i = 0; i < interpolants.size(); i++){ - mk_c(ctx)->save_ast_trail(interpolants[i]); - interps[i] = of_ast(interpolants[i]); - _m.dec_ref(interpolants[i]); - } - } - } - - static std::ostringstream itp_err; - - int Z3_check_interpolant(Z3_context ctx, - unsigned num, - Z3_ast *cnsts, - unsigned *parents, - Z3_ast *itp, - Z3_string *error, - unsigned num_theory, - Z3_ast *theory){ - - ast_manager &_m = mk_c(ctx)->m(); - itp_err.clear(); - - // need a solver -- make one here, but how? - params_ref p = params_ref::get_empty(); //FIXME - scoped_ptr sf(mk_smt_solver_factory()); - scoped_ptr sp((*(sf))(_m, p, false, true, false, symbol("AUFLIA"))); - - ptr_vector cnsts_vec(num); // get constraints in a vector - for (unsigned i = 0; i < num; i++){ - ast *a = to_ast(cnsts[i]); - cnsts_vec[i] = a; - } - - ptr_vector itp_vec(num); // get interpolants in a vector - for (unsigned i = 0; i < num - 1; i++){ - ast *a = to_ast(itp[i]); - itp_vec[i] = a; - } - - ::vector parents_vec; // get parents in a vector - if (parents){ - parents_vec.resize(num); - for (unsigned i = 0; i < num; i++) - parents_vec[i] = parents[i]; - } - - ptr_vector theory_vec; // get background theory in a vector - if (theory){ - theory_vec.resize(num_theory); - for (unsigned i = 0; i < num_theory; i++) - theory_vec[i] = to_ast(theory[i]); - } - - bool res = iz3check(_m, - sp.get(), - itp_err, - cnsts_vec, - parents_vec, - itp_vec, - theory_vec); - - *error = res ? nullptr : itp_err.str().c_str(); - return res; - } - - - static std::string Z3_profile_string; - - Z3_string Z3_interpolation_profile(Z3_context ctx){ - std::ostringstream f; - profiling::print(f); - Z3_profile_string = f.str(); - return Z3_profile_string.c_str(); - } - - - Z3_interpolation_options - Z3_mk_interpolation_options(){ - return (Z3_interpolation_options) new interpolation_options_struct; - } - - void - Z3_del_interpolation_options(Z3_interpolation_options opts){ - delete opts; - } - - void - Z3_set_interpolation_option(Z3_interpolation_options opts, - Z3_string name, - Z3_string value){ - opts->map[name] = value; - } - - Z3_ast_vector Z3_API Z3_get_interpolant(Z3_context c, Z3_ast pf, Z3_ast pat, Z3_params p){ - Z3_TRY; - LOG_Z3_get_interpolant(c, pf, pat, p); - RESET_ERROR_CODE(); - - Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); - mk_c(c)->save_object(v); - - ast *_pf = to_ast(pf); - ast *_pat = to_ast(pat); - - ptr_vector interp; - ptr_vector cnsts; // to throw away - - ast_manager &_m = mk_c(c)->m(); - - iz3interpolate(_m, - _pf, - cnsts, - _pat, - interp, - (interpolation_options_struct *)nullptr // ignore params for now - ); - - // copy result back - for (unsigned i = 0; i < interp.size(); i++){ - v->m_ast_vector.push_back(interp[i]); - _m.dec_ref(interp[i]); - } - RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(nullptr); - } - - Z3_lbool Z3_API Z3_compute_interpolant(Z3_context c, Z3_ast pat, Z3_params p, Z3_ast_vector *out_interp, Z3_model *model){ - Z3_TRY; - LOG_Z3_compute_interpolant(c, pat, p, out_interp, model); - RESET_ERROR_CODE(); - - - // params_ref &_p = to_params(p)->m_params; - params_ref _p; - _p.set_bool("proof", true); // this is currently useless - - scoped_proof_mode spm(mk_c(c)->m(), PGM_ENABLED); - scoped_ptr sf = mk_smt_solver_factory(); - scoped_ptr m_solver((*sf)(mk_c(c)->m(), _p, true, true, true, ::symbol::null)); - m_solver.get()->updt_params(_p); // why do we have to do this? - - - // some boilerplate stolen from Z3_solver_check - unsigned timeout = p?to_params(p)->m_params.get_uint("timeout", mk_c(c)->get_timeout()):UINT_MAX; - unsigned rlimit = p?to_params(p)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()):0; - bool use_ctrl_c = p?to_params(p)->m_params.get_bool("ctrl_c", false): false; - cancel_eh eh(mk_c(c)->m().limit()); - api::context::set_interruptable si(*(mk_c(c)), eh); - - ast *_pat = to_ast(pat); - - ptr_vector interp; - ptr_vector cnsts; // to throw away - - ast_manager &_m = mk_c(c)->m(); - - model_ref m; - lbool _status; - - { - scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); - scoped_timer timer(timeout, &eh); - scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); - try { - _status = iz3interpolate(_m, - *(m_solver.get()), - _pat, - cnsts, - interp, - m, - nullptr // ignore params for now - ); - } - catch (z3_exception & ex) { - mk_c(c)->handle_exception(ex); - RETURN_Z3_compute_interpolant Z3_L_UNDEF; - } - } - - for (unsigned i = 0; i < cnsts.size(); i++) - _m.dec_ref(cnsts[i]); - - Z3_lbool status = of_lbool(_status); - - Z3_ast_vector_ref *v = nullptr; - *model = nullptr; - - if (_status == l_false){ - // copy result back - v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); - mk_c(c)->save_object(v); - for (unsigned i = 0; i < interp.size(); i++){ - v->m_ast_vector.push_back(interp[i]); - _m.dec_ref(interp[i]); - } - } - else { - model_ref mr; - m_solver.get()->get_model(mr); - if(mr.get()){ - Z3_model_ref *tmp_val = alloc(Z3_model_ref, *mk_c(c)); - tmp_val->m_model = mr.get(); - mk_c(c)->save_object(tmp_val); - *model = of_model(tmp_val); - } - } - - *out_interp = of_ast_vector(v); - - RETURN_Z3_compute_interpolant status; - Z3_CATCH_RETURN(Z3_L_UNDEF); - } - - -}; - - -static void tokenize(const std::string &str, std::vector &tokens){ - for (unsigned i = 0; i < str.size();){ - if (str[i] == ' '){ i++; continue; } - unsigned beg = i; - while (i < str.size() && str[i] != ' ')i++; - if (i > beg) - tokens.push_back(str.substr(beg, i - beg)); - } -} - -static void get_file_params(const char *filename, hash_map ¶ms){ - std::ifstream f(filename); - if (f){ - std::string first_line; - std::getline(f, first_line); - // std::cout << "first line: '" << first_line << "'" << std::endl; - if (first_line.size() >= 2 && first_line[0] == ';' && first_line[1] == '!'){ - std::vector tokens; - tokenize(first_line.substr(2, first_line.size() - 2), tokens); - for (unsigned i = 0; i < tokens.size(); i++){ - std::string &tok = tokens[i]; - size_t eqpos = tok.find('='); - if (eqpos != std::string::npos){ - std::string left = tok.substr(0, eqpos); - std::string right = tok.substr(eqpos + 1, tok.size() - eqpos - 1); - params[left] = right; - } - } - } - f.close(); - } -} - -extern "C" { - -#if 0 - static void iZ3_write_seq(Z3_context ctx, int num, Z3_ast *cnsts, const char *filename, int num_theory, Z3_ast *theory){ - int num_fmlas = num+num_theory; - std::vector fmlas(num_fmlas); - if(num_theory) - std::copy(theory,theory+num_theory,fmlas.begin()); - for(int i = 0; i < num_theory; i++) - fmlas[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),fmlas[i]); - std::copy(cnsts,cnsts+num,fmlas.begin()+num_theory); - Z3_string smt = Z3_benchmark_to_smtlib_string(ctx,"none","AUFLIA","unknown","",num_fmlas-1,&fmlas[0],fmlas[num_fmlas-1]); - std::ofstream f(filename); - if(num_theory) - f << ";! THEORY=" << num_theory << "\n"; - f << smt; - f.close(); - } - - void Z3_write_interpolation_problem(Z3_context ctx, int num, Z3_ast *cnsts, unsigned *parents, const char *filename, int num_theory, Z3_ast *theory){ - if(!parents){ - iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); - return; - } - std::vector tcnsts(num); - hash_map syms; - for(int j = 0; j < num - 1; j++){ - std::ostringstream oss; - oss << "$P" << j; - std::string name = oss.str(); - Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); - Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); - syms[j] = symbol; - tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); - } - tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); - for(int j = num-2; j >= 0; j--){ - int parent = parents[j]; - // assert(parent >= 0 && parent < num); - tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); - } - iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); - } -#else - - - static Z3_ast and_vec(Z3_context ctx, svector &c){ - return (c.size() > 1) ? Z3_mk_and(ctx, c.size(), &c[0]) : c[0]; - } - - static Z3_ast parents_vector_to_tree(Z3_context ctx, int num, Z3_ast *cnsts, unsigned *parents){ - Z3_ast res; - if (!parents){ - res = Z3_mk_interpolant(ctx, cnsts[0]); - for (int i = 1; i < num - 1; i++){ - Z3_ast bar[2] = { res, cnsts[i] }; - res = Z3_mk_interpolant(ctx, Z3_mk_and(ctx, 2, bar)); - } - if (num > 1){ - Z3_ast bar[2] = { res, cnsts[num - 1] }; - res = Z3_mk_and(ctx, 2, bar); - } - } - else { - std::vector > chs(num); - for (int i = 0; i < num - 1; i++){ - svector &c = chs[i]; - c.push_back(cnsts[i]); - Z3_ast foo = Z3_mk_interpolant(ctx, and_vec(ctx, c)); - chs[parents[i]].push_back(foo); - } - { - svector &c = chs[num - 1]; - c.push_back(cnsts[num - 1]); - res = and_vec(ctx, c); - } - } - Z3_inc_ref(ctx, res); - return res; - } - - void Z3_write_interpolation_problem(Z3_context ctx, unsigned num, Z3_ast *cnsts, unsigned *parents, const char *filename, unsigned num_theory, Z3_ast *theory){ - std::ofstream f(filename); - if (num > 0){ -#if 0 - // Suggested shorthand: - ptr_vector cnsts_vec; - cnsts_vec.append(num, to_exprs(cnsts)); - cnsts_vec.append(num_theory, to_exprs(theory)); -#endif - ptr_vector cnsts_vec(num); // get constraints in a vector - for (unsigned i = 0; i < num; i++){ - expr *a = to_expr(cnsts[i]); - cnsts_vec[i] = a; - } - for (unsigned i = 0; i < num_theory; i++){ - expr *a = to_expr(theory[i]); - cnsts_vec.push_back(a); - } - Z3_ast tree = parents_vector_to_tree(ctx, num, cnsts, parents); - iz3pp(mk_c(ctx)->m(), cnsts_vec, to_expr(tree), f); - Z3_dec_ref(ctx, tree); - } - f.close(); - -#if 0 - - - if(!parents){ - iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); - return; - } - std::vector tcnsts(num); - hash_map syms; - for(int j = 0; j < num - 1; j++){ - std::ostringstream oss; - oss << "$P" << j; - std::string name = oss.str(); - Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); - Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); - syms[j] = symbol; - tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); - } - tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); - for(int j = num-2; j >= 0; j--){ - int parent = parents[j]; - // assert(parent >= 0 && parent < num); - tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); - } - iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); -#endif - - } - - -#endif - - static std::vector read_cnsts; - static std::vector read_parents; - static std::ostringstream read_error; - static std::string read_msg; - static std::vector read_theory; - - static bool iZ3_parse(Z3_context ctx, const char *filename, const char **error, svector &assertions){ - read_error.clear(); - try { - std::string foo(filename); - Z3_ast assrts = Z3_parse_smtlib2_file(ctx, filename, 0, nullptr, nullptr, 0, nullptr, nullptr); - Z3_app app = Z3_to_app(ctx, assrts); - int nconjs = Z3_get_app_num_args(ctx, app); - assertions.resize(nconjs); - for (int k = 0; k < nconjs; k++) - assertions[k] = Z3_get_app_arg(ctx, app, k); - } - catch (...) { - read_error << "SMTLIB parse error: " << Z3_get_parser_error(ctx); - read_msg = read_error.str(); - *error = read_msg.c_str(); - return false; - } - Z3_set_error_handler(ctx, nullptr); - return true; - } - - - int Z3_read_interpolation_problem(Z3_context ctx, unsigned *_num, Z3_ast *cnsts[], unsigned *parents[], const char *filename, Z3_string_ptr error, unsigned *ret_num_theory, Z3_ast *theory[]){ - - hash_map file_params; - get_file_params(filename, file_params); - - unsigned num_theory = 0; - if (file_params.find("THEORY") != file_params.end()) - num_theory = atoi(file_params["THEORY"].c_str()); - - svector assertions; - if (!iZ3_parse(ctx, filename, error, assertions)) - return false; - - if (num_theory > assertions.size()) - num_theory = assertions.size(); - unsigned num = assertions.size() - num_theory; - - read_cnsts.resize(num); - read_parents.resize(num); - read_theory.resize(num_theory); - - for (unsigned j = 0; j < num_theory; j++) - read_theory[j] = assertions[j]; - for (unsigned j = 0; j < num; j++) - read_cnsts[j] = assertions[j + num_theory]; - - if (ret_num_theory) - *ret_num_theory = num_theory; - if (theory) - *theory = &read_theory[0]; - - if (!parents){ - *_num = num; - *cnsts = &read_cnsts[0]; - return true; - } - - for (unsigned j = 0; j < num; j++) - read_parents[j] = SHRT_MAX; - - hash_map pred_map; - - for (unsigned j = 0; j < num; j++){ - Z3_ast lhs = nullptr, rhs = read_cnsts[j]; - - if (Z3_get_decl_kind(ctx, Z3_get_app_decl(ctx, Z3_to_app(ctx, rhs))) == Z3_OP_IMPLIES){ - Z3_app app1 = Z3_to_app(ctx, rhs); - Z3_ast lhs1 = Z3_get_app_arg(ctx, app1, 0); - Z3_ast rhs1 = Z3_get_app_arg(ctx, app1, 1); - if (Z3_get_decl_kind(ctx, Z3_get_app_decl(ctx, Z3_to_app(ctx, lhs1))) == Z3_OP_AND){ - Z3_app app2 = Z3_to_app(ctx, lhs1); - int nconjs = Z3_get_app_num_args(ctx, app2); - for (int k = nconjs - 1; k >= 0; --k) - rhs1 = Z3_mk_implies(ctx, Z3_get_app_arg(ctx, app2, k), rhs1); - rhs = rhs1; - } - } - - while (1){ - Z3_app app = Z3_to_app(ctx, rhs); - Z3_func_decl func = Z3_get_app_decl(ctx, app); - Z3_decl_kind dk = Z3_get_decl_kind(ctx, func); - if (dk == Z3_OP_IMPLIES){ - if (lhs){ - Z3_ast child = lhs; - if (pred_map.find(child) == pred_map.end()){ - read_error << "formula " << j + 1 << ": unknown: " << Z3_ast_to_string(ctx, child); - goto fail; - } - int child_num = pred_map[child]; - if (read_parents[child_num] != SHRT_MAX){ - read_error << "formula " << j + 1 << ": multiple reference: " << Z3_ast_to_string(ctx, child); - goto fail; - } - read_parents[child_num] = j; - } - lhs = Z3_get_app_arg(ctx, app, 0); - rhs = Z3_get_app_arg(ctx, app, 1); - } - else { - if (!lhs){ - read_error << "formula " << j + 1 << ": should be (implies {children} fmla parent)"; - goto fail; - } - read_cnsts[j] = lhs; - Z3_ast name = rhs; - if (pred_map.find(name) != pred_map.end()){ - read_error << "formula " << j + 1 << ": duplicate symbol"; - goto fail; - } - pred_map[name] = j; - break; - } - } - } - - for (unsigned j = 0; j < num - 1; j++) - if (read_parents[j] == SHRT_MAX){ - read_error << "formula " << j + 1 << ": unreferenced"; - goto fail; - } - - *_num = num; - *cnsts = &read_cnsts[0]; - *parents = &read_parents[0]; - return true; - - fail: - read_msg = read_error.str(); - *error = read_msg.c_str(); - return false; - - } -} - - -#if 0 -/** Constant reprepresenting a root of a formula tree for tree interpolation */ -#define IZ3_ROOT SHRT_MAX - -/** This function uses Z3 to determine satisfiability of a set of - constraints. If UNSAT, an interpolant is returned, based on the - refutation generated by Z3. If SAT, a model is returned. - - If "parents" is non-null, computes a tree interpolant. The tree is - defined by the array "parents". This array maps each formula in - the tree to its parent, where formulas are indicated by their - integer index in "cnsts". The parent of formula n must have index - greater than n. The last formula is the root of the tree. Its - parent entry should be the constant IZ3_ROOT. - - If "parents" is null, computes a sequence interpolant. - - \param ctx The Z3 context. Must be generated by iz3_mk_context - \param num The number of constraints in the sequence - \param cnsts Array of constraints (AST's in context ctx) - \param parents The parents vector defining the tree structure - \param options Interpolation options (may be NULL) - \param interps Array to return interpolants (size at least num-1, may be NULL) - \param model Returns a Z3 model if constraints SAT (may be NULL) - \param labels Returns relevant labels if SAT (may be NULL) - \param incremental - - VERY IMPORTANT: All the Z3 formulas in cnsts must be in Z3 - context ctx. The model and interpolants returned are also - in this context. - - The return code is as in Z3_check_assumptions, that is, - - Z3_L_FALSE = constraints UNSAT (interpolants returned) - Z3_L_TRUE = constraints SAT (model returned) - Z3_L_UNDEF = Z3 produced no result, or interpolation not possible - - Currently, this function supports integer and boolean variables, - as well as arrays over these types, with linear arithmetic, - uninterpreted functions and quantifiers over integers (that is - AUFLIA). Interpolants are produced in AULIA. However, some - uses of array operations may cause quantifiers to appear in the - interpolants even when there are no quantifiers in the input formulas. - Although quantifiers may appear in the input formulas, Z3 may give up in - this case, returning Z3_L_UNDEF. - - If "incremental" is true, cnsts must contain exactly the set of - formulas that are currently asserted in the context. If false, - there must be no formulas currently asserted in the context. - Setting "incremental" to true makes it posisble to incrementally - add and remove constraints from the context until the context - becomes UNSAT, at which point an interpolant is computed. Caution - must be used, however. Before popping the context, if you wish to - keep the interolant formulas, you *must* preserve them by using - Z3_persist_ast. Also, if you want to simplify the interpolant - formulas using Z3_simplify, you must first pop all of the - assertions in the context (or use a different context). Otherwise, - the formulas will be simplified *relative* to these constraints, - which is almost certainly not what you want. - - - Current limitations on tree interpolants. In a tree interpolation - problem, each constant (0-ary function symbol) must occur only - along one path from root to leaf. Function symbols (of arity > 0) - are considered to have global scope (i.e., may appear in any - interpolant formula). - - def_API('Z3_interpolate', BOOL, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(PARAMS), _out_array(1, AST), _out(MODEL), _out(LITERALS), _in(UINT), _in(UINT), _in_array(9, AST))) -*/ - -Z3_lbool Z3_API Z3_interpolate(Z3_context ctx, - unsigned num, - Z3_ast *cnsts, - unsigned *parents, - Z3_params options, - Z3_ast *interps, - Z3_model *model, - Z3_literals *labels, - unsigned incremental, - unsigned num_theory, - Z3_ast *theory); -#endif diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index 09a255738..6318283c6 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -421,34 +421,7 @@ extern "C" { expr * r = to_func_entry(e)->m_func_entry->get_arg(i); RETURN_Z3(of_expr(r)); Z3_CATCH_RETURN(nullptr); - } - - // ---------------------------- - // - // DEPRECATED API - // - // ---------------------------- - - void Z3_API Z3_del_model(Z3_context c, Z3_model m) { - Z3_model_dec_ref(c, m); - } - - unsigned Z3_API Z3_get_model_num_constants(Z3_context c, Z3_model m) { - return Z3_model_get_num_consts(c, m); - } - - Z3_func_decl Z3_API Z3_get_model_constant(Z3_context c, Z3_model m, unsigned i) { - return Z3_model_get_const_decl(c, m, i); - } - - unsigned Z3_API Z3_get_model_num_funcs(Z3_context c, Z3_model m) { - return Z3_model_get_num_funcs(c, m); - } - - Z3_func_decl Z3_API Z3_get_model_func_decl(Z3_context c, Z3_model m, unsigned i) { - return Z3_model_get_func_decl(c, m, i); - } - + } unsigned get_model_func_num_entries_core(Z3_context c, Z3_model m, unsigned i) { RESET_ERROR_CODE(); diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index 2712eef6f..21c55f428 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -17,8 +17,8 @@ Revision History: --*/ #include #include "util/cancel_eh.h" -#include "util/file_path.h" #include "util/scoped_timer.h" +#include "util/file_path.h" #include "parsers/smt2/smt2parser.h" #include "opt/opt_context.h" #include "opt/opt_cmds.h" @@ -31,6 +31,7 @@ Revision History: #include "api/api_model.h" #include "api/api_ast_vector.h" + extern "C" { struct Z3_optimize_ref : public api::object { @@ -139,8 +140,16 @@ extern "C" { r = to_optimize_ptr(o)->optimize(); } catch (z3_exception& ex) { - mk_c(c)->handle_exception(ex); + if (!mk_c(c)->m().canceled()) { + mk_c(c)->handle_exception(ex); + } r = l_undef; + if (ex.msg() == std::string("canceled") && mk_c(c)->m().canceled()) { + to_optimize_ptr(o)->set_reason_unknown(ex.msg()); + } + else { + mk_c(c)->handle_exception(ex); + } } // to_optimize_ref(d).cleanup(); } @@ -296,6 +305,11 @@ extern "C" { parse_wcnf(*to_optimize_ptr(opt), s, h); return; } + if (ext && std::string("lp") == ext) { + unsigned_vector h; + parse_lp(*to_optimize_ptr(opt), s, h); + return; + } scoped_ptr ctx = alloc(cmd_context, false, &m); install_opt_cmds(*ctx.get(), to_optimize_ptr(opt)); std::stringstream errstrm; diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index 28db62cc3..d791dc2a5 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -20,6 +20,7 @@ Revision History: #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" +#include "api/api_ast_vector.h" #include "cmd_context/cmd_context.h" #include "smt/smt_solver.h" #include "parsers/smt2/smt2parser.h" @@ -41,7 +42,7 @@ extern "C" { // --------------- // Support for SMTLIB2 - Z3_ast parse_smtlib2_stream(bool exec, Z3_context c, std::istream& is, + Z3_ast_vector parse_smtlib2_stream(bool exec, Z3_context c, std::istream& is, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], @@ -51,6 +52,8 @@ extern "C" { Z3_TRY; scoped_ptr ctx = alloc(cmd_context, false, &(mk_c(c)->m())); ctx->set_ignore_check(true); + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); + mk_c(c)->save_object(v); for (unsigned i = 0; i < num_decls; ++i) { ctx->insert(to_symbol(decl_names[i]), to_func_decl(decls[i])); } @@ -69,7 +72,7 @@ extern "C" { ctx = nullptr; mk_c(c)->m_parser_error_buffer = errstrm.str(); SET_ERROR_CODE(Z3_PARSER_ERROR); - return of_ast(mk_c(c)->m().mk_true()); + return of_ast_vector(v); } } catch (z3_exception& e) { @@ -77,16 +80,18 @@ extern "C" { mk_c(c)->m_parser_error_buffer = errstrm.str(); ctx = nullptr; SET_ERROR_CODE(Z3_PARSER_ERROR); - return of_ast(mk_c(c)->m().mk_true()); + return of_ast_vector(v); } ptr_vector::const_iterator it = ctx->begin_assertions(); ptr_vector::const_iterator end = ctx->end_assertions(); - unsigned size = static_cast(end - it); - return of_ast(mk_c(c)->mk_and(size, it)); + for (; it != end; ++it) { + v->m_ast_vector.push_back(*it); + } + return of_ast_vector(v); Z3_CATCH_RETURN(nullptr); } - Z3_ast Z3_API Z3_parse_smtlib2_string(Z3_context c, Z3_string str, + Z3_ast_vector Z3_API Z3_parse_smtlib2_string(Z3_context c, Z3_string str, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], @@ -97,12 +102,12 @@ extern "C" { LOG_Z3_parse_smtlib2_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); std::string s(str); std::istringstream is(s); - Z3_ast r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + Z3_ast_vector r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } - Z3_ast Z3_API Z3_parse_smtlib2_file(Z3_context c, Z3_string file_name, + Z3_ast_vector Z3_API Z3_parse_smtlib2_file(Z3_context c, Z3_string file_name, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], @@ -113,10 +118,10 @@ extern "C" { LOG_Z3_parse_smtlib2_string(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); std::ifstream is(file_name); if (!is) { - SET_ERROR_CODE(Z3_PARSER_ERROR); + SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR); return nullptr; } - Z3_ast r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + Z3_ast_vector r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); RETURN_Z3(r); Z3_CATCH_RETURN(nullptr); } diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 1b2e9dcc3..657ff025c 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -31,16 +31,21 @@ Revision History: #include "api/api_stats.h" #include "api/api_ast_vector.h" #include "solver/tactic2solver.h" -#include "solver/smt_logics.h" +#include "util/scoped_ctrl_c.h" +#include "util/cancel_eh.h" +#include "util/scoped_timer.h" +#include "util/file_path.h" #include "tactic/portfolio/smt_strategic_solver.h" #include "smt/smt_solver.h" #include "smt/smt_implied_equalities.h" +#include "solver/smt_logics.h" #include "cmd_context/cmd_context.h" #include "parsers/smt2/smt2parser.h" #include "sat/dimacs.h" #include "sat/sat_solver.h" #include "sat/tactic/goal2sat.h" + extern "C" { static void init_solver_core(Z3_context c, Z3_solver _s) { @@ -128,6 +133,15 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + + void Z3_API Z3_solver_import_model_converter(Z3_context c, Z3_solver src, Z3_solver dst) { + Z3_TRY; + LOG_Z3_solver_import_model_converter(c, src, dst); + model_converter_ref mc = to_solver_ref(src)->get_model_converter(); + to_solver_ref(dst)->set_model_converter(mc.get()); + Z3_CATCH; + } + void solver_from_stream(Z3_context c, Z3_solver s, std::istream& is) { scoped_ptr ctx = alloc(cmd_context, false, &(mk_c(c)->m())); ctx->set_ignore_check(true); @@ -146,7 +160,7 @@ extern "C" { for (; it != end; ++it) { to_solver_ref(s)->assert_expr(*it); } - // to_solver_ref(s)->set_model_converter(ctx->get_model_converter()); + to_solver_ref(s)->set_model_converter(ctx->get_model_converter()); } void Z3_API Z3_solver_from_string(Z3_context c, Z3_solver s, Z3_string c_str) { @@ -168,10 +182,10 @@ extern "C" { } else if (ext && std::string("dimacs") == ext) { ast_manager& m = to_solver_ref(s)->get_manager(); - sat::solver solver(to_solver_ref(s)->get_params(), m.limit(), nullptr); + sat::solver solver(to_solver_ref(s)->get_params(), m.limit()); parse_dimacs(is, solver); sat2goal s2g; - model_converter_ref mc; + ref mc; atom2bool_var a2b(m); goal g(m); s2g(solver, a2b, to_solver_ref(s)->get_params(), g, mc); @@ -322,6 +336,7 @@ extern "C" { to_solver_ref(s)->assert_expr(to_expr(a), to_expr(p)); Z3_CATCH; } + Z3_ast_vector Z3_API Z3_solver_get_assertions(Z3_context c, Z3_solver s) { Z3_TRY; @@ -338,6 +353,22 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + + Z3_ast_vector Z3_API Z3_solver_get_units(Z3_context c, Z3_solver s) { + Z3_TRY; + LOG_Z3_solver_get_units(c, s); + RESET_ERROR_CODE(); + init_solver(c, s); + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); + mk_c(c)->save_object(v); + expr_ref_vector fmls = to_solver_ref(s)->get_units(mk_c(c)->m()); + for (expr* f : fmls) { + v->m_ast_vector.push_back(f); + } + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(0); + } + static Z3_lbool _solver_check(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]) { for (unsigned i = 0; i < num_assumptions; i++) { if (!is_expr(to_ast(assumptions[i]))) { @@ -361,7 +392,9 @@ extern "C" { } catch (z3_exception & ex) { to_solver_ref(s)->set_reason_unknown(eh); - mk_c(c)->handle_exception(ex); + if (!mk_c(c)->m().canceled()) { + mk_c(c)->handle_exception(ex); + } return Z3_L_UNDEF; } } @@ -552,4 +585,48 @@ extern "C" { Z3_CATCH_RETURN(Z3_L_UNDEF); } + Z3_ast_vector Z3_API Z3_solver_cube(Z3_context c, Z3_solver s, Z3_ast_vector vs, unsigned cutoff) { + Z3_TRY; + LOG_Z3_solver_cube(c, s, vs, cutoff); + ast_manager& m = mk_c(c)->m(); + expr_ref_vector result(m), vars(m); + for (ast* a : to_ast_vector_ref(vs)) { + if (!is_expr(a)) { + SET_ERROR_CODE(Z3_INVALID_USAGE); + } + else { + vars.push_back(to_expr(a)); + } + } + unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); + unsigned rlimit = to_solver(s)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); + bool use_ctrl_c = to_solver(s)->m_params.get_bool("ctrl_c", false); + cancel_eh eh(mk_c(c)->m().limit()); + api::context::set_interruptable si(*(mk_c(c)), eh); + { + scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); + scoped_timer timer(timeout, &eh); + scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); + try { + result.append(to_solver_ref(s)->cube(vars, cutoff)); + } + catch (z3_exception & ex) { + mk_c(c)->handle_exception(ex); + return 0; + } + } + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); + mk_c(c)->save_object(v); + for (expr* e : result) { + v->m_ast_vector.push_back(e); + } + to_ast_vector_ref(vs).reset(); + for (expr* a : vars) { + to_ast_vector_ref(vs).push_back(a); + } + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(0); + } + + }; diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index fb2c10b68..345284fd6 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -25,25 +25,25 @@ Revision History: #include "util/cancel_eh.h" #include "util/scoped_timer.h" -Z3_apply_result_ref::Z3_apply_result_ref(api::context& c, ast_manager & m): api::object(c), m_core(m) { +Z3_apply_result_ref::Z3_apply_result_ref(api::context& c, ast_manager & m): api::object(c) { } extern "C" { -#define RETURN_TACTIC(_t_) { \ +#define RETURN_TACTIC(_t_) { \ Z3_tactic_ref * _ref_ = alloc(Z3_tactic_ref, *mk_c(c)); \ - _ref_->m_tactic = _t_; \ - mk_c(c)->save_object(_ref_); \ - Z3_tactic _result_ = of_tactic(_ref_); \ - RETURN_Z3(_result_); \ + _ref_->m_tactic = _t_; \ + mk_c(c)->save_object(_ref_); \ + Z3_tactic _result_ = of_tactic(_ref_); \ + RETURN_Z3(_result_); \ } -#define RETURN_PROBE(_t_) { \ +#define RETURN_PROBE(_t_) { \ Z3_probe_ref * _ref_ = alloc(Z3_probe_ref, *mk_c(c)); \ - _ref_->m_probe = _t_; \ - mk_c(c)->save_object(_ref_); \ - Z3_probe _result_ = of_probe(_ref_); \ - RETURN_Z3(_result_); \ + _ref_->m_probe = _t_; \ + mk_c(c)->save_object(_ref_); \ + Z3_probe _result_ = of_probe(_ref_); \ + RETURN_Z3(_result_); \ } Z3_tactic Z3_API Z3_mk_tactic(Z3_context c, Z3_string name) { @@ -418,7 +418,9 @@ extern "C" { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); try { - exec(*to_tactic_ref(t), new_goal, ref->m_subgoals, ref->m_mc, ref->m_pc, ref->m_core); + exec(*to_tactic_ref(t), new_goal, ref->m_subgoals); + ref->m_pc = new_goal->pc(); + ref->m_mc = new_goal->mc(); return of_apply_result(ref); } catch (z3_exception & ex) { @@ -513,22 +515,5 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } - Z3_model Z3_API Z3_apply_result_convert_model(Z3_context c, Z3_apply_result r, unsigned i, Z3_model m) { - Z3_TRY; - LOG_Z3_apply_result_convert_model(c, r, i, m); - RESET_ERROR_CODE(); - if (i > to_apply_result(r)->m_subgoals.size()) { - SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(nullptr); - } - model_ref new_m = to_model_ref(m)->copy(); - if (to_apply_result(r)->m_mc) - to_apply_result(r)->m_mc->operator()(new_m, i); - Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); - m_ref->m_model = new_m; - mk_c(c)->save_object(m_ref); - RETURN_Z3(of_model(m_ref)); - Z3_CATCH_RETURN(nullptr); - } }; diff --git a/src/api/api_tactic.h b/src/api/api_tactic.h index d33f3afe2..421c03fcd 100644 --- a/src/api/api_tactic.h +++ b/src/api/api_tactic.h @@ -50,7 +50,6 @@ struct Z3_apply_result_ref : public api::object { goal_ref_buffer m_subgoals; model_converter_ref m_mc; proof_converter_ref m_pc; - expr_dependency_ref m_core; Z3_apply_result_ref(api::context& c, ast_manager & m); ~Z3_apply_result_ref() override {} }; diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index b81927474..665ffb438 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -147,20 +147,12 @@ namespace z3 { Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } - void init_interp(config & c) { - m_ctx = Z3_mk_interpolation_context(c); - m_enable_exceptions = true; - Z3_set_error_handler(m_ctx, 0); - Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); - } context(context const & s); context & operator=(context const & s); public: - struct interpolation {}; context() { config c; init(c); } context(config & c) { init(c); } - context(config & c, interpolation) { init_interp(c); } ~context() { Z3_del_context(m_ctx); } operator Z3_context() const { return m_ctx; } @@ -187,7 +179,7 @@ namespace z3 { \brief The C++ API uses by defaults exceptions on errors. For applications that don't work well with exceptions (there should be only few) you have the ability to turn off exceptions. The tradeoffs are that applications - have to very careful about using check_error() after calls that may result in an + have to be very careful about using check_error() after calls that may result in an erroneous state. */ void set_enable_exceptions(bool f) { m_enable_exceptions = f; } @@ -329,17 +321,12 @@ namespace z3 { /** \brief parsing */ - expr parse_string(char const* s); - expr parse_file(char const* file); + expr_vector parse_string(char const* s); + expr_vector parse_file(char const* file); - expr parse_string(char const* s, sort_vector const& sorts, func_decl_vector const& decls); - expr parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls); + expr_vector parse_string(char const* s, sort_vector const& sorts, func_decl_vector const& decls); + expr_vector parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls); - /** - \brief Interpolation support - */ - check_result compute_interpolant(expr const& pat, params const& p, expr_vector& interp, model& m); - expr_vector get_interpolant(expr const& proof, expr const& pat, params const& p); }; @@ -440,6 +427,7 @@ namespace z3 { void set(char const * k, unsigned n) { Z3_params_set_uint(ctx(), m_params, ctx().str_symbol(k), n); } void set(char const * k, double n) { Z3_params_set_double(ctx(), m_params, ctx().str_symbol(k), n); } void set(char const * k, symbol const & s) { Z3_params_set_symbol(ctx(), m_params, ctx().str_symbol(k), s); } + void set(char const * k, char const* s) { Z3_params_set_symbol(ctx(), m_params, ctx().str_symbol(k), ctx().str_symbol(s)); } friend std::ostream & operator<<(std::ostream & out, params const & p); }; @@ -844,7 +832,6 @@ namespace z3 { */ friend expr operator!(expr const & a); - /** \brief Return an expression representing a and b. @@ -901,6 +888,16 @@ namespace z3 { friend expr ite(expr const & c, expr const & t, expr const & e); + bool is_true() const { return is_app() && Z3_OP_TRUE == decl().decl_kind(); } + bool is_false() const { return is_app() && Z3_OP_FALSE == decl().decl_kind(); } + bool is_not() const { return is_app() && Z3_OP_NOT == decl().decl_kind(); } + bool is_and() const { return is_app() && Z3_OP_AND == decl().decl_kind(); } + bool is_or() const { return is_app() && Z3_OP_OR == decl().decl_kind(); } + bool is_xor() const { return is_app() && Z3_OP_XOR == decl().decl_kind(); } + bool is_implies() const { return is_app() && Z3_OP_IMPLIES == decl().decl_kind(); } + bool is_eq() const { return is_app() && Z3_OP_EQ == decl().decl_kind(); } + bool is_ite() const { return is_app() && Z3_OP_ITE == decl().decl_kind(); } + friend expr distinct(expr_vector const& args); friend expr concat(expr const& a, expr const& b); friend expr concat(expr_vector const& args); @@ -1915,7 +1912,7 @@ namespace z3 { bool is_uint(unsigned i) const { Z3_bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r != 0; } bool is_double(unsigned i) const { Z3_bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r != 0; } unsigned uint_value(unsigned i) const { unsigned r = Z3_stats_get_uint_value(ctx(), m_stats, i); check_error(); return r; } - double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } + double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } friend std::ostream & operator<<(std::ostream & out, stats const & s); }; inline std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } @@ -2033,6 +2030,97 @@ namespace z3 { param_descrs get_param_descrs() { return param_descrs(ctx(), Z3_solver_get_param_descrs(ctx(), m_solver)); } + + expr_vector cube(expr_vector& vars, unsigned cutoff) { + Z3_ast_vector r = Z3_solver_cube(ctx(), m_solver, vars, cutoff); + check_error(); + return expr_vector(ctx(), r); + } + + class cube_iterator { + solver& m_solver; + unsigned& m_cutoff; + expr_vector& m_vars; + expr_vector m_cube; + bool m_end; + bool m_empty; + + void inc() { + assert(!m_end && !m_empty); + m_cube = m_solver.cube(m_vars, m_cutoff); + m_cutoff = 0xFFFFFFFF; + if (m_cube.size() == 1 && m_cube[0].is_false()) { + m_cube = z3::expr_vector(m_solver.ctx()); + m_end = true; + } + else if (m_cube.empty()) { + m_empty = true; + } + } + public: + cube_iterator(solver& s, expr_vector& vars, unsigned& cutoff, bool end): + m_solver(s), + m_cutoff(cutoff), + m_vars(vars), + m_cube(s.ctx()), + m_end(end), + m_empty(false) { + if (!m_end) { + inc(); + } + } + + cube_iterator& operator++() { + assert(!m_end); + if (m_empty) { + m_end = true; + } + else { + inc(); + } + return *this; + } + cube_iterator operator++(int) { assert(false); return *this; } + expr_vector const * operator->() const { return &(operator*()); } + expr_vector const& operator*() const { return m_cube; } + + bool operator==(cube_iterator const& other) { + return other.m_end == m_end; + }; + bool operator!=(cube_iterator const& other) { + return other.m_end != m_end; + }; + + }; + + class cube_generator { + solver& m_solver; + unsigned m_cutoff; + expr_vector m_default_vars; + expr_vector& m_vars; + public: + cube_generator(solver& s): + m_solver(s), + m_cutoff(0xFFFFFFFF), + m_default_vars(s.ctx()), + m_vars(m_default_vars) + {} + + cube_generator(solver& s, expr_vector& vars): + m_solver(s), + m_cutoff(0xFFFFFFFF), + m_default_vars(s.ctx()), + m_vars(vars) + {} + + cube_iterator begin() { return cube_iterator(m_solver, m_vars, m_cutoff, false); } + cube_iterator end() { return cube_iterator(m_solver, m_vars, m_cutoff, true); } + void set_cutoff(unsigned c) { m_cutoff = c; } + }; + + cube_generator cubes() { return cube_generator(*this); } + cube_generator cubes(expr_vector& vars) { return cube_generator(*this, vars); } + }; inline std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } @@ -2056,7 +2144,6 @@ namespace z3 { return *this; } void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } - // fails for some compilers: // void add(expr_vector const& v) { check_context(*this, v); for (expr e : v) add(e); } unsigned size() const { return Z3_goal_size(ctx(), m_goal); } expr operator[](int i) const { assert(0 <= i); Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } @@ -2067,6 +2154,17 @@ 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; } + model convert_model(model const & m) const { + check_context(*this, m); + Z3_model new_m = Z3_goal_convert_model(ctx(), m_goal, m); + check_error(); + return model(ctx(), new_m); + } + model get_model() const { + Z3_model new_m = Z3_goal_convert_model(ctx(), m_goal, 0); + check_error(); + return model(ctx(), new_m); + } expr as_expr() const { unsigned n = size(); if (n == 0) @@ -2080,6 +2178,7 @@ namespace z3 { return expr(ctx(), Z3_mk_and(ctx(), n, args.ptr())); } } + std::string dimacs() const { return std::string(Z3_goal_to_dimacs_string(ctx(), m_goal)); } friend std::ostream & operator<<(std::ostream & out, goal const & g); }; inline std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } @@ -2104,25 +2203,6 @@ namespace z3 { } unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } 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); - check_error(); - return model(ctx(), new_m); - } - expr as_expr() const { - unsigned n = size(); - if (n == 0) - return ctx().bool_val(true); - else if (n == 1) - return operator[](0).as_expr(); - else { - array args(n); - for (unsigned i = 0; i < n; i++) - args[i] = operator[](i).as_expr(); - return expr(ctx(), Z3_mk_or(ctx(), n, args.ptr())); - } - } friend std::ostream & operator<<(std::ostream & out, apply_result const & r); }; inline std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } @@ -2292,6 +2372,7 @@ namespace z3 { class optimize : public object { Z3_optimize m_opt; + public: class handle { unsigned m_h; @@ -2300,6 +2381,17 @@ namespace z3 { unsigned h() const { return m_h; } }; optimize(context& c):object(c) { m_opt = Z3_mk_optimize(c); Z3_optimize_inc_ref(c, m_opt); } + optimize(optimize& o):object(o) { + Z3_optimize_inc_ref(o.ctx(), o.m_opt); + m_opt = o.m_opt; + } + optimize& operator=(optimize const& o) { + Z3_optimize_inc_ref(o.ctx(), o.m_opt); + Z3_optimize_dec_ref(ctx(), m_opt); + m_opt = o.m_opt; + m_ctx = o.m_ctx; + return *this; + } ~optimize() { Z3_optimize_dec_ref(ctx(), m_opt); } operator Z3_optimize() const { return m_opt; } void add(expr const& e) { @@ -2864,22 +2956,19 @@ namespace z3 { - inline expr interpolant(expr const& a) { - return expr(a.ctx(), Z3_mk_interpolant(a.ctx(), a)); + inline expr_vector context::parse_string(char const* s) { + Z3_ast_vector r = Z3_parse_smtlib2_string(*this, s, 0, 0, 0, 0, 0, 0); + check_error(); + return expr_vector(*this, r); + + } + inline expr_vector context::parse_file(char const* s) { + Z3_ast_vector r = Z3_parse_smtlib2_file(*this, s, 0, 0, 0, 0, 0, 0); + check_error(); + return expr_vector(*this, r); } - inline expr context::parse_string(char const* s) { - Z3_ast r = Z3_parse_smtlib2_string(*this, s, 0, 0, 0, 0, 0, 0); - check_parser_error(); - return expr(*this, r); - } - inline expr context::parse_file(char const* s) { - Z3_ast r = Z3_parse_smtlib2_file(*this, s, 0, 0, 0, 0, 0, 0); - check_parser_error(); - return expr(*this, r); - } - - inline expr context::parse_string(char const* s, sort_vector const& sorts, func_decl_vector const& decls) { + inline expr_vector context::parse_string(char const* s, sort_vector const& sorts, func_decl_vector const& decls) { array sort_names(sorts.size()); array decl_names(decls.size()); array sorts1(sorts); @@ -2890,12 +2979,13 @@ namespace z3 { for (unsigned i = 0; i < decls.size(); ++i) { decl_names[i] = decls[i].name(); } - Z3_ast r = Z3_parse_smtlib2_string(*this, s, sorts.size(), sort_names.ptr(), sorts1.ptr(), decls.size(), decl_names.ptr(), decls1.ptr()); - check_parser_error(); - return expr(*this, r); + + Z3_ast_vector r = Z3_parse_smtlib2_string(*this, s, sorts.size(), sort_names.ptr(), sorts1.ptr(), decls.size(), decl_names.ptr(), decls1.ptr()); + check_error(); + return expr_vector(*this, r); } - inline expr context::parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls) { + inline expr_vector context::parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls) { array sort_names(sorts.size()); array decl_names(decls.size()); array sorts1(sorts); @@ -2906,33 +2996,12 @@ namespace z3 { for (unsigned i = 0; i < decls.size(); ++i) { decl_names[i] = decls[i].name(); } - Z3_ast r = Z3_parse_smtlib2_file(*this, s, sorts.size(), sort_names.ptr(), sorts1.ptr(), decls.size(), decl_names.ptr(), decls1.ptr()); - check_parser_error(); - return expr(*this, r); + Z3_ast_vector r = Z3_parse_smtlib2_file(*this, s, sorts.size(), sort_names.ptr(), sorts1.ptr(), decls.size(), decl_names.ptr(), decls1.ptr()); + check_error(); + return expr_vector(*this, r); } - inline check_result context::compute_interpolant(expr const& pat, params const& p, expr_vector& i, model& m) { - Z3_ast_vector interp = 0; - Z3_model mdl = 0; - Z3_lbool r = Z3_compute_interpolant(*this, pat, p, &interp, &mdl); - switch (r) { - case Z3_L_FALSE: - i = expr_vector(*this, interp); - break; - case Z3_L_TRUE: - m = model(*this, mdl); - break; - case Z3_L_UNDEF: - break; - } - return to_check_result(r); - } - - inline expr_vector context::get_interpolant(expr const& proof, expr const& pat, params const& p) { - return expr_vector(*this, Z3_get_interpolant(*this, proof, pat, p)); - } - inline expr expr::substitute(expr_vector const& src, expr_vector const& dst) { assert(src.size() == dst.size()); array _src(src.size()); diff --git a/src/api/dotnet/ApplyResult.cs b/src/api/dotnet/ApplyResult.cs index 608be7080..db2922460 100644 --- a/src/api/dotnet/ApplyResult.cs +++ b/src/api/dotnet/ApplyResult.cs @@ -55,19 +55,6 @@ namespace Microsoft.Z3 } } - /// - /// Convert a model for the subgoal into a model for the original - /// goal g, that the ApplyResult was obtained from. - /// - /// A model for g - public Model ConvertModel(uint i, Model m) - { - Contract.Requires(m != null); - Contract.Ensures(Contract.Result() != null); - - return new Model(Context, Native.Z3_apply_result_convert_model(Context.nCtx, NativeObject, i, m.NativeObject)); - } - /// /// A string representation of the ApplyResult. /// diff --git a/src/api/dotnet/CMakeLists.txt b/src/api/dotnet/CMakeLists.txt index add1b0ded..76516bf39 100644 --- a/src/api/dotnet/CMakeLists.txt +++ b/src/api/dotnet/CMakeLists.txt @@ -80,7 +80,6 @@ set(Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE Global.cs Goal.cs IDecRefQueue.cs - InterpolationContext.cs IntExpr.cs IntNum.cs IntSort.cs diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index b3b24a6d1..38da21370 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -3325,7 +3325,7 @@ namespace Microsoft.Z3 /// Parse the given string using the SMT-LIB2 parser. /// /// A conjunction of assertions in the scope (up to push/pop) at the end of the string. - public BoolExpr ParseSMTLIB2String(string str, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) + public BoolExpr[] ParseSMTLIB2String(string str, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) { Contract.Ensures(Contract.Result() != null); @@ -3335,16 +3335,17 @@ namespace Microsoft.Z3 uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); - return (BoolExpr)Expr.Create(this, Native.Z3_parse_smtlib2_string(nCtx, str, + ASTVector assertions = new ASTVector(this, Native.Z3_parse_smtlib2_string(nCtx, str, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls))); + return assertions.ToBoolExprArray(); } /// /// Parse the given file using the SMT-LIB2 parser. /// /// - public BoolExpr ParseSMTLIB2File(string fileName, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) + public BoolExpr[] ParseSMTLIB2File(string fileName, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) { Contract.Ensures(Contract.Result() != null); @@ -3354,9 +3355,10 @@ namespace Microsoft.Z3 uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); - return (BoolExpr)Expr.Create(this, Native.Z3_parse_smtlib2_file(nCtx, fileName, + ASTVector assertions = new ASTVector(this, Native.Z3_parse_smtlib2_file(nCtx, fileName, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls))); + return assertions.ToBoolExprArray(); } #endregion diff --git a/src/api/dotnet/Expr.cs b/src/api/dotnet/Expr.cs index 9310d1e7d..f09eecbdd 100644 --- a/src/api/dotnet/Expr.cs +++ b/src/api/dotnet/Expr.cs @@ -317,14 +317,6 @@ namespace Microsoft.Z3 #endregion - #region Interpolation - /// - /// Indicates whether the term is marked for interpolation. - /// - /// - public bool IsInterpolant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_INTERP; } } - #endregion - #region Arithmetic Terms /// /// Indicates whether the term is of integer sort. diff --git a/src/api/dotnet/Goal.cs b/src/api/dotnet/Goal.cs index 521b453f8..03e573538 100644 --- a/src/api/dotnet/Goal.cs +++ b/src/api/dotnet/Goal.cs @@ -174,6 +174,21 @@ namespace Microsoft.Z3 get { return Native.Z3_goal_is_decided_unsat(Context.nCtx, NativeObject) != 0; } } + /// + /// Convert a model for the goal into a model of the + /// original goal from which this goal was derived. + /// + /// A model for g + public Model ConvertModel(Model m) + { + Contract.Ensures(Contract.Result() != null); + if (m != null) + return new Model(Context, Native.Z3_goal_convert_model(Context.nCtx, NativeObject, m.NativeObject)); + else + return new Model(Context, Native.Z3_goal_convert_model(Context.nCtx, NativeObject, IntPtr.Zero)); + } + + /// /// Translates (copies) the Goal to the target Context . /// @@ -208,6 +223,15 @@ namespace Microsoft.Z3 return Native.Z3_goal_to_string(Context.nCtx, NativeObject); } + /// + /// Goal to DIMACS formatted string conversion. + /// + /// A string representation of the Goal. + public string ToDimacs() + { + return Native.Z3_goal_to_dimacs_string(Context.nCtx, NativeObject); + } + /// /// Goal to BoolExpr conversion. /// diff --git a/src/api/dotnet/InterpolationContext.cs b/src/api/dotnet/InterpolationContext.cs deleted file mode 100644 index 3f2feb5a6..000000000 --- a/src/api/dotnet/InterpolationContext.cs +++ /dev/null @@ -1,164 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Diagnostics.Contracts; -using System.Runtime.InteropServices; - -namespace Microsoft.Z3 -{ - /// - /// The InterpolationContext is suitable for generation of interpolants. - /// - /// For more information on interpolation please refer - /// too the C/C++ API, which is well documented. - [ContractVerification(true)] - public class InterpolationContext : Context - { - - /// - /// Constructor. - /// - public InterpolationContext() : base() { } - - /// - /// Constructor. - /// - /// - public InterpolationContext(Dictionary settings) : base(settings) { } - - #region Terms - /// - /// Create an expression that marks a formula position for interpolation. - /// - public BoolExpr MkInterpolant(BoolExpr a) - { - Contract.Requires(a != null); - Contract.Ensures(Contract.Result() != null); - - CheckContextMatch(a); - return new BoolExpr(this, Native.Z3_mk_interpolant(nCtx, a.NativeObject)); - } - #endregion - - /// - /// Computes an interpolant. - /// - /// For more information on interpolation please refer - /// too the function Z3_get_interpolant in the C/C++ API, which is - /// well documented. - public BoolExpr[] GetInterpolant(Expr pf, Expr pat, Params p) - { - Contract.Requires(pf != null); - Contract.Requires(pat != null); - Contract.Requires(p != null); - Contract.Ensures(Contract.Result() != null); - - CheckContextMatch(pf); - CheckContextMatch(pat); - CheckContextMatch(p); - - ASTVector seq = new ASTVector(this, Native.Z3_get_interpolant(nCtx, pf.NativeObject, pat.NativeObject, p.NativeObject)); - return seq.ToBoolExprArray(); - } - - /// - /// Computes an interpolant. - /// - /// For more information on interpolation please refer - /// too the function Z3_compute_interpolant in the C/C++ API, which is - /// well documented. - public Z3_lbool ComputeInterpolant(Expr pat, Params p, out BoolExpr[] interp, out Model model) - { - Contract.Requires(pat != null); - Contract.Requires(p != null); - Contract.Ensures(Contract.ValueAtReturn(out interp) != null); - Contract.Ensures(Contract.ValueAtReturn(out model) != null); - - CheckContextMatch(pat); - CheckContextMatch(p); - - IntPtr i = IntPtr.Zero, m = IntPtr.Zero; - int r = Native.Z3_compute_interpolant(nCtx, pat.NativeObject, p.NativeObject, ref i, ref m); - interp = new ASTVector(this, i).ToBoolExprArray(); - model = new Model(this, m); - return (Z3_lbool)r; - } - - /// - /// Return a string summarizing cumulative time used for interpolation. - /// - /// For more information on interpolation please refer - /// too the function Z3_interpolation_profile in the C/C++ API, which is - /// well documented. - public string InterpolationProfile() - { - return Native.Z3_interpolation_profile(nCtx); - } - - /// - /// Checks the correctness of an interpolant. - /// - /// For more information on interpolation please refer - /// too the function Z3_check_interpolant in the C/C++ API, which is - /// well documented. - public int CheckInterpolant(Expr[] cnsts, uint[] parents, BoolExpr[] interps, out string error, Expr[] theory) - { - Contract.Requires(cnsts.Length == parents.Length); - Contract.Requires(cnsts.Length == interps.Length + 1); - IntPtr n_err_str; - int r = Native.Z3_check_interpolant(nCtx, - (uint)cnsts.Length, - Expr.ArrayToNative(cnsts), - parents, - Expr.ArrayToNative(interps), - out n_err_str, - (uint)theory.Length, - Expr.ArrayToNative(theory)); - error = Marshal.PtrToStringAnsi(n_err_str); - return r; - } - - /// - /// Reads an interpolation problem from a file. - /// - /// For more information on interpolation please refer - /// too the function Z3_read_interpolation_problem in the C/C++ API, which is - /// well documented. - public int ReadInterpolationProblem(string filename, out Expr[] cnsts, out uint[] parents, out string error, out Expr[] theory) - { - uint num = 0, num_theory = 0; - IntPtr[] n_cnsts; - IntPtr[] n_theory; - IntPtr n_err_str; - int r = Native.Z3_read_interpolation_problem(nCtx, ref num, out n_cnsts, out parents, filename, out n_err_str, ref num_theory, out n_theory); - error = Marshal.PtrToStringAnsi(n_err_str); - cnsts = new Expr[num]; - parents = new uint[num]; - theory = new Expr[num_theory]; - for (int i = 0; i < num; i++) - cnsts[i] = Expr.Create(this, n_cnsts[i]); - for (int i = 0; i < num_theory; i++) - theory[i] = Expr.Create(this, n_theory[i]); - return r; - } - - /// - /// Writes an interpolation problem to a file. - /// - /// For more information on interpolation please refer - /// too the function Z3_write_interpolation_problem in the C/C++ API, which is - /// well documented. - public void WriteInterpolationProblem(string filename, Expr[] cnsts, uint[] parents, Expr[] theory) - { - Contract.Requires(cnsts.Length == parents.Length); - Native.Z3_write_interpolation_problem(nCtx, (uint)cnsts.Length, Expr.ArrayToNative(cnsts), parents, filename, (uint)theory.Length, Expr.ArrayToNative(theory)); - } - } -} diff --git a/src/api/dotnet/Params.cs b/src/api/dotnet/Params.cs index 37d73d5b1..5b143d525 100644 --- a/src/api/dotnet/Params.cs +++ b/src/api/dotnet/Params.cs @@ -91,7 +91,7 @@ namespace Microsoft.Z3 public Params Add(string name, bool value) { Native.Z3_params_set_bool(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, (value) ? 1 : 0); - return this; + return this; } /// @@ -100,7 +100,7 @@ namespace Microsoft.Z3 public Params Add(string name, uint value) { Native.Z3_params_set_uint(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, value); - return this; + return this; } /// diff --git a/src/api/dotnet/Solver.cs b/src/api/dotnet/Solver.cs index a176e790b..a288990a2 100644 --- a/src/api/dotnet/Solver.cs +++ b/src/api/dotnet/Solver.cs @@ -57,7 +57,7 @@ namespace Microsoft.Z3 } } - /// + /// /// Sets parameter on the solver /// public void Set(string name, bool value) { Parameters = Context.MkParams().Add(name, value); } @@ -266,6 +266,20 @@ namespace Microsoft.Z3 } } + /// + /// Currently inferred units. + /// + public BoolExpr[] Units + { + get + { + Contract.Ensures(Contract.Result() != null); + + ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_units(Context.nCtx, NativeObject)); + return assertions.ToBoolExprArray(); + } + } + /// /// Checks whether the assertions in the solver are consistent or not. /// @@ -331,10 +345,10 @@ namespace Microsoft.Z3 } /// - /// The model of the last Check. + /// The model of the last Check(params Expr[] assumptions). /// /// - /// The result is null if Check was not invoked before, + /// The result is null if Check(params Expr[] assumptions) was not invoked before, /// if its results was not SATISFIABLE, or if model production is not enabled. /// public Model Model @@ -350,10 +364,10 @@ namespace Microsoft.Z3 } /// - /// The proof of the last Check. + /// The proof of the last Check(params Expr[] assumptions). /// /// - /// The result is null if Check was not invoked before, + /// The result is null if Check(params Expr[] assumptions) was not invoked before, /// if its results was not UNSATISFIABLE, or if proof production is disabled. /// public Expr Proof @@ -400,6 +414,42 @@ namespace Microsoft.Z3 } } + /// + /// Backtrack level that can be adjusted by conquer process + /// + public uint BacktrackLevel { get; set; } + + /// + /// Variables available and returned by the cuber. + /// + public BoolExpr[] CubeVariables { get; set; } + + + /// + /// Return a set of cubes. + /// + public IEnumerable Cube() + { + ASTVector cv = new ASTVector(Context); + if (CubeVariables != null) + foreach (var b in CubeVariables) cv.Push(b); + + while (true) { + var lvl = BacktrackLevel; + BacktrackLevel = uint.MaxValue; + ASTVector r = new ASTVector(Context, Native.Z3_solver_cube(Context.nCtx, NativeObject, cv.NativeObject, lvl)); + var v = r.ToBoolExprArray(); + CubeVariables = cv.ToBoolExprArray(); + if (v.Length == 1 && v[0].IsFalse) { + break; + } + yield return v; + if (v.Length == 0) { + break; + } + } + } + /// /// Create a clone of the current solver with respect to ctx. /// @@ -410,6 +460,13 @@ namespace Microsoft.Z3 return new Solver(ctx, Native.Z3_solver_translate(Context.nCtx, NativeObject, ctx.nCtx)); } + /// + /// Import model converter from other solver. + /// + public void ImportModelConverter(Solver src) + { + Native.Z3_solver_import_model_converter(Context.nCtx, src.NativeObject, NativeObject); + } /// /// Solver statistics. @@ -437,6 +494,7 @@ namespace Microsoft.Z3 : base(ctx, obj) { Contract.Requires(ctx != null); + this.BacktrackLevel = uint.MaxValue; } internal class DecRefQueue : IDecRefQueue diff --git a/src/api/java/ApplyResult.java b/src/api/java/ApplyResult.java index 6fafbd888..6cfedd404 100644 --- a/src/api/java/ApplyResult.java +++ b/src/api/java/ApplyResult.java @@ -46,19 +46,6 @@ public class ApplyResult extends Z3Object { return res; } - /** - * Convert a model for the subgoal {@code i} into a model for the - * original goal {@code g}, that the ApplyResult was obtained from. - * - * @return A model for {@code g} - * @throws Z3Exception - **/ - public Model convertModel(int i, Model m) - { - return new Model(getContext(), - Native.applyResultConvertModel(getContext().nCtx(), getNativeObject(), i, m.getNativeObject())); - } - /** * A string representation of the ApplyResult. **/ diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index dce2bc4ea..7f0774955 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -137,7 +137,6 @@ set(Z3_JAVA_JAR_SOURCE_FILES GoalDecRefQueue.java Goal.java IDecRefQueue.java - InterpolationContext.java IntExpr.java IntNum.java IntSort.java diff --git a/src/api/java/Context.java b/src/api/java/Context.java index c45dce933..dee9dbaf6 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -2549,8 +2549,9 @@ public class Context implements AutoCloseable { * set of assertions returned are the ones in the * last scope level. **/ - public BoolExpr parseSMTLIB2String(String str, Symbol[] sortNames, - Sort[] sorts, Symbol[] declNames, FuncDecl[] decls) + public BoolExpr[] parseSMTLIB2String(String str, Symbol[] sortNames, + Sort[] sorts, Symbol[] declNames, FuncDecl[] decls) + { int csn = Symbol.arrayLength(sortNames); int cs = Sort.arrayLength(sorts); @@ -2559,17 +2560,18 @@ public class Context implements AutoCloseable { if (csn != cs || cdn != cd) { throw new Z3Exception("Argument size mismatch"); } - return (BoolExpr) Expr.create(this, Native.parseSmtlib2String(nCtx(), + ASTVector v = new ASTVector(this, Native.parseSmtlib2String(nCtx(), str, AST.arrayLength(sorts), Symbol.arrayToNative(sortNames), AST.arrayToNative(sorts), AST.arrayLength(decls), Symbol.arrayToNative(declNames), AST.arrayToNative(decls))); + return v.ToBoolExprArray(); } /** * Parse the given file using the SMT-LIB2 parser. * @see #parseSMTLIB2String **/ - public BoolExpr parseSMTLIB2File(String fileName, Symbol[] sortNames, + public BoolExpr[] parseSMTLIB2File(String fileName, Symbol[] sortNames, Sort[] sorts, Symbol[] declNames, FuncDecl[] decls) { @@ -2579,11 +2581,12 @@ public class Context implements AutoCloseable { int cd = AST.arrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); - return (BoolExpr) Expr.create(this, Native.parseSmtlib2File(nCtx(), + ASTVector v = new ASTVector(this, Native.parseSmtlib2File(nCtx(), fileName, AST.arrayLength(sorts), Symbol.arrayToNative(sortNames), AST.arrayToNative(sorts), AST.arrayLength(decls), Symbol.arrayToNative(declNames), AST.arrayToNative(decls))); + return v.ToBoolExprArray(); } /** diff --git a/src/api/java/Goal.java b/src/api/java/Goal.java index 25b1fe511..903325850 100644 --- a/src/api/java/Goal.java +++ b/src/api/java/Goal.java @@ -240,6 +240,21 @@ public class Goal extends Z3Object { (unsatCores), (proofs))); } + /** + * Convert a model for the goal into a model of the + * original goal from which this goal was derived. + * + * @return A model for {@code g} + * @throws Z3Exception + **/ + public Model convertModel(Model m) + { + return new Model(getContext(), + Native.goalConvertModel(getContext().nCtx(), getNativeObject(), m.getNativeObject())); + } + + + @Override void incRef() { Native.goalIncRef(getContext().nCtx(), getNativeObject()); diff --git a/src/api/java/InterpolationContext.java b/src/api/java/InterpolationContext.java deleted file mode 100644 index 99a63821f..000000000 --- a/src/api/java/InterpolationContext.java +++ /dev/null @@ -1,216 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - InterpolationContext.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -import com.microsoft.z3.enumerations.Z3_lbool; - -import java.util.Map; - -/** - * The InterpolationContext is suitable for generation of interpolants. - * - * Remarks: For more information on interpolation please refer - * too the C/C++ API, which is well documented. - **/ -public class InterpolationContext extends Context -{ - /** - * Constructor. - **/ - public static InterpolationContext mkContext() - { - long m_ctx; - synchronized(creation_lock) { - m_ctx = Native.mkInterpolationContext(0); - } - return new InterpolationContext(m_ctx); - } - - /** - * Constructor. - * - * - * Remarks: - * @see Context#Context - **/ - public static InterpolationContext mkContext(Map settings) - { - long m_ctx; - synchronized(creation_lock) { - long cfg = Native.mkConfig(); - for (Map.Entry kv : settings.entrySet()) - Native.setParamValue(cfg, kv.getKey(), kv.getValue()); - m_ctx = Native.mkInterpolationContext(cfg); - Native.delConfig(cfg); - } - return new InterpolationContext(m_ctx); - } - - private InterpolationContext(long m_ctx) { - super(m_ctx); - } - - /** - * Create an expression that marks a formula position for interpolation. - * @throws Z3Exception - **/ - public BoolExpr MkInterpolant(BoolExpr a) - { - checkContextMatch(a); - return new BoolExpr(this, Native.mkInterpolant(nCtx(), a.getNativeObject())); - } - - /** - * Computes an interpolant. - * Remarks: For more information on interpolation please refer - * too the function Z3_get_interpolant in the C/C++ API, which is - * well documented. - * @throws Z3Exception - **/ - public BoolExpr[] GetInterpolant(Expr pf, Expr pat, Params p) - { - checkContextMatch(pf); - checkContextMatch(pat); - checkContextMatch(p); - - ASTVector seq = new ASTVector(this, Native.getInterpolant(nCtx(), pf.getNativeObject(), pat.getNativeObject(), p.getNativeObject())); - return seq.ToBoolExprArray(); - } - - public class ComputeInterpolantResult - { - public Z3_lbool status = Z3_lbool.Z3_L_UNDEF; - public BoolExpr[] interp = null; - public Model model = null; - }; - - /** - * Computes an interpolant. - * Remarks: For more information on interpolation please refer - * too the function Z3_compute_interpolant in the C/C++ API, which is - * well documented. - * @throws Z3Exception - **/ - public ComputeInterpolantResult ComputeInterpolant(Expr pat, Params p) - { - checkContextMatch(pat); - checkContextMatch(p); - - ComputeInterpolantResult res = new ComputeInterpolantResult(); - Native.LongPtr n_i = new Native.LongPtr(); - Native.LongPtr n_m = new Native.LongPtr(); - res.status = Z3_lbool.fromInt(Native.computeInterpolant(nCtx(), pat.getNativeObject(), p.getNativeObject(), n_i, n_m)); - if (res.status == Z3_lbool.Z3_L_FALSE) - res.interp = (new ASTVector(this, n_i.value)).ToBoolExprArray(); - if (res.status == Z3_lbool.Z3_L_TRUE) - res.model = new Model(this, n_m.value); - return res; - } - - /// - /// Return a string summarizing cumulative time used for interpolation. - /// - /// Remarks: For more information on interpolation please refer - /// too the function Z3_interpolation_profile in the C/C++ API, which is - /// well documented. - public String InterpolationProfile() - { - return Native.interpolationProfile(nCtx()); - } - - public class CheckInterpolantResult - { - public int return_value = 0; - public String error = null; - } - - /// - /// Checks the correctness of an interpolant. - /// - /// Remarks: For more information on interpolation please refer - /// too the function Z3_check_interpolant in the C/C++ API, which is - /// well documented. - public CheckInterpolantResult CheckInterpolant(Expr[] cnsts, int[] parents, BoolExpr[] interps, String error, Expr[] theory) - { - CheckInterpolantResult res = new CheckInterpolantResult(); - Native.StringPtr n_err_str = new Native.StringPtr(); - res.return_value = Native.checkInterpolant(nCtx(), - cnsts.length, - Expr.arrayToNative(cnsts), - parents, - Expr.arrayToNative(interps), - n_err_str, - theory.length, - Expr.arrayToNative(theory)); - res.error = n_err_str.value; - return res; - } - - public class ReadInterpolationProblemResult - { - public int return_value = 0; - public Expr[] cnsts; - public int[] parents; - public String error; - public Expr[] theory; - }; - - /// - /// Reads an interpolation problem from a file. - /// - /// Remarks: For more information on interpolation please refer - /// too the function Z3_read_interpolation_problem in the C/C++ API, which is - /// well documented. - public ReadInterpolationProblemResult ReadInterpolationProblem(String filename, Expr[] cnsts, int[] parents, String error, Expr[] theory) - { - ReadInterpolationProblemResult res = new ReadInterpolationProblemResult(); - - Native.IntPtr n_num = new Native.IntPtr(); - Native.IntPtr n_num_theory = new Native.IntPtr(); - Native.ObjArrayPtr n_cnsts = new Native.ObjArrayPtr(); - Native.UIntArrayPtr n_parents = new Native.UIntArrayPtr(); - Native.ObjArrayPtr n_theory = new Native.ObjArrayPtr(); - Native.StringPtr n_err_str = new Native.StringPtr(); - res.return_value = Native.readInterpolationProblem(nCtx(), n_num, n_cnsts, n_parents, filename, n_err_str, n_num_theory, n_theory); - int num = n_num.value; - int num_theory = n_num_theory.value; - res.error = n_err_str.value; - res.cnsts = new Expr[num]; - res.parents = new int[num]; - theory = new Expr[num_theory]; - for (int i = 0; i < num; i++) - { - res.cnsts[i] = Expr.create(this, n_cnsts.value[i]); - res.parents[i] = n_parents.value[i]; - } - for (int i = 0; i < num_theory; i++) - res.theory[i] = Expr.create(this, n_theory.value[i]); - return res; - } - - /// - /// Writes an interpolation problem to a file. - /// - /// Remarks: For more information on interpolation please refer - /// too the function Z3_write_interpolation_problem in the C/C++ API, which is - /// well documented. - public void WriteInterpolationProblem(String filename, Expr[] cnsts, int[] parents, String error, Expr[] theory) - { - Native.writeInterpolationProblem(nCtx(), cnsts.length, Expr.arrayToNative(cnsts), parents, filename, theory.length, Expr.arrayToNative(theory)); - } -} diff --git a/src/api/java/Solver.java b/src/api/java/Solver.java index 5bd4e65ba..0370a9571 100644 --- a/src/api/java/Solver.java +++ b/src/api/java/Solver.java @@ -121,22 +121,6 @@ public class Solver extends Z3Object { } } - /** - * Load solver assertions from a file. - */ - public void fromFile(String file) - { - Native.solverFromFile(getContext().nCtx(), getNativeObject(), file); - } - - /** - * Load solver assertions from a string. - */ - public void fromString(String str) - { - Native.solverFromString(getContext().nCtx(), getNativeObject(), str); - } - /** * Assert multiple constraints into the solver, and track them (in the @@ -188,6 +172,23 @@ public class Solver extends Z3Object { constraint.getNativeObject(), p.getNativeObject()); } + /// + /// Load solver assertions from a file. + /// + public void fromFile(String file) + { + Native.solverFromFile(getContext().nCtx(), getNativeObject(), file); + } + + /// + /// Load solver assertions from a string. + /// + public void fromString(String str) + { + Native.solverFromString(getContext().nCtx(), getNativeObject(), str); + } + + /** * The number of assertions in the solver. * diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 5766c79f9..a676f8c43 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1654,7 +1654,6 @@ struct mk_list f n let get_subgoal (x:apply_result) (i:int) = Z3native.apply_result_get_subgoal (gc x) x i - let convert_model (x:apply_result) (i:int) (m:Model.model) = Z3native.apply_result_convert_model (gc x) x i m let to_string (x:apply_result) = Z3native.apply_result_to_string (gc x) x end @@ -1995,52 +1994,6 @@ struct cs sort_names sorts cd decl_names decls end -module Interpolation = -struct - let mk_interpolant = Z3native.mk_interpolant - - let mk_interpolation_context (settings:(string * string) list) = - let cfg = Z3native.mk_config () in - let f e = Z3native.set_param_value cfg (fst e) (snd e) in - List.iter f settings; - let res = Z3native.mk_interpolation_context cfg in - Z3native.del_config cfg; - Z3native.set_ast_print_mode res (int_of_ast_print_mode PRINT_SMTLIB2_COMPLIANT); - Z3native.set_internal_error_handler res; - res - - let get_interpolant (ctx:context) (pf:expr) (pat:expr) (p:Params.params) = - let av = Z3native.get_interpolant ctx pf pat p in - AST.ASTVector.to_expr_list av - - let compute_interpolant (ctx:context) (pat:expr) (p:Params.params) = - let (r, interp, model) = Z3native.compute_interpolant ctx pat p in - let res = lbool_of_int r in - match res with - | L_TRUE -> (res, None, Some model) - | L_FALSE -> (res, Some (AST.ASTVector.to_expr_list interp), None) - | _ -> (res, None, None) - - let get_interpolation_profile = Z3native.interpolation_profile - - let read_interpolation_problem (ctx:context) (filename:string) = - let (r, num, cnsts, parents, error, num_theory, theory) = - Z3native.read_interpolation_problem ctx filename - in - match r with - | 0 -> raise (Error "Interpolation problem could not be read.") - | _ -> (cnsts, parents, theory) - - let check_interpolant (ctx:context) (num:int) (cnsts:Expr.expr list) (parents:int list) (interps:Expr.expr list) (num_theory:int) (theory:Expr.expr list) = - let (r, str) = Z3native.check_interpolant ctx num cnsts parents interps num_theory theory in - match (lbool_of_int r) with - | L_UNDEF -> raise (Error "Interpolant could not be verified.") - | L_FALSE -> raise (Error "Interpolant could not be verified.") - | _ -> () - - let write_interpolation_problem (ctx:context) (num:int) (cnsts:Expr.expr list) (parents:int list) (filename:string) (num_theory:int) (theory:Expr.expr list) = - Z3native.write_interpolation_problem ctx num cnsts parents filename num_theory theory -end let set_global_param = Z3native.global_param_set diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 14d2ceac4..9b424b508 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -2362,7 +2362,7 @@ sig (** Indicates whether the term is a proof by condensed transitivity of a relation - Condensed transitivity proof. This proof object is only used if the parameter PROOF_MODE is 1. + Condensed transitivity proof. It combines several symmetry and transitivity proofs. Example: T1: (R a b) @@ -2443,14 +2443,11 @@ sig (** Indicates whether the term is a proof by rewriting A proof for rewriting an expression t into an expression s. - This proof object is used if the parameter PROOF_MODE is 1. This proof object can have n antecedents. The antecedents are proofs for equalities used as substitution rules. - The object is also used in a few cases if the parameter PROOF_MODE is 2. - The cases are: + The object is also used in a few cases. The cases are: - When applying contextual simplification (CONTEXT_SIMPLIFIER=true) - - When converting bit-vectors to Booleans (BIT2BOOL=true) - - When pulling ite expression up (PULL_CHEAP_ITE_TREES=true) *) + - When converting bit-vectors to Booleans (BIT2BOOL=true) *) val is_rewrite_star : Expr.expr -> bool (** Indicates whether the term is a proof for pulling quantifiers out. @@ -2967,11 +2964,6 @@ sig (** Retrieves a subgoal from the apply_result. *) val get_subgoal : apply_result -> int -> Goal.goal - (** Convert a model for a subgoal into a model for the original - goal [g], that the ApplyResult was obtained from. - #return A model for [g] *) - val convert_model : apply_result -> int -> Model.model -> Model.model - (** A string representation of the ApplyResult. *) val to_string : apply_result -> string end @@ -3427,51 +3419,6 @@ sig val parse_smtlib2_file : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> Expr.expr end -(** Interpolation *) -module Interpolation : -sig - - (** Create an AST node marking a formula position for interpolation. - The expression must have Boolean sort. *) - val mk_interpolant : context -> Expr.expr -> Expr.expr - - (** The interpolation context is suitable for generation of interpolants. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val mk_interpolation_context : (string * string) list -> context - - (** Gets an interpolant. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val get_interpolant : context -> Expr.expr -> Expr.expr -> Params.params -> Expr.expr list - - (** Computes an interpolant. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val compute_interpolant : context -> Expr.expr -> Params.params -> (Z3enums.lbool * Expr.expr list option * Model.model option) - - (** Retrieves an interpolation profile. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val get_interpolation_profile : context -> string - - (** Read an interpolation problem from file. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val read_interpolation_problem : context -> string -> (Expr.expr list * int list * Expr.expr list) - - (** Check the correctness of an interpolant. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val check_interpolant : context -> int -> Expr.expr list -> int list -> Expr.expr list -> int -> Expr.expr list -> unit - - (** Write an interpolation problem to file suitable for reading with - Z3_read_interpolation_problem. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val write_interpolation_problem : context -> int -> Expr.expr list -> int list -> string -> int -> Expr.expr list -> unit - -end (** Set a global (or module) parameter, which is shared by all Z3 contexts. diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 36283356d..7657fb347 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -1415,6 +1415,17 @@ def is_or(a): """ return is_app_of(a, Z3_OP_OR) +def is_implies(a): + """Return `True` if `a` is a Z3 implication expression. + + >>> p, q = Bools('p q') + >>> is_implies(Implies(p, q)) + True + >>> is_implies(And(p, q)) + False + """ + return is_app_of(a, Z3_OP_IMPLIES) + def is_not(a): """Return `True` if `a` is a Z3 not expression. @@ -2754,6 +2765,8 @@ def _py2expr(a, ctx=None): return IntVal(a, ctx) if isinstance(a, float): return RealVal(a, ctx) + if is_expr(a): + return a if __debug__: _z3_assert(False, "Python bool, int, long or float expected") @@ -4388,6 +4401,117 @@ def is_store(a): """ return is_app_of(a, Z3_OP_STORE) +######################################### +# +# Sets +# +######################################### + + +def SetSort(s): + """ Create a set sort over element sort s""" + return ArraySort(s, BoolSort()) + +def EmptySet(s): + """Create the empty set + >>> EmptySet(IntSort()) + K(Int, False) + """ + ctx = s.ctx + return ArrayRef(Z3_mk_empty_set(ctx.ref(), s.ast), ctx) + +def FullSet(s): + """Create the full set + >>> FullSet(IntSort()) + K(Int, True) + """ + ctx = s.ctx + return ArrayRef(Z3_mk_full_set(ctx.ref(), s.ast), ctx) + +def SetUnion(*args): + """ Take the union of sets + >>> a = Const('a', SetSort(IntSort())) + >>> b = Const('b', SetSort(IntSort())) + >>> SetUnion(a, b) + union(a, b) + """ + args = _get_args(args) + ctx = _ctx_from_ast_arg_list(args) + _args, sz = _to_ast_array(args) + return ArrayRef(Z3_mk_set_union(ctx.ref(), sz, _args), ctx) + +def SetIntersect(*args): + """ Take the union of sets + >>> a = Const('a', SetSort(IntSort())) + >>> b = Const('b', SetSort(IntSort())) + >>> SetIntersect(a, b) + intersect(a, b) + """ + args = _get_args(args) + ctx = _ctx_from_ast_arg_list(args) + _args, sz = _to_ast_array(args) + return ArrayRef(Z3_mk_set_intersect(ctx.ref(), sz, _args), ctx) + +def SetAdd(s, e): + """ Add element e to set s + >>> a = Const('a', SetSort(IntSort())) + >>> SetAdd(a, 1) + Store(a, 1, True) + """ + ctx = _ctx_from_ast_arg_list([s,e]) + e = _py2expr(e, ctx) + return ArrayRef(Z3_mk_set_add(ctx.ref(), s.as_ast(), e.as_ast()), ctx) + +def SetDel(s, e): + """ Remove element e to set s + >>> a = Const('a', SetSort(IntSort())) + >>> SetDel(a, 1) + Store(a, 1, False) + """ + ctx = _ctx_from_ast_arg_list([s,e]) + e = _py2expr(e, ctx) + return ArrayRef(Z3_mk_set_del(ctx.ref(), s.as_ast(), e.as_ast()), ctx) + +def SetComplement(s): + """ The complement of set s + >>> a = Const('a', SetSort(IntSort())) + >>> SetComplement(a) + complement(a) + """ + ctx = s.ctx + return ArrayRef(Z3_mk_set_complement(ctx.ref(), s.as_ast()), ctx) + +def SetDifference(a, b): + """ The set difference of a and b + >>> a = Const('a', SetSort(IntSort())) + >>> b = Const('b', SetSort(IntSort())) + >>> SetDifference(a, b) + difference(a, b) + """ + ctx = _ctx_from_ast_arg_list([a, b]) + return ArrayRef(Z3_mk_set_difference(ctx.ref(), a.as_ast(), b.as_ast()), ctx) + +def IsMember(e, s): + """ Check if e is a member of set s + >>> a = Const('a', SetSort(IntSort())) + >>> IsMember(1, a) + a[1] + """ + ctx = _ctx_from_ast_arg_list([s,e]) + e = _py2expr(e, ctx) + return BoolRef(Z3_mk_set_member(ctx.ref(), e.as_ast(), s.as_ast()), ctx) + +def IsSubset(a, b): + """ Check if a is a subset of b + >>> a = Const('a', SetSort(IntSort())) + >>> b = Const('b', SetSort(IntSort())) + >>> IsSubset(a, b) + subset(a, b) + """ + ctx = _ctx_from_ast_arg_list([a, b]) + return BoolRef(Z3_mk_set_subset(ctx.ref(), a.as_ast(), b.as_ast()), ctx) + + ######################################### # # Datatypes @@ -5032,6 +5156,35 @@ class Goal(Z3PPObject): """ self.assert_exprs(*args) + def convert_model(self, model): + """Retrieve model from a satisfiable goal + >>> a, b = Ints('a b') + >>> g = Goal() + >>> g.add(Or(a == 0, a == 1), Or(b == 0, b == 1), a > b) + >>> t = Then(Tactic('split-clause'), Tactic('solve-eqs')) + >>> r = t(g) + >>> r[0] + [Or(b == 0, b == 1), Not(0 <= b)] + >>> r[1] + [Or(b == 0, b == 1), Not(1 <= b)] + >>> # Remark: the subgoal r[0] is unsatisfiable + >>> # Creating a solver for solving the second subgoal + >>> s = Solver() + >>> s.add(r[1]) + >>> s.check() + sat + >>> s.model() + [b = 0] + >>> # Model s.model() does not assign a value to `a` + >>> # It is a model for subgoal `r[1]`, but not for goal `g` + >>> # The method convert_model creates a model for `g` from a model for `r[1]`. + >>> r[1].convert_model(s.model()) + [b = 0, a = 1] + """ + if __debug__: + _z3_assert(isinstance(model, ModelRef), "Z3 Model expected") + return ModelRef(Z3_goal_convert_model(self.ctx.ref(), self.goal, model.model), self.ctx) + def __repr__(self): return obj_to_string(self) @@ -5039,6 +5192,10 @@ class Goal(Z3PPObject): """Return a textual representation of the s-expression representing the goal.""" return Z3_goal_to_string(self.ctx.ref(), self.goal) + def dimacs(self): + """Return a textual representation of the goal in DIMACS format.""" + return Z3_goal_to_dimacs_string(self.ctx.ref(), self.goal) + def translate(self, target): """Copy goal `self` to context `target`. @@ -6095,6 +6252,7 @@ class Solver(Z3PPObject): def __init__(self, solver=None, ctx=None): assert solver is None or ctx is not None self.ctx = _get_ctx(ctx) + self.backtrack_level = 4000000000 self.solver = None if solver is None: self.solver = Z3_mk_solver(self.ctx.ref()) @@ -6401,10 +6559,37 @@ class Solver(Z3PPObject): except Z3Exception as e: _handle_parse_error(e, self.ctx) + def cube(self, vars = None): + """Get set of cubes""" + self.cube_vs = AstVector(None, self.ctx) + if vars is not None: + for v in vars: + self.cube_vs.push(v) + while True: + lvl = self.backtrack_level + self.backtrack_level = 4000000000 + r = AstVector(Z3_solver_cube(self.ctx.ref(), self.solver, self.cube_vs.vector, lvl), self.ctx) + if (len(r) == 1 and is_false(r[0])): + return + yield r + if (len(r) == 0): + return + + def cube_vars(self): + return self.cube_vs + def proof(self): """Return a proof for the last `check()`. Proof construction must be enabled.""" return _to_expr_ref(Z3_solver_get_proof(self.ctx.ref(), self.solver), self.ctx) + def from_file(self, filename): + """Parse assertions from a file""" + Z3_solver_from_file(self.ctx.ref(), self.solver, filename) + + def from_string(self, s): + """Parse assertions from a string""" + Z3_solver_from_string(self.ctx.ref(), self.solver, s) + def assertions(self): """Return an AST vector containing all added constraints. @@ -6419,6 +6604,11 @@ class Solver(Z3PPObject): """ return AstVector(Z3_solver_get_assertions(self.ctx.ref(), self.solver), self.ctx) + def units(self): + """Return an AST vector containing all currently inferred units. + """ + return AstVector(Z3_solver_get_units(self.ctx.ref(), self.solver), self.ctx) + def statistics(self): """Return statistics for the last `check()`. @@ -7187,36 +7377,6 @@ class ApplyResult(Z3PPObject): """Return a textual representation of the s-expression representing the set of subgoals in `self`.""" return Z3_apply_result_to_string(self.ctx.ref(), self.result) - def convert_model(self, model, idx=0): - """Convert a model for a subgoal into a model for the original goal. - - >>> a, b = Ints('a b') - >>> g = Goal() - >>> g.add(Or(a == 0, a == 1), Or(b == 0, b == 1), a > b) - >>> t = Then(Tactic('split-clause'), Tactic('solve-eqs')) - >>> r = t(g) - >>> r[0] - [Or(b == 0, b == 1), Not(0 <= b)] - >>> r[1] - [Or(b == 0, b == 1), Not(1 <= b)] - >>> # Remark: the subgoal r[0] is unsatisfiable - >>> # Creating a solver for solving the second subgoal - >>> s = Solver() - >>> s.add(r[1]) - >>> s.check() - sat - >>> s.model() - [b = 0] - >>> # Model s.model() does not assign a value to `a` - >>> # It is a model for subgoal `r[1]`, but not for goal `g` - >>> # The method convert_model creates a model for `g` from a model for `r[1]`. - >>> r.convert_model(s.model(), 1) - [b = 0, a = 1] - """ - if __debug__: - _z3_assert(idx < len(self), "index out of bounds") - _z3_assert(isinstance(model, ModelRef), "Z3 Model expected") - return ModelRef(Z3_apply_result_convert_model(self.ctx.ref(), self.result, idx, model.model), self.ctx) def as_expr(self): """Return a Z3 expression consisting of all subgoals. @@ -8193,19 +8353,19 @@ def parse_smt2_string(s, sorts={}, decls={}, ctx=None): the symbol table used for the SMT 2.0 parser. >>> parse_smt2_string('(declare-const x Int) (assert (> x 0)) (assert (< x 10))') - And(x > 0, x < 10) + [x > 0, x < 10] >>> x, y = Ints('x y') >>> f = Function('f', IntSort(), IntSort()) >>> parse_smt2_string('(assert (> (+ foo (g bar)) 0))', decls={ 'foo' : x, 'bar' : y, 'g' : f}) - x + f(y) > 0 + [x + f(y) > 0] >>> parse_smt2_string('(declare-const a U) (assert (> a 0))', sorts={ 'U' : IntSort() }) - a > 0 + [a > 0] """ ctx = _get_ctx(ctx) ssz, snames, ssorts = _dict2sarray(sorts, ctx) dsz, dnames, ddecls = _dict2darray(decls, ctx) try: - return _to_expr_ref(Z3_parse_smtlib2_string(ctx.ref(), s, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) + return AstVector(Z3_parse_smtlib2_string(ctx.ref(), s, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) except Z3Exception as e: _handle_parse_error(e, ctx) @@ -8217,151 +8377,11 @@ def parse_smt2_file(f, sorts={}, decls={}, ctx=None): ctx = _get_ctx(ctx) ssz, snames, ssorts = _dict2sarray(sorts, ctx) dsz, dnames, ddecls = _dict2darray(decls, ctx) - try: - return _to_expr_ref(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) + try: + return AstVector(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) except Z3Exception as e: _handle_parse_error(e, ctx) -def Interpolant(a,ctx=None): - """Create an interpolation operator. - - The argument is an interpolation pattern (see tree_interpolant). - - >>> x = Int('x') - >>> print(Interpolant(x>0)) - interp(x > 0) - """ - ctx = _get_ctx(_ctx_from_ast_arg_list([a], ctx)) - s = BoolSort(ctx) - a = s.cast(a) - return BoolRef(Z3_mk_interpolant(ctx.ref(), a.as_ast()), ctx) - -def tree_interpolant(pat,p=None,ctx=None): - """Compute interpolant for a tree of formulas. - - The input is an interpolation pattern over a set of formulas C. - The pattern pat is a formula combining the formulas in C using - logical conjunction and the "interp" operator (see Interp). This - interp operator is logically the identity operator. It marks the - sub-formulas of the pattern for which interpolants should be - computed. The interpolant is a map sigma from marked subformulas - to formulas, such that, for each marked subformula phi of pat - (where phi sigma is phi with sigma(psi) substituted for each - subformula psi of phi such that psi in dom(sigma)): - - 1) phi sigma implies sigma(phi), and - - 2) sigma(phi) is in the common uninterpreted vocabulary between - the formulas of C occurring in phi and those not occurring in - phi - - and moreover pat sigma implies false. In the simplest case - an interpolant for the pattern "(and (interp A) B)" maps A - to an interpolant for A /\ B. - - The return value is a vector of formulas representing sigma. This - vector contains sigma(phi) for each marked subformula of pat, in - pre-order traversal. This means that subformulas of phi occur before phi - in the vector. Also, subformulas that occur multiply in pat will - occur multiply in the result vector. - - If pat is satisfiable, raises an object of class ModelRef - that represents a model of pat. - - If neither a proof of unsatisfiability nor a model is obtained - (for example, because of a timeout, or because models are disabled) - then None is returned. - - If parameters p are supplied, these are used in creating the - solver that determines satisfiability. - - >>> x = Int('x') - >>> y = Int('y') - >>> print(tree_interpolant(And(Interpolant(x < 0), Interpolant(y > 2), x == y))) - [Not(x >= 0), Not(y <= 2)] - - # >>> g = And(Interpolant(x<0),x<2) - # >>> try: - # ... print tree_interpolant(g).sexpr() - # ... except ModelRef as m: - # ... print m.sexpr() - (define-fun x () Int - (- 1)) - """ - f = pat - ctx = _get_ctx(_ctx_from_ast_arg_list([f], ctx)) - ptr = (AstVectorObj * 1)() - mptr = (Model * 1)() - if p is None: - p = ParamsRef(ctx) - res = Z3_compute_interpolant(ctx.ref(),f.as_ast(),p.params,ptr,mptr) - if res == Z3_L_FALSE: - return AstVector(ptr[0],ctx) - if mptr[0]: - raise ModelRef(mptr[0], ctx) - return None - -def binary_interpolant(a,b,p=None,ctx=None): - """Compute an interpolant for a binary conjunction. - - If a & b is unsatisfiable, returns an interpolant for a & b. - This is a formula phi such that - - 1) a implies phi - 2) b implies not phi - 3) All the uninterpreted symbols of phi occur in both a and b. - - If a & b is satisfiable, raises an object of class ModelRef - that represents a model of a &b. - - If neither a proof of unsatisfiability nor a model is obtained - (for example, because of a timeout, or because models are disabled) - then None is returned. - - If parameters p are supplied, these are used in creating the - solver that determines satisfiability. - - x = Int('x') - print(binary_interpolant(x<0,x>2)) - Not(x >= 0) - """ - f = And(Interpolant(a),b) - ti = tree_interpolant(f,p,ctx) - return ti[0] if ti is not None else None - -def sequence_interpolant(v,p=None,ctx=None): - """Compute interpolant for a sequence of formulas. - - If len(v) == N, and if the conjunction of the formulas in v is - unsatisfiable, the interpolant is a sequence of formulas w - such that len(w) = N-1 and v[0] implies w[0] and for i in 0..N-1: - - 1) w[i] & v[i+1] implies w[i+1] (or false if i+1 = N) - 2) All uninterpreted symbols in w[i] occur in both v[0]..v[i] - and v[i+1]..v[n] - - Requires len(v) >= 1. - - If a & b is satisfiable, raises an object of class ModelRef - that represents a model of a & b. - - If neither a proof of unsatisfiability nor a model is obtained - (for example, because of a timeout, or because models are disabled) - then None is returned. - - If parameters p are supplied, these are used in creating the - solver that determines satisfiability. - - x = Int('x') - y = Int('y') - print(sequence_interpolant([x < 0, y == x , y > 2])) - [Not(x >= 0), Not(y >= 0)] - """ - f = v[0] - for i in range(1,len(v)): - f = And(Interpolant(f),v[i]) - return tree_interpolant(f,p,ctx) - ######################################### # @@ -9826,6 +9846,14 @@ def String(name, ctx=None): ctx = _get_ctx(ctx) return SeqRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), StringSort(ctx).ast), ctx) +def SubString(s, offset, length): + """Extract substring or subsequence starting at offset""" + return Extract(s, offset, length) + +def SubSeq(s, offset, length): + """Extract substring or subsequence starting at offset""" + return Extract(s, offset, length) + def Strings(names, ctx=None): """Return a tuple of String constants. """ ctx = _get_ctx(ctx) diff --git a/src/api/z3.h b/src/api/z3.h index 37ea68f84..382bc4b07 100644 --- a/src/api/z3.h +++ b/src/api/z3.h @@ -32,7 +32,6 @@ Notes: #include "z3_rcf.h" #include "z3_fixedpoint.h" #include "z3_optimization.h" -#include "z3_interp.h" #include "z3_fpa.h" #include "z3_spacer.h" #endif diff --git a/src/api/z3_api.h b/src/api/z3_api.h index ac6c39137..3e47833b4 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -83,14 +83,14 @@ typedef const char * Z3_string; typedef Z3_string * Z3_string_ptr; /** - \brief True value. It is just an alias for \c 1. + \brief True value. It is just an alias for \c true. */ -#define Z3_TRUE 1 +#define Z3_TRUE true /** - \brief False value. It is just an alias for \c 0. + \brief False value. It is just an alias for \c false. */ -#define Z3_FALSE 0 +#define Z3_FALSE false /** \brief Lifted Boolean type: \c false, \c undefined, \c true. @@ -212,8 +212,6 @@ typedef enum - Z3_OP_OEQ Binary equivalence modulo namings. This binary predicate is used in proof terms. It captures equisatisfiability and equivalence modulo renamings. - - Z3_OP_INTERP Marks a sub-formula for interpolation. - - Z3_OP_ANUM Arithmetic numeral. - Z3_OP_AGNUM Arithmetic algebraic numeral. Algebraic numbers are used to represent irrational numbers in Z3. @@ -991,7 +989,6 @@ typedef enum { Z3_OP_NOT, Z3_OP_IMPLIES, Z3_OP_OEQ, - Z3_OP_INTERP, // Arithmetic Z3_OP_ANUM = 0x200, @@ -5175,9 +5172,9 @@ extern "C" { It returns a formula comprising of the conjunction of assertions in the scope (up to push/pop) at the end of the string. - def_API('Z3_parse_smtlib2_string', AST, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) + def_API('Z3_parse_smtlib2_string', AST_VECTOR, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ - Z3_ast Z3_API Z3_parse_smtlib2_string(Z3_context c, + Z3_ast_vector Z3_API Z3_parse_smtlib2_string(Z3_context c, Z3_string str, unsigned num_sorts, Z3_symbol const sort_names[], @@ -5189,9 +5186,9 @@ extern "C" { /** \brief Similar to #Z3_parse_smtlib2_string, but reads the benchmark from a file. - def_API('Z3_parse_smtlib2_file', AST, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) + def_API('Z3_parse_smtlib2_file', AST_VECTOR, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ - Z3_ast Z3_API Z3_parse_smtlib2_file(Z3_context c, + Z3_ast_vector Z3_API Z3_parse_smtlib2_file(Z3_context c, Z3_string file_name, unsigned num_sorts, Z3_symbol const sort_names[], @@ -5450,6 +5447,15 @@ extern "C" { */ Z3_goal Z3_API Z3_goal_translate(Z3_context source, Z3_goal g, Z3_context target); + /** + \brief Convert a model of the formulas of a goal to a model of an original goal. + The model may be null, in which case the returned model is valid if the goal was + established satisfiable. + + def_API('Z3_goal_convert_model', MODEL, (_in(CONTEXT), _in(GOAL), _in(MODEL))) + */ + Z3_model Z3_API Z3_goal_convert_model(Z3_context c, Z3_goal g, Z3_model m); + /** \brief Convert a goal into a string. @@ -5457,6 +5463,13 @@ extern "C" { */ Z3_string Z3_API Z3_goal_to_string(Z3_context c, Z3_goal g); + /** + \brief Convert a goal into a DIMACS formatted string. + + def_API('Z3_goal_to_dimacs_string', STRING, (_in(CONTEXT), _in(GOAL))) + */ + Z3_string Z3_API Z3_goal_to_dimacs_string(Z3_context c, Z3_goal g); + /*@}*/ /** @name Tactics and Probes */ @@ -5809,14 +5822,6 @@ extern "C" { */ Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i); - /** - \brief Convert a model for the subgoal \c Z3_apply_result_get_subgoal(c, r, i) into a model for the original goal \c g. - Where \c g is the goal used to create \c r using \c Z3_tactic_apply(c, t, g). - - def_API('Z3_apply_result_convert_model', MODEL, (_in(CONTEXT), _in(APPLY_RESULT), _in(UINT), _in(MODEL))) - */ - Z3_model Z3_API Z3_apply_result_convert_model(Z3_context c, Z3_apply_result r, unsigned i, Z3_model m); - /*@}*/ /** @name Solvers*/ @@ -5916,6 +5921,13 @@ extern "C" { */ Z3_solver Z3_API Z3_solver_translate(Z3_context source, Z3_solver s, Z3_context target); + /** + \brief Ad-hoc method for importing model convertion from solver. + + def_API('Z3_solver_import_model_converter', VOID, (_in(CONTEXT), _in(SOLVER), _in(SOLVER))) + */ + void Z3_API Z3_solver_import_model_converter(Z3_context ctx, Z3_solver src, Z3_solver dst); + /** \brief Return a string describing all solver available parameters. @@ -6016,13 +6028,6 @@ extern "C" { */ void Z3_API Z3_solver_assert_and_track(Z3_context c, Z3_solver s, Z3_ast a, Z3_ast p); - /** - \brief Return the set of asserted formulas on the solver. - - def_API('Z3_solver_get_assertions', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) - */ - Z3_ast_vector Z3_API Z3_solver_get_assertions(Z3_context c, Z3_solver s); - /** \brief load solver assertions from a file. @@ -6037,6 +6042,20 @@ extern "C" { */ void Z3_API Z3_solver_from_string(Z3_context c, Z3_solver s, Z3_string file_name); + /** + \brief Return the set of asserted formulas on the solver. + + def_API('Z3_solver_get_assertions', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) + */ + Z3_ast_vector Z3_API Z3_solver_get_assertions(Z3_context c, Z3_solver s); + + /** + \brief Return the set of units modulo model conversion. + + def_API('Z3_solver_get_units', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) + */ + Z3_ast_vector Z3_API Z3_solver_get_units(Z3_context c, Z3_solver s); + /** \brief Check whether the assertions in a given solver are consistent or not. @@ -6104,6 +6123,28 @@ extern "C" { Z3_ast_vector assumptions, Z3_ast_vector variables, Z3_ast_vector consequences); + + + /** + \brief extract a next cube for a solver. The last cube is the constant \c true or \c false. + The number of (non-constant) cubes is by default 1. For the sat solver cubing is controlled + using parameters sat.lookahead.cube.cutoff and sat.lookahead.cube.fraction. + + The third argument is a vector of variables that may be used for cubing. + The contents of the vector is only used in the first call. The initial list of variables + is used in subsequent calls until it returns the unsatisfiable cube. + The vector is modified to contain a set of Autarky variables that occor in clauses that + are affected by the (last literal in the) cube. These variables could be used by a different + cuber (on a different solver object) for further recursive cubing. + + The last argument is a backtracking level. It instructs the cube process to backtrack below + the indicated level for the next cube. + + def_API('Z3_solver_cube', AST_VECTOR, (_in(CONTEXT), _in(SOLVER), _in(AST_VECTOR), _in(UINT))) + */ + + Z3_ast_vector Z3_API Z3_solver_cube(Z3_context c, Z3_solver s, Z3_ast_vector vars, unsigned backtrack_level); + /** \brief Retrieve the model for the last #Z3_solver_check or #Z3_solver_check_assumptions diff --git a/src/api/z3_interp.h b/src/api/z3_interp.h deleted file mode 100644 index 2441d4339..000000000 --- a/src/api/z3_interp.h +++ /dev/null @@ -1,284 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - z3_interp.h - -Abstract: - - API for interpolation - -Author: - - Kenneth McMillan (kenmcmil) - -Notes: - ---*/ -#ifndef Z3_INTERPOLATION_H_ -#define Z3_INTERPOLATION_H_ - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - - /** \defgroup capi C API */ - /*@{*/ - - /** @name Interpolation facilities */ - /*@{*/ - /** - \brief Create an AST node marking a formula position for interpolation. - - The node \c a must have Boolean sort. - - def_API('Z3_mk_interpolant', AST, (_in(CONTEXT), _in(AST))) - */ - Z3_ast Z3_API Z3_mk_interpolant(Z3_context c, Z3_ast a); - - - /** \brief This function generates a Z3 context suitable for generation of - interpolants. Formulas can be generated as abstract syntax trees in - this context using the Z3 C API. - - Interpolants are also generated as AST's in this context. - - If cfg is non-null, it will be used as the base configuration - for the Z3 context. This makes it possible to set Z3 options - to be used during interpolation. This feature should be used - with some caution however, as it may be that certain Z3 options - are incompatible with interpolation. - - def_API('Z3_mk_interpolation_context', CONTEXT, (_in(CONFIG),)) - - */ - - Z3_context Z3_API Z3_mk_interpolation_context(Z3_config cfg); - - /** Compute an interpolant from a refutation. This takes a proof of - "false" from a set of formulas C, and an interpolation - pattern. The pattern pat is a formula combining the formulas in C - using logical conjunction and the "interp" operator (see - #Z3_mk_interpolant). This interp operator is logically the identity - operator. It marks the sub-formulas of the pattern for which interpolants should - be computed. The interpolant is a map sigma from marked subformulas to - formulas, such that, for each marked subformula phi of pat (where phi sigma - is phi with sigma(psi) substituted for each subformula psi of phi such that - psi in dom(sigma)): - - 1) phi sigma implies sigma(phi), and - - 2) sigma(phi) is in the common uninterpreted vocabulary between - the formulas of C occurring in phi and those not occurring in - phi - - and moreover pat sigma implies false. In the simplest case - an interpolant for the pattern "(and (interp A) B)" maps A - to an interpolant for A /\ B. - - The return value is a vector of formulas representing sigma. The - vector contains sigma(phi) for each marked subformula of pat, in - pre-order traversal. This means that subformulas of phi occur before phi - in the vector. Also, subformulas that occur multiply in pat will - occur multiply in the result vector. - - In particular, calling Z3_get_interpolant on a pattern of the - form (interp ... (interp (and (interp A_1) A_2)) ... A_N) will - result in a sequence interpolant for A_1, A_2,... A_N. - - Neglecting interp markers, the pattern must be a conjunction of - formulas in C, the set of premises of the proof. Otherwise an - error is flagged. - - Any premises of the proof not present in the pattern are - treated as "background theory". Predicate and function symbols - occurring in the background theory are treated as interpreted and - thus always allowed in the interpolant. - - Interpolant may not necessarily be computable from all - proofs. To be sure an interpolant can be computed, the proof - must be generated by an SMT solver for which interpolation is - supported, and the premises must be expressed using only - theories and operators for which interpolation is supported. - - Currently, the only SMT solver that is supported is the legacy - SMT solver. Such a solver is available as the default solver in - \c Z3_context objects produced by #Z3_mk_interpolation_context. - Currently, the theories supported are equality with - uninterpreted functions, linear integer arithmetic, and the - theory of arrays (in SMT-LIB terms, this is AUFLIA). - Quantifiers are allowed. Use of any other operators (including - "labels") may result in failure to compute an interpolant from a - proof. - - Parameters: - - \param c logical context. - \param pf a refutation from premises (assertions) C - \param pat an interpolation pattern over C - \param p parameters - - def_API('Z3_get_interpolant', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(PARAMS))) - */ - - Z3_ast_vector Z3_API Z3_get_interpolant(Z3_context c, Z3_ast pf, Z3_ast pat, Z3_params p); - - /* Compute an interpolant for an unsatisfiable conjunction of formulas. - - This takes as an argument an interpolation pattern as in - #Z3_get_interpolant. This is a conjunction, some subformulas of - which are marked with the "interp" operator (see #Z3_mk_interpolant). - - The conjunction is first checked for unsatisfiability. The result - of this check is returned in the out parameter "status". If the result - is unsat, an interpolant is computed from the refutation as in #Z3_get_interpolant - and returned as a vector of formulas. Otherwise the return value is - an empty formula. - - See #Z3_get_interpolant for a discussion of supported theories. - - The advantage of this function over #Z3_get_interpolant is that - it is not necessary to create a suitable SMT solver and generate - a proof. The disadvantage is that it is not possible to use the - solver incrementally. - - Parameters: - - \param c logical context. - \param pat an interpolation pattern - \param p parameters for solver creation - \param status returns the status of the sat check - \param model returns model if satisfiable - - Return value: status of SAT check - - def_API('Z3_compute_interpolant', INT, (_in(CONTEXT), _in(AST), _in(PARAMS), _out(AST_VECTOR), _out(MODEL))) - */ - - Z3_lbool Z3_API Z3_compute_interpolant(Z3_context c, - Z3_ast pat, - Z3_params p, - Z3_ast_vector *interp, - Z3_model *model); - - /** Return a string summarizing cumulative time used for - interpolation. This string is purely for entertainment purposes - and has no semantics. - - \param ctx The context (currently ignored) - - - def_API('Z3_interpolation_profile', STRING, (_in(CONTEXT),)) - */ - - Z3_string Z3_API Z3_interpolation_profile(Z3_context ctx); - - /** - \brief Read an interpolation problem from file. - - \param ctx The Z3 context. This resets the error handler of ctx. - \param filename The file name to read. - \param num Returns length of sequence. - \param cnsts Returns sequence of formulas (do not free) - \param parents Returns the parents vector (or NULL for sequence) - \param error Returns an error message in case of failure (do not free the string) - \param num_theory Number of theory terms - \param theory Theory terms - - Returns true on success. - - File formats: Currently two formats are supported, based on - SMT-LIB2. For sequence interpolants, the sequence of constraints is - represented by the sequence of "assert" commands in the file. - - For tree interpolants, one symbol of type bool is associated to - each vertex of the tree. For each vertex v there is an "assert" - of the form: - - (implies (and c1 ... cn f) v) - - where c1 .. cn are the children of v (which must precede v in the file) - and f is the formula associated to node v. The last formula in the - file is the root vertex, and is represented by the predicate "false". - - A solution to a tree interpolation problem can be thought of as a - valuation of the vertices that makes all the implications true - where each value is represented using the common symbols between - the formulas in the subtree and the remainder of the formulas. - - def_API('Z3_read_interpolation_problem', INT, (_in(CONTEXT), _out(UINT), _out_managed_array(1, AST), _out_managed_array(1, UINT), _in(STRING), _out(STRING), _out(UINT), _out_managed_array(6, AST))) - - */ - - int Z3_API Z3_read_interpolation_problem(Z3_context ctx, - unsigned *num, - Z3_ast *cnsts[], - unsigned *parents[], - Z3_string filename, - Z3_string_ptr error, - unsigned *num_theory, - Z3_ast *theory[]); - - - - /** Check the correctness of an interpolant. The Z3 context must - have no constraints asserted when this call is made. That means - that after interpolating, you must first fully pop the Z3 - context before calling this. See Z3_interpolate for meaning of parameters. - - \param ctx The Z3 context. Must be generated by Z3_mk_interpolation_context - \param num The number of constraints in the sequence - \param cnsts Array of constraints (AST's in context ctx) - \param parents The parents vector (or NULL for sequence) - \param interps The interpolant to check - \param error Returns an error message if interpolant incorrect (do not free the string) - \param num_theory Number of theory terms - \param theory Theory terms - - Return value is Z3_L_TRUE if interpolant is verified, Z3_L_FALSE if - incorrect, and Z3_L_UNDEF if unknown. - - def_API('Z3_check_interpolant', INT, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in_array(1, AST), _out(STRING), _in(UINT), _in_array(6, AST))) - */ - - int Z3_API Z3_check_interpolant(Z3_context ctx, - unsigned num, - Z3_ast cnsts[], - unsigned parents[], - Z3_ast *interps, - Z3_string_ptr error, - unsigned num_theory, - Z3_ast theory[]); - - /** Write an interpolation problem to file suitable for reading with - Z3_read_interpolation_problem. The output file is a sequence - of SMT-LIB2 format commands, suitable for reading with command-line Z3 - or other interpolating solvers. - - \param ctx The Z3 context. Must be generated by z3_mk_interpolation_context - \param num The number of constraints in the sequence - \param cnsts Array of constraints - \param parents The parents vector (or NULL for sequence) - \param filename The file name to write - \param num_theory Number of theory terms - \param theory Theory terms - - def_API('Z3_write_interpolation_problem', VOID, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(STRING), _in(UINT), _in_array(5, AST))) - */ - - void Z3_API Z3_write_interpolation_problem(Z3_context ctx, - unsigned num, - Z3_ast cnsts[], - unsigned parents[], - Z3_string filename, - unsigned num_theory, - Z3_ast theory[]); - /*@}*/ - /*@}*/ - -#ifdef __cplusplus -} -#endif // __cplusplus - -#endif diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 12ca136c5..cae1618c0 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -646,7 +646,6 @@ basic_decl_plugin::basic_decl_plugin(): m_iff_decl(nullptr), m_xor_decl(nullptr), m_not_decl(nullptr), - m_interp_decl(nullptr), m_implies_decl(nullptr), m_proof_sort(nullptr), @@ -865,7 +864,6 @@ void basic_decl_plugin::set_manager(ast_manager * m, family_id id) { m_iff_decl = mk_bool_op_decl("iff", OP_IFF, 2, false, true, false, false, true); m_xor_decl = mk_bool_op_decl("xor", OP_XOR, 2, true, true); m_not_decl = mk_bool_op_decl("not", OP_NOT, 1); - m_interp_decl = mk_bool_op_decl("interp", OP_INTERP, 1); m_implies_decl = mk_implies_decl(); m_proof_sort = m->mk_sort(symbol("Proof"), sort_info(id, PROOF_SORT)); @@ -890,7 +888,6 @@ void basic_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("or", OP_OR)); op_names.push_back(builtin_name("xor", OP_XOR)); op_names.push_back(builtin_name("not", OP_NOT)); - op_names.push_back(builtin_name("interp", OP_INTERP)); op_names.push_back(builtin_name("=>", OP_IMPLIES)); if (logic == symbol::null) { // user friendly aliases @@ -902,7 +899,6 @@ void basic_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("||", OP_OR)); op_names.push_back(builtin_name("equals", OP_EQ)); op_names.push_back(builtin_name("equiv", OP_IFF)); - op_names.push_back(builtin_name("@@", OP_INTERP)); } } @@ -923,7 +919,6 @@ void basic_decl_plugin::finalize() { DEC_REF(m_and_decl); DEC_REF(m_or_decl); DEC_REF(m_not_decl); - DEC_REF(m_interp_decl); DEC_REF(m_iff_decl); DEC_REF(m_xor_decl); DEC_REF(m_implies_decl); @@ -1056,7 +1051,6 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; - case OP_INTERP: return m_interp_decl; case OP_IFF: return m_iff_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; @@ -1099,7 +1093,6 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; - case OP_INTERP: return m_interp_decl; case OP_IFF: return m_iff_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; @@ -1348,6 +1341,10 @@ ast_manager::ast_manager(ast_manager const & src, bool disable_proofs): copy_families_plugins(src); } +void ast_manager::update_fresh_id(ast_manager const& m) { + m_fresh_id = std::max(m_fresh_id, m.m_fresh_id); +} + void ast_manager::init() { m_int_real_coercions = true; m_debug_ref_count = false; @@ -1506,7 +1503,7 @@ void ast_manager::compress_ids() { else n->m_id = m_expr_id_gen.mk(); asts.push_back(n); - } + } m_ast_table.finalize(); for (ast* a : asts) m_ast_table.insert(a); @@ -2221,9 +2218,10 @@ func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const } else { string_buffer<64> buffer; - buffer << prefix; if (prefix == symbol::null) buffer << "sk"; + else + buffer << prefix; buffer << "!"; if (suffix != symbol::null) buffer << suffix << "!"; diff --git a/src/ast/ast.h b/src/ast/ast.h index 35e9a1cbb..e85f164e1 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -628,6 +628,9 @@ public: sort * const * get_domain() const { return m_domain; } sort * get_range() const { return m_range; } unsigned get_size() const { return get_obj_size(m_arity); } + sort * const * begin() const { return get_domain(); } + sort * const * end() const { return get_domain() + get_arity(); } + }; // ----------------------------------- @@ -1038,7 +1041,7 @@ enum basic_sort_kind { }; enum basic_op_kind { - OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_IFF, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, OP_INTERP, LAST_BASIC_OP, + OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_IFF, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, LAST_BASIC_OP, PR_UNDEF, PR_TRUE, PR_ASSERTED, PR_GOAL, PR_MODUS_PONENS, PR_REFLEXIVITY, PR_SYMMETRY, PR_TRANSITIVITY, PR_TRANSITIVITY_STAR, PR_MONOTONICITY, PR_QUANT_INTRO, PR_DISTRIBUTIVITY, PR_AND_ELIM, PR_NOT_OR_ELIM, PR_REWRITE, PR_REWRITE_STAR, PR_PULL_QUANT, @@ -1060,7 +1063,6 @@ protected: func_decl * m_iff_decl; func_decl * m_xor_decl; func_decl * m_not_decl; - func_decl * m_interp_decl; func_decl * m_implies_decl; ptr_vector m_eq_decls; // cached eqs ptr_vector m_ite_decls; // cached ites @@ -1451,6 +1453,8 @@ public: void show_id_gen(); + void update_fresh_id(ast_manager const& other); + protected: reslimit m_limit; small_object_allocator m_alloc; @@ -2049,7 +2053,6 @@ public: app * mk_true() const { return m_true; } app * mk_false() const { return m_false; } app * mk_bool_val(bool b) { return b?m_true:m_false; } - app * mk_interp(expr * arg) { return mk_app(m_basic_family_id, OP_INTERP, arg); } func_decl* mk_and_decl() { diff --git a/src/ast/ast_pp_util.cpp b/src/ast/ast_pp_util.cpp index 23c9ebe51..378710b36 100644 --- a/src/ast/ast_pp_util.cpp +++ b/src/ast/ast_pp_util.cpp @@ -36,7 +36,6 @@ void ast_pp_util::collect(expr_ref_vector const& es) { } void ast_pp_util::display_decls(std::ostream& out) { - smt2_pp_environment_dbg env(m); ast_smt_pp pp(m); coll.order_deps(); unsigned n = coll.get_num_sorts(); @@ -47,10 +46,10 @@ void ast_pp_util::display_decls(std::ostream& out) { for (unsigned i = 0; i < n; ++i) { func_decl* f = coll.get_func_decls()[i]; if (f->get_family_id() == null_family_id && !m_removed.contains(f)) { - ast_smt2_pp(out, f, env); - out << "\n"; + ast_smt2_pp(out, f, m_env) << "\n"; } } + SASSERT(coll.get_num_preds() == 0); } void ast_pp_util::remove_decl(func_decl* f) { @@ -60,18 +59,17 @@ void ast_pp_util::remove_decl(func_decl* f) { void ast_pp_util::display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat) { if (neat) { - smt2_pp_environment_dbg env(m); - for (unsigned i = 0; i < fmls.size(); ++i) { + for (expr* f : fmls) { out << "(assert "; - ast_smt2_pp(out, fmls[i], env); + ast_smt2_pp(out, f, m_env); out << ")\n"; } } else { ast_smt_pp ll_smt2_pp(m); - for (unsigned i = 0; i < fmls.size(); ++i) { + for (expr* f : fmls) { out << "(assert "; - ll_smt2_pp.display_expr_smt2(out, fmls[i]); + ll_smt2_pp.display_expr_smt2(out, f); out << ")\n"; } } diff --git a/src/ast/ast_pp_util.h b/src/ast/ast_pp_util.h index 1b2686511..c3b8aa601 100644 --- a/src/ast/ast_pp_util.h +++ b/src/ast/ast_pp_util.h @@ -20,16 +20,18 @@ Revision History: #define AST_PP_UTIL_H_ #include "ast/decl_collector.h" +#include "ast/ast_smt2_pp.h" #include "util/obj_hashtable.h" class ast_pp_util { ast_manager& m; obj_hashtable m_removed; + smt2_pp_environment_dbg m_env; public: decl_collector coll; - ast_pp_util(ast_manager& m): m(m), coll(m, false) {} + ast_pp_util(ast_manager& m): m(m), m_env(m), coll(m, false) {} void collect(expr* e); @@ -42,6 +44,8 @@ class ast_pp_util { void display_decls(std::ostream& out); void display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat = true); + + smt2_pp_environment& env() { return m_env; } }; #endif /* AST_PP_UTIL_H_ */ diff --git a/src/ast/ast_printer.cpp b/src/ast/ast_printer.cpp index 536e1af92..7f2460e06 100644 --- a/src/ast/ast_printer.cpp +++ b/src/ast/ast_printer.cpp @@ -34,7 +34,7 @@ public: out << f->get_name(); } void pp(sort * s, format_ns::format_ref & r) const override { mk_smt2_format(s, env(), params_ref(), r); } - void pp(func_decl * f, format_ns::format_ref & r) const override { mk_smt2_format(f, env(), params_ref(), r); } + void pp(func_decl * f, format_ns::format_ref & r) const override { mk_smt2_format(f, env(), params_ref(), r, "declare-fun"); } void pp(expr * n, format_ns::format_ref & r) const override { sbuffer buf; mk_smt2_format(n, env(), params_ref(), 0, nullptr, r, buf); diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index aeaebc6bf..a2958e3c9 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -31,7 +31,7 @@ using namespace format_ns; #define MAX_INDENT 16 #define SMALL_INDENT 2 -format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len) const { +format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len, bool is_skolem) const { ast_manager & m = get_manager(); if (is_smt2_quoted_symbol(s)) { std::string str = mk_smt2_quoted_symbol(s); @@ -69,7 +69,7 @@ format * smt2_pp_environment::pp_fdecl_name(func_decl * f, unsigned & len) const } else { symbol s = f->get_name(); - return pp_fdecl_name(s, len); + return pp_fdecl_name(s, len, f->is_skolem()); } } @@ -116,8 +116,10 @@ format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) { (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast()))); if (f->get_parameter(i).is_int()) fs.push_back(mk_int(get_manager(), f->get_parameter(i).get_int())); - else if (f->get_parameter(i).is_rational()) - fs.push_back(mk_string(get_manager(), f->get_parameter(i).get_rational().to_string().c_str())); + else if (f->get_parameter(i).is_rational()) { + std::string str = f->get_parameter(i).get_rational().to_string(); + fs.push_back(mk_string(get_manager(), str.c_str())); + } else fs.push_back(pp_fdecl_ref(to_func_decl(f->get_parameter(i).get_ast()))); } @@ -614,9 +616,9 @@ class smt2_printer { return f; ptr_buffer buf; buf.push_back(f); - for (unsigned i = 0; i < names.size(); i++) { - buf.push_back(pp_simple_attribute(is_pos ? ":lblpos " : ":lblneg ", names[i])); - } + for (symbol const& n : names) + buf.push_back(pp_simple_attribute(is_pos ? ":lblpos " : ":lblneg ", n)); + return mk_seq1(m(), buf.begin(), buf.end(), f2f(), "!"); } @@ -891,8 +893,21 @@ class smt2_printer { } } + void register_var_names(unsigned n) { + unsigned idx = 1; + for (unsigned i = 0; i < n; i++) { + symbol name = next_name("x", idx); + SASSERT(!m_var_names_set.contains(name)); + m_var_names.push_back(name); + m_var_names_set.insert(name); + } + } + void unregister_var_names(quantifier * q) { - unsigned num_decls = q->get_num_decls(); + unregister_var_names(q->get_num_decls()); + } + + void unregister_var_names(unsigned num_decls) { for (unsigned i = 0; i < num_decls; i++) { symbol s = m_var_names.back(); m_var_names.pop_back(); @@ -900,25 +915,28 @@ class smt2_printer { } } - format * pp_var_decls(quantifier * q) { + format * pp_var_args(unsigned num_decls, sort* const* srts) { ptr_buffer buf; - unsigned num_decls = q->get_num_decls(); SASSERT(num_decls <= m_var_names.size()); symbol * it = m_var_names.end() - num_decls; for (unsigned i = 0; i < num_decls; i++, it++) { - format * fs[1] = { m_env.pp_sort(q->get_decl_sort(i)) }; + format * fs[1] = { m_env.pp_sort(srts[i]) }; std::string var_name; if (is_smt2_quoted_symbol (*it)) { var_name = mk_smt2_quoted_symbol (*it); } else { - var_name = it->str ();\ + var_name = it->str (); } buf.push_back(mk_seq1(m(), fs, fs+1, f2f(), var_name.c_str ())); } return mk_seq5(m(), buf.begin(), buf.end(), f2f()); } + format * pp_var_decls(quantifier * q) { + return pp_var_args(q->get_num_decls(), q->get_decl_sorts()); + } + void process_quantifier(quantifier * q, frame & fr) { if (fr.m_idx == 0) { begin_scope(); @@ -1009,10 +1027,8 @@ class smt2_printer { void reset_expr2alias_stack() { SASSERT(!m_expr2alias_stack.empty()); - ptr_vector::iterator it = m_expr2alias_stack.begin(); - ptr_vector::iterator end = m_expr2alias_stack.end(); - for (; it != end; ++it) - (*it)->reset(); + for (expr2alias * e : m_expr2alias_stack) + e->reset(); m_expr2alias = m_expr2alias_stack[0]; } @@ -1113,7 +1129,7 @@ public: r = m_env.pp_sort(s); } - void operator()(func_decl * f, format_ref & r) { + void operator()(func_decl * f, format_ref & r, char const* cmd) { unsigned arity = f->get_arity(); unsigned len; format * fname = m_env.pp_fdecl_name(f, len); @@ -1125,9 +1141,26 @@ public: } args[1] = mk_seq5(m(), buf.begin(), buf.end(), f2f()); args[2] = m_env.pp_sort(f->get_range()); - r = mk_seq1(m(), args, args+3, f2f(), "declare-fun"); + r = mk_seq1(m(), args, args+3, f2f(), cmd); } + + void operator()(func_decl * f, expr * e, format_ref & r, char const* cmd) { + unsigned len; + format * fname = m_env.pp_fdecl_name(f, len); + register_var_names(f->get_arity()); + format * args[4]; + args[0] = fname; + args[1] = pp_var_args(f->get_arity(), f->get_domain()); + args[2] = m_env.pp_sort(f->get_range()); + process(e, r); + args[3] = r; + r = mk_seq1(m(), args, args+4, f2f(), cmd); + unregister_var_names(f->get_arity()); + } + + + }; void mk_smt2_format(expr * n, smt2_pp_environment & env, params_ref const & p, @@ -1142,9 +1175,14 @@ void mk_smt2_format(sort * s, smt2_pp_environment & env, params_ref const & p, f pr(s, r); } -void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ref & r) { +void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd) { smt2_printer pr(env, p); - pr(f, r); + pr(f, r, cmd); +} + +void mk_smt2_format(func_decl * f, expr * e, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd) { + smt2_printer pr(env, p); + pr(f, e, r, cmd); } void mk_smt2_format(unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, @@ -1185,17 +1223,29 @@ std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & e return out; } -std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p, unsigned indent) { +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; - mk_smt2_format(f, env, p, r); + mk_smt2_format(f, env, p, r, cmd); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { + ast_manager & m = env.get_manager(); + format_ref r(fm(m)); + sbuffer var_names; + mk_smt2_format(f, e, env, p, r, cmd); + if (indent > 0) + r = mk_indent(m, indent, r.get()); + pp(out, r.get(), m, p); + return out; +} + + std::ostream & ast_smt2_pp(std::ostream & out, unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix) { ast_manager & m = env.get_manager(); @@ -1208,6 +1258,15 @@ std::ostream & ast_smt2_pp(std::ostream & out, unsigned sz, expr * const* es, sm return out; } +std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p) { + unsigned len; + ast_manager & m = env.get_manager(); + format_ref r(fm(m)); + r = env.pp_fdecl_name(s, len, is_skolem); + pp(out, r.get(), m, p); + return out; +} + mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix): m_ast(t), m_manager(m), @@ -1226,6 +1285,8 @@ mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent, unsigned num m_var_prefix(var_prefix) { } + + std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) { smt2_pp_environment_dbg env(p.m_manager); if (p.m_ast == nullptr) { @@ -1273,14 +1334,14 @@ std::ostream& operator<<(std::ostream& out, app_ref_vector const& e) { } std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e) { - for (unsigned i = 0; i < e.size(); ++i) - out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; + for (func_decl* f : e) + out << mk_ismt2_pp(f, e.get_manager()) << "\n"; return out; } std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e) { - for (unsigned i = 0; i < e.size(); ++i) - out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; + for (sort* s : e) + out << mk_ismt2_pp(s, e.get_manager()) << "\n"; return out; } diff --git a/src/ast/ast_smt2_pp.h b/src/ast/ast_smt2_pp.h index 69b8d67f9..13093f138 100644 --- a/src/ast/ast_smt2_pp.h +++ b/src/ast/ast_smt2_pp.h @@ -31,10 +31,12 @@ Revision History: #include "ast/dl_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/datatype_decl_plugin.h" +#include "ast/ast_smt_pp.h" #include "util/smt2_util.h" class smt2_pp_environment { protected: + mutable smt_renaming m_renaming; format_ns::format * mk_neg(format_ns::format * f) const; format_ns::format * mk_float(rational const & val) const; bool is_indexed_fdecl(func_decl * f) const; @@ -61,7 +63,7 @@ public: virtual format_ns::format * pp_string_literal(app * t); virtual format_ns::format * pp_sort(sort * s); virtual format_ns::format * pp_fdecl_ref(func_decl * f); - format_ns::format * pp_fdecl_name(symbol const & fname, unsigned & len) const; + format_ns::format * pp_fdecl_name(symbol const & fname, unsigned & len, bool is_skolem) const; format_ns::format * pp_fdecl_name(func_decl * f, unsigned & len) const; }; @@ -95,12 +97,14 @@ void mk_smt2_format(expr * n, smt2_pp_environment & env, params_ref const & p, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names); void mk_smt2_format(sort * s, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r); -void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r); +void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r, char const* cmd); std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0); -std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0); +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "declare-fun"); +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun"); +std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p = params_ref()); /** \brief Internal wrapper (for debugging purposes only) diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index b7404859f..635899d23 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -45,14 +45,6 @@ symbol smt_renaming::fix_symbol(symbol s, int k) { std::ostringstream buffer; char const * data = s.is_numerical() ? "" : s.bare_str(); - if (data[0] && !data[1]) { - switch (data[0]) { - case '/': data = "op_div"; break; - case '%': data = "op_mod"; break; - default: break; - } - } - if (k == 0 && *data) { if (s.is_numerical()) { return s; @@ -70,14 +62,17 @@ symbol smt_renaming::fix_symbol(symbol s, int k) { return symbol(buffer.str().c_str()); } - if (is_smt2_quoted_symbol(s)) { + if (!s.bare_str()) { + buffer << "null"; + } + else if (is_smt2_quoted_symbol(s)) { buffer << mk_smt2_quoted_symbol(s); } else { buffer << s; } if (k > 0) { - buffer << k; + buffer << "!" << k; } return symbol(buffer.str().c_str()); @@ -124,15 +119,30 @@ bool smt_renaming::all_is_legal(char const* s) { smt_renaming::smt_renaming() { for (unsigned i = 0; i < ARRAYSIZE(m_predef_names); ++i) { symbol s(m_predef_names[i]); - m_translate.insert(s, s); + m_translate.insert(s, sym_b(s, false)); m_rev_translate.insert(s, s); } } - -symbol smt_renaming::get_symbol(symbol s0) { +// Ensure that symbols that are used both with skolems and non-skolems are named apart. +symbol smt_renaming::get_symbol(symbol s0, bool is_skolem) { + sym_b sb; symbol s; - if (m_translate.find(s0, s)) { + if (m_translate.find(s0, sb)) { + if (is_skolem == sb.is_skolem) + return sb.name; + if (sb.name_aux != symbol::null) { + return sb.name_aux; + } + int k = 0; + symbol s1; + do { + s = fix_symbol(s0, k++); + } + while (s == s0 || (m_rev_translate.find(s, s1) && s1 != s0)); + m_rev_translate.insert(s, s0); + sb.name_aux = s; + m_translate.insert(s, sb); return s; } @@ -141,7 +151,7 @@ symbol smt_renaming::get_symbol(symbol s0) { s = fix_symbol(s0, k++); } while (m_rev_translate.contains(s)); - m_translate.insert(s0, s); + m_translate.insert(s0, sym_b(s, is_skolem)); m_rev_translate.insert(s, s0); return s; } @@ -202,7 +212,7 @@ class smt_printer { } void pp_decl(func_decl* d) { - symbol sym = m_renaming.get_symbol(d->get_name()); + symbol sym = m_renaming.get_symbol(d->get_name(), d->is_skolem()); if (d->get_family_id() == m_dt_fid) { datatype_util util(m_manager); if (util.is_recognizer(d)) { @@ -313,7 +323,7 @@ class smt_printer { if (num_sorts > 0) { m_out << "("; } - m_out << m_renaming.get_symbol(s->get_name()); + m_out << m_renaming.get_symbol(s->get_name(), false); if (num_sorts > 0) { for (unsigned i = 0; i < num_sorts; ++i) { m_out << " "; @@ -324,7 +334,7 @@ class smt_printer { return; } else { - sym = m_renaming.get_symbol(s->get_name()); + sym = m_renaming.get_symbol(s->get_name(), false); } visit_params(true, sym, s->get_num_parameters(), s->get_parameters()); } @@ -395,17 +405,17 @@ class smt_printer { else if (m_manager.is_label(n, pos, names) && names.size() >= 1) { m_out << "(! "; pp_marked_expr(n->get_arg(0)); - m_out << (pos?":lblpos":":lblneg") << " " << m_renaming.get_symbol(names[0]) << ")"; + m_out << (pos?":lblpos":":lblneg") << " " << m_renaming.get_symbol(names[0], false) << ")"; } else if (m_manager.is_label_lit(n, names) && names.size() >= 1) { - m_out << "(! true :lblpos " << m_renaming.get_symbol(names[0]) << ")"; + m_out << "(! true :lblpos " << m_renaming.get_symbol(names[0], false) << ")"; } else if (num_args == 0) { if (decl->private_parameters()) { - m_out << m_renaming.get_symbol(decl->get_name()); + m_out << m_renaming.get_symbol(decl->get_name(), decl->is_skolem()); } else { - symbol sym = m_renaming.get_symbol(decl->get_name()); + symbol sym = m_renaming.get_symbol(decl->get_name(), decl->is_skolem()); visit_params(false, sym, decl->get_num_parameters(), decl->get_parameters()); } } @@ -498,7 +508,7 @@ class smt_printer { for (unsigned i = 0; i < q->get_num_decls(); ++i) { sort* s = q->get_decl_sort(i); m_out << "("; - print_bound(m_renaming.get_symbol(q->get_decl_name(i))); + print_bound(m_renaming.get_symbol(q->get_decl_name(i), false)); m_out << " "; visit_sort(s, true); m_out << ") "; @@ -563,7 +573,7 @@ class smt_printer { unsigned num_decls = q->get_num_decls(); if (idx < num_decls) { unsigned offs = num_decls-idx-1; - symbol name = m_renaming.get_symbol(q->get_decl_name(offs)); + symbol name = m_renaming.get_symbol(q->get_decl_name(offs), false); print_bound(name); return; } @@ -805,15 +815,15 @@ public: m_out << ")"; } m_out << "("; - m_out << m_renaming.get_symbol(d->name()); + m_out << m_renaming.get_symbol(d->name(), false); m_out << " "; bool first_constr = true; for (datatype::constructor* f : *d) { if (!first_constr) m_out << " "; else first_constr = false; m_out << "("; - m_out << m_renaming.get_symbol(f->name()); + m_out << m_renaming.get_symbol(f->name(), false); for (datatype::accessor* a : *f) { - m_out << " (" << m_renaming.get_symbol(a->name()) << " "; + m_out << " (" << m_renaming.get_symbol(a->name(), false) << " "; visit_sort(a->range()); m_out << ")"; } diff --git a/src/ast/ast_smt_pp.h b/src/ast/ast_smt_pp.h index 71842904c..84beb34a2 100644 --- a/src/ast/ast_smt_pp.h +++ b/src/ast/ast_smt_pp.h @@ -24,8 +24,10 @@ Revision History: #include "util/map.h" class smt_renaming { + struct sym_b { symbol name; bool is_skolem; symbol name_aux; sym_b(symbol n, bool s): name(n), is_skolem(s) {} sym_b():name(),is_skolem(false) {}}; typedef map symbol2symbol; - symbol2symbol m_translate; + typedef map symbol2sym_b; + symbol2sym_b m_translate; symbol2symbol m_rev_translate; symbol fix_symbol(symbol s, int k); @@ -35,8 +37,8 @@ class smt_renaming { bool all_is_legal(char const* s); public: smt_renaming(); - symbol get_symbol(symbol s0); - symbol operator()(symbol const & s) { return get_symbol(s); } + symbol get_symbol(symbol s0, bool is_skolem = false); + symbol operator()(symbol const & s, bool is_skolem = false) { return get_symbol(s, is_skolem); } }; class ast_smt_pp { diff --git a/src/ast/ast_translation.cpp b/src/ast/ast_translation.cpp index 8d6ebf221..5ab6ab351 100644 --- a/src/ast/ast_translation.cpp +++ b/src/ast/ast_translation.cpp @@ -23,6 +23,7 @@ Revision History: #include "ast/format.h" #include "ast/ast_translation.h" #include "ast/ast_ll_pp.h" +#include "ast/ast_pp.h" ast_translation::~ast_translation() { reset_cache(); @@ -47,9 +48,10 @@ void ast_translation::reset_cache() { void ast_translation::cache(ast * s, ast * t) { SASSERT(!m_cache.contains(s)); if (s->get_ref_count() > 1) { - m_cache.insert(s, t); m_from_manager.inc_ref(s); m_to_manager.inc_ref(t); + m_cache.insert(s, t); + ++m_insert_count; } } @@ -75,10 +77,16 @@ void ast_translation::push_frame(ast * n) { } bool ast_translation::visit(ast * n) { - ast * r; - if (n->get_ref_count() > 1 && m_cache.find(n, r)) { - m_result_stack.push_back(r); - return true; + if (n->get_ref_count() > 1) { + ast * r; + if (m_cache.find(n, r)) { + m_result_stack.push_back(r); + ++m_hit_count; + return true; + } + else { + ++m_miss_count; + } } push_frame(n); return false; @@ -160,7 +168,7 @@ void ast_translation::mk_func_decl(func_decl * f, frame & fr) { new_fi.set_chainable(fi->is_chainable()); new_fi.set_pairwise(fi->is_pairwise()); new_fi.set_injective(fi->is_injective()); - new_fi.set_skolem(fi->is_skolem()); +/// TBD new_fi.set_skolem(fi->is_skolem()); new_fi.set_idempotent(fi->is_idempotent()); new_f = m_to_manager.mk_func_decl(f->get_name(), @@ -187,20 +195,32 @@ ast * ast_translation::process(ast const * _n) { SASSERT(m_frame_stack.empty()); SASSERT(m_extra_children_stack.empty()); + ++m_num_process; + if (m_num_process > (1 << 14)) { + reset_cache(); + m_num_process = 0; + } if (!visit(const_cast(_n))) { while (!m_frame_stack.empty()) { loop: + ++m_loop_count; frame & fr = m_frame_stack.back(); ast * n = fr.m_n; ast * r; TRACE("ast_translation", tout << mk_ll_pp(n, m_from_manager, false) << "\n";); - if (fr.m_idx == 0 && n->get_ref_count() > 1 && m_cache.find(n, r)) { - SASSERT(m_result_stack.size() == fr.m_rpos); - m_result_stack.push_back(r); - m_extra_children_stack.shrink(fr.m_cpos); - m_frame_stack.pop_back(); - TRACE("ast_translation", tout << "hit\n";); - continue; + if (fr.m_idx == 0 && n->get_ref_count() > 1) { + if (m_cache.find(n, r)) { + SASSERT(m_result_stack.size() == fr.m_rpos); + m_result_stack.push_back(r); + m_extra_children_stack.shrink(fr.m_cpos); + m_frame_stack.pop_back(); + TRACE("ast_translation", tout << "hit\n";); + m_hit_count++; + continue; + } + else { + m_miss_count++; + } } switch (n->get_kind()) { case AST_VAR: { @@ -227,7 +247,7 @@ ast * ast_translation::process(ast const * _n) { while (fr.m_idx <= num) { expr * arg = to_app(n)->get_arg(fr.m_idx - 1); fr.m_idx++; - if (!visit(arg)) + if (!visit(arg)) goto loop; } func_decl * new_f = to_func_decl(m_result_stack[fr.m_rpos]); diff --git a/src/ast/ast_translation.h b/src/ast/ast_translation.h index b278791d7..d5e29e785 100644 --- a/src/ast/ast_translation.h +++ b/src/ast/ast_translation.h @@ -37,6 +37,11 @@ class ast_translation { ptr_vector m_extra_children_stack; // for sort and func_decl, since they have nested AST in their parameters ptr_vector m_result_stack; obj_map m_cache; + unsigned m_loop_count; + unsigned m_hit_count; + unsigned m_miss_count; + unsigned m_insert_count; + unsigned m_num_process; void cache(ast * s, ast * t); void collect_decl_extra_children(decl * d); @@ -50,25 +55,53 @@ class ast_translation { public: ast_translation(ast_manager & from, ast_manager & to, bool copy_plugins = true) : m_from_manager(from), m_to_manager(to) { - if (copy_plugins) - m_to_manager.copy_families_plugins(m_from_manager); + m_loop_count = 0; + m_hit_count = 0; + m_miss_count = 0; + m_insert_count = 0; + m_num_process = 0; + if (&from != &to) { + if (copy_plugins) + m_to_manager.copy_families_plugins(m_from_manager); + m_to_manager.update_fresh_id(m_from_manager); + } } ~ast_translation(); template T * operator()(T const * n) { + return translate(n); + } + + template + T * translate(T const * n) { + if (&from() == &to()) return const_cast(n); SASSERT(!n || from().contains(const_cast(n))); ast * r = process(n); SASSERT((!n && !r) || to().contains(const_cast(r))); return static_cast(r); } + ast_manager & from() const { return m_from_manager; } ast_manager & to() const { return m_to_manager; } + template + ref_vector operator()(ref_vector const& src) { + ref_vector dst(to()); + for (expr* v : src) dst.push_back(translate(v)); + return dst; + } + void reset_cache(); void cleanup(); + + unsigned loop_count() const { return m_loop_count; } + unsigned hit_count() const { return m_hit_count; } + unsigned miss_count() const { return m_miss_count; } + unsigned insert_count() const { return m_insert_count; } + unsigned long long get_num_collision() const { return m_cache.get_num_collision(); } }; // Translation with non-persistent cache. @@ -80,6 +113,7 @@ inline expr * translate(expr const * e, ast_manager & from, ast_manager & to) { return ast_translation(from, to)(e); } + class expr_dependency_translation { ast_translation & m_translation; ptr_vector m_buffer; diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index e3e7c7167..4869949b7 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -717,7 +717,7 @@ void bv_decl_plugin::get_op_names(svector & op_names, symbol const op_names.push_back(builtin_name("rotate_left",OP_ROTATE_LEFT)); op_names.push_back(builtin_name("rotate_right",OP_ROTATE_RIGHT)); - if (logic == symbol::null || logic == symbol("ALL")) { + if (logic == symbol::null || logic == symbol("ALL") || logic == "QF_FD") { op_names.push_back(builtin_name("bvumul_noovfl",OP_BUMUL_NO_OVFL)); op_names.push_back(builtin_name("bvsmul_noovfl",OP_BSMUL_NO_OVFL)); op_names.push_back(builtin_name("bvsmul_noudfl",OP_BSMUL_NO_UDFL)); diff --git a/src/ast/decl_collector.cpp b/src/ast/decl_collector.cpp index 1baac4515..bec60e61c 100644 --- a/src/ast/decl_collector.cpp +++ b/src/ast/decl_collector.cpp @@ -21,14 +21,13 @@ Revision History: #include "ast/ast_pp.h" void decl_collector::visit_sort(sort * n) { + SASSERT(!m_visited.is_marked(n)); family_id fid = n->get_family_id(); if (m().is_uninterp(n)) m_sorts.push_back(n); - if (fid == m_dt_fid) { + else if (fid == m_dt_fid) { m_sorts.push_back(n); - unsigned num_cnstr = m_dt_util.get_datatype_num_constructors(n); - for (unsigned i = 0; i < num_cnstr; i++) { - func_decl * cnstr = m_dt_util.get_datatype_constructors(n)->get(i); + for (func_decl * cnstr : *m_dt_util.get_datatype_constructors(n)) { m_todo.push_back(cnstr); ptr_vector const & cnstr_acc = *m_dt_util.get_constructor_accessors(cnstr); unsigned num_cas = cnstr_acc.size(); @@ -48,12 +47,15 @@ bool decl_collector::is_bool(sort * s) { } void decl_collector::visit_func(func_decl * n) { - family_id fid = n->get_family_id(); - if (fid == null_family_id) { - if (m_sep_preds && is_bool(n->get_range())) - m_preds.push_back(n); - else - m_decls.push_back(n); + if (!m_visited.is_marked(n)) { + family_id fid = n->get_family_id(); + if (fid == null_family_id) { + if (m_sep_preds && is_bool(n->get_range())) + m_preds.push_back(n); + else + m_decls.push_back(n); + } + m_visited.mark(n, true); } } @@ -72,7 +74,6 @@ void decl_collector::visit(ast* n) { n = m_todo.back(); m_todo.pop_back(); if (!m_visited.is_marked(n)) { - m_visited.mark(n, true); switch(n->get_kind()) { case AST_APP: { app * a = to_app(n); @@ -99,8 +100,8 @@ void decl_collector::visit(ast* n) { break; case AST_FUNC_DECL: { func_decl * d = to_func_decl(n); - for (unsigned i = 0; i < d->get_arity(); ++i) { - m_todo.push_back(d->get_domain(i)); + for (sort* srt : *d) { + m_todo.push_back(srt); } m_todo.push_back(d->get_range()); visit_func(d); @@ -111,6 +112,7 @@ void decl_collector::visit(ast* n) { default: UNREACHABLE(); } + m_visited.mark(n, true); } } } diff --git a/src/ast/decl_collector.h b/src/ast/decl_collector.h index 041438864..07539dbd8 100644 --- a/src/ast/decl_collector.h +++ b/src/ast/decl_collector.h @@ -38,7 +38,6 @@ class decl_collector { void visit_sort(sort* n); bool is_bool(sort* s); - void visit_func(func_decl* n); typedef obj_hashtable sort_set; sort_set* collect_deps(sort* s); @@ -48,9 +47,10 @@ class decl_collector { public: // if preds == true, then predicates are stored in a separate collection. - decl_collector(ast_manager & m, bool preds=true); + decl_collector(ast_manager & m, bool preds = true); ast_manager & m() { return m_manager; } + void visit_func(func_decl* n); void visit(ast * n); void visit(unsigned n, expr* const* es); void visit(expr_ref_vector const& es); diff --git a/src/ast/expr2var.cpp b/src/ast/expr2var.cpp index c6b4257a0..b1fdba2b5 100644 --- a/src/ast/expr2var.cpp +++ b/src/ast/expr2var.cpp @@ -58,11 +58,9 @@ void expr2var::display(std::ostream & out) const { } void expr2var::mk_inv(expr_ref_vector & var2expr) const { - obj_map::iterator it = m_mapping.begin(); - obj_map::iterator end = m_mapping.end(); - for (; it != end; ++it) { - expr * t = it->m_key; - var x = it->m_value; + for (auto & kv : m_mapping) { + expr * t = kv.m_key; + var x = kv.m_value; if (x >= var2expr.size()) var2expr.resize(x+1, nullptr); var2expr.set(x, t); diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index 704de822c..3208a50fe 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -710,7 +710,7 @@ bool pattern_inference_cfg::reduce_quantifier( result = new_q; - IF_IVERBOSE(10, + IF_VERBOSE(10, verbose_stream() << "(smt.inferred-patterns :qid " << q->get_qid() << "\n"; for (unsigned i = 0; i < new_patterns.size(); i++) verbose_stream() << " " << mk_ismt2_pp(new_patterns[i], m, 2) << "\n"; diff --git a/src/ast/pb_decl_plugin.cpp b/src/ast/pb_decl_plugin.cpp index 57c6a0a8c..c8e707cee 100644 --- a/src/ast/pb_decl_plugin.cpp +++ b/src/ast/pb_decl_plugin.cpp @@ -19,6 +19,7 @@ Revision History: #include "ast/pb_decl_plugin.h" #include "ast/ast_util.h" +#include "ast/ast_pp.h" pb_decl_plugin::pb_decl_plugin(): m_at_most_sym("at-most"), @@ -54,7 +55,7 @@ func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p } func_decl_info info(m_family_id, k, 1, parameters); return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info); - } + } case OP_PB_GE: case OP_PB_LE: case OP_PB_EQ: { @@ -128,9 +129,15 @@ app * pb_util::mk_le(unsigned num_args, rational const * coeffs, expr * const * normalize(num_args, coeffs, k); m_params.reset(); m_params.push_back(parameter(floor(m_k))); + bool all_ones = true; for (unsigned i = 0; i < num_args; ++i) { + all_ones &= m_coeffs[i].is_one(); m_params.push_back(parameter(m_coeffs[i])); } + if (all_ones && k.is_unsigned()) { + m_params[0] = parameter(floor(m_k).get_unsigned()); + return m.mk_app(m_fid, OP_AT_MOST_K, 1, m_params.c_ptr(), num_args, args, m.mk_bool_sort()); + } return m.mk_app(m_fid, OP_PB_LE, m_params.size(), m_params.c_ptr(), num_args, args, m.mk_bool_sort()); } @@ -138,9 +145,15 @@ app * pb_util::mk_ge(unsigned num_args, rational const * coeffs, expr * const * normalize(num_args, coeffs, k); m_params.reset(); m_params.push_back(parameter(ceil(m_k))); + bool all_ones = true; for (unsigned i = 0; i < num_args; ++i) { + all_ones &= m_coeffs[i].is_one(); m_params.push_back(parameter(m_coeffs[i])); } + if (all_ones && k.is_unsigned()) { + m_params[0] = parameter(ceil(m_k).get_unsigned()); + return m.mk_app(m_fid, OP_AT_LEAST_K, 1, m_params.c_ptr(), num_args, args, m.mk_bool_sort()); + } return m.mk_app(m_fid, OP_PB_GE, m_params.size(), m_params.c_ptr(), num_args, args, m.mk_bool_sort()); } @@ -149,6 +162,9 @@ app * pb_util::mk_eq(unsigned num_args, rational const * coeffs, expr * const * if (!m_k.is_int()) { return m.mk_false(); } + if (num_args == 0) { + return m_k.is_zero() ? m.mk_true() : m.mk_false(); + } m_params.reset(); m_params.push_back(parameter(m_k)); for (unsigned i = 0; i < num_args; ++i) { diff --git a/src/ast/pb_decl_plugin.h b/src/ast/pb_decl_plugin.h index ea4049e13..aec14a64a 100644 --- a/src/ast/pb_decl_plugin.h +++ b/src/ast/pb_decl_plugin.h @@ -123,7 +123,6 @@ public: app* mk_fresh_bool(); - private: rational to_rational(parameter const& p) const; }; diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 744c8a235..9824884a4 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -800,9 +800,54 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu result = m_util.mk_numeral(div(v1, v2), is_int); return BR_DONE; } + expr_ref quot(m()); + if (divides(arg1, arg2, quot)) { + result = m_util.mk_mul(quot, m_util.mk_idiv(arg1, arg1)); + return BR_REWRITE2; + } return BR_FAILED; } +bool arith_rewriter::divides(expr* d, expr* n, expr_ref& quot) { + if (d == n) { + quot = m_util.mk_numeral(rational(1), m_util.is_int(d)); + return true; + } + if (m_util.is_mul(n)) { + expr_ref_vector muls(m()); + muls.push_back(n); + expr* n1, *n2; + rational r1, r2; + for (unsigned i = 0; i < muls.size(); ++i) { + if (m_util.is_mul(muls[i].get(), n1, n2)) { + muls[i] = n1; + muls.push_back(n2); + --i; + } + } + if (m_util.is_numeral(d, r1) && !r1.is_zero()) { + for (unsigned i = 0; i < muls.size(); ++i) { + if (m_util.is_numeral(muls[i].get(), r2) && (r2 / r1).is_int()) { + muls[i] = m_util.mk_numeral(r2 / r1, m_util.is_int(d)); + quot = m_util.mk_mul(muls.size(), muls.c_ptr()); + return true; + } + } + } + else { + for (unsigned i = 0; i < muls.size(); ++i) { + if (d == muls[i].get()) { + muls[i] = muls.back(); + muls.pop_back(); + quot = m_util.mk_mul(muls.size(), muls.c_ptr()); + return true; + } + } + } + } + return false; +} + br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(m().get_sort(arg1)); numeral v1, v2; diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h index 95668ea44..65fbfb43f 100644 --- a/src/ast/rewriter/arith_rewriter.h +++ b/src/ast/rewriter/arith_rewriter.h @@ -95,6 +95,7 @@ class arith_rewriter : public poly_rewriter { expr_ref neg_monomial(expr * e) const; expr * mk_sin_value(rational const & k); app * mk_sqrt(rational const & k); + bool divides(expr* d, expr* n, expr_ref& quot); public: arith_rewriter(ast_manager & m, params_ref const & p = params_ref()): diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp index ec9eaaa05..ba2c6547d 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp @@ -92,6 +92,8 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { func_decl_ref_vector m_keys; expr_ref_vector m_values; unsigned_vector m_keyval_lim; + func_decl_ref_vector m_newbits; + unsigned_vector m_newbits_lim; bool m_blast_mul; bool m_blast_add; @@ -118,7 +120,8 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { m_out(m), m_bindings(m), m_keys(m), - m_values(m) { + m_values(m), + m_newbits(m) { updt_params(p); } @@ -160,6 +163,7 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { void push() { m_keyval_lim.push_back(m_keys.size()); + m_newbits_lim.push_back(m_newbits.size()); } unsigned get_num_scopes() const { @@ -178,9 +182,27 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { m_keys.resize(lim); m_values.resize(lim); m_keyval_lim.resize(new_sz); + + lim = m_newbits_lim[new_sz]; + m_newbits.shrink(lim); + m_newbits_lim.shrink(new_sz); } } + unsigned m_keypos; + + void start_rewrite() { + m_keypos = m_keys.size(); + } + + void end_rewrite(obj_map& const2bits, ptr_vector & newbits) { + for (unsigned i = m_keypos; i < m_keys.size(); ++i) { + const2bits.insert(m_keys[i].get(), m_values[i].get()); + } + for (func_decl* f : m_newbits) newbits.push_back(f); + + } + template app * mk_mkbv(V const & bits) { return m().mk_app(butil().get_family_id(), OP_MKBV, bits.size(), bits.c_ptr()); @@ -201,6 +223,7 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { m_out.reset(); for (unsigned i = 0; i < bv_size; i++) { m_out.push_back(m().mk_fresh_const(nullptr, b)); + m_newbits.push_back(to_app(m_out.back())->get_decl()); } r = mk_mkbv(m_out); m_const2bits.insert(f, r); @@ -635,6 +658,8 @@ struct bit_blaster_rewriter::imp : public rewriter_tpl { } void push() { m_cfg.push(); } void pop(unsigned s) { m_cfg.pop(s); } + void start_rewrite() { m_cfg.start_rewrite(); } + void end_rewrite(obj_map& const2bits, ptr_vector & newbits) { m_cfg.end_rewrite(const2bits, newbits); } unsigned get_num_scopes() const { return m_cfg.get_num_scopes(); } }; @@ -683,3 +708,10 @@ unsigned bit_blaster_rewriter::get_num_scopes() const { return m_imp->get_num_scopes(); } +void bit_blaster_rewriter::start_rewrite() { + m_imp->start_rewrite(); +} + +void bit_blaster_rewriter::end_rewrite(obj_map& const2bits, ptr_vector & newbits) { + m_imp->end_rewrite(const2bits, newbits); +} diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h index 6ffed00ae..24b6b0c0a 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h @@ -33,11 +33,15 @@ public: ast_manager & m() const; unsigned get_num_steps() const; void cleanup(); - obj_map const& const2bits() const; + void start_rewrite(); + void end_rewrite(obj_map& const2bits, ptr_vector & newbits); void operator()(expr * e, expr_ref & result, proof_ref & result_proof); void push(); void pop(unsigned num_scopes); unsigned get_num_scopes() const; +private: + obj_map const& const2bits() const; + }; #endif diff --git a/src/ast/rewriter/pb2bv_rewriter.cpp b/src/ast/rewriter/pb2bv_rewriter.cpp index 0bdeda475..3862aecae 100644 --- a/src/ast/rewriter/pb2bv_rewriter.cpp +++ b/src/ast/rewriter/pb2bv_rewriter.cpp @@ -25,6 +25,10 @@ Notes: #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "util/lbool.h" +#include "util/uint_set.h" +#include "util/gparams.h" + +const unsigned g_primes[7] = { 2, 3, 5, 7, 11, 13, 17}; struct pb2bv_rewriter::imp { @@ -35,10 +39,12 @@ struct pb2bv_rewriter::imp { func_decl_ref_vector m_fresh; // all fresh variables unsigned_vector m_fresh_lim; unsigned m_num_translated; + unsigned m_compile_bv; + unsigned m_compile_card; struct card2bv_rewriter { - typedef expr* literal; - typedef ptr_vector literal_vector; + typedef expr* pliteral; + typedef ptr_vector pliteral_vector; psort_nw m_sort; ast_manager& m; imp& m_imp; @@ -49,6 +55,9 @@ struct pb2bv_rewriter::imp { expr_ref_vector m_args; rational m_k; vector m_coeffs; + bool m_keep_cardinality_constraints; + symbol m_pb_solver; + unsigned m_min_arity; template expr_ref mk_le_ge(expr_ref_vector& fmls, expr* a, expr* b, expr* bound) { @@ -68,7 +77,28 @@ struct pb2bv_rewriter::imp { fmls.push_back(bv.mk_ule(bound, result)); } return result; + } + typedef std::pair ca; + + struct compare_coeffs { + bool operator()(ca const& x, ca const& y) const { + return x.first > y.first; + } + }; + + void sort_args() { + vector cas; + for (unsigned i = 0; i < m_args.size(); ++i) { + cas.push_back(std::make_pair(m_coeffs[i], expr_ref(m_args[i].get(), m))); + } + std::sort(cas.begin(), cas.end(), compare_coeffs()); + m_coeffs.reset(); + m_args.reset(); + for (ca const& ca : cas) { + m_coeffs.push_back(ca.first); + m_args.push_back(ca.second); + } } // @@ -84,21 +114,25 @@ struct pb2bv_rewriter::imp { // is_le = l_false - >= // template - expr_ref mk_le_ge(unsigned sz, expr * const* args, rational const & k) { + expr_ref mk_le_ge(rational const & k) { + //sort_args(); + unsigned sz = m_args.size(); + expr * const* args = m_args.c_ptr(); TRACE("pb", for (unsigned i = 0; i < sz; ++i) { tout << m_coeffs[i] << "*" << mk_pp(args[i], m) << " "; if (i + 1 < sz && !m_coeffs[i+1].is_neg()) tout << "+ "; } switch (is_le) { - case l_true: tout << "<= "; break; + case l_true: tout << "<= "; break; case l_undef: tout << "= "; break; case l_false: tout << ">= "; break; } - tout << m_k << "\n";); + tout << k << "\n";); + if (k.is_zero()) { if (is_le != l_false) { - return expr_ref(m.mk_not(mk_or(m, sz, args)), m); + return expr_ref(m.mk_not(::mk_or(m_args)), m); } else { return expr_ref(m.mk_true(), m); @@ -107,6 +141,35 @@ struct pb2bv_rewriter::imp { if (k.is_neg()) { return expr_ref((is_le == l_false)?m.mk_true():m.mk_false(), m); } + + if (m_pb_solver == "totalizer") { + expr_ref result(m); + switch (is_le) { + case l_true: if (mk_le_tot(sz, args, k, result)) return result; else break; + case l_false: if (mk_ge_tot(sz, args, k, result)) return result; else break; + case l_undef: break; + } + } + + if (m_pb_solver == "sorting") { + expr_ref result(m); + switch (is_le) { + case l_true: if (mk_le(sz, args, k, result)) return result; else break; + case l_false: if (mk_ge(sz, args, k, result)) return result; else break; + case l_undef: if (mk_eq(sz, args, k, result)) return result; else break; + } + } + + if (m_pb_solver == "segmented") { + expr_ref result(m); + switch (is_le) { + case l_true: return mk_seg_le(k); + case l_false: return mk_seg_ge(k); + case l_undef: break; + } + } + + // fall back to divide and conquer encoding. SASSERT(k.is_pos()); expr_ref zero(m), bound(m); expr_ref_vector es(m), fmls(m); @@ -138,12 +201,12 @@ struct pb2bv_rewriter::imp { } switch (is_le) { case l_true: - return mk_and(fmls); + return ::mk_and(fmls); case l_false: if (!es.empty()) { fmls.push_back(bv.mk_ule(bound, es.back())); } - return mk_or(fmls); + return ::mk_or(fmls); case l_undef: if (es.empty()) { fmls.push_back(m.mk_bool_val(k.is_zero())); @@ -151,35 +214,415 @@ struct pb2bv_rewriter::imp { else { fmls.push_back(m.mk_eq(bound, es.back())); } - return mk_and(fmls); + return ::mk_and(fmls); default: UNREACHABLE(); return expr_ref(m.mk_true(), m); } } + /** + \brief Totalizer encoding. Based on a version by Miguel. + */ + + bool mk_le_tot(unsigned sz, expr * const * args, rational const& _k, expr_ref& result) { + SASSERT(sz == m_coeffs.size()); + if (!_k.is_unsigned() || sz == 0) return false; + unsigned k = _k.get_unsigned(); + expr_ref_vector args1(m); + rational bound; + flip(sz, args, args1, _k, bound); + if (bound.get_unsigned() < k) { + return mk_ge_tot(sz, args1.c_ptr(), bound, result); + } + if (k > 20) { + return false; + } + result = m.mk_not(bounded_addition(sz, args, k + 1)); + TRACE("pb", tout << result << "\n";); + return true; + } + + bool mk_ge_tot(unsigned sz, expr * const * args, rational const& _k, expr_ref& result) { + SASSERT(sz == m_coeffs.size()); + if (!_k.is_unsigned() || sz == 0) return false; + unsigned k = _k.get_unsigned(); + expr_ref_vector args1(m); + rational bound; + flip(sz, args, args1, _k, bound); + if (bound.get_unsigned() < k) { + return mk_le_tot(sz, args1.c_ptr(), bound, result); + } + if (k > 20) { + return false; + } + result = bounded_addition(sz, args, k); + TRACE("pb", tout << result << "\n";); + return true; + } + + void flip(unsigned sz, expr* const* args, expr_ref_vector& args1, rational const& k, rational& bound) { + bound = -k; + for (unsigned i = 0; i < sz; ++i) { + args1.push_back(mk_not(args[i])); + bound += m_coeffs[i]; + } + } + + expr_ref bounded_addition(unsigned sz, expr * const * args, unsigned k) { + SASSERT(sz > 0); + expr_ref result(m); + vector es; + vector coeffs; + for (unsigned i = 0; i < m_coeffs.size(); ++i) { + unsigned_vector v; + expr_ref_vector e(m); + unsigned c = m_coeffs[i].get_unsigned(); + v.push_back(c >= k ? k : c); + e.push_back(args[i]); + es.push_back(e); + coeffs.push_back(v); + } + while (es.size() > 1) { + for (unsigned i = 0; i + 1 < es.size(); i += 2) { + expr_ref_vector o(m); + unsigned_vector oc; + tot_adder(es[i], coeffs[i], es[i + 1], coeffs[i + 1], k, o, oc); + es[i / 2].set(o); + coeffs[i / 2] = oc; + } + if ((es.size() % 2) == 1) { + es[es.size() / 2].set(es.back()); + coeffs[es.size() / 2] = coeffs.back(); + } + es.shrink((1 + es.size())/2); + coeffs.shrink((1 + coeffs.size())/2); + } + SASSERT(coeffs.size() == 1); + SASSERT(coeffs[0].back() <= k); + if (coeffs[0].back() == k) { + result = es[0].back(); + } + else { + result = m.mk_false(); + } + return result; + } + + void tot_adder(expr_ref_vector const& l, unsigned_vector const& lc, + expr_ref_vector const& r, unsigned_vector const& rc, + unsigned k, + expr_ref_vector& o, unsigned_vector & oc) { + SASSERT(l.size() == lc.size()); + SASSERT(r.size() == rc.size()); + uint_set sums; + vector trail; + u_map sum2def; + for (unsigned i = 0; i <= l.size(); ++i) { + for (unsigned j = (i == 0) ? 1 : 0; j <= r.size(); ++j) { + unsigned sum = std::min(k, ((i == 0) ? 0 : lc[i - 1]) + ((j == 0) ? 0 : rc[j - 1])); + sums.insert(sum); + } + } + for (unsigned u : sums) { + oc.push_back(u); + } + std::sort(oc.begin(), oc.end()); + DEBUG_CODE( + for (unsigned i = 0; i + 1 < oc.size(); ++i) { + SASSERT(oc[i] < oc[i+1]); + }); + for (unsigned i = 0; i < oc.size(); ++i) { + sum2def.insert(oc[i], i); + trail.push_back(expr_ref_vector(m)); + } + for (unsigned i = 0; i <= l.size(); ++i) { + for (unsigned j = (i == 0) ? 1 : 0; j <= r.size(); ++j) { + if (i != 0 && j != 0 && (lc[i - 1] >= k || rc[j - 1] >= k)) continue; + unsigned sum = std::min(k, ((i == 0) ? 0 : lc[i - 1]) + ((j == 0) ? 0 : rc[j - 1])); + expr_ref_vector ands(m); + if (i != 0) { + ands.push_back(l[i - 1]); + } + if (j != 0) { + ands.push_back(r[j - 1]); + } + trail[sum2def.find(sum)].push_back(::mk_and(ands)); + } + } + for (unsigned i = 0; i < oc.size(); ++i) { + o.push_back(::mk_or(trail[sum2def.find(oc[i])])); + } + } + + /** + \brief MiniSat+ based encoding of PB constraints. + Translating Pseudo-Boolean Constraints into SAT, + Niklas Een, Niklas Soerensson, JSAT 2006. + */ + + + vector m_min_base; + rational m_min_cost; + vector m_base; + + void create_basis(vector const& seq, rational carry_in, rational cost) { + if (cost >= m_min_cost) { + return; + } + rational delta_cost(0); + for (unsigned i = 0; i < seq.size(); ++i) { + delta_cost += seq[i]; + } + if (cost + delta_cost < m_min_cost) { + m_min_cost = cost + delta_cost; + m_min_base = m_base; + m_min_base.push_back(delta_cost + rational::one()); + } + + for (unsigned i = 0; i < sizeof(g_primes)/sizeof(*g_primes); ++i) { + vector seq1; + rational p(g_primes[i]); + rational rest = carry_in; + // create seq1 + for (unsigned j = 0; j < seq.size(); ++j) { + rest += seq[j] % p; + if (seq[j] >= p) { + seq1.push_back(div(seq[j], p)); + } + } + + m_base.push_back(p); + create_basis(seq1, div(rest, p), cost + rest); + m_base.pop_back(); + } + } + + bool create_basis() { + m_base.reset(); + m_min_cost = rational(INT_MAX); + m_min_base.reset(); + rational cost(0); + create_basis(m_coeffs, rational::zero(), cost); + m_base = m_min_base; + TRACE("pb", + tout << "Base: "; + for (unsigned i = 0; i < m_base.size(); ++i) { + tout << m_base[i] << " "; + } + tout << "\n";); + return + !m_base.empty() && + m_base.back().is_unsigned() && + m_base.back().get_unsigned() <= 20*m_base.size(); + } + + /** + \brief Check if 'out mod n >= lim'. + */ + expr_ref mod_ge(ptr_vector const& out, unsigned n, unsigned lim) { + TRACE("pb", for (unsigned i = 0; i < out.size(); ++i) tout << mk_pp(out[i], m) << " "; tout << "\n"; + tout << "n:" << n << " lim: " << lim << "\n";); + if (lim == n) { + return expr_ref(m.mk_false(), m); + } + if (lim == 0) { + return expr_ref(m.mk_true(), m); + } + SASSERT(0 < lim && lim < n); + expr_ref_vector ors(m); + for (unsigned j = 0; j + lim - 1 < out.size(); j += n) { + expr_ref tmp(m); + tmp = out[j + lim - 1]; + if (j + n - 1 < out.size()) { + tmp = m.mk_and(tmp, m.mk_not(out[j + n - 1])); + } + ors.push_back(tmp); + } + return ::mk_or(ors); + } + + // x0 + 5x1 + 3x2 >= k + // x0 x1 x1 -> s0 s1 s2 + // s2 x1 x2 -> s3 s4 s5 + // k = 7: s5 or (s4 & not s2 & s0) + // k = 6: s4 + // k = 5: s4 or (s3 & not s2 & s1) + // k = 4: s4 or (s3 & not s2 & s0) + // k = 3: s3 + // + bool mk_ge(unsigned sz, expr * const* args, rational bound, expr_ref& result) { + if (!create_basis()) return false; + if (!bound.is_unsigned()) return false; + vector coeffs(m_coeffs); + result = m.mk_true(); + expr_ref_vector carry(m), new_carry(m); + m_base.push_back(bound + rational::one()); + for (rational b_i : m_base) { + unsigned B = b_i.get_unsigned(); + unsigned d_i = (bound % b_i).get_unsigned(); + bound = div(bound, b_i); + for (unsigned j = 0; j < coeffs.size(); ++j) { + rational c = coeffs[j] % b_i; + SASSERT(c.is_unsigned()); + for (unsigned k = 0; k < c.get_unsigned(); ++k) { + carry.push_back(args[j]); + } + coeffs[j] = div(coeffs[j], b_i); + } + TRACE("pb", tout << "Carry: " << carry << "\n"; + for (auto c : coeffs) tout << c << " "; + tout << "\n"; + ); + ptr_vector out; + m_sort.sorting(carry.size(), carry.c_ptr(), out); + + expr_ref gt = mod_ge(out, B, d_i + 1); + expr_ref ge = mod_ge(out, B, d_i); + result = mk_and(ge, result); + result = mk_or(gt, result); + TRACE("pb", tout << "b: " << b_i << " d: " << d_i << " gt: " << gt << " ge: " << ge << " " << result << "\n";); + + new_carry.reset(); + for (unsigned j = B - 1; j < out.size(); j += B) { + new_carry.push_back(out[j]); + } + carry.reset(); + carry.append(new_carry); + } + TRACE("pb", tout << "bound: " << bound << " Carry: " << carry << " result: " << result << "\n";); + return true; + } + + /** + \brief Segment based encoding. + The PB terms are partitoned into segments, such that each segment contains arguments with the same cofficient. + The segments are sorted, such that the segment with highest coefficient is first. + Then for each segment create circuits based on sorting networks the arguments of the segment. + */ + + expr_ref mk_seg_ge(rational const& k) { + rational bound(-k); + for (unsigned i = 0; i < m_args.size(); ++i) { + m_args[i] = mk_not(m_args[i].get()); + bound += m_coeffs[i]; + } + return mk_seg_le(bound); + } + + expr_ref mk_seg_le(rational const& k) { + sort_args(); + unsigned sz = m_args.size(); + expr* const* args = m_args.c_ptr(); + + // Create sorted entries. + vector> outs; + vector coeffs; + for (unsigned i = 0, seg_size = 0; i < sz; i += seg_size) { + seg_size = segment_size(i); + ptr_vector out; + m_sort.sorting(seg_size, args + i, out); + out.push_back(m.mk_false()); + outs.push_back(out); + coeffs.push_back(m_coeffs[i]); + } + return mk_seg_le_rec(outs, coeffs, 0, k); + } + + expr_ref mk_seg_le_rec(vector> const& outs, vector const& coeffs, unsigned i, rational const& k) { + rational const& c = coeffs[i]; + ptr_vector const& out = outs[i]; + if (k.is_neg()) { + return expr_ref(m.mk_false(), m); + } + if (i == outs.size()) { + return expr_ref(m.mk_true(), m); + } + if (i + 1 == outs.size() && k >= rational(out.size()-1)*c) { + return expr_ref(m.mk_true(), m); + } + expr_ref_vector fmls(m); + fmls.push_back(m.mk_implies(m.mk_not(out[0]), mk_seg_le_rec(outs, coeffs, i + 1, k))); + rational k1; + for (unsigned j = 0; j + 1 < out.size(); ++j) { + k1 = k - rational(j+1)*c; + if (k1.is_neg()) { + fmls.push_back(m.mk_not(out[j])); + break; + } + fmls.push_back(m.mk_implies(m.mk_and(out[j], m.mk_not(out[j+1])), mk_seg_le_rec(outs, coeffs, i + 1, k1))); + } + return ::mk_and(fmls); + } + + // The number of arguments with the same coefficient. + unsigned segment_size(unsigned start) const { + unsigned i = start; + while (i < m_args.size() && m_coeffs[i] == m_coeffs[start]) ++i; + return i - start; + } + + expr_ref mk_and(expr_ref& a, expr_ref& b) { + if (m.is_true(a)) return b; + if (m.is_true(b)) return a; + if (m.is_false(a)) return a; + if (m.is_false(b)) return b; + return expr_ref(m.mk_and(a, b), m); + } + + expr_ref mk_or(expr_ref& a, expr_ref& b) { + if (m.is_true(a)) return a; + if (m.is_true(b)) return b; + if (m.is_false(a)) return b; + if (m.is_false(b)) return a; + return expr_ref(m.mk_or(a, b), m); + } + + bool mk_le(unsigned sz, expr * const* args, rational const& k, expr_ref& result) { + expr_ref_vector args1(m); + rational bound(-k); + for (unsigned i = 0; i < sz; ++i) { + args1.push_back(mk_not(args[i])); + bound += m_coeffs[i]; + } + return mk_ge(sz, args1.c_ptr(), bound, result); + } + + bool mk_eq(unsigned sz, expr * const* args, rational const& k, expr_ref& result) { + expr_ref r1(m), r2(m); + if (mk_ge(sz, args, k, r1) && mk_le(sz, args, k, r2)) { + result = m.mk_and(r1, r2); + return true; + } + else { + return false; + } + } + expr_ref mk_bv(func_decl * f, unsigned sz, expr * const* args) { + ++m_imp.m_compile_bv; decl_kind kind = f->get_decl_kind(); rational k = pb.get_k(f); m_coeffs.reset(); + m_args.reset(); for (unsigned i = 0; i < sz; ++i) { m_coeffs.push_back(pb.get_coeff(f, i)); + m_args.push_back(args[i]); } + CTRACE("pb", k.is_neg(), tout << expr_ref(m.mk_app(f, sz, args), m) << "\n";); SASSERT(!k.is_neg()); switch (kind) { case OP_PB_GE: case OP_AT_LEAST_K: { - expr_ref_vector nargs(m); - nargs.append(sz, args); - dualize(f, nargs, k); + dualize(f, m_args, k); SASSERT(!k.is_neg()); - return mk_le_ge(sz, nargs.c_ptr(), k); + return mk_le_ge(k); } case OP_PB_LE: case OP_AT_MOST_K: - return mk_le_ge(sz, args, k); + return mk_le_ge(k); case OP_PB_EQ: - return mk_le_ge(sz, args, k); + return mk_le_ge(k); default: UNREACHABLE(); return expr_ref(m.mk_true(), m); @@ -230,7 +673,7 @@ struct pb2bv_rewriter::imp { public: - card2bv_rewriter(imp& i, ast_manager& m): + card2bv_rewriter(imp& i, ast_manager& m): m_sort(*this), m(m), m_imp(i), @@ -238,29 +681,34 @@ struct pb2bv_rewriter::imp { pb(m), bv(m), m_trail(m), - m_args(m) + m_args(m), + m_keep_cardinality_constraints(false), + m_pb_solver(symbol("solver")), + m_min_arity(9) {} + void set_pb_solver(symbol const& s) { m_pb_solver = s; } + bool mk_app(bool full, func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { - if (f->get_family_id() == pb.get_family_id()) { - mk_pb(full, f, sz, args, result); + if (f->get_family_id() == pb.get_family_id() && mk_pb(full, f, sz, args, result)) { + // skip } else if (au.is_le(f) && is_pb(args[0], args[1])) { - result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + result = mk_le_ge(m_k); } else if (au.is_lt(f) && is_pb(args[0], args[1])) { ++m_k; - result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + result = mk_le_ge(m_k); } else if (au.is_ge(f) && is_pb(args[1], args[0])) { - result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + result = mk_le_ge(m_k); } else if (au.is_gt(f) && is_pb(args[1], args[0])) { ++m_k; - result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + result = mk_le_ge(m_k); } else if (m.is_eq(f) && is_pb(args[0], args[1])) { - result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + result = mk_le_ge(m_k); } else { return false; @@ -278,6 +726,11 @@ struct pb2bv_rewriter::imp { } } + bool mk_app(bool full, expr* e, expr_ref& r) { + app* a; + return (is_app(e) && (a = to_app(e), mk_app(full, a->get_decl(), a->get_num_args(), a->get_args(), r))); + } + bool is_pb(expr* x, expr* y) { m_args.reset(); m_coeffs.reset(); @@ -349,53 +802,95 @@ struct pb2bv_rewriter::imp { return false; } - void mk_pb(bool full, func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { + bool mk_pb(bool full, func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { SASSERT(f->get_family_id() == pb.get_family_id()); if (is_or(f)) { result = m.mk_or(sz, args); } else if (pb.is_at_most_k(f) && pb.get_k(f).is_unsigned()) { + if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.le(full, pb.get_k(f).get_unsigned(), sz, args); + ++m_imp.m_compile_card; } else if (pb.is_at_least_k(f) && pb.get_k(f).is_unsigned()) { + if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.ge(full, pb.get_k(f).get_unsigned(), sz, args); + ++m_imp.m_compile_card; } else if (pb.is_eq(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.eq(full, pb.get_k(f).get_unsigned(), sz, args); + ++m_imp.m_compile_card; } else if (pb.is_le(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.le(full, pb.get_k(f).get_unsigned(), sz, args); + ++m_imp.m_compile_card; } else if (pb.is_ge(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.ge(full, pb.get_k(f).get_unsigned(), sz, args); + ++m_imp.m_compile_card; + } + else if (pb.is_eq(f) && pb.get_k(f).is_unsigned() && has_small_coefficients(f) && m_pb_solver == "solver") { + return false; + } + else if (pb.is_le(f) && pb.get_k(f).is_unsigned() && has_small_coefficients(f) && m_pb_solver == "solver") { + return false; + } + else if (pb.is_ge(f) && pb.get_k(f).is_unsigned() && has_small_coefficients(f) && m_pb_solver == "solver") { + return false; } else { result = mk_bv(f, sz, args); } + TRACE("pb", tout << "full: " << full << " " << expr_ref(m.mk_app(f, sz, args), m) << " " << result << "\n"; + ); + return true; + } + + bool has_small_coefficients(func_decl* f) { + unsigned sz = f->get_arity(); + unsigned sum = 0; + for (unsigned i = 0; i < sz; ++i) { + rational c = pb.get_coeff(f, i); + if (!c.is_unsigned()) return false; + unsigned sum1 = sum + c.get_unsigned(); + if (sum1 < sum) return false; + sum = sum1; + } + return true; } // definitions used for sorting network - literal mk_false() { return m.mk_false(); } - literal mk_true() { return m.mk_true(); } - literal mk_max(literal a, literal b) { return trail(m.mk_or(a, b)); } - literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } - literal mk_not(literal a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } + pliteral mk_false() { return m.mk_false(); } + pliteral mk_true() { return m.mk_true(); } + pliteral mk_max(pliteral a, pliteral b) { return trail(m.mk_or(a, b)); } + pliteral mk_min(pliteral a, pliteral b) { return trail(m.mk_and(a, b)); } + pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } - std::ostream& pp(std::ostream& out, literal lit) { return out << mk_ismt2_pp(lit, m); } + std::ostream& pp(std::ostream& out, pliteral lit) { return out << mk_ismt2_pp(lit, m); } - literal trail(literal l) { + pliteral trail(pliteral l) { m_trail.push_back(l); return l; } - literal fresh() { - expr_ref fr(m.mk_fresh_const("sn", m.mk_bool_sort()), m); + pliteral fresh(char const* n) { + expr_ref fr(m.mk_fresh_const(n, m.mk_bool_sort()), m); m_imp.m_fresh.push_back(to_app(fr)->get_decl()); return trail(fr); } - void mk_clause(unsigned n, literal const* lits) { - m_imp.m_lemmas.push_back(mk_or(m, n, lits)); + void mk_clause(unsigned n, pliteral const* lits) { + m_imp.m_lemmas.push_back(::mk_or(m, n, lits)); } + + void keep_cardinality_constraints(bool f) { + m_keep_cardinality_constraints = f; + } + + void set_at_most1(sorting_network_encoding enc) { m_sort.cfg().m_encoding = enc; } + }; struct card2bv_rewriter_cfg : public default_rewriter_cfg { @@ -407,6 +902,10 @@ struct pb2bv_rewriter::imp { return m_r.mk_app_core(f, num, args, result); } card2bv_rewriter_cfg(imp& i, ast_manager & m):m_r(i, m) {} + void keep_cardinality_constraints(bool f) { m_r.keep_cardinality_constraints(f); } + void set_pb_solver(symbol const& s) { m_r.set_pb_solver(s); } + void set_at_most1(sorting_network_encoding enc) { m_r.set_at_most1(enc); } + }; class card_pb_rewriter : public rewriter_tpl { @@ -415,22 +914,77 @@ struct pb2bv_rewriter::imp { card_pb_rewriter(imp& i, ast_manager & m): rewriter_tpl(m, false, m_cfg), m_cfg(i, m) {} + void keep_cardinality_constraints(bool f) { m_cfg.keep_cardinality_constraints(f); } + void set_pb_solver(symbol const& s) { m_cfg.set_pb_solver(s); } + void set_at_most1(sorting_network_encoding e) { m_cfg.set_at_most1(e); } + void rewrite(bool full, expr* e, expr_ref& r, proof_ref& p) { + expr_ref ee(e, m()); + if (m_cfg.m_r.mk_app(full, e, r)) { + ee = r; + // mp proof? + } + (*this)(ee, r, p); + } }; card_pb_rewriter m_rw; + bool keep_cardinality() const { + params_ref const& p = m_params; + return + p.get_bool("keep_cardinality_constraints", false) || + p.get_bool("sat.cardinality.solver", false) || + p.get_bool("cardinality.solver", false) || + gparams::get_module("sat").get_bool("cardinality.solver", false); + } + + symbol pb_solver() const { + params_ref const& p = m_params; + symbol s = p.get_sym("sat.pb.solver", symbol()); + if (s != symbol()) return s; + s = p.get_sym("pb.solver", symbol()); + if (s != symbol()) return s; + return gparams::get_module("sat").get_sym("pb.solver", symbol("solver")); + } + + sorting_network_encoding atmost1_encoding() const { + symbol enc = m_params.get_sym("atmost1_encoding", symbol()); + if (enc == symbol()) { + enc = gparams::get_module("sat").get_sym("atmost1_encoding", symbol()); + } + if (enc == symbol("grouped")) return sorting_network_encoding::grouped_at_most_1; + if (enc == symbol("bimander")) return sorting_network_encoding::bimander_at_most_1; + if (enc == symbol("ordered")) return sorting_network_encoding::ordered_at_most_1; + return grouped_at_most_1; + } + + imp(ast_manager& m, params_ref const& p): m(m), m_params(p), m_lemmas(m), m_fresh(m), m_num_translated(0), m_rw(*this, m) { + updt_params(p); + m_compile_bv = 0; + m_compile_card = 0; + } + + void updt_params(params_ref const & p) { + m_params.append(p); + m_rw.keep_cardinality_constraints(keep_cardinality()); + m_rw.set_pb_solver(pb_solver()); + m_rw.set_at_most1(atmost1_encoding()); + } + void collect_param_descrs(param_descrs& r) const { + r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: true) retain cardinality constraints (don't bit-blast them) and use built-in cardinality solver"); + r.insert("pb.solver", CPK_SYMBOL, "(default: solver) retain pb constraints (don't bit-blast them) and use built-in pb solver"); } - void updt_params(params_ref const & p) {} unsigned get_num_steps() const { return m_rw.get_num_steps(); } void cleanup() { m_rw.cleanup(); } - void operator()(expr * e, expr_ref & result, proof_ref & result_proof) { - m_rw(e, result, result_proof); + void operator()(bool full, expr * e, expr_ref & result, proof_ref & result_proof) { + // m_rw(e, result, result_proof); + m_rw.rewrite(full, e, result, result_proof); } void push() { m_fresh_lim.push_back(m_fresh.size()); @@ -453,6 +1007,8 @@ struct pb2bv_rewriter::imp { } void collect_statistics(statistics & st) const { + st.update("pb-compile-bv", m_compile_bv); + st.update("pb-compile-card", m_compile_card); st.update("pb-aux-variables", m_fresh.size()); st.update("pb-aux-clauses", m_rw.m_cfg.m_r.m_sort.m_stats.m_num_compiled_clauses); } @@ -463,11 +1019,13 @@ struct pb2bv_rewriter::imp { pb2bv_rewriter::pb2bv_rewriter(ast_manager & m, params_ref const& p) { m_imp = alloc(imp, m, p); } pb2bv_rewriter::~pb2bv_rewriter() { dealloc(m_imp); } void pb2bv_rewriter::updt_params(params_ref const & p) { m_imp->updt_params(p); } +void pb2bv_rewriter::collect_param_descrs(param_descrs& r) const { m_imp->collect_param_descrs(r); } + ast_manager & pb2bv_rewriter::m() const { return m_imp->m; } unsigned pb2bv_rewriter::get_num_steps() const { return m_imp->get_num_steps(); } void pb2bv_rewriter::cleanup() { ast_manager& mgr = m(); params_ref p = m_imp->m_params; dealloc(m_imp); m_imp = alloc(imp, mgr, p); } func_decl_ref_vector const& pb2bv_rewriter::fresh_constants() const { return m_imp->m_fresh; } -void pb2bv_rewriter::operator()(expr * e, expr_ref & result, proof_ref & result_proof) { (*m_imp)(e, result, result_proof); } +void pb2bv_rewriter::operator()(bool full, expr * e, expr_ref & result, proof_ref & result_proof) { (*m_imp)(full, e, result, result_proof); } void pb2bv_rewriter::push() { m_imp->push(); } void pb2bv_rewriter::pop(unsigned num_scopes) { m_imp->pop(num_scopes); } void pb2bv_rewriter::flush_side_constraints(expr_ref_vector& side_constraints) { m_imp->flush_side_constraints(side_constraints); } diff --git a/src/ast/rewriter/pb2bv_rewriter.h b/src/ast/rewriter/pb2bv_rewriter.h index a4176922a..3460f08ab 100644 --- a/src/ast/rewriter/pb2bv_rewriter.h +++ b/src/ast/rewriter/pb2bv_rewriter.h @@ -31,11 +31,12 @@ public: ~pb2bv_rewriter(); void updt_params(params_ref const & p); + void collect_param_descrs(param_descrs& r) const; ast_manager & m() const; unsigned get_num_steps() const; void cleanup(); func_decl_ref_vector const& fresh_constants() const; - void operator()(expr * e, expr_ref & result, proof_ref & result_proof); + void operator()(bool full, expr * e, expr_ref & result, proof_ref & result_proof); void push(); void pop(unsigned num_scopes); void flush_side_constraints(expr_ref_vector& side_constraints); diff --git a/src/ast/rewriter/pb_rewriter.cpp b/src/ast/rewriter/pb_rewriter.cpp index 5660f9d65..cb42052b9 100644 --- a/src/ast/rewriter/pb_rewriter.cpp +++ b/src/ast/rewriter/pb_rewriter.cpp @@ -115,14 +115,15 @@ expr_ref pb_rewriter::translate_pb2lia(obj_map& vars, expr* fml) { else { tmp = a.mk_add(es.size(), es.c_ptr()); } + rational k = util.get_k(fml); if (util.is_le(fml)) { - result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false)); + result = a.mk_le(tmp, a.mk_numeral(k, false)); } else if (util.is_ge(fml)) { - result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false)); + result = a.mk_ge(tmp, a.mk_numeral(k, false)); } else { - result = m().mk_eq(tmp, a.mk_numeral(util.get_k(fml), false)); + result = m().mk_eq(tmp, a.mk_numeral(k, false)); } } else { @@ -233,6 +234,7 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons } bool is_eq = f->get_decl_kind() == OP_PB_EQ; + br_status st = BR_DONE; pb_ast_rewriter_util pbu(m); pb_rewriter_util util(pbu); @@ -250,29 +252,73 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons default: { bool all_unit = true; unsigned sz = vec.size(); + rational slack(0); m_args.reset(); m_coeffs.reset(); - for (unsigned i = 0; i < sz; ++i) { - m_args.push_back(vec[i].first); - m_coeffs.push_back(vec[i].second); + for (auto const& kv : vec) { + m_args.push_back(kv.first); + m_coeffs.push_back(kv.second); + SASSERT(kv.second.is_pos()); + slack += kv.second; all_unit &= m_coeffs.back().is_one(); } if (is_eq) { if (sz == 0) { result = k.is_zero()?m.mk_true():m.mk_false(); } + else if (k.is_zero()) { + result = mk_not(m, mk_or(m, sz, m_args.c_ptr())); + } + else if (k.is_one() && all_unit && m_args.size() == 1) { + result = m_args.back(); + } + else if (slack == k) { + result = mk_and(m, sz, m_args.c_ptr()); + } else { result = m_util.mk_eq(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k); } } - else if (all_unit && k.is_one()) { + else if (all_unit && k.is_one() && sz < 10) { result = mk_or(m, sz, m_args.c_ptr()); } else if (all_unit && k == rational(sz)) { result = mk_and(m, sz, m_args.c_ptr()); } else { - result = m_util.mk_ge(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k); + expr_ref_vector conj(m), disj(m); + unsigned j = 0; + sz = m_args.size(); + for (unsigned i = 0; i < sz; ++i) { + rational& c = m_coeffs[i]; + if (slack < c + k) { + conj.push_back(m_args[i]); + slack -= c; + k -= c; + } + else if (c >= k && k.is_pos()) { + disj.push_back(m_args[i]); + } + else { + m_args[j] = m_args[i]; + m_coeffs[j] = m_coeffs[i]; + ++j; + } + } + m_args.shrink(j); + m_coeffs.shrink(j); + sz = j; + if (sz > 0) { + disj.push_back(m_util.mk_ge(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k)); + } + if (!disj.empty()) { + conj.push_back(mk_or(disj)); + } + result = mk_and(conj); + + if (disj.size() > 1 || conj.size() > 1) { + st = BR_REWRITE3; + } } break; } @@ -283,11 +329,11 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons tout << tmp << "\n"; tout << result << "\n"; ); - + TRACE("pb_validate", validate_rewrite(f, num_args, args, result);); - return BR_DONE; + return st; } diff --git a/src/ast/rewriter/pb_rewriter_def.h b/src/ast/rewriter/pb_rewriter_def.h index aa2c2a61f..e4a7e012d 100644 --- a/src/ast/rewriter/pb_rewriter_def.h +++ b/src/ast/rewriter/pb_rewriter_def.h @@ -45,25 +45,25 @@ void pb_rewriter_util::unique(typename PBU::args_t& args, typename PBU::num } } // remove constants - for (unsigned i = 0; i < args.size(); ++i) { + unsigned j = 0, sz = args.size(); + for (unsigned i = 0; i < sz; ++i) { if (m_util.is_true(args[i].first)) { k -= args[i].second; - std::swap(args[i], args[args.size()-1]); - args.pop_back(); - --i; } else if (m_util.is_false(args[i].first)) { - std::swap(args[i], args[args.size()-1]); - args.pop_back(); - --i; + // no-op + } + else { + args[j++] = args[i]; } } + args.shrink(j); // sort and coalesce arguments: typename PBU::compare cmp; std::sort(args.begin(), args.end(), cmp); // coallesce - unsigned i, j; + unsigned i; for (i = 0, j = 1; j < args.size(); ++j) { if (args[i].first == args[j].first) { args[i].second += args[j].second; diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h index f27c9ffcf..d65960857 100644 --- a/src/ast/rewriter/poly_rewriter_def.h +++ b/src/ast/rewriter/poly_rewriter_def.h @@ -431,7 +431,8 @@ struct poly_rewriter::hoist_cmul_lt { hoist_cmul_lt(poly_rewriter & r):m_r(r) {} bool operator()(expr * t1, expr * t2) const { - expr * pp1, * pp2; + expr * pp1 = nullptr; + expr * pp2 = nullptr; numeral c1, c2; bool is_mul1 = m_r.is_mul(t1, c1, pp1); bool is_mul2 = m_r.is_mul(t2, c2, pp2); diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index dfb6542d6..4d4c4f708 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -336,9 +336,9 @@ void rewriter_tpl::process_app(app * t, frame & fr) { UNREACHABLE(); } // TODO: add rewrite rules support - expr * def; - proof * def_pr; - quantifier * def_q; + expr * def = nullptr; + proof * def_pr = nullptr; + quantifier * def_q = nullptr; // When get_macro succeeds, then // we know that: // forall X. f(X) = def[X] diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index 95b043bd7..c96096c65 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -89,6 +89,7 @@ public: ~re2automaton(); eautomaton* operator()(expr* e); void set_solver(expr_solver* solver); + bool has_solver() const { return m_solver; } eautomaton* mk_product(eautomaton *a1, eautomaton *a2); }; diff --git a/src/cmd_context/CMakeLists.txt b/src/cmd_context/CMakeLists.txt index f7b888343..8da871f9a 100644 --- a/src/cmd_context/CMakeLists.txt +++ b/src/cmd_context/CMakeLists.txt @@ -8,14 +8,12 @@ z3_add_component(cmd_context context_params.cpp echo_tactic.cpp eval_cmd.cpp - interpolant_cmds.cpp parametric_cmd.cpp pdecl.cpp simplify_cmd.cpp tactic_cmds.cpp tactic_manager.cpp COMPONENT_DEPENDENCIES - interp rewriter solver EXTRA_REGISTER_MODULE_HEADERS diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 4e8806d62..1d8fdb3de 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -105,19 +105,19 @@ public: char const * get_descr(cmd_context & ctx) const override { return "retrieve model for the last check-sat command.\nSupply optional index if retrieving a model corresponding to a box optimization objective"; } + unsigned get_arity() const override { return VAR_ARITY; } cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_UINT; } void set_next_arg(cmd_context & ctx, unsigned index) override { m_index = index; } void execute(cmd_context & ctx) override { - if (!ctx.is_model_available() || ctx.get_check_sat_result() == nullptr) - throw cmd_exception("model is not available"); model_ref m; + if (ctx.ignore_check()) + return; + if (!ctx.is_model_available(m) || !ctx.get_check_sat_result()) + throw cmd_exception("model is not available"); if (m_index > 0 && ctx.get_opt()) { ctx.get_opt()->get_box_model(m, m_index); } - else { - ctx.get_check_sat_result()->get_model(m); - } ctx.display_model(m); } void reset(cmd_context& ctx) override { @@ -127,10 +127,9 @@ public: ATOMIC_CMD(get_assignment_cmd, "get-assignment", "retrieve assignment", { - if (!ctx.is_model_available() || ctx.get_check_sat_result() == 0) - throw cmd_exception("model is not available"); model_ref m; - ctx.get_check_sat_result()->get_model(m); + if (!ctx.is_model_available(m) || ctx.get_check_sat_result() == 0) + throw cmd_exception("model is not available"); ctx.regular_stream() << "("; dictionary const & macros = ctx.get_macros(); bool first = true; @@ -395,7 +394,7 @@ class set_option_cmd : public set_get_option_cmd { env_params::updt_params(); ctx.global_params_updated(); } - catch (gparams::exception ex) { + catch (const gparams::exception & ex) { throw cmd_exception(ex.msg()); } } @@ -620,7 +619,7 @@ public: try { ctx.regular_stream() << gparams::get_value(opt) << std::endl; } - catch (gparams::exception ex) { + catch (const gparams::exception &) { ctx.print_unsupported(opt, m_line, m_pos); } } diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 9f93e1b93..0bb4b091e 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -43,9 +43,9 @@ Notes: #include "model/model_v2_pp.h" #include "model/model_params.hpp" #include "tactic/tactic_exception.h" +#include "tactic/generic_model_converter.h" #include "solver/smt_logics.h" #include "cmd_context/basic_cmds.h" -#include "cmd_context/interpolant_cmds.h" #include "cmd_context/cmd_context.h" func_decls::func_decls(ast_manager & m, func_decl * f): @@ -396,7 +396,7 @@ protected: datalog::dl_decl_util m_dlutil; format_ns::format * pp_fdecl_name(symbol const & s, func_decls const & fs, func_decl * f, unsigned & len) { - format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len); + format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len, f->is_skolem()); if (!fs.more_than_one()) return f_name; if (!fs.clash(f)) @@ -406,7 +406,7 @@ protected: format_ns::format * pp_fdecl_ref_core(symbol const & s, func_decls const & fs, func_decl * f) { unsigned len; - format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len); + format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len, f->is_skolem()); if (!fs.more_than_one()) return f_name; return pp_signature(f_name, f); @@ -483,7 +483,6 @@ cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l): install_basic_cmds(*this); install_ext_basic_cmds(*this); install_core_tactic_cmds(*this); - install_interpolant_cmds(*this); SASSERT(m != 0 || !has_manager()); if (m_main_ctx) { set_verbose_stream(diagnostic_stream()); @@ -498,6 +497,7 @@ cmd_context::~cmd_context() { finalize_tactic_cmds(); finalize_probes(); reset(true); + m_mc0 = nullptr; m_solver = nullptr; m_check_sat_result = nullptr; } @@ -870,6 +870,23 @@ void cmd_context::insert(symbol const & s, object_ref * r) { m_object_refs.insert(s, r); } +void cmd_context::model_add(symbol const & s, unsigned arity, sort *const* domain, expr * t) { + if (!m_mc0.get()) m_mc0 = alloc(generic_model_converter, m(), "cmd_context"); + if (m_solver.get() && !m_solver->mc0()) m_solver->set_model_converter(m_mc0.get()); + func_decl_ref fn(m().mk_func_decl(s, arity, domain, m().get_sort(t)), m()); + dictionary::entry * e = m_func_decls.insert_if_not_there2(s, func_decls()); + func_decls & fs = e->get_data().m_value; + fs.insert(m(), fn); + VERIFY(fn->get_range() == m().get_sort(t)); + m_mc0->add(fn, t); +} + +void cmd_context::model_del(func_decl* f) { + if (!m_mc0.get()) m_mc0 = alloc(generic_model_converter, m(), "cmd_context"); + if (m_solver.get() && !m_solver->mc0()) m_solver->set_model_converter(m_mc0.get()); + m_mc0->hide(f); +} + void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* e) { expr_ref eq(m()); app_ref lhs(m()); @@ -1256,8 +1273,8 @@ void cmd_context::reset(bool finalize) { reset_macros(); reset_func_decls(); restore_assertions(0); - if (m_solver) - m_solver = nullptr; + m_solver = nullptr; + m_mc0 = nullptr; m_scopes.reset(); m_opt = nullptr; m_pp_env = nullptr; @@ -1412,7 +1429,8 @@ void cmd_context::restore_assertions(unsigned old_sz) { SASSERT(m_assertions.empty()); return; } - if (old_sz == m_assertions.size()) return; + if (old_sz == m_assertions.size()) + return; SASSERT(old_sz < m_assertions.size()); SASSERT(!m_interactive_mode || m_assertions.size() == m_assertion_strings.size()); restore(m(), m_assertions, old_sz); @@ -1500,12 +1518,20 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions scoped_rlimit _rlimit(m().limit(), rlimit); try { r = m_solver->check_sat(num_assumptions, assumptions); + if (r == l_undef && m().canceled()) { + m_solver->set_reason_unknown(eh); + } } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { - m_solver->set_reason_unknown(ex.msg()); + if (m().canceled()) { + m_solver->set_reason_unknown(eh); + } + else { + m_solver->set_reason_unknown(ex.msg()); + } r = l_undef; } m_solver->set_status(r); @@ -1517,7 +1543,6 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions } display_sat_result(r); if (r == l_true) { - complete_model(); validate_model(); } validate_check_sat_result(r); @@ -1525,9 +1550,8 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions // get_opt()->display_assignment(regular_stream()); } - if (r == l_true && m_params.m_dump_models) { - model_ref md; - get_check_sat_result()->get_model(md); + model_ref md; + if (r == l_true && m_params.m_dump_models && is_model_available(md)) { display_model(md); } } @@ -1600,6 +1624,7 @@ void cmd_context::display_dimacs() { void cmd_context::display_model(model_ref& mdl) { if (mdl) { + if (m_mc0) (*m_mc0)(mdl); model_params p; if (p.v1() || p.v2()) { std::ostringstream buffer; @@ -1691,14 +1716,10 @@ struct contains_underspecified_op_proc { /** \brief Complete the model if necessary. */ -void cmd_context::complete_model() { - if (!is_model_available() || - gparams::get_value("model.completion") != "true") +void cmd_context::complete_model(model_ref& md) const { + if (gparams::get_value("model.completion") != "true" || !md.get()) return; - model_ref md; - get_check_sat_result()->get_model(md); - SASSERT(md.get() != 0); params_ref p; p.set_uint("max_degree", UINT_MAX); // evaluate algebraic numbers of any degree. p.set_uint("sort_store", true); @@ -1761,12 +1782,11 @@ void cmd_context::complete_model() { \brief Check if the current model satisfies the quantifier free formulas. */ void cmd_context::validate_model() { + model_ref md; if (!validate_model_enabled()) return; - if (!is_model_available()) + if (!is_model_available(md)) return; - model_ref md; - get_check_sat_result()->get_model(md); SASSERT(md.get() != 0); params_ref p; p.set_uint("max_degree", UINT_MAX); // evaluate algebraic numbers of any degree. @@ -1807,6 +1827,7 @@ void cmd_context::validate_model() { continue; } TRACE("model_validate", model_smt2_pp(tout, *this, *(md.get()), 0);); + IF_VERBOSE(10, verbose_stream() << "model check failed on: " << mk_pp(a, m()) << "\n";); invalid_model = true; } } @@ -1892,12 +1913,12 @@ void cmd_context::display_assertions() { regular_stream() << ")" << std::endl; } -bool cmd_context::is_model_available() const { +bool cmd_context::is_model_available(model_ref& md) const { if (produce_models() && has_manager() && (cs_state() == css_sat || cs_state() == css_unknown)) { - model_ref md; get_check_sat_result()->get_model(md); + complete_model(md); return md.get() != nullptr; } return false; @@ -1925,7 +1946,7 @@ void cmd_context::pp(expr * n, format_ns::format_ref & r) const { } void cmd_context::pp(func_decl * f, format_ns::format_ref & r) const { - mk_smt2_format(f, get_pp_env(), params_ref(), r); + mk_smt2_format(f, get_pp_env(), params_ref(), r, "declare-fun"); } void cmd_context::display(std::ostream & out, sort * s, unsigned indent) const { diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 90b3f6c56..a0093191d 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -23,20 +23,21 @@ Notes: #include #include -#include "ast/ast.h" -#include "ast/ast_printer.h" -#include "cmd_context/pdecl.h" -#include "util/dictionary.h" -#include "solver/solver.h" -#include "ast/datatype_decl_plugin.h" #include "util/stopwatch.h" #include "util/cmd_context_types.h" #include "util/event_handler.h" #include "util/sexpr.h" +#include "util/dictionary.h" +#include "util/scoped_ptr_vector.h" +#include "ast/ast.h" +#include "ast/ast_printer.h" +#include "ast/datatype_decl_plugin.h" +#include "tactic/generic_model_converter.h" +#include "solver/solver.h" +#include "solver/progress_callback.h" +#include "cmd_context/pdecl.h" #include "cmd_context/tactic_manager.h" #include "cmd_context/check_logic.h" -#include "solver/progress_callback.h" -#include "util/scoped_ptr_vector.h" #include "cmd_context/context_params.h" @@ -194,6 +195,7 @@ protected: static std::ostringstream g_error_stream; + generic_model_converter_ref m_mc0; ast_manager * m_manager; bool m_own_manager; bool m_manager_initialized; @@ -323,6 +325,7 @@ public: void set_numeral_as_real(bool f) { m_numeral_as_real = f; } void set_interactive_mode(bool flag) { m_interactive_mode = flag; } void set_ignore_check(bool flag) { m_ignore_check = flag; } + bool ignore_check() const { return m_ignore_check; } void set_exit_on_error(bool flag) { m_exit_on_error = flag; } bool exit_on_error() const { return m_exit_on_error; } bool interactive_mode() const { return m_interactive_mode; } @@ -363,7 +366,7 @@ public: void set_check_sat_result(check_sat_result * r) { m_check_sat_result = r; } check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); } check_sat_state cs_state() const; - void complete_model(); + void complete_model(model_ref& mdl) const; void validate_model(); void display_model(model_ref& mdl); @@ -382,6 +385,8 @@ public: void insert_user_tactic(symbol const & s, sexpr * d); void insert_aux_pdecl(pdecl * p); void insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* e); + void model_add(symbol const & s, unsigned arity, sort *const* domain, expr * t); + void model_del(func_decl* f); func_decl * find_func_decl(symbol const & s) const; func_decl * find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, unsigned arity, sort * const * domain, sort * range) const; @@ -441,7 +446,9 @@ public: dictionary const & get_macros() const { return m_macros; } - bool is_model_available() const; + model_converter* get_model_converter() { return m_mc0.get(); } + + bool is_model_available(model_ref& md) const; double get_seconds() const { return m_watch.get_seconds(); } diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index 85ac2274b..ff39907da 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -135,7 +135,7 @@ void context_params::set(char const * param, char const * value) { } void context_params::updt_params() { - updt_params(gparams::get()); + updt_params(gparams::get_ref()); } void context_params::updt_params(params_ref const & p) { diff --git a/src/cmd_context/echo_tactic.cpp b/src/cmd_context/echo_tactic.cpp index b511d509f..1ea556bef 100644 --- a/src/cmd_context/echo_tactic.cpp +++ b/src/cmd_context/echo_tactic.cpp @@ -27,18 +27,15 @@ class echo_tactic : public skip_tactic { public: echo_tactic(cmd_context & ctx, char const * msg, bool newline):m_ctx(ctx), m_msg(msg), m_newline(newline) {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { #pragma omp critical (echo_tactic) { m_ctx.regular_stream() << m_msg; if (m_newline) m_ctx.regular_stream() << std::endl; } - skip_tactic::operator()(in, result, mc, pc, core); + skip_tactic::operator()(in, result); } }; @@ -61,11 +58,8 @@ public: m_p->dec_ref(); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { double val = (*m_p)(*(in.get())).get_value(); #pragma omp critical (probe_value_tactic) { @@ -75,7 +69,7 @@ public: if (m_newline) m_ctx.diagnostic_stream() << std::endl; } - skip_tactic::operator()(in, result, mc, pc, core); + skip_tactic::operator()(in, result); } }; diff --git a/src/cmd_context/eval_cmd.cpp b/src/cmd_context/eval_cmd.cpp index d4004fca8..3f564edff 100644 --- a/src/cmd_context/eval_cmd.cpp +++ b/src/cmd_context/eval_cmd.cpp @@ -56,16 +56,14 @@ public: } void execute(cmd_context & ctx) override { - if (!ctx.is_model_available()) + model_ref md; + if (!ctx.is_model_available(md)) throw cmd_exception("model is not available"); if (!m_target) throw cmd_exception("no arguments passed to eval"); - model_ref md; unsigned index = m_params.get_uint("model_index", 0); - check_sat_result * last_result = ctx.get_check_sat_result(); - SASSERT(last_result); if (index == 0 || !ctx.get_opt()) { - last_result->get_model(md); + // already have model. } else { ctx.get_opt()->get_box_model(md, index); diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 465bcb956..fa1d56fe2 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -271,7 +271,7 @@ UNARY_CMD(elim_unused_vars_cmd, "dbg-elim-unused-vars", "", "eliminate unu return; } expr_ref r(ctx.m()); - elim_unused_vars(ctx.m(), to_quantifier(arg), gparams::get(), r); + elim_unused_vars(ctx.m(), to_quantifier(arg), gparams::get_ref(), r); SASSERT(!is_quantifier(r) || !to_quantifier(r)->may_have_unused_vars()); ctx.display(ctx.regular_stream(), r); ctx.regular_stream() << std::endl; diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp deleted file mode 100644 index dd1d0acec..000000000 --- a/src/cmd_context/interpolant_cmds.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/*++ - Copyright (c) 2013 Microsoft Corporation - - Module Name: - - interpolant_cmds.cpp - - Abstract: - Commands for interpolation. - - Author: - - Leonardo (leonardo) 2011-12-23 - - Notes: - - --*/ -#include -#include "cmd_context/cmd_context.h" -#include "cmd_context/cmd_util.h" -#include "util/scoped_timer.h" -#include "util/scoped_ctrl_c.h" -#include "util/cancel_eh.h" -#include "ast/ast_pp.h" -#include "ast/ast_smt_pp.h" -#include "ast/ast_smt2_pp.h" -#include "cmd_context/parametric_cmd.h" -#include "util/mpq.h" -#include "ast/expr2var.h" -#include "ast/pp.h" -#include "interp/iz3interp.h" -#include "interp/iz3checker.h" -#include "interp/iz3profiling.h" -#include "interp/interp_params.hpp" -#include "ast/scoped_proof.h" - -static void show_interpolant_and_maybe_check(cmd_context & ctx, - ptr_vector &cnsts, - expr *t, - ptr_vector &interps, - params_ref &m_params, - bool check) -{ - - if (m_params.get_bool("som", false)) - m_params.set_bool("flat", true); - th_rewriter s(ctx.m(), m_params); - - ctx.regular_stream() << "(interpolants"; - for(unsigned i = 0; i < interps.size(); i++){ - expr_ref r(ctx.m()); - proof_ref pr(ctx.m()); - s(to_expr(interps[i]),r,pr); - ctx.regular_stream() << "\n " << r; - } - ctx.regular_stream() << ")\n"; - - s.cleanup(); - - // verify, for the paranoid... - if(check || interp_params(m_params).check()){ - std::ostringstream err; - ast_manager &_m = ctx.m(); - - // need a solver -- make one here FIXME is this right? - bool proofs_enabled, models_enabled, unsat_core_enabled; - params_ref p; - ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled); - scoped_ptr sp = (ctx.get_solver_factory())(_m, p, false, true, false, ctx.get_logic()); - - if(iz3check(_m,sp.get(),err,cnsts,t,interps)) - ctx.regular_stream() << "correct\n"; - else - ctx.regular_stream() << "incorrect: " << err.str().c_str() << "\n"; - } - - for(unsigned i = 0; i < interps.size(); i++){ - ctx.m().dec_ref(interps[i]); - } - - interp_params itp_params(m_params); - if(itp_params.profile()) - profiling::print(ctx.regular_stream()); - -} - -static void check_can_interpolate(cmd_context & ctx){ - if (!ctx.produce_interpolants()) - throw cmd_exception("interpolation is not enabled, use command (set-option :produce-interpolants true)"); -} - - -static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ref &m_params, bool check) { - - check_can_interpolate(ctx); - - // get the proof, if there is one - - if (!ctx.has_manager() || - ctx.cs_state() != cmd_context::css_unsat) - throw cmd_exception("proof is not available"); - expr_ref pr(ctx.m()); - pr = ctx.get_check_sat_result()->get_proof(); - if (pr == 0) - throw cmd_exception("proof is not available"); - - // get the assertions from the context - - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); - ptr_vector cnsts((unsigned)(end - it)); - for (int i = 0; it != end; ++it, ++i) - cnsts[i] = *it; - - // compute an interpolant - - ptr_vector interps; - - try { - iz3interpolate(ctx.m(),pr.get(),cnsts,t,interps,nullptr); - } - catch (iz3_bad_tree &) { - throw cmd_exception("interpolation pattern contains non-asserted formula"); - } - catch (iz3_incompleteness &) { - throw cmd_exception("incompleteness in interpolator"); - } - - show_interpolant_and_maybe_check(ctx, cnsts, t, interps, m_params, check); -} - -static void get_interpolant(cmd_context & ctx, expr * t, params_ref &m_params) { - get_interpolant_and_maybe_check(ctx,t,m_params,false); -} - -#if 0 -static void get_and_check_interpolant(cmd_context & ctx, params_ref &m_params, expr * t) { - get_interpolant_and_maybe_check(ctx,t,m_params,true); -} -#endif - -static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ref &m_params, bool check){ - - // create a fresh solver suitable for interpolation - bool proofs_enabled, models_enabled, unsat_core_enabled; - params_ref p; - ast_manager &_m = ctx.m(); - // TODO: the following is a HACK to enable proofs in the old smt solver - // When we stop using that solver, this hack can be removed - scoped_proof_mode spm(_m,PGM_ENABLED); - ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled); - p.set_bool("proof", true); - scoped_ptr sp = (ctx.get_interpolating_solver_factory())(_m, p, true, models_enabled, false, ctx.get_logic()); - - ptr_vector cnsts; - ptr_vector interps; - model_ref m; - - // compute an interpolant - - lbool res; - try { - res = iz3interpolate(_m, *sp.get(), t, cnsts, interps, m, nullptr); - } - catch (iz3_incompleteness &) { - throw cmd_exception("incompleteness in interpolator"); - } - - switch(res){ - case l_false: - ctx.regular_stream() << "unsat\n"; - show_interpolant_and_maybe_check(ctx, cnsts, t, interps, m_params, check); - break; - - case l_true: - ctx.regular_stream() << "sat\n"; - // TODO: how to return the model to the context, if it exists? - break; - - case l_undef: - ctx.regular_stream() << "unknown\n"; - // TODO: how to return the model to the context, if it exists? - break; - } - - for(unsigned i = 0; i < cnsts.size(); i++) - ctx.m().dec_ref(cnsts[i]); - -} - -static expr *make_tree(cmd_context & ctx, const ptr_vector &exprs){ - if(exprs.size() == 0) - throw cmd_exception("not enough arguments"); - expr *foo = exprs[0]; - for(unsigned i = 1; i < exprs.size(); i++){ - foo = ctx.m().mk_and(ctx.m().mk_interp(foo),exprs[i]); - } - return foo; -} - -static void get_interpolant(cmd_context & ctx, const ptr_vector &exprs, params_ref &m_params) { - expr_ref foo(make_tree(ctx, exprs),ctx.m()); - get_interpolant(ctx,foo.get(),m_params); -} - -static void compute_interpolant(cmd_context & ctx, const ptr_vector &exprs, params_ref &m_params) { - expr_ref foo(make_tree(ctx, exprs),ctx.m()); - compute_interpolant_and_maybe_check(ctx,foo.get(),m_params,false); -} - - -// UNARY_CMD(get_interpolant_cmd, "get-interpolant", "", "get interpolant for marked positions in fmla", CPK_EXPR, expr *, get_interpolant(ctx, arg);); - -// UNARY_CMD(get_and_check_interpolant_cmd, "get-and-check-interpolant", "", "get and check interpolant for marked positions in fmla", CPK_EXPR, expr *, get_and_check_interpolant(ctx, arg);); - -class get_interpolant_cmd : public parametric_cmd { -protected: - ptr_vector m_targets; -public: - get_interpolant_cmd(char const * name = "get-interpolant"):parametric_cmd(name) {} - - char const * get_usage() const override { return "+"; } - - char const * get_main_descr() const override { - return "get interpolant for formulas"; - } - - void init_pdescrs(cmd_context & ctx, param_descrs & p) override { - } - - void prepare(cmd_context & ctx) override { - parametric_cmd::prepare(ctx); - m_targets.resize(0); - } - - cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { - return CPK_EXPR; - } - - void set_next_arg(cmd_context & ctx, expr * arg) override { - m_targets.push_back(arg); - } - - void execute(cmd_context & ctx) override { - get_interpolant(ctx,m_targets,m_params); - } -}; - -class compute_interpolant_cmd : public get_interpolant_cmd { -public: - compute_interpolant_cmd(char const * name = "compute-interpolant"):get_interpolant_cmd(name) {} - - void execute(cmd_context & ctx) override { - compute_interpolant(ctx,m_targets,m_params); - } - -}; - -void install_interpolant_cmds(cmd_context & ctx) { - ctx.insert(alloc(get_interpolant_cmd)); - ctx.insert(alloc(compute_interpolant_cmd)); - // ctx.insert(alloc(get_and_check_interpolant_cmd)); -} diff --git a/src/cmd_context/interpolant_cmds.h b/src/cmd_context/interpolant_cmds.h deleted file mode 100644 index daef70926..000000000 --- a/src/cmd_context/interpolant_cmds.h +++ /dev/null @@ -1,24 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - interpolant_cmds.h - - Abstract: - Commands for interpolation. - - Author: - - Leonardo (leonardo) 2011-12-23 - - Notes: - - --*/ -#ifndef INTERPOLANT_CMDS_H_ -#define INTERPOLANT_CMDS_H_ - -class cmd_context; -void install_interpolant_cmds(cmd_context & ctx); - -#endif diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp index f9ba79f6d..5a0f6468b 100644 --- a/src/cmd_context/pdecl.cpp +++ b/src/cmd_context/pdecl.cpp @@ -18,6 +18,7 @@ Revision History: --*/ #include "cmd_context/pdecl.h" #include "ast/datatype_decl_plugin.h" +#include using namespace format_ns; class psort_inst_cache { @@ -866,7 +867,6 @@ psort * pdecl_manager::mk_psort_cnst(sort * s) { } psort * pdecl_manager::register_psort(psort * n) { - TRACE("register_psort", tout << "registering psort...\n"; n->display(tout); tout << "\n";); psort * r = m_table.insert_if_not_there(n); if (r != n) { del_decl_core(n); @@ -946,7 +946,7 @@ void pdecl_manager::del_decl_core(pdecl * p) { } void pdecl_manager::del_decl(pdecl * p) { - TRACE("pdecl_manager", p->display(tout); tout << "\n";); + TRACE("register_psort", tout << "del psort "; p->display(tout); tout << "\n";); if (p->is_psort()) { psort * _p = static_cast(p); if (_p->is_sort_wrapper()) diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index 78187ebe1..cbe44da2d 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -200,6 +200,8 @@ public: if (!m_tactic) { throw cmd_exception("check-sat-using needs a tactic argument"); } + if (ctx.ignore_check()) + return; params_ref p = ctx.params().merge_default_params(ps()); tactic_ref tref = using_params(sexpr2tactic(ctx, m_tactic), p); tref->set_logic(ctx.get_logic()); @@ -324,9 +326,6 @@ public: unsigned rlimit = p.get_uint("rlimit", ctx.params().rlimit()); goal_ref_buffer result_goals; - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); std::string reason_unknown; bool failed = false; @@ -337,7 +336,7 @@ public: scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); try { - exec(t, g, result_goals, mc, pc, core); + exec(t, g, result_goals); } catch (tactic_exception & ex) { ctx.regular_stream() << "(error \"tactic failed: " << ex.msg() << "\")" << std::endl; @@ -396,8 +395,8 @@ public: } } - if (!failed && mc && p.get_bool("print_model_converter", false)) - mc->display(ctx.regular_stream()); + if (!failed && g->mc() && p.get_bool("print_model_converter", false)) + g->mc()->display(ctx.regular_stream()); if (p.get_bool("print_statistics", false)) display_statistics(ctx, tref.get()); diff --git a/src/duality/CMakeLists.txt b/src/duality/CMakeLists.txt deleted file mode 100644 index eb2d5c4f2..000000000 --- a/src/duality/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -z3_add_component(duality - SOURCES - duality_profiling.cpp - duality_rpfp.cpp - duality_solver.cpp - duality_wrapper.cpp - COMPONENT_DEPENDENCIES - interp - qe - smt -) diff --git a/src/duality/duality.h b/src/duality/duality.h deleted file mode 100644 index 9bf323d8b..000000000 --- a/src/duality/duality.h +++ /dev/null @@ -1,1365 +0,0 @@ -/*++ - Copyright (c) 2012 Microsoft Corporation - - Module Name: - - duality.h - - Abstract: - - main header for duality - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - -#pragma once - -#include "duality/duality_wrapper.h" -#include -#include -#include - -// make hash_map and hash_set available -using namespace stl_ext; - -namespace Duality { - - struct implicant_solver; - - /* Generic operations on Z3 formulas */ - - struct Z3User { - - context &ctx; - - typedef func_decl FuncDecl; - typedef expr Term; - - Z3User(context &_ctx) : ctx(_ctx){} - - const char *string_of_int(int n); - - Term conjoin(const std::vector &args); - - Term sum(const std::vector &args); - - Term CloneQuantifier(const Term &t, const Term &new_body); - - Term SubstRec(hash_map &memo, const Term &t); - - Term SubstRec(hash_map &memo, hash_map &map, const Term &t); - - void Strengthen(Term &x, const Term &y); - - // return the func_del of an app if it is uninterpreted - - bool get_relation(const Term &t, func_decl &R); - - // return true if term is an individual variable - // TODO: have to check that it is not a background symbol - - bool is_variable(const Term &t); - - FuncDecl SuffixFuncDecl(const Term &t, int n); - - - Term SubstRecHide(hash_map &memo, const Term &t, int number); - - void CollectConjuncts(const Term &f, std::vector &lits, bool pos = true); - - void SortTerms(std::vector &terms); - - Term SortSum(const Term &t); - - void Summarize(const Term &t); - - int CountOperators(const Term &t); - - Term SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val); - - Term CloneQuantAndSimp(const expr &t, const expr &body); - - Term RemoveRedundancy(const Term &t); - - Term IneqToEq(const Term &t); - - bool IsLiteral(const expr &lit, expr &atom, expr &val); - - expr Negate(const expr &f); - - expr SimplifyAndOr(const std::vector &args, bool is_and); - - expr ReallySimplifyAndOr(const std::vector &args, bool is_and); - - int MaxIndex(hash_map &memo, const Term &t); - - bool IsClosedFormula(const Term &t); - - Term AdjustQuantifiers(const Term &t); - - FuncDecl RenumberPred(const FuncDecl &f, int n); - - FuncDecl NumberPred(const FuncDecl &f, int n); - - Term ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming); - - - protected: - - void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); - int CountOperatorsRec(hash_set &memo, const Term &t); - void RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo); - Term RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t); - Term SubstAtomTriv(const expr &foo, const expr &atom, const expr &val); - expr ReduceAndOr(const std::vector &args, bool is_and, std::vector &res); - expr FinishAndOr(const std::vector &args, bool is_and); - expr PullCommonFactors(std::vector &args, bool is_and); - Term IneqToEqRec(hash_map &memo, const Term &t); - Term CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall); - Term PushQuantifier(const expr &t, const expr &body, bool is_forall); - void CollectJuncts(const Term &f, std::vector &lits, decl_kind op, bool negate); - Term DeleteBoundRec(hash_map > &memo, int level, int num, const Term &t); - Term DeleteBound(int level, int num, const Term &t); - - }; - - /** This class represents a relation post-fixed point (RPFP) problem as - * a "problem graph". The graph consists of Nodes and hyper-edges. - * - * A node consists of - * - Annotation, a symbolic relation - * - Bound, a symbolic relation giving an upper bound on Annotation - * - * - * A hyper-edge consists of: - * - Children, a sequence of children Nodes, - * - F, a symbolic relational transformer, - * - Parent, a single parent Node. - * - * The graph is "solved" when: - * - For every Node n, n.Annotation subseteq n.Bound - * - For every hyperedge e, e.F(e.Children.Annotation) subseteq e.Parent.Annotation - * - * where, if x is a sequence of Nodes, x.Annotation is the sequences - * of Annotations of the nodes in the sequence. - * - * A symbolic Transformer consists of - * - RelParams, a sequence of relational symbols - * - IndParams, a sequence of individual symbols - * - Formula, a formula over RelParams and IndParams - * - * A Transformer t represents a function that takes sequence R of relations - * and yields the relation lambda (t.Indparams). Formula(R/RelParams). - * - * As a special case, a nullary Transformer (where RelParams is the empty sequence) - * represents a fixed relation. - * - * An RPFP consists of - * - Nodes, a set of Nodes - * - Edges, a set of hyper-edges - * - Context, a prover context that contains formula AST's - * - * Multiple RPFP's can use the same Context, but you should be careful - * that only one RPFP asserts constraints in the context at any time. - * - * */ - class RPFP : public Z3User - { - public: - - class Edge; - class Node; - bool HornClauses; - - - /** Interface class for interpolating solver. */ - - class LogicSolver { - public: - - context *ctx; /** Z3 context for formulas */ - solver *slvr; /** Z3 solver */ - bool need_goals; /** Can the solver use the goal tree to optimize interpolants? */ - solver aux_solver; /** For temporary use -- don't leave assertions here. */ - - /** Tree interpolation. This method assumes the formulas in TermTree - "assumptions" are currently asserted in the solver. The return - value indicates whether the assertions are satisfiable. In the - UNSAT case, a tree interpolant is returned in "interpolants". - In the SAT case, a model is returned. - */ - - virtual - lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = nullptr, - bool weak = false - ) = 0; - - /** Declare a constant in the background theory. */ - virtual void declare_constant(const func_decl &f) = 0; - - /** Is this a background constant? */ - virtual bool is_constant(const func_decl &f) = 0; - - /** Get the constants in the background vocabulary */ - virtual hash_set &get_constants() = 0; - - /** Assert a background axiom. */ - virtual void assert_axiom(const expr &axiom) = 0; - - /** Get the background axioms. */ - virtual const std::vector &get_axioms() = 0; - - /** Return a string describing performance. */ - virtual std::string profile() = 0; - - virtual void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &theory - ){} - - /** Cancel, throw Canceled object if possible. */ - virtual void cancel(){ } - - /* Note: aux solver uses extensional array theory, since it - needs to be able to produce counter-models for - interpolants the have array equalities in them. - */ - LogicSolver(context &c) : aux_solver(c,true){} - - virtual ~LogicSolver(){} - }; - - /** This solver uses iZ3. */ - class iZ3LogicSolver : public LogicSolver { - public: - interpolating_context *ictx; /** iZ3 context for formulas */ - interpolating_solver *islvr; /** iZ3 solver */ - - lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = nullptr, - bool weak = false) override - { - literals _labels; - islvr->SetWeakInterpolants(weak); - return islvr->interpolate_tree(assumptions,interpolants,_model,_labels,true); - } - - void assert_axiom(const expr &axiom) override { -#if 1 - // HACK: large "distict" predicates can kill the legacy SMT solver. - // encode them with a UIF - if(axiom.is_app() && axiom.decl().get_decl_kind() == Distinct) - if(axiom.num_args() > 10){ - sort s = axiom.arg(0).get_sort(); - std::vector sv; - sv.push_back(s); - int nargs = axiom.num_args(); - std::vector args(nargs); - func_decl f = ctx->fresh_func_decl("@distinct",sv,ctx->int_sort()); - for(int i = 0; i < nargs; i++){ - expr a = axiom.arg(i); - expr new_cnstr = f(a) == ctx->int_val(i); - args[i] = new_cnstr; - } - expr cnstr = ctx->make(And,args); - islvr->AssertInterpolationAxiom(cnstr); - return; - } -#endif - islvr->AssertInterpolationAxiom(axiom); - } - - const std::vector &get_axioms() override { - return islvr->GetInterpolationAxioms(); - } - - std::string profile() override { - return islvr->profile(); - } - -#if 0 - iZ3LogicSolver(config &_config){ - ctx = ictx = new interpolating_context(_config); - slvr = islvr = new interpolating_solver(*ictx); - need_goals = false; - islvr->SetWeakInterpolants(true); - } -#endif - - iZ3LogicSolver(context &c, bool models = true) : LogicSolver(c) { - ctx = ictx = &c; - slvr = islvr = new interpolating_solver(*ictx, models); - need_goals = false; - islvr->SetWeakInterpolants(true); - } - - void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &theory - ) override { -#if 0 - islvr->write_interpolation_problem(file_name,assumptions,theory); -#endif - - } - - void cancel() override {islvr->cancel();} - - /** Declare a constant in the background theory. */ - void declare_constant(const func_decl &f) override { - bckg.insert(f); - } - - /** Is this a background constant? */ - bool is_constant(const func_decl &f) override { - return bckg.find(f) != bckg.end(); - } - - /** Get the constants in the background vocabulary */ - hash_set &get_constants() override { - return bckg; - } - - ~iZ3LogicSolver() override { - // delete ictx; - delete islvr; - } - private: - hash_set bckg; - - }; - -#if 0 - /** Create a logic solver from a Z3 configuration. */ - static iZ3LogicSolver *CreateLogicSolver(config &_config){ - return new iZ3LogicSolver(_config); - } -#endif - - /** Create a logic solver from a low-level Z3 context. - Only use this if you know what you're doing. */ - static iZ3LogicSolver *CreateLogicSolver(context c){ - return new iZ3LogicSolver(c); - } - - LogicSolver *ls; - - protected: - int nodeCount; - int edgeCount; - - class stack_entry - { - public: - std::list edges; - std::list nodes; - std::list > constraints; - }; - - - public: - model dualModel; - protected: - literals dualLabels; - std::list stack; - std::vector axioms; // only saved here for printing purposes - solver &aux_solver; - hash_set *proof_core; - - public: - - /** Construct an RPFP graph with a given interpolating prover context. It is allowed to - have multiple RPFP's use the same context, but you should never have teo RPFP's - with the same conext asserting nodes or edges at the same time. Note, if you create - axioms in one RPFP, them create a second RPFP with the same context, the second will - inherit the axioms. - */ - - RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx)), dualModel(*(_ls->ctx)), aux_solver(_ls->aux_solver) - { - ls = _ls; - nodeCount = 0; - edgeCount = 0; - stack.push_back(stack_entry()); - HornClauses = false; - proof_core = nullptr; - } - - virtual ~RPFP(); - - /** Symbolic representation of a relational transformer */ - class Transformer - { - public: - std::vector RelParams; - std::vector IndParams; - Term Formula; - RPFP *owner; - hash_map labels; - - Transformer *Clone() - { - return new Transformer(*this); - } - - void SetEmpty(){ - Formula = owner->ctx.bool_val(false); - } - - void SetFull(){ - Formula = owner->ctx.bool_val(true); - } - - bool IsEmpty(){ - return eq(Formula,owner->ctx.bool_val(false)); - } - - bool IsFull(){ - return eq(Formula,owner->ctx.bool_val(true)); - } - - void UnionWith(const Transformer &other){ - Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); - Formula = Formula || t; - } - - void IntersectWith(const Transformer &other){ - Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); - Formula = Formula && t; - } - - bool SubsetEq(const Transformer &other){ - Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); - expr test = Formula && !t; - owner->aux_solver.push(); - owner->aux_solver.add(test); - check_result res = owner->aux_solver.check(); - owner->aux_solver.pop(1); - return res == unsat; - } - - void Complement(){ - Formula = !Formula; - } - - void Simplify(){ - Formula = Formula.simplify(); - } - - Transformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula, RPFP *_owner) - : RelParams(_RelParams), IndParams(_IndParams), Formula(_Formula) {owner = _owner;} - }; - - /** Create a symbolic transformer. */ - Transformer CreateTransformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula) - { - // var ops = new Util.ContextOps(ctx); - // var foo = ops.simplify_lhs(_Formula); - // t.Formula = foo.Item1; - // t.labels = foo.Item2; - return Transformer(_RelParams,_IndParams,_Formula,this); - } - - /** Create a relation (nullary relational transformer) */ - Transformer CreateRelation(const std::vector &_IndParams, const Term &_Formula) - { - return CreateTransformer(std::vector(), _IndParams, _Formula); - } - - /** A node in the RPFP graph */ - class Node - { - public: - FuncDecl Name; - Transformer Annotation; - Transformer Bound; - Transformer Underapprox; - RPFP *owner; - int number; - Edge *Outgoing; - std::vector Incoming; - Term dual; - Node *map; - unsigned recursion_bound; - - Node(const FuncDecl &_Name, const Transformer &_Annotation, const Transformer &_Bound, const Transformer &_Underapprox, const Term &_dual, RPFP *_owner, int _number) - : Name(_Name), Annotation(_Annotation), Bound(_Bound), Underapprox(_Underapprox), dual(_dual) {owner = _owner; number = _number; Outgoing = nullptr; recursion_bound = UINT_MAX;} - }; - - /** Create a node in the graph. The input is a term R(v_1...v_n) - * where R is an arbitrary relational symbol and v_1...v_n are - * arbitary distinct variables. The names are only of mnemonic value, - * however, the number and type of arguments determine the type - * of the relation at this node. */ - - Node *CreateNode(const Term &t) - { - std::vector _IndParams; - int nargs = t.num_args(); - _IndParams.reserve(nargs); - for(int i = 0; i < nargs; i++) - _IndParams.push_back(t.arg(i)); - Node *n = new Node(t.decl(), - CreateRelation(_IndParams,ctx.bool_val(true)), - CreateRelation(_IndParams,ctx.bool_val(true)), - CreateRelation(_IndParams,ctx.bool_val(false)), - expr(ctx), this, ++nodeCount - ); - nodes.push_back(n); - return n; - } - - /** Clone a node (can be from another graph). */ - - Node *CloneNode(Node *old) - { - Node *n = new Node(old->Name, - old->Annotation, - old->Bound, - old->Underapprox, - expr(ctx), - this, - ++nodeCount - ); - nodes.push_back(n); - n->map = old; - return n; - } - - /** Delete a node. You can only do this if not connected to any edges.*/ - void DeleteNode(Node *node){ - if(node->Outgoing || !node->Incoming.empty()) - throw "cannot delete RPFP node"; - for(std::vector::iterator it = nodes.end(), en = nodes.begin(); it != en;){ - if(*(--it) == node){ - nodes.erase(it); - break; - } - } - delete node; - } - - /** This class represents a hyper-edge in the RPFP graph */ - - class Edge - { - public: - Transformer F; - Node *Parent; - std::vector Children; - RPFP *owner; - int number; - // these should be internal... - Term dual; - hash_map relMap; - hash_map varMap; - Edge *map; - Term labeled; - std::vector constraints; - - Edge(Node *_Parent, const Transformer &_F, const std::vector &_Children, RPFP *_owner, int _number) - : F(_F), Parent(_Parent), Children(_Children), dual(expr(_owner->ctx)) { - owner = _owner; - number = _number; - } - }; - - - /** Create a hyper-edge. */ - Edge *CreateEdge(Node *_Parent, const Transformer &_F, const std::vector &_Children) - { - Edge *e = new Edge(_Parent,_F,_Children,this,++edgeCount); - _Parent->Outgoing = e; - for(unsigned i = 0; i < _Children.size(); i++) - _Children[i]->Incoming.push_back(e); - edges.push_back(e); - return e; - } - - - /** Delete a hyper-edge and unlink it from any nodes. */ - void DeleteEdge(Edge *edge){ - if(edge->Parent) - edge->Parent->Outgoing = nullptr; - for(unsigned int i = 0; i < edge->Children.size(); i++){ - std::vector &ic = edge->Children[i]->Incoming; - for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ - if(*it == edge){ - ic.erase(it); - break; - } - } - } - for(std::vector::iterator it = edges.end(), en = edges.begin(); it != en;){ - if(*(--it) == edge){ - edges.erase(it); - break; - } - } - delete edge; - } - - /** Create an edge that lower-bounds its parent. */ - Edge *CreateLowerBoundEdge(Node *_Parent) - { - return CreateEdge(_Parent, _Parent->Annotation, std::vector()); - } - - - /** For incremental solving, asserts the constraint associated - * with this edge in the SMT context. If this edge is removed, - * you must pop the context accordingly. The second argument is - * the number of pushes we are inside. */ - - virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); - - /* Constrain an edge by the annotation of one of its children. */ - - void ConstrainParent(Edge *parent, Node *child); - - /** For incremental solving, asserts the negation of the upper bound associated - * with a node. - * */ - - void AssertNode(Node *n); - - /** Assert a constraint on an edge in the SMT context. - */ - void ConstrainEdge(Edge *e, const Term &t); - - /** Fix the truth values of atomic propositions in the given - edge to their values in the current assignment. */ - void FixCurrentState(Edge *root); - - void FixCurrentStateFull(Edge *edge, const expr &extra); - - void FixCurrentStateFull(Edge *edge, const std::vector &assumps, const hash_map &renaming); - - /** Declare a constant in the background theory. */ - - void DeclareConstant(const FuncDecl &f); - - /** Assert a background axiom. Background axioms can be used to provide the - * theory of auxilliary functions or relations. All symbols appearing in - * background axioms are considered global, and may appear in both transformer - * and relational solutions. Semantically, a solution to the RPFP gives - * an interpretation of the unknown relations for each interpretation of the - * auxilliary symbols that is consistent with the axioms. Axioms should be - * asserted before any calls to Push. They cannot be de-asserted by Pop. */ - - void AssertAxiom(const Term &t); - -#if 0 - /** Do not call this. */ - - void RemoveAxiom(const Term &t); -#endif - - /** Solve an RPFP graph. This means either strengthen the annotation - * so that the bound at the given root node is satisfied, or - * show that this cannot be done by giving a dual solution - * (i.e., a counterexample). - * - * In the current implementation, this only works for graphs that - * are: - * - tree-like - * - * - closed. - * - * In a tree-like graph, every nod has out most one incoming and one out-going edge, - * and there are no cycles. In a closed graph, every node has exactly one out-going - * edge. This means that the leaves of the tree are all hyper-edges with no - * children. Such an edge represents a relation (nullary transformer) and thus - * a lower bound on its parent. The parameter root must be the root of this tree. - * - * If Solve returns LBool.False, this indicates success. The annotation of the tree - * has been updated to satisfy the upper bound at the root. - * - * If Solve returns LBool.True, this indicates a counterexample. For each edge, - * you can then call Eval to determine the values of symbols in the transformer formula. - * You can also call Empty on a node to determine if its value in the counterexample - * is the empty relation. - * - * \param root The root of the tree - * \param persist Number of context pops through which result should persist - * - * - */ - - lbool Solve(Node *root, int persist); - - /** Same as Solve, but annotates only a single node. */ - - lbool SolveSingleNode(Node *root, Node *node); - - /** Get the constraint tree (but don't solve it) */ - - TermTree *GetConstraintTree(Node *root, Node *skip_descendant = nullptr); - - /** Dispose of the dual model (counterexample) if there is one. */ - - void DisposeDualModel(); - - /** Check satisfiability of asserted edges and nodes. Same functionality as - * Solve, except no primal solution (interpolant) is generated in the unsat case. */ - - check_result Check(Node *root, std::vector underapproxes = std::vector(), - std::vector *underapprox_core = nullptr); - - /** Update the model, attempting to make the propositional literals in assumps true. If possible, - return sat, else return unsat and keep the old model. */ - - check_result CheckUpdateModel(Node *root, std::vector assumps); - - /** Determines the value in the counterexample of a symbol occurring in the transformer formula of - * a given edge. */ - - Term Eval(Edge *e, const Term& t); - - /** Return the fact derived at node p in a counterexample. */ - - Term EvalNode(Node *p); - - /** Returns true if the given node is empty in the primal solution. For procedure summaries, - this means that the procedure is not called in the current counter-model. */ - - bool Empty(Node *p); - - /** Compute an underapproximation of every node in a tree rooted at "root", - based on a previously computed counterexample. */ - - Term ComputeUnderapprox(Node *root, int persist); - - /** Try to strengthen the annotation of a node by removing disjuncts. */ - void Generalize(Node *root, Node *node); - - - /** Compute disjunctive interpolant for node by case splitting */ - void InterpolateByCases(Node *root, Node *node); - - /** Push a scope. Assertions made after Push can be undone by Pop. */ - - void Push(); - - /** Exception thrown when bad clause is encountered */ - - struct bad_clause { - std::string msg; - int i; - bad_clause(const std::string &_msg, int _i){ - msg = _msg; - i = _i; - } - }; - - struct parse_error { - std::string msg; - parse_error(const std::string &_msg){ - msg = _msg; - } - }; - - struct file_not_found { - }; - - struct bad_format { - }; - - // thrown on internal error - struct Bad { - }; - - // thrown on more serious internal error - struct ReallyBad { - }; - - // throw when greedy reduction fails - struct greedy_reduce_failed {}; - - /** Pop a scope (see Push). Note, you cannot pop axioms. */ - - void Pop(int num_scopes); - - /** Erase the proof by performing a Pop, Push and re-assertion of - all the popped constraints */ - void PopPush(); - - /** Return true if the given edge is used in the proof of unsat. - Can be called only after Solve or Check returns an unsat result. */ - - bool EdgeUsedInProof(Edge *edge); - - - /** Convert a collection of clauses to Nodes and Edges in the RPFP. - - Predicate unknowns are uninterpreted predicates not - occurring in the background theory. - - Clauses are of the form - - B => P(t_1,...,t_k) - - where P is a predicate unknown and predicate unknowns - occur only positivey in H and only under existential - quantifiers in prenex form. - - Each predicate unknown maps to a node. Each clause maps to - an edge. Let C be a clause B => P(t_1,...,t_k) where the - sequence of predicate unknowns occurring in B (in order - of occurrence) is P_1..P_n. The clause maps to a transformer - T where: - - T.Relparams = P_1..P_n - T.Indparams = x_1...x+k - T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k - - Throws exception bad_clause(msg,i) if a clause i is - in the wrong form. - - */ - - struct label_struct { - symbol name; - expr value; - bool pos; - label_struct(const symbol &s, const expr &e, bool b) - : name(s), value(e), pos(b) {} - }; - - -#ifdef _WINDOWS - __declspec(dllexport) -#endif - void FromClauses(const std::vector &clauses, const std::vector *bounds = nullptr); - - void FromFixpointContext(fixedpoint fp, std::vector &queries); - - void WriteSolution(std::ostream &s); - - void WriteCounterexample(std::ostream &s, Node *node); - - enum FileFormat {DualityFormat, SMT2Format, HornFormat}; - - /** Write the RPFP to a file (currently in SMTLIB 1.2 format) */ - void WriteProblemToFile(std::string filename, FileFormat format = DualityFormat); - - /** Read the RPFP from a file (in specified format) */ - void ReadProblemFromFile(std::string filename, FileFormat format = DualityFormat); - - /** Translate problem to Horn clause form */ - void ToClauses(std::vector &cnsts, FileFormat format = DualityFormat); - - /** Translate the RPFP to a fixed point context, with queries */ - fixedpoint ToFixedPointProblem(std::vector &queries); - - /** Nodes of the graph. */ - std::vector nodes; - - /** Edges of the graph. */ - std::vector edges; - - /** Fuse a vector of transformers. If the total number of inputs of the transformers - is N, then the result is an N-ary transformer whose output is the union of - the outputs of the given transformers. The is, suppose we have a vector of transformers - {T_i(r_i1,...,r_iN(i) : i=1..M}. The result is a transformer - - F(r_11,...,r_iN(1),...,r_M1,...,r_MN(M)) = - T_1(r_11,...,r_iN(1)) U ... U T_M(r_M1,...,r_MN(M)) - */ - - Transformer Fuse(const std::vector &trs); - - /** Fuse edges so that each node is the output of at most one edge. This - transformation is solution-preserving, but changes the numbering of edges in - counterexamples. - */ - void FuseEdges(); - - void RemoveDeadNodes(); - - Term SubstParams(const std::vector &from, - const std::vector &to, const Term &t); - - Term SubstParamsNoCapture(const std::vector &from, - const std::vector &to, const Term &t); - - Term Localize(Edge *e, const Term &t); - - void EvalNodeAsConstraint(Node *p, Transformer &res); - - TermTree *GetGoalTree(Node *root); - - int EvalTruth(hash_map &memo, Edge *e, const Term &f); - - void GetLabels(Edge *e, std::vector &labels); - - // int GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos); - - /** Compute and save the proof core for future calls to - EdgeUsedInProof. You only need to call this if you will pop - the solver before calling EdgeUsedInProof. - */ - void ComputeProofCore(); - - int CumulativeDecisions(); - - void GreedyReduceNodes(std::vector &nodes); - - check_result CheckWithConstrainedNodes(std::vector &posnodes,std::vector &negnodes); - - solver &slvr(){ - return *ls->slvr; - } - - protected: - - void ClearProofCore(){ - if(proof_core) - delete proof_core; - proof_core = nullptr; - } - - Term SuffixVariable(const Term &t, int n); - - Term HideVariable(const Term &t, int n); - - void RedVars(Node *node, Term &b, std::vector &v); - - Term RedDualRela(Edge *e, std::vector &args, int idx); - - Term LocalizeRec(Edge *e, hash_map &memo, const Term &t); - - void SetEdgeMaps(Edge *e); - - Term ReducedDualEdge(Edge *e); - - TermTree *ToTermTree(Node *root, Node *skip_descendant = nullptr); - - TermTree *ToGoalTree(Node *root); - - void CollapseTermTreeRec(TermTree *root, TermTree *node); - - TermTree *CollapseTermTree(TermTree *node); - - void DecodeTree(Node *root, TermTree *interp, int persist); - - Term GetUpperBound(Node *n); - - TermTree *AddUpperBound(Node *root, TermTree *t); - -#if 0 - void WriteInterps(System.IO.StreamWriter f, TermTree t); -#endif - - void WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s); - - void WriteEdgeAssignment(std::ostream &s, Edge *e); - - - // Scan the clause body for occurrences of the predicate unknowns - - Term ScanBody(hash_map &memo, - const Term &t, - hash_map &pmap, - std::vector &res, - std::vector &nodes); - - Term RemoveLabelsRec(hash_map &memo, const Term &t, std::vector &lbls); - - Term RemoveLabels(const Term &t, std::vector &lbls); - - Term GetAnnotation(Node *n); - - - Term GetUnderapprox(Node *n); - - Term UnderapproxFlag(Node *n); - - hash_map underapprox_flag_rev; - - Node *UnderapproxFlagRev(const Term &flag); - - Term ProjectFormula(std::vector &keep_vec, const Term &f); - - int SubtermTruth(hash_map &memo, const Term &); - - void ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set *done, bool truth, hash_set &dont_cares); - - void Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares); - - Term UnderapproxFormula(const Term &f, hash_set &dont_cares); - - void ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares, bool extensional = true); - - public: - Term UnderapproxFullFormula(const Term &f, bool extensional = true); - - protected: - Term ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants); - - hash_map resolve_ite_memo; - - Term ResolveIte(hash_map &memo, const Term &t, std::vector &lits, - hash_set *done, hash_set &dont_cares); - - struct ArrayValue { - bool defined; - std::map entries; - expr def_val; - }; - - void EvalArrayTerm(const Term &t, ArrayValue &res); - - Term EvalArrayEquality(const Term &f); - - Term ModelValueAsConstraint(const Term &t); - - void GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, - hash_set *done, bool truth); - - Term SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t); - - Term SubstBound(hash_map &subst, const Term &t); - - void ConstrainEdgeLocalized(Edge *e, const Term &t); - - void GreedyReduce(solver &s, std::vector &conjuncts); - - void NegateLits(std::vector &lits); - - expr SimplifyOr(std::vector &lits); - - expr SimplifyAnd(std::vector &lits); - - void SetAnnotation(Node *root, const expr &t); - - void AddEdgeToSolver(Edge *edge); - - void AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge); - - void AddToProofCore(hash_set &core); - - void GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under); - - Term StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits); - - expr NegateLit(const expr &f); - - expr GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox); - - bool IsVar(const expr &t); - - void GetVarsRec(hash_set &memo, const expr &cnst, std::vector &vars); - - expr UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params); - - void AddParamsToTransformer(Transformer &trans, const std::vector ¶ms); - - expr AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms); - - expr GetRelRec(hash_set &memo, const expr &t, const func_decl &rel); - - expr GetRel(Edge *edge, int child_idx); - - void GetDefs(const expr &cnst, hash_map &defs); - - void GetDefsRec(const expr &cnst, hash_map &defs); - - void AddParamsToNode(Node *node, const std::vector ¶ms); - - void UnhoistLoop(Edge *loop_edge, Edge *init_edge); - - void Unhoist(); - - Term ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts); - - Term ElimIte(const Term &t); - - void MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node); - - virtual void slvr_add(const expr &e); - - virtual void slvr_pop(int i); - - virtual void slvr_push(); - - virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = nullptr, unsigned *core_size = nullptr, expr *core = nullptr); - - virtual lbool ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = nullptr, - bool weak = false); - - virtual bool proof_core_contains(const expr &e); - - }; - - - /** RPFP solver base class. */ - - class Solver { - - public: - - class Counterexample { - private: - RPFP *tree; - RPFP::Node *root; - public: - Counterexample(){ - tree = nullptr; - root = nullptr; - } - Counterexample(RPFP *_tree, RPFP::Node *_root){ - tree = _tree; - root = _root; - } - ~Counterexample(){ - if(tree) delete tree; - } - void swap(Counterexample &other){ - std::swap(tree,other.tree); - std::swap(root,other.root); - } - void set(RPFP *_tree, RPFP::Node *_root){ - if(tree) delete tree; - tree = _tree; - root = _root; - } - void clear(){ - if(tree) delete tree; - tree = nullptr; - } - RPFP *get_tree() const {return tree;} - RPFP::Node *get_root() const {return root;} - private: - Counterexample &operator=(const Counterexample &); - Counterexample(const Counterexample &); - }; - - /** Solve the problem. You can optionally give an old - counterexample to use as a guide. This is chiefly useful for - abstraction refinement metholdologies, and is only used as a - heuristic. */ - - virtual bool Solve() = 0; - - virtual Counterexample &GetCounterexample() = 0; - - virtual bool SetOption(const std::string &option, const std::string &value) = 0; - - /** Learn heuristic information from another solver. This - is chiefly useful for abstraction refinement, when we want to - solve a series of similar problems. */ - - virtual void LearnFrom(Solver *old_solver) = 0; - - /** Return true if the solution be incorrect due to recursion bounding. - That is, the returned "solution" might contain all derivable facts up to the - given recursion bound, but not be actually a fixed point. - */ - - virtual bool IsResultRecursionBounded() = 0; - - virtual ~Solver(){} - - static Solver *Create(const std::string &solver_class, RPFP *rpfp); - - /** This can be called asynchrnously to cause Solve to throw a - Canceled exception at some time in the future. - */ - virtual void Cancel() = 0; - - /** Object thrown on cancellation */ - struct Canceled {}; - - /** Object thrown on incompleteness */ - struct Incompleteness {}; - }; -} - - -// Allow to hash on nodes and edges in deterministic way - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::RPFP::Node *p) const { - return p->number; - } - }; -} - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::RPFP::Edge *p) const { - return p->number; - } - }; -} - -// allow to walk sets of nodes without address dependency - -namespace std { - template <> - class less { - public: - bool operator()(Duality::RPFP::Node * const &s, Duality::RPFP::Node * const &t) const { - return s->number < t->number; // s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// #define LIMIT_STACK_WEIGHT 5 - - -namespace Duality { - /** Caching version of RPFP. Instead of asserting constraints, returns assumption literals */ - - class RPFP_caching : public RPFP { - public: - - /** appends assumption literals for edge to lits. if with_children is true, - includes that annotation of the edge's children. - */ - void AssertEdgeCache(Edge *e, std::vector &lits, bool with_children = false); - - /** appends assumption literals for node to lits */ - void AssertNodeCache(Node *, std::vector lits); - - /** check assumption lits, and return core */ - check_result CheckCore(const std::vector &assumps, std::vector &core); - - /** Clone another RPFP into this one, keeping a map */ - void Clone(RPFP *other); - - /** Get the clone of a node */ - Node *GetNodeClone(Node *other_node); - - /** Get the clone of an edge */ - Edge *GetEdgeClone(Edge *other_edge); - - /** Try to strengthen the parent of an edge */ - void GeneralizeCache(Edge *edge); - - /** Try to propagate some facts from children to parents of edge. - Return true if success. */ - bool PropagateCache(Edge *edge); - - /** Construct a caching RPFP using a LogicSolver */ - RPFP_caching(LogicSolver *_ls) : RPFP(_ls) {} - - /** Constraint an edge by its child's annotation. Return - assumption lits. */ - void ConstrainParentCache(Edge *parent, Node *child, std::vector &lits); - -#ifdef LIMIT_STACK_WEIGHT - virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); -#endif - - ~RPFP_caching() override {} - - protected: - hash_map AssumptionLits; - hash_map NodeCloneMap; - hash_map EdgeCloneMap; - std::vector alit_stack; - std::vector alit_stack_sizes; - - // to let us use one solver per edge - struct edge_solver { - hash_map AssumptionLits; - uptr slvr; - }; - hash_map edge_solvers; - -#ifdef LIMIT_STACK_WEIGHT - struct weight_counter { - int val; - weight_counter(){val = 0;} - void swap(weight_counter &other){ - std::swap(val,other.val); - } - }; - - struct big_stack_entry { - weight_counter weight_added; - std::vector new_alits; - std::vector alit_stack; - std::vector alit_stack_sizes; - }; - - std::vector new_alits; - weight_counter weight_added; - std::vector big_stack; -#endif - - - - void GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map = nullptr); - - void GreedyReduceCache(std::vector &assumps, std::vector &core); - - void FilterCore(std::vector &core, std::vector &full_core); - void ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits); - - void slvr_add(const expr &e) override; - - void slvr_pop(int i) override; - - void slvr_push() override; - - check_result slvr_check(unsigned n = 0, expr * const assumptions = nullptr, unsigned *core_size = nullptr, expr *core = nullptr) override; - - lbool ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = nullptr, - bool weak = false) override; - - bool proof_core_contains(const expr &e) override; - - void GetTermTreeAssertionLiterals(TermTree *assumptions); - - void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); - - edge_solver &SolverForEdge(Edge *edge, bool models, bool axioms); - - public: - struct scoped_solver_for_edge { - solver *orig_slvr; - RPFP_caching *rpfp; - edge_solver *es; - scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false, bool axioms = false){ - rpfp = _rpfp; - orig_slvr = rpfp->ls->slvr; - es = &(rpfp->SolverForEdge(edge,models,axioms)); - rpfp->ls->slvr = es->slvr.get(); - rpfp->AssumptionLits.swap(es->AssumptionLits); - } - ~scoped_solver_for_edge(){ - rpfp->ls->slvr = orig_slvr; - rpfp->AssumptionLits.swap(es->AssumptionLits); - } - }; - - }; - -} diff --git a/src/duality/duality_profiling.cpp b/src/duality/duality_profiling.cpp deleted file mode 100644 index 6bb995e2d..000000000 --- a/src/duality/duality_profiling.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - duality_profiling.cpp - - Abstract: - - collection performance information for duality - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - - -#include -#include -#include -#include -#include - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include "duality/duality_wrapper.h" -#include "interp/iz3profiling.h" - -namespace Duality { - - void show_time(){ - output_time(std::cout,current_time()); - std::cout << "\n"; - } - - typedef std::map nmap; - - struct node { - std::string name; - clock_t time; - clock_t start_time; - nmap sub; - struct node *parent; - - node(); - } top; - - node::node(){ - time = 0; - parent = nullptr; - } - - struct node *current; - - struct init { - init(){ - top.name = "TOTAL"; - current = ⊤ - } - } initializer; - - struct time_entry { - clock_t t; - time_entry(){t = 0;}; - void add(clock_t incr){t += incr;} - }; - - struct ltstr - { - bool operator()(const char* s1, const char* s2) const - { - return strcmp(s1, s2) < 0; - } - }; - - typedef std::map tmap; - - static std::ostream *pfs; - - void print_node(node &top, int indent, tmap &totals){ - for(int i = 0; i < indent; i++) (*pfs) << " "; - (*pfs) << top.name; - int dots = 70 - 2 * indent - top.name.size(); - for(int i = 0; i second,indent+1,totals); - } - - void print_profile(std::ostream &os) { - pfs = &os; - top.time = 0; - for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++) - top.time += it->second.time; - tmap totals; - print_node(top,0,totals); - (*pfs) << "TOTALS:" << std::endl; - for(tmap::iterator it = totals.begin(); it != totals.end(); it++){ - (*pfs) << (it->first) << " "; - output_time(*pfs, it->second.t); - (*pfs) << std::endl; - } - profiling::print(os); // print the interpolation stats - } - - void timer_start(const char *name){ - node &child = current->sub[name]; - if(child.name.empty()){ // a new node - child.parent = current; - child.name = name; - } - child.start_time = current_time(); - current = &child; - } - - void timer_stop(const char *name){ - if (current->name != name || !current->parent) { -#if 0 - std::cerr << "imbalanced timer_start and timer_stop"; - exit(1); -#endif - // in case we lost a timer stop due to an exception - while (current->name != name && current->parent) - current = current->parent; - if (current->parent) { - current->time += (current_time() - current->start_time); - current = current->parent; - } - return; - } - current->time += (current_time() - current->start_time); - current = current->parent; - } -} diff --git a/src/duality/duality_profiling.h b/src/duality/duality_profiling.h deleted file mode 100755 index 5f0e5120c..000000000 --- a/src/duality/duality_profiling.h +++ /dev/null @@ -1,38 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - duality_profiling.h - - Abstract: - - collection performance information for duality - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - -#ifndef DUALITYPROFILING_H -#define DUALITYPROFILING_H - -#include - -namespace Duality { - /** Start a timer with given name */ - void timer_start(const char *); - /** Stop a timer with given name */ - void timer_stop(const char *); - /** Print out timings */ - void print_profile(std::ostream &s); - /** Show the current time. */ - void show_time(); -} - -#endif - diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp deleted file mode 100644 index 3358eb45e..000000000 --- a/src/duality/duality_rpfp.cpp +++ /dev/null @@ -1,4250 +0,0 @@ -/*++ - Copyright (c) 2012 Microsoft Corporation - - Module Name: - - duality_rpfp.h - - Abstract: - - implements relational post-fixedpoint problem - (RPFP) data structure. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - - - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include -#include -#include -#include - - -#include "duality/duality.h" -#include "duality/duality_profiling.h" - -// TODO: do we need these? -#ifdef Z3OPS - -class Z3_subterm_truth { -public: - virtual bool eval(Z3_ast f) = 0; - ~Z3_subterm_truth(){} -}; - -Z3_subterm_truth *Z3_mk_subterm_truth(Z3_context ctx, Z3_model model); - -#endif - -#include - -// TODO: use something official for this -int debug_gauss = 0; - -namespace Duality { - - static char string_of_int_buffer[20]; - - const char *Z3User::string_of_int(int n){ - sprintf(string_of_int_buffer,"%d",n); - return string_of_int_buffer; - } - - RPFP::Term RPFP::SuffixVariable(const Term &t, int n) - { - std::string name = t.decl().name().str() + "_" + string_of_int(n); - return ctx.constant(name.c_str(), t.get_sort()); - } - - RPFP::Term RPFP::HideVariable(const Term &t, int n) - { - std::string name = std::string("@p_") + t.decl().name().str() + "_" + string_of_int(n); - return ctx.constant(name.c_str(), t.get_sort()); - } - - void RPFP::RedVars(Node *node, Term &b, std::vector &v) - { - int idx = node->number; - if(HornClauses) - b = ctx.bool_val(true); - else { - std::string name = std::string("@b_") + string_of_int(idx); - symbol sym = ctx.str_symbol(name.c_str()); - b = ctx.constant(sym,ctx.bool_sort()); - } - // ctx.constant(name.c_str(), ctx.bool_sort()); - v = node->Annotation.IndParams; - for(unsigned i = 0; i < v.size(); i++) - v[i] = SuffixVariable(v[i],idx); - } - - void Z3User::SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t){ - if(memo.find(t) != memo.end()) - return; - memo.insert(t); - if(t.is_app()){ - decl_kind k = t.decl().get_decl_kind(); - if (k == And || k == Or || k == Not || k == Implies || k == Iff) { - ops++; - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - SummarizeRec(memo, lits, ops, t.arg(i)); - return; - } - } - lits.push_back(t); - } - - int RPFP::CumulativeDecisions(){ -#if 0 - std::string stats = Z3_statistics_to_string(ctx); - int pos = stats.find("decisions:"); - pos += 10; - int end = stats.find('\n',pos); - std::string val = stats.substr(pos,end-pos); - return atoi(val.c_str()); -#endif - return slvr().get_num_decisions(); - } - - - void Z3User::Summarize(const Term &t){ - hash_set memo; std::vector lits; int ops = 0; - SummarizeRec(memo, lits, ops, t); - std::cout << ops << ": "; - for(unsigned i = 0; i < lits.size(); i++){ - if(i > 0) std::cout << ", "; - std::cout << lits[i]; - } - } - - int Z3User::CountOperatorsRec(hash_set &memo, const Term &t){ - if(memo.find(t) != memo.end()) - return 0; - memo.insert(t); - if(t.is_app()){ - decl_kind k = t.decl().get_decl_kind(); - if (k == And || k == Or) { - int count = 1; - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - count += CountOperatorsRec(memo, t.arg(i)); - return count; - } - return 0; - } - if(t.is_quantifier()){ - int nbv = t.get_quantifier_num_bound(); - return CountOperatorsRec(memo,t.body()) + 2 * nbv; // count 2 for each quantifier - } - return 0; - } - - int Z3User::CountOperators(const Term &t){ - hash_set memo; - return CountOperatorsRec(memo,t); - } - - - Z3User::Term Z3User::conjoin(const std::vector &args){ - return ctx.make(And,args); - } - - Z3User::Term Z3User::sum(const std::vector &args){ - return ctx.make(Plus,args); - } - - RPFP::Term RPFP::RedDualRela(Edge *e, std::vector &args, int idx){ - Node *child = e->Children[idx]; - Term b(ctx); - std::vector v; - RedVars(child, b, v); - for (unsigned i = 0; i < args.size(); i++) { - if (eq(args[i].get_sort(), ctx.bool_sort())) - args[i] = ctx.make(Iff, args[i], v[i]); - else - args[i] = args[i] == v[i]; - } - return args.size() > 0 ? (b && conjoin(args)) : b; - } - - Z3User::Term Z3User::CloneQuantifier(const Term &t, const Term &new_body){ -#if 0 - Z3_context c = ctx; - Z3_ast a = t; - std::vector pats; - int num_pats = Z3_get_quantifier_num_patterns(c,a); - for(int i = 0; i < num_pats; i++) - pats.push_back(Z3_get_quantifier_pattern_ast(c,a,i)); - std::vector no_pats; - int num_no_pats = Z3_get_quantifier_num_patterns(c,a); - for(int i = 0; i < num_no_pats; i++) - no_pats.push_back(Z3_get_quantifier_no_pattern_ast(c,a,i)); - int bound = Z3_get_quantifier_num_bound(c,a); - std::vector sorts; - std::vector names; - for(int i = 0; i < bound; i++){ - sorts.push_back(Z3_get_quantifier_bound_sort(c,a,i)); - names.push_back(Z3_get_quantifier_bound_name(c,a,i)); - } - Z3_ast foo = Z3_mk_quantifier_ex(c, - Z3_is_quantifier_forall(c,a), - Z3_get_quantifier_weight(c,a), - 0, - 0, - num_pats, - VEC2PTR(pats), - num_no_pats, - VEC2PTR(no_pats), - bound, - VEC2PTR(sorts), - VEC2PTR(names), - new_body); - return expr(ctx,foo); -#endif - return clone_quantifier(t,new_body); - } - - - RPFP::Term RPFP::LocalizeRec(Edge *e, hash_map &memo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - hash_map::iterator it = e->varMap.find(t); - if (it != e->varMap.end()){ - res = it->second; - return res; - } - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - args.reserve(nargs); - for (int i = 0; i < nargs; i++) - args.push_back(LocalizeRec(e, memo, t.arg(i))); - hash_map::iterator rit = e->relMap.find(f); - if (rit != e->relMap.end()) - res = RedDualRela(e, args, (rit->second)); - else { - if (args.size() == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)) { - res = HideVariable(t, e->number); - } - else { - res = f(args.size(), VEC2PTR(args)); - } - } - } - else if (t.is_quantifier()) { - std::vector pats; - t.get_patterns(pats); - for (unsigned i = 0; i < pats.size(); i++) - pats[i] = LocalizeRec(e, memo, pats[i]); - Term body = LocalizeRec(e, memo, t.body()); - res = clone_quantifier(t, body, pats); - } - else res = t; - return res; - } - - void RPFP::SetEdgeMaps(Edge *e){ - timer_start("SetEdgeMaps"); - e->relMap.clear(); - e->varMap.clear(); - for(unsigned i = 0; i < e->F.RelParams.size(); i++){ - e->relMap[e->F.RelParams[i]] = i; - } - Term b(ctx); - std::vector v; - RedVars(e->Parent, b, v); - for(unsigned i = 0; i < e->F.IndParams.size(); i++){ - // func_decl parentParam = e->Parent.Annotation.IndParams[i]; - expr oldname = e->F.IndParams[i]; - expr newname = v[i]; - e->varMap[oldname] = newname; - } - timer_stop("SetEdgeMaps"); - - } - - - RPFP::Term RPFP::Localize(Edge *e, const Term &t){ - timer_start("Localize"); - hash_map memo; - if(e->F.IndParams.size() > 0 && e->varMap.empty()) - SetEdgeMaps(e); // TODO: why is this needed? - Term res = LocalizeRec(e,memo,t); - timer_stop("Localize"); - return res; - } - - - RPFP::Term RPFP::ReducedDualEdge(Edge *e) - { - SetEdgeMaps(e); - timer_start("RedVars"); - Term b(ctx); - std::vector v; - RedVars(e->Parent, b, v); - timer_stop("RedVars"); - // ast_to_track = (ast)b; - return implies(b, Localize(e, e->F.Formula)); - } - - TermTree *RPFP::ToTermTree(Node *root, Node *skip_descendant) - { - if(skip_descendant && root == skip_descendant) - return new TermTree(ctx.bool_val(true)); - Edge *e = root->Outgoing; - if(!e) return new TermTree(ctx.bool_val(true), std::vector()); - std::vector children(e->Children.size()); - for(unsigned i = 0; i < children.size(); i++) - children[i] = ToTermTree(e->Children[i],skip_descendant); - // Term top = ReducedDualEdge(e); - Term top = e->dual.null() ? ctx.bool_val(true) : e->dual; - TermTree *res = new TermTree(top, children); - for(unsigned i = 0; i < e->constraints.size(); i++) - res->addTerm(e->constraints[i]); - return res; - } - - TermTree *RPFP::GetGoalTree(Node *root){ - std::vector children(1); - children[0] = ToGoalTree(root); - return new TermTree(ctx.bool_val(false),children); - } - - TermTree *RPFP::ToGoalTree(Node *root) - { - Term b(ctx); - std::vector v; - RedVars(root, b, v); - Term goal = root->Name(v); - Edge *e = root->Outgoing; - if(!e) return new TermTree(goal, std::vector()); - std::vector children(e->Children.size()); - for(unsigned i = 0; i < children.size(); i++) - children[i] = ToGoalTree(e->Children[i]); - // Term top = ReducedDualEdge(e); - return new TermTree(goal, children); - } - - Z3User::Term Z3User::SubstRec(hash_map &memo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - args.reserve(nargs); - for (int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, t.arg(i))); - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()) { - std::vector pats; - t.get_patterns(pats); - for (unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstRec(memo, pats[i]); - Term body = SubstRec(memo, t.body()); - res = clone_quantifier(t, body, pats); - } - // res = CloneQuantifier(t,SubstRec(memo, t.body())); - else res = t; - return res; - } - - Z3User::Term Z3User::SubstRec(hash_map &memo, hash_map &map, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - args.reserve(nargs); - for (int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, map, t.arg(i))); - hash_map::iterator it = map.find(f); - if (it != map.end()) - f = it->second; - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()) { - std::vector pats; - t.get_patterns(pats); - for (unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstRec(memo, map, pats[i]); - Term body = SubstRec(memo, map, t.body()); - res = clone_quantifier(t, body, pats); - } - // res = CloneQuantifier(t,SubstRec(memo, t.body())); - else res = t; - return res; - } - - Z3User::Term Z3User::ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - args.reserve(nargs); - for (int i = 0; i < nargs; i++) - args.push_back(ExtractStores(memo, t.arg(i), cnstrs, renaming)); - res = f(args.size(), VEC2PTR(args)); - if (f.get_decl_kind() == Store) { - func_decl fresh = ctx.fresh_func_decl("@arr", res.get_sort()); - expr y = fresh(); - expr equ = ctx.make(Equal, y, res); - cnstrs.push_back(equ); - renaming[y] = res; - res = y; - } - } - else res = t; - return res; - } - - - bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ - if (!(lit.is_quantifier() && IsClosedFormula(lit))) { - if (!lit.is_app()) - return false; - decl_kind k = lit.decl().get_decl_kind(); - if (k == Not) { - if (IsLiteral(lit.arg(0), atom, val)) { - val = eq(val, ctx.bool_val(true)) ? ctx.bool_val(false) : ctx.bool_val(true); - return true; - } - return false; - } - if (k == And || k == Or || k == Iff || k == Implies) - return false; - } - atom = lit; - val = ctx.bool_val(true); - return true; - } - - expr Z3User::Negate(const expr &f){ - if(f.is_app() && f.decl().get_decl_kind() == Not) - return f.arg(0); - else if(eq(f,ctx.bool_val(true))) - return ctx.bool_val(false); - else if(eq(f,ctx.bool_val(false))) - return ctx.bool_val(true); - return !f; - } - - expr Z3User::ReduceAndOr(const std::vector &args, bool is_and, std::vector &res){ - for(unsigned i = 0; i < args.size(); i++) - if (!eq(args[i], ctx.bool_val(is_and))) { - if (eq(args[i], ctx.bool_val(!is_and))) - return ctx.bool_val(!is_and); - res.push_back(args[i]); - } - return expr(); - } - - expr Z3User::FinishAndOr(const std::vector &args, bool is_and){ - if(args.size() == 0) - return ctx.bool_val(is_and); - if(args.size() == 1) - return args[0]; - return ctx.make(is_and ? And : Or,args); - } - - expr Z3User::SimplifyAndOr(const std::vector &args, bool is_and){ - std::vector sargs; - expr res = ReduceAndOr(args,is_and,sargs); - if(!res.null()) return res; - return FinishAndOr(sargs,is_and); - } - - expr Z3User::PullCommonFactors(std::vector &args, bool is_and){ - - // first check if there's anything to do... - if(args.size() < 2) - return FinishAndOr(args,is_and); - for (unsigned i = 0; i < args.size(); i++) { - const expr &a = args[i]; - if (!(a.is_app() && a.decl().get_decl_kind() == (is_and ? Or : And))) - return FinishAndOr(args, is_and); - } - std::vector common; - for (unsigned i = 0; i < args.size(); i++) { - unsigned n = args[i].num_args(); - std::vector v(n), w; - for (unsigned j = 0; j < n; j++) - v[j] = args[i].arg(j); - std::less comp; - std::sort(v.begin(), v.end(), comp); - if (i == 0) - common.swap(v); - else { - std::set_intersection(common.begin(), common.end(), v.begin(), v.end(), std::inserter(w, w.begin()), comp); - common.swap(w); - } - } - if(common.empty()) - return FinishAndOr(args,is_and); - std::set common_set(common.begin(),common.end()); - for(unsigned i = 0; i < args.size(); i++){ - unsigned n = args[i].num_args(); - std::vector lits; - for (unsigned j = 0; j < n; j++) { - const expr b = args[i].arg(j); - if (common_set.find(b) == common_set.end()) - lits.push_back(b); - } - args[i] = SimplifyAndOr(lits,!is_and); - } - common.push_back(SimplifyAndOr(args,is_and)); - return SimplifyAndOr(common,!is_and); - } - - expr Z3User::ReallySimplifyAndOr(const std::vector &args, bool is_and){ - std::vector sargs; - expr res = ReduceAndOr(args,is_and,sargs); - if(!res.null()) return res; - return PullCommonFactors(sargs,is_and); - } - - Z3User::Term Z3User::SubstAtomTriv(const expr &foo, const expr &atom, const expr &val){ - if(eq(foo,atom)) - return val; - else if(foo.is_app() && foo.decl().get_decl_kind() == Not && eq(foo.arg(0),atom)) - return Negate(val); - else - return foo; - } - - Z3User::Term Z3User::PushQuantifier(const expr &t, const expr &body, bool is_forall){ - if (t.get_quantifier_num_bound() == 1) { - std::vector fmlas, free, not_free; - CollectJuncts(body, fmlas, is_forall ? Or : And, false); - for (unsigned i = 0; i < fmlas.size(); i++) { - const expr &fmla = fmlas[i]; - if (fmla.has_free(0)) - free.push_back(fmla); - else - not_free.push_back(fmla); - } - decl_kind op = is_forall ? Or : And; - if (free.empty()) - return DeleteBound(0, 1, SimplifyAndOr(not_free, op == And)); - expr q = clone_quantifier(is_forall ? Forall : Exists, t, SimplifyAndOr(free, op == And)); - if (!not_free.empty()) - q = ctx.make(op, q, DeleteBound(0, 1, SimplifyAndOr(not_free, op == And))); - return q; - } - return clone_quantifier(is_forall ? Forall : Exists,t,body); - } - - Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall) { - if (body.is_app()) { - if (body.decl().get_decl_kind() == (is_forall ? And : Or)) { // quantifier distributes - int nargs = body.num_args(); - std::vector args(nargs); - for (int i = 0; i < nargs; i++) - args[i] = CloneQuantAndSimp(t, body.arg(i), is_forall); - return SimplifyAndOr(args, body.decl().get_decl_kind() == And); - } - else if (body.decl().get_decl_kind() == (is_forall ? Or : And)) { // quantifier distributes - return PushQuantifier(t, body, is_forall); // may distribute partially - } - else if (body.decl().get_decl_kind() == Not) { - return ctx.make(Not, CloneQuantAndSimp(t, body.arg(0), !is_forall)); - } - } - if (t.get_quantifier_num_bound() == 1 && !body.has_free(0)) - return DeleteBound(0, 1, body); // drop the quantifier - return clone_quantifier(is_forall ? Forall : Exists, t, body); - } - - Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ - return CloneQuantAndSimp(t,body,t.is_quantifier_forall()); - } - - Z3User::Term Z3User::SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val) { - std::pair foo(t, expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if (!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - decl_kind k = f.get_decl_kind(); - - // TODO: recur here, but how much? We don't want to be quadractic in formula size - - if (k == And || k == Or) { - int nargs = t.num_args(); - std::vector args(nargs); - for (int i = 0; i < nargs; i++) - args[i] = SubstAtom(memo, t.arg(i), atom, val); - res = ReallySimplifyAndOr(args, k == And); - return res; - } - } - else if (t.is_quantifier() && atom.is_quantifier()) { - if (eq(t, atom)) - res = val; - else - res = clone_quantifier(t, SubstAtom(memo, t.body(), atom, val)); - return res; - } - res = SubstAtomTriv(t, atom, val); - return res; - } - - void Z3User::RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo) { - for (unsigned i = 0; i < args.size(); i++) { - const expr &lit = args[i]; - expr atom, val; - if (IsLiteral(lit, atom, val)) { - if (atom.is_app() && atom.decl().get_decl_kind() == Equal) - if (pol ? eq(val, ctx.bool_val(true)) : eq(val, ctx.bool_val(false))) { - expr lhs = atom.arg(0), rhs = atom.arg(1); - if (lhs.is_numeral()) - std::swap(lhs, rhs); - if (rhs.is_numeral() && lhs.is_app()) { - for (unsigned j = 0; j < args.size(); j++) - if (j != i) { - smemo.clear(); - smemo[lhs] = rhs; - args[j] = SubstRec(smemo, args[j]); - } - } - } - for (unsigned j = 0; j < args.size(); j++) - if (j != i) { - smemo.clear(); - args[j] = SubstAtom(smemo, args[j], atom, pol ? val : !val); - } - } - } - } - - - Z3User::Term Z3User::RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t) { - std::pair foo(t, expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if (!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - args.reserve(nargs); - for (int i = 0; i < nargs; i++) - args.push_back(RemoveRedundancyRec(memo, smemo, t.arg(i))); - - decl_kind k = f.get_decl_kind(); - if (k == And) { - RemoveRedundancyOp(true, args, smemo); - res = ReallySimplifyAndOr(args, true); - } - else if (k == Or) { - RemoveRedundancyOp(false, args, smemo); - res = ReallySimplifyAndOr(args, false); - } - else { - if (k == Equal && args[0].get_id() > args[1].get_id()) - std::swap(args[0], args[1]); - res = f(args.size(), VEC2PTR(args)); - } - } - else if (t.is_quantifier()) { - Term body = RemoveRedundancyRec(memo, smemo, t.body()); - res = CloneQuantAndSimp(t, body); - } - else res = t; - return res; - } - - Z3User::Term Z3User::RemoveRedundancy(const Term &t){ - hash_map memo; - hash_map smemo; - return RemoveRedundancyRec(memo,smemo,t); - } - - Z3User::Term Z3User::AdjustQuantifiers(const Term &t) - { - if(t.is_quantifier() || (t.is_app() && t.has_quantifiers())) - return t.qe_lite(); - return t; - } - - Z3User::Term Z3User::IneqToEqRec(hash_map &memo, const Term &t) { - std::pair foo(t, expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if (!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - args.reserve(nargs); - for (int i = 0; i < nargs; i++) - args.push_back(IneqToEqRec(memo, t.arg(i))); - - decl_kind k = f.get_decl_kind(); - if (k == And) { - for (int i = 0; i < nargs - 1; i++) { - if ((args[i].is_app() && args[i].decl().get_decl_kind() == Geq && - args[i + 1].is_app() && args[i + 1].decl().get_decl_kind() == Leq) - || - (args[i].is_app() && args[i].decl().get_decl_kind() == Leq && - args[i + 1].is_app() && args[i + 1].decl().get_decl_kind() == Geq)) - if (eq(args[i].arg(0), args[i + 1].arg(0)) && eq(args[i].arg(1), args[i + 1].arg(1))) { - args[i] = ctx.make(Equal, args[i].arg(0), args[i].arg(1)); - args[i + 1] = ctx.bool_val(true); - } - } - } - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()) { - Term body = IneqToEqRec(memo, t.body()); - res = clone_quantifier(t, body); - } - else res = t; - return res; - } - - Z3User::Term Z3User::IneqToEq(const Term &t){ - hash_map memo; - return IneqToEqRec(memo,t); - } - - Z3User::Term Z3User::SubstRecHide(hash_map &memo, const Term &t, int number) { - std::pair foo(t, expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if (!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted) { - std::string name = std::string("@q_") + t.decl().name().str() + "_" + string_of_int(number); - res = ctx.constant(name.c_str(), t.get_sort()); - return res; - } - args.reserve(nargs); - for (int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, t.arg(i))); - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()) - res = CloneQuantifier(t, SubstRec(memo, t.body())); - else res = t; - return res; - } - - RPFP::Term RPFP::SubstParams(const std::vector &from, - const std::vector &to, const Term &t) { - hash_map memo; - bool some_diff = false; - for (unsigned i = 0; i < from.size(); i++) - if (i < to.size() && !eq(from[i], to[i])) { - memo[from[i]] = to[i]; - some_diff = true; - } - return some_diff ? SubstRec(memo, t) : t; - } - - RPFP::Term RPFP::SubstParamsNoCapture(const std::vector &from, - const std::vector &to, const Term &t) { - hash_map memo; - bool some_diff = false; - for (unsigned i = 0; i < from.size(); i++) - if (i < to.size() && !eq(from[i], to[i])) { - memo[from[i]] = to[i]; - // if the new param is not being mapped to anything else, we need to rename it to prevent capture - // note, if the new param *is* mapped later in the list, it will override this substitution - const expr &w = to[i]; - if (memo.find(w) == memo.end()) { - std::string old_name = w.decl().name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); - expr y = fresh(); - memo[w] = y; - } - some_diff = true; - } - return some_diff ? SubstRec(memo, t) : t; - } - - - - RPFP::Transformer RPFP::Fuse(const std::vector &trs) { - assert(!trs.empty()); - const std::vector ¶ms = trs[0]->IndParams; - std::vector fmlas(trs.size()); - fmlas[0] = trs[0]->Formula; - for (unsigned i = 1; i < trs.size(); i++) - fmlas[i] = SubstParamsNoCapture(trs[i]->IndParams, params, trs[i]->Formula); - std::vector rel_params = trs[0]->RelParams; - for (unsigned i = 1; i < trs.size(); i++) { - const std::vector ¶ms2 = trs[i]->RelParams; - hash_map map; - for (unsigned j = 0; j < params2.size(); j++) { - func_decl rel = RenumberPred(params2[j], rel_params.size()); - rel_params.push_back(rel); - map[params2[j]] = rel; - } - hash_map memo; - fmlas[i] = SubstRec(memo, map, fmlas[i]); - } - return Transformer(rel_params, params, ctx.make(Or, fmlas), trs[0]->owner); - } - - - void Z3User::Strengthen(Term &x, const Term &y) - { - if (eq(x,ctx.bool_val(true))) - x = y; - else - x = x && y; - } - - void RPFP::SetAnnotation(Node *root, const expr &t){ - hash_map memo; - Term b; - std::vector v; - RedVars(root, b, v); - memo[b] = ctx.bool_val(true); - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = root->Annotation.IndParams[i]; - Term annot = SubstRec(memo, t); - // Strengthen(ref root.Annotation.Formula, annot); - root->Annotation.Formula = annot; - } - - void RPFP::DecodeTree(Node *root, TermTree *interp, int persist) { - std::vector &ic = interp->getChildren(); - if (ic.size() > 0) { - std::vector &nc = root->Outgoing->Children; - for (unsigned i = 0; i < nc.size(); i++) - DecodeTree(nc[i], ic[i], persist); - } - SetAnnotation(root, interp->getTerm()); -#if 0 - if(persist != 0) - Z3_persist_ast(ctx,root->Annotation.Formula,persist); -#endif - } - - RPFP::Term RPFP::GetUpperBound(Node *n) - { - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned int i = 0; i < v.size(); i++) - memo[n->Bound.IndParams[i]] = v[i]; - Term cnst = SubstRec(memo, n->Bound.Formula); - return b && !cnst; - } - - RPFP::Term RPFP::GetAnnotation(Node *n) - { - if(eq(n->Annotation.Formula,ctx.bool_val(true))) - return n->Annotation.Formula; - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[n->Annotation.IndParams[i]] = v[i]; - Term cnst = SubstRec(memo, n->Annotation.Formula); - return !b || cnst; - } - - RPFP::Term RPFP::GetUnderapprox(Node *n) - { - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[n->Underapprox.IndParams[i]] = v[i]; - Term cnst = SubstRecHide(memo, n->Underapprox.Formula, n->number); - return !b || cnst; - } - - TermTree *RPFP::AddUpperBound(Node *root, TermTree *t) - { - Term f = !((ast)(root->dual)) ? ctx.bool_val(true) : root->dual; - std::vector c(1); c[0] = t; - return new TermTree(f, c); - } - -#if 0 - void RPFP::WriteInterps(System.IO.StreamWriter f, TermTree t) - { - foreach (var c in t.getChildren()) - WriteInterps(f, c); - f.WriteLine(t.getTerm()); - } -#endif - - - expr RPFP::GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox) { - if (e->dual.null()) { - timer_start("ReducedDualEdge"); - e->dual = ReducedDualEdge(e); - timer_stop("ReducedDualEdge"); - timer_start("getting children"); - if (underapprox) { - std::vector cus(e->Children.size()); - for (unsigned i = 0; i < e->Children.size(); i++) - cus[i] = !UnderapproxFlag(e->Children[i]) || GetUnderapprox(e->Children[i]); - expr cnst = conjoin(cus); - e->dual = e->dual && cnst; - } - timer_stop("getting children"); - timer_start("Persisting"); - std::list::reverse_iterator it = stack.rbegin(); - for (int i = 0; i < persist && it != stack.rend(); i++) - it++; - if (it != stack.rend()) - it->edges.push_back(e); - timer_stop("Persisting"); - //Console.WriteLine("{0}", cnst); - } - return e->dual; - } - - /** For incremental solving, asserts the constraint associated - * with this edge in the SMT context. If this edge is removed, - * you must pop the context accordingly. The second argument is - * the number of pushes we are inside. The constraint formula - * will survive "persist" pops of the context. If you plan - * to reassert the edge after popping the context once, - * you can save time re-constructing the formula by setting - * "persist" to one. If you set "persist" too high, however, - * you could have a memory leak. - * - * The flag "with children" causes the annotations of the children - * to be asserted. The flag underapprox causes the underapproximations - * of the children to be asserted *conditionally*. See Check() on - * how to actually enforce these constraints. - * - */ - - void RPFP::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) { - if (eq(e->F.Formula, ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); - timer_start("solver add"); - slvr_add(e->dual); - timer_stop("solver add"); - if (with_children) - for (unsigned i = 0; i < e->Children.size(); i++) - ConstrainParent(e, e->Children[i]); - } - - -#ifdef LIMIT_STACK_WEIGHT - void RPFP_caching::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) - { - unsigned old_new_alits = new_alits.size(); - if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); - timer_start("solver add"); - slvr_add(e->dual); - timer_stop("solver add"); - if(old_new_alits < new_alits.size()) - weight_added.val++; - if(with_children) - for(unsigned i = 0; i < e->Children.size(); i++) - ConstrainParent(e,e->Children[i]); - } -#endif - - // caching verion of above - void RPFP_caching::AssertEdgeCache(Edge *e, std::vector &lits, bool with_children) { - if (eq(e->F.Formula, ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, 0, with_children, false); - GetAssumptionLits(fmla, lits); - if (with_children) - for (unsigned i = 0; i < e->Children.size(); i++) - ConstrainParentCache(e, e->Children[i], lits); - } - - void RPFP::slvr_add(const expr &e){ - slvr().add(e); - } - - void RPFP_caching::slvr_add(const expr &e){ - GetAssumptionLits(e,alit_stack); - } - - void RPFP::slvr_pop(int i){ - slvr().pop(i); - } - - void RPFP::slvr_push(){ - slvr().push(); - } - - void RPFP_caching::slvr_pop(int i){ - for(int j = 0; j < i; j++){ -#ifdef LIMIT_STACK_WEIGHT - if(alit_stack_sizes.empty()){ - if(big_stack.empty()) - throw "stack underflow"; - for(unsigned k = 0; k < new_alits.size(); k++){ - if(AssumptionLits.find(new_alits[k]) == AssumptionLits.end()) - throw "foo!"; - AssumptionLits.erase(new_alits[k]); - } - big_stack_entry &bsb = big_stack.back(); - bsb.alit_stack_sizes.swap(alit_stack_sizes); - bsb.alit_stack.swap(alit_stack); - bsb.new_alits.swap(new_alits); - bsb.weight_added.swap(weight_added); - big_stack.pop_back(); - slvr().pop(1); - continue; - } -#endif - alit_stack.resize(alit_stack_sizes.back()); - alit_stack_sizes.pop_back(); - } - } - - void RPFP_caching::slvr_push(){ -#ifdef LIMIT_STACK_WEIGHT - if(weight_added.val > LIMIT_STACK_WEIGHT){ - big_stack.resize(big_stack.size()+1); - big_stack_entry &bsb = big_stack.back(); - bsb.alit_stack_sizes.swap(alit_stack_sizes); - bsb.alit_stack.swap(alit_stack); - bsb.new_alits.swap(new_alits); - bsb.weight_added.swap(weight_added); - slvr().push(); - for(unsigned i = 0; i < bsb.alit_stack.size(); i++) - slvr().add(bsb.alit_stack[i]); - return; - } -#endif - alit_stack_sizes.push_back(alit_stack.size()); - } - - check_result RPFP::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ - return slvr().check(n, assumptions, core_size, core); - } - - check_result RPFP_caching::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ - unsigned oldsiz = alit_stack.size(); - if(n && assumptions) - std::copy(assumptions,assumptions+n,std::inserter(alit_stack,alit_stack.end())); - check_result res; - if (core_size && core) { - std::vector full_core(alit_stack.size()), core1(n); - std::copy(assumptions, assumptions + n, core1.begin()); - res = slvr().check(alit_stack.size(), VEC2PTR(alit_stack), core_size, VEC2PTR(full_core)); - full_core.resize(*core_size); - if (res == unsat) { - FilterCore(core1, full_core); - *core_size = core1.size(); - std::copy(core1.begin(), core1.end(), core); - } - } - else - res = slvr().check(alit_stack.size(), VEC2PTR(alit_stack)); - alit_stack.resize(oldsiz); - return res; - } - - lbool RPFP::ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals, - bool weak) { - return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); - } - - lbool RPFP_caching::ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals, - bool weak) { - GetTermTreeAssertionLiterals(assumptions); - return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); - } - - void RPFP_caching::GetTermTreeAssertionLiteralsRec(TermTree *assumptions){ - std::vector alits; - hash_map map; - GetAssumptionLits(assumptions->getTerm(),alits,&map); - std::vector &ts = assumptions->getTerms(); - for(unsigned i = 0; i < ts.size(); i++) - GetAssumptionLits(ts[i],alits,&map); - assumptions->setTerm(ctx.bool_val(true)); - ts = alits; - for(unsigned i = 0; i < alits.size(); i++) - ts.push_back(ctx.make(Implies,alits[i],map[alits[i]])); - for(unsigned i = 0; i < assumptions->getChildren().size(); i++) - GetTermTreeAssertionLiterals(assumptions->getChildren()[i]); - return; - } - - void RPFP_caching::GetTermTreeAssertionLiterals(TermTree *assumptions) { - // optimize binary case - if (assumptions->getChildren().size() == 1 - && assumptions->getChildren()[0]->getChildren().size() == 0) { - hash_map map; - TermTree *child = assumptions->getChildren()[0]; - std::vector dummy; - GetAssumptionLits(child->getTerm(), dummy, &map); - std::vector &ts = child->getTerms(); - for (unsigned i = 0; i < ts.size(); i++) - GetAssumptionLits(ts[i], dummy, &map); - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - if (!proof_core) { // save the proof core for later use - proof_core = new hash_set < ast > ; - for (unsigned i = 0; i < assumps.size(); i++) - proof_core->insert(assumps[i]); - } - std::vector *cnsts[2] = { &child->getTerms(), &assumptions->getTerms() }; - for (unsigned i = 0; i < assumps.size(); i++) { - expr &as = assumps[i]; - expr alit = (as.is_app() && as.decl().get_decl_kind() == Implies) ? as.arg(0) : as; - bool isA = map.find(alit) != map.end(); - cnsts[isA ? 0 : 1]->push_back(as); - } - } - else - GetTermTreeAssertionLiteralsRec(assumptions); - } - - void RPFP::AddToProofCore(hash_set &core){ - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++) - core.insert(assumps[i]); - } - - void RPFP::ComputeProofCore(){ - if(!proof_core){ - proof_core = new hash_set; - AddToProofCore(*proof_core); - } - } - - - void RPFP_caching::GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map) { - std::vector conjs; - CollectConjuncts(fmla, conjs); - for (unsigned i = 0; i < conjs.size(); i++) { - const expr &conj = conjs[i]; - std::pair foo(conj, expr(ctx)); - std::pair::iterator, bool> bar = AssumptionLits.insert(foo); - Term &res = bar.first->second; - if (bar.second) { - func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); - res = pred(); -#ifdef LIMIT_STACK_WEIGHT - new_alits.push_back(conj); -#endif - slvr().add(ctx.make(Implies, res, conj)); - // std::cout << res << ": " << conj << "\n"; - } - if (opt_map) - (*opt_map)[res] = conj; - lits.push_back(res); - } - } - - void RPFP::ConstrainParent(Edge *parent, Node *child){ - ConstrainEdgeLocalized(parent,GetAnnotation(child)); - } - - void RPFP_caching::ConstrainParentCache(Edge *parent, Node *child, std::vector &lits){ - ConstrainEdgeLocalizedCache(parent,GetAnnotation(child),lits); - } - - - /** For incremental solving, asserts the negation of the upper bound associated - * with a node. - * */ - - void RPFP::AssertNode(Node *n) - { - if (n->dual.null()) { - n->dual = GetUpperBound(n); - stack.back().nodes.push_back(n); - slvr_add(n->dual); - } - } - - // caching version of above - void RPFP_caching::AssertNodeCache(Node *n, std::vector lits){ - if (n->dual.null()) { - n->dual = GetUpperBound(n); - stack.back().nodes.push_back(n); - GetAssumptionLits(n->dual, lits); - } - } - - /** Clone another RPFP into this one, keeping a map */ - void RPFP_caching::Clone(RPFP *other) { -#if 0 - for(unsigned i = 0; i < other->nodes.size(); i++) - NodeCloneMap[other->nodes[i]] = CloneNode(other->nodes[i]); -#endif - for (unsigned i = 0; i < other->edges.size(); i++) { - Edge *edge = other->edges[i]; - Node *parent = CloneNode(edge->Parent); - std::vector cs; - for (unsigned j = 0; j < edge->Children.size(); j++) - // cs.push_back(NodeCloneMap[edge->Children[j]]); - cs.push_back(CloneNode(edge->Children[j])); - EdgeCloneMap[edge] = CreateEdge(parent, edge->F, cs); - } - } - - /** Get the clone of a node */ - RPFP::Node *RPFP_caching::GetNodeClone(Node *other_node){ - return NodeCloneMap[other_node]; - } - - /** Get the clone of an edge */ - RPFP::Edge *RPFP_caching::GetEdgeClone(Edge *other_edge){ - return EdgeCloneMap[other_edge]; - } - - /** check assumption lits, and return core */ - check_result RPFP_caching::CheckCore(const std::vector &assumps, std::vector &core){ - core.resize(assumps.size()); - unsigned core_size; - check_result res = slvr().check(assumps.size(),(expr *)VEC2PTR(assumps),&core_size, VEC2PTR(core)); - if(res == unsat) - core.resize(core_size); - else - core.clear(); - return res; - } - - - /** Assert a constraint on an edge in the SMT context. - */ - - void RPFP::ConstrainEdge(Edge *e, const Term &t) - { - Term tl = Localize(e, t); - ConstrainEdgeLocalized(e,tl); - } - - void RPFP::ConstrainEdgeLocalized(Edge *e, const Term &tl) - { - e->constraints.push_back(tl); - stack.back().constraints.push_back(std::pair(e,tl)); - slvr_add(tl); - } - - void RPFP_caching::ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits) - { - e->constraints.push_back(tl); - stack.back().constraints.push_back(std::pair(e,tl)); - GetAssumptionLits(tl,lits); - } - - - /** Declare a constant in the background theory. */ - - void RPFP::DeclareConstant(const FuncDecl &f){ - ls->declare_constant(f); - } - - /** Assert a background axiom. Background axioms can be used to provide the - * theory of auxilliary functions or relations. All symbols appearing in - * background axioms are considered global, and may appear in both transformer - * and relational solutions. Semantically, a solution to the RPFP gives - * an interpretation of the unknown relations for each interpretation of the - * auxilliary symbols that is consistent with the axioms. Axioms should be - * asserted before any calls to Push. They cannot be de-asserted by Pop. */ - - void RPFP::AssertAxiom(const Term &t) - { - ls->assert_axiom(t); - axioms.push_back(t); // for printing only - } - -#if 0 - /** Do not call this. */ - - void RPFP::RemoveAxiom(const Term &t) - { - slvr().RemoveInterpolationAxiom(t); - } -#endif - - /** Solve an RPFP graph. This means either strengthen the annotation - * so that the bound at the given root node is satisfied, or - * show that this cannot be done by giving a dual solution - * (i.e., a counterexample). - * - * In the current implementation, this only works for graphs that - * are: - * - tree-like - * - * - closed. - * - * In a tree-like graph, every nod has out most one incoming and one out-going edge, - * and there are no cycles. In a closed graph, every node has exactly one out-going - * edge. This means that the leaves of the tree are all hyper-edges with no - * children. Such an edge represents a relation (nullary transformer) and thus - * a lower bound on its parent. The parameter root must be the root of this tree. - * - * If Solve returns LBool.False, this indicates success. The annotation of the tree - * has been updated to satisfy the upper bound at the root. - * - * If Solve returns LBool.True, this indicates a counterexample. For each edge, - * you can then call Eval to determine the values of symbols in the transformer formula. - * You can also call Empty on a node to determine if its value in the counterexample - * is the empty relation. - * - * \param root The root of the tree - * \param persist Number of context pops through which result should persist - * - * - */ - - lbool RPFP::Solve(Node *root, int persist) - { - timer_start("Solve"); - TermTree *tree = GetConstraintTree(root); - TermTree *interpolant = nullptr; - TermTree *goals = nullptr; - if(ls->need_goals) - goals = GetGoalTree(root); - ClearProofCore(); - - // if (dualModel != null) dualModel.Dispose(); - // if (dualLabels != null) dualLabels.Dispose(); - - timer_start("interpolate_tree"); - lbool res = ls_interpolate_tree(tree, interpolant, dualModel,goals,true); - timer_stop("interpolate_tree"); - if (res == l_false) { - DecodeTree(root, interpolant->getChildren()[0], persist); - delete interpolant; - } - - delete tree; - if(goals) - delete goals; - - timer_stop("Solve"); - return res; - } - - void RPFP::CollapseTermTreeRec(TermTree *root, TermTree *node){ - root->addTerm(node->getTerm()); - std::vector &cnsts = node->getTerms(); - for(unsigned i = 0; i < cnsts.size(); i++) - root->addTerm(cnsts[i]); - std::vector &chs = node->getChildren(); - for(unsigned i = 0; i < chs.size(); i++){ - CollapseTermTreeRec(root,chs[i]); - } - } - - TermTree *RPFP::CollapseTermTree(TermTree *node){ - std::vector &chs = node->getChildren(); - for(unsigned i = 0; i < chs.size(); i++) - CollapseTermTreeRec(node,chs[i]); - for(unsigned i = 0; i < chs.size(); i++) - delete chs[i]; - chs.clear(); - return node; - } - - lbool RPFP::SolveSingleNode(Node *root, Node *node) - { - timer_start("Solve"); - TermTree *tree = CollapseTermTree(GetConstraintTree(root,node)); - tree->getChildren().push_back(CollapseTermTree(ToTermTree(node))); - TermTree *interpolant = nullptr; - ClearProofCore(); - - timer_start("interpolate_tree"); - lbool res = ls_interpolate_tree(tree, interpolant, dualModel,nullptr,true); - timer_stop("interpolate_tree"); - if (res == l_false) { - DecodeTree(node, interpolant->getChildren()[0], 0); - delete interpolant; - } - - delete tree; - timer_stop("Solve"); - return res; - } - - /** Get the constraint tree (but don't solve it) */ - - TermTree *RPFP::GetConstraintTree(Node *root, Node *skip_descendant) - { - return AddUpperBound(root, ToTermTree(root,skip_descendant)); - } - - /** Dispose of the dual model (counterexample) if there is one. */ - - void RPFP::DisposeDualModel() - { - dualModel = model(ctx,nullptr); - } - - RPFP::Term RPFP::UnderapproxFlag(Node *n){ - expr flag = ctx.constant((std::string("@under") + string_of_int(n->number)).c_str(), ctx.bool_sort()); - underapprox_flag_rev[flag] = n; - return flag; - } - - RPFP::Node *RPFP::UnderapproxFlagRev(const Term &flag){ - return underapprox_flag_rev[flag]; - } - - /** Check satisfiability of asserted edges and nodes. Same functionality as - * Solve, except no primal solution (interpolant) is generated in the unsat case. - * The vector underapproxes gives the set of node underapproximations to be enforced - * (assuming these were conditionally asserted by AssertEdge). - * - */ - - check_result RPFP::Check(Node *root, std::vector underapproxes, std::vector *underapprox_core) { - timer_start("Check"); - ClearProofCore(); - // if (dualModel != null) dualModel.Dispose(); - check_result res; - if (!underapproxes.size()) - res = slvr_check(); - else { - std::vector us(underapproxes.size()); - for (unsigned i = 0; i < underapproxes.size(); i++) - us[i] = UnderapproxFlag(underapproxes[i]); - slvr_check(); // TODO: no idea why I need to do this - if (underapprox_core) { - std::vector unsat_core(us.size()); - unsigned core_size = 0; - res = slvr_check(us.size(), VEC2PTR(us), &core_size, VEC2PTR(unsat_core)); - underapprox_core->resize(core_size); - for (unsigned i = 0; i < core_size; i++) - (*underapprox_core)[i] = UnderapproxFlagRev(unsat_core[i]); - } - else { - res = slvr_check(us.size(), VEC2PTR(us)); - bool dump = false; - if (dump) { - std::vector cnsts; - // cnsts.push_back(axioms[0]); - cnsts.push_back(root->dual); - cnsts.push_back(root->Outgoing->dual); - ls->write_interpolation_problem("temp.smt", cnsts, std::vector()); - } - } - // check_result temp = slvr_check(); - } - dualModel = slvr().get_model(); - timer_stop("Check"); - return res; - } - - check_result RPFP::CheckUpdateModel(Node *root, std::vector assumps){ - // check_result temp1 = slvr_check(); // no idea why I need to do this - ClearProofCore(); - check_result res = slvr_check(assumps.size(), VEC2PTR(assumps)); - model mod = slvr().get_model(); - if(!mod.null()) - dualModel = mod;; - return res; - } - - /** Determines the value in the counterexample of a symbol occurring in the transformer formula of - * a given edge. */ - - RPFP::Term RPFP::Eval(Edge *e, const Term& t) - { - Term tl = Localize(e, t); - return dualModel.eval(tl); - } - - /** Returns true if the given node is empty in the primal solution. For proceudure summaries, - this means that the procedure is not called in the current counter-model. */ - - bool RPFP::Empty(Node *p) - { - Term b; std::vector v; - RedVars(p, b, v); - // dualModel.show_internals(); - // std::cout << "b: " << b << std::endl; - expr bv = dualModel.eval(b); - // std::cout << "bv: " << bv << std::endl; - bool res = !eq(bv,ctx.bool_val(true)); - return res; - } - - RPFP::Term RPFP::EvalNode(Node *p) - { - Term b; std::vector v; - RedVars(p, b, v); - std::vector args; - for(unsigned i = 0; i < v.size(); i++) - args.push_back(dualModel.eval(v[i])); - return (p->Name)(args); - } - - void RPFP::EvalArrayTerm(const RPFP::Term &t, ArrayValue &res){ - if (t.is_app()) { - decl_kind k = t.decl().get_decl_kind(); - if (k == AsArray) { - func_decl fd = t.decl().get_func_decl_parameter(0); - func_interp r = dualModel.get_func_interp(fd); - int num = r.num_entries(); - res.defined = true; - for (int i = 0; i < num; i++) { - expr arg = r.get_arg(i, 0); - expr value = r.get_value(i); - res.entries[arg] = value; - } - res.def_val = r.else_value(); - return; - } - else if (k == Store) { - EvalArrayTerm(t.arg(0), res); - if (!res.defined)return; - expr addr = t.arg(1); - expr val = t.arg(2); - if (addr.is_numeral() && val.is_numeral()) { - if (eq(val, res.def_val)) - res.entries.erase(addr); - else - res.entries[addr] = val; - } - else - res.defined = false; - return; - } - } - res.defined = false; - } - - int eae_count = 0; - - RPFP::Term RPFP::EvalArrayEquality(const RPFP::Term &f) { - ArrayValue lhs, rhs; - eae_count++; - EvalArrayTerm(f.arg(0), lhs); - EvalArrayTerm(f.arg(1), rhs); - if (lhs.defined && rhs.defined) { - if (eq(lhs.def_val, rhs.def_val)) - if (lhs.entries == rhs.entries) - return ctx.bool_val(true); - return ctx.bool_val(false); - } - return f; - } - - /** Compute truth values of all boolean subterms in current model. - Assumes formulas has been simplified by Z3, so only boolean ops - ands and, or, not. Returns result in memo. - */ - - int RPFP::SubtermTruth(hash_map &memo, const Term &f) { - TRACE("duality", tout << f << "\n";); - if (memo.find(f) != memo.end()) { - return memo[f]; - } - int res; - if (f.is_app()) { - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if (k == Implies) { - res = SubtermTruth(memo, !f.arg(0) || f.arg(1)); - goto done; - } - if (k == And) { - res = 1; - for (int i = 0; i < nargs; i++) { - int ar = SubtermTruth(memo, f.arg(i)); - if (ar == 0) { - res = 0; - goto done; - } - if (ar == 2)res = 2; - } - goto done; - } - else if (k == Or) { - res = 0; - for (int i = 0; i < nargs; i++) { - int ar = SubtermTruth(memo, f.arg(i)); - if (ar == 1) { - res = 1; - goto done; - } - if (ar == 2)res = 2; - } - goto done; - } - else if (k == Not) { - int ar = SubtermTruth(memo, f.arg(0)); - res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); - goto done; - } - } - { - bool pos; std::vector names; - if (f.is_label(pos, names)) { - res = SubtermTruth(memo, f.arg(0)); - goto done; - } - } - { - expr bv = dualModel.eval(f); - if (bv.is_app() && bv.decl().get_decl_kind() == Equal && - bv.arg(0).is_array()) { - bv = EvalArrayEquality(bv); - } - // Hack!!!! array equalities can occur negatively! - if (bv.is_app() && bv.decl().get_decl_kind() == Not && - bv.arg(0).decl().get_decl_kind() == Equal && - bv.arg(0).arg(0).is_array()) { - bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); - } - if (eq(bv, ctx.bool_val(true))) - res = 1; - else if (eq(bv, ctx.bool_val(false))) - res = 0; - else - res = 2; - } - done: - memo[f] = res; - return res; - } - - int RPFP::EvalTruth(hash_map &memo, Edge *e, const Term &f){ - Term tl = Localize(e, f); - return SubtermTruth(memo,tl); - } - - /** Compute truth values of all boolean subterms in current model. - Assumes formulas has been simplified by Z3, so only boolean ops - ands and, or, not. Returns result in memo. - */ - - - void RPFP::GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, - hash_set *done, bool truth) { - if (done[truth].find(f) != done[truth].end()) - return; /* already processed */ - if (f.is_app()) { - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if (k == Implies) { - GetLabelsRec(memo, f.arg(1) || !f.arg(0), labels, done, truth); - goto done; - } - if (k == Iff) { - int b = SubtermTruth(memo, f.arg(0)); - if (b == 2) { - goto done; - // bypass error - std::ostringstream os; - os << "disaster in SubtermTruth processing " << f; - throw default_exception(os.str()); - } - GetLabelsRec(memo, f.arg(1), labels, done, truth ? b : !b); - goto done; - } - if (truth ? k == And : k == Or) { - for (int i = 0; i < nargs; i++) - GetLabelsRec(memo, f.arg(i), labels, done, truth); - goto done; - } - if (truth ? k == Or : k == And) { - for (int i = 0; i < nargs; i++) { - Term a = f.arg(i); - timer_start("SubtermTruth"); - int b = SubtermTruth(memo, a); - timer_stop("SubtermTruth"); - if (truth ? (b == 1) : (b == 0)) { - GetLabelsRec(memo, a, labels, done, truth); - goto done; - } - } - /* Unreachable! */ - // throw "error in RPFP::GetLabelsRec"; - goto done; - } - else if (k == Not) { - GetLabelsRec(memo, f.arg(0), labels, done, !truth); - goto done; - } - else { - bool pos; std::vector names; - if (f.is_label(pos, names)) { - GetLabelsRec(memo, f.arg(0), labels, done, truth); - if (pos == truth) - for (unsigned i = 0; i < names.size(); i++) - labels.push_back(names[i]); - goto done; - } - } - } - done: - done[truth].insert(f); - } - - void RPFP::GetLabels(Edge *e, std::vector &labels){ - if(!e->map || e->map->labeled.null()) - return; - Term tl = Localize(e, e->map->labeled); - hash_map memo; - hash_set done[2]; - GetLabelsRec(memo,tl,labels,done,true); - } - -#ifdef Z3OPS - static Z3_subterm_truth *stt; -#endif - - int ir_count = 0; - - void RPFP::ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set *done, bool truth, hash_set &dont_cares) { - if (done[truth].find(f) != done[truth].end()) - return; /* already processed */ -#if 0 - int this_count = ir_count++; - if(this_count == 50092) - std::cout << "foo!\n"; -#endif - if (f.is_app()) { - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if (k == Implies) { - ImplicantRed(memo, f.arg(1) || !f.arg(0), lits, done, truth, dont_cares); - goto done; - } - if (k == Iff) { - int b = SubtermTruth(memo, f.arg(0)); - if (b == 2) { - std::ostringstream os; - os << "disaster in SubtermTruth processing " << f; - throw default_exception(os.str()); - } - ImplicantRed(memo, f.arg(1), lits, done, truth ? b : !b, dont_cares); - goto done; - } - if (truth ? k == And : k == Or) { - for (int i = 0; i < nargs; i++) - ImplicantRed(memo, f.arg(i), lits, done, truth, dont_cares); - goto done; - } - if (truth ? k == Or : k == And) { - for (int i = 0; i < nargs; i++) { - Term a = f.arg(i); -#if 0 - if(i == nargs - 1){ // last chance! - ImplicantRed(memo,a,lits,done,truth,dont_cares); - goto done; - } -#endif - timer_start("SubtermTruth"); -#ifdef Z3OPS - bool b = stt->eval(a); -#else - int b = SubtermTruth(memo, a); -#endif - timer_stop("SubtermTruth"); - if (truth ? (b == 1) : (b == 0)) { - ImplicantRed(memo, a, lits, done, truth, dont_cares); - goto done; - } - } - /* Unreachable! */ - // TODO: need to indicate this failure to caller - // std::cerr << "error in RPFP::ImplicantRed"; - goto done; - } - else if (k == Not) { - ImplicantRed(memo, f.arg(0), lits, done, !truth, dont_cares); - goto done; - } - } - { - if (dont_cares.find(f) == dont_cares.end()) { - expr rf = ResolveIte(memo, f, lits, done, dont_cares); - expr bv = truth ? rf : !rf; - lits.push_back(bv); - } - } - done: - done[truth].insert(f); - } - - void RPFP::ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares, bool extensional) { - if (done.find(f) != done.end()) - return; /* already processed */ - if (f.is_app()) { - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if (k == Implies || k == Iff || k == And || k == Or || k == Not) { - for (int i = 0; i < nargs; i++) - ImplicantFullRed(memo, f.arg(i), lits, done, dont_cares, extensional); - goto done; - } - } - { - if (dont_cares.find(f) == dont_cares.end()) { - int b = SubtermTruth(memo, f); - if (b != 0 && b != 1) goto done; - if (f.is_app() && f.decl().get_decl_kind() == Equal && f.arg(0).is_array()) { - if (b == 1 && !extensional) { - expr x = dualModel.eval(f.arg(0)); expr y = dualModel.eval(f.arg(1)); - if (!eq(x, y)) - b = 0; - } - if (b == 0) - goto done; - } - expr bv = (b == 1) ? f : !f; - lits.push_back(bv); - } - } - done: - done.insert(f); - } - - RPFP::Term RPFP::ResolveIte(hash_map &memo, const Term &t, std::vector &lits, - hash_set *done, hash_set &dont_cares) { - if (resolve_ite_memo.find(t) != resolve_ite_memo.end()) - return resolve_ite_memo[t]; - Term res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if (f.get_decl_kind() == Ite) { - timer_start("SubtermTruth"); -#ifdef Z3OPS - bool sel = stt->eval(t.arg(0)); -#else - int xval = SubtermTruth(memo, t.arg(0)); - bool sel; - if (xval == 0)sel = false; - else if (xval == 1)sel = true; - else - throw "unresolved ite in model"; -#endif - timer_stop("SubtermTruth"); - ImplicantRed(memo, t.arg(0), lits, done, sel, dont_cares); - res = ResolveIte(memo, t.arg(sel ? 1 : 2), lits, done, dont_cares); - } - else { - for (int i = 0; i < nargs; i++) - args.push_back(ResolveIte(memo, t.arg(i), lits, done, dont_cares)); - res = f(args.size(), VEC2PTR(args)); - } - } - else res = t; - resolve_ite_memo[t] = res; - return res; - } - - RPFP::Term RPFP::ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts) { - std::pair foo(t, expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if (bar.second) { - if (t.is_app()) { - int nargs = t.num_args(); - std::vector args; - if (t.decl().get_decl_kind() == Equal) { - expr lhs = t.arg(0); - expr rhs = t.arg(1); - if (rhs.decl().get_decl_kind() == Ite) { - expr rhs_args[3]; - lhs = ElimIteRec(memo, lhs, cnsts); - for (int i = 0; i < 3; i++) - rhs_args[i] = ElimIteRec(memo, rhs.arg(i), cnsts); - res = (rhs_args[0] && (lhs == rhs_args[1])) || ((!rhs_args[0]) && (lhs == rhs_args[2])); - goto done; - } - } - if (t.decl().get_decl_kind() == Ite) { - func_decl sym = ctx.fresh_func_decl("@ite", t.get_sort()); - res = sym(); - cnsts.push_back(ElimIteRec(memo, ctx.make(Equal, res, t), cnsts)); - } - else { - for (int i = 0; i < nargs; i++) - args.push_back(ElimIteRec(memo, t.arg(i), cnsts)); - res = t.decl()(args.size(), VEC2PTR(args)); - } - } - else if (t.is_quantifier()) - res = clone_quantifier(t, ElimIteRec(memo, t.body(), cnsts)); - else - res = t; - } - done: - return res; - } - - RPFP::Term RPFP::ElimIte(const Term &t){ - hash_map memo; - std::vector cnsts; - expr res = ElimIteRec(memo,t,cnsts); - if(!cnsts.empty()){ - cnsts.push_back(res); - res = ctx.make(And,cnsts); - } - return res; - } - - void RPFP::Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares){ - hash_set done[2]; - ImplicantRed(memo,f,lits,done,true, dont_cares); - } - - - /** Underapproximate a formula using current counterexample. */ - - RPFP::Term RPFP::UnderapproxFormula(const Term &f, hash_set &dont_cares){ - /* first compute truth values of subterms */ - hash_map memo; -#ifdef Z3OPS - stt = Z3_mk_subterm_truth(ctx,dualModel); -#endif - // SubtermTruth(memo,f); - /* now compute an implicant */ - std::vector lits; - Implicant(memo,f,lits, dont_cares); -#ifdef Z3OPS - delete stt; stt = 0; -#endif - /* return conjunction of literals */ - return conjoin(lits); - } - - RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, bool extensional){ - hash_set dont_cares; - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - /* first compute truth values of subterms */ - hash_map memo; - hash_set done; - std::vector lits; - ImplicantFullRed(memo,f,lits,done,dont_cares, extensional); - timer_stop("UnderapproxFormula"); - /* return conjunction of literals */ - return conjoin(lits); - } - - struct VariableProjector : Z3User { - - struct elim_cand { - Term var; - int sup; - Term val; - }; - - typedef expr Term; - - hash_set keep; - hash_map var_ord; - int num_vars; - std::vector elim_cands; - hash_map > sup_map; - hash_map elim_map; - std::vector ready_cands; - hash_map cand_map; - params simp_params; - - VariableProjector(Z3User &_user, std::vector &keep_vec) : - Z3User(_user), simp_params() { - num_vars = 0; - for (unsigned i = 0; i < keep_vec.size(); i++) { - keep.insert(keep_vec[i]); - var_ord[keep_vec[i]] = num_vars++; - } - } - int VarNum(const Term &v) { - if (var_ord.find(v) == var_ord.end()) - var_ord[v] = num_vars++; - return var_ord[v]; - } - - bool IsVar(const Term &t){ - return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; - } - - bool IsPropLit(const Term &t, Term &a) { - if (IsVar(t)) { - a = t; - return true; - } - else if (t.is_app() && t.decl().get_decl_kind() == Not) - return IsPropLit(t.arg(0), a); - return false; - } - - void CountOtherVarsRec(hash_map &memo, - const Term &t, - int id, - int &count) { - std::pair foo(t, 0); - std::pair::iterator, bool> bar = memo.insert(foo); - // int &res = bar.first->second; - if (!bar.second) return; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted) { - if (cand_map.find(t) != cand_map.end()) { - count++; - sup_map[t].push_back(id); - } - } - for (int i = 0; i < nargs; i++) - CountOtherVarsRec(memo, t.arg(i), id, count); - } - else if (t.is_quantifier()) - CountOtherVarsRec(memo, t.body(), id, count); - } - - void NewElimCand(const Term &lhs, const Term &rhs) { - if (debug_gauss) { - std::cout << "mapping " << lhs << " to " << rhs << std::endl; - } - elim_cand cand; - cand.var = lhs; - cand.sup = 0; - cand.val = rhs; - elim_cands.push_back(cand); - cand_map[lhs] = elim_cands.size() - 1; - } - - void MakeElimCand(const Term &lhs, const Term &rhs) { - if (eq(lhs, rhs)) - return; - if (!IsVar(lhs)) { - if (IsVar(rhs)) { - MakeElimCand(rhs, lhs); - return; - } - else { - std::cout << "would have mapped a non-var\n"; - return; - } - } - if (IsVar(rhs) && VarNum(rhs) > VarNum(lhs)) { - MakeElimCand(rhs, lhs); - return; - } - if (keep.find(lhs) != keep.end()) - return; - if (cand_map.find(lhs) == cand_map.end()) - NewElimCand(lhs, rhs); - else { - int cand_idx = cand_map[lhs]; - if (IsVar(rhs) && cand_map.find(rhs) == cand_map.end() - && keep.find(rhs) == keep.end()) - NewElimCand(rhs, elim_cands[cand_idx].val); - elim_cands[cand_idx].val = rhs; - } - } - - Term FindRep(const Term &t) { - if (cand_map.find(t) == cand_map.end()) - return t; - Term &res = elim_cands[cand_map[t]].val; - if (IsVar(res)) { - assert(VarNum(res) < VarNum(t)); - res = FindRep(res); - return res; - } - return t; - } - - void GaussElimCheap(const std::vector &lits_in, - std::vector &lits_out) { - for (unsigned i = 0; i < lits_in.size(); i++) { - Term lit = lits_in[i]; - if (lit.is_app()) { - decl_kind k = lit.decl().get_decl_kind(); - if (k == Equal || k == Iff) - MakeElimCand(FindRep(lit.arg(0)), FindRep(lit.arg(1))); - } - } - - for (unsigned i = 0; i < elim_cands.size(); i++) { - elim_cand &cand = elim_cands[i]; - hash_map memo; - CountOtherVarsRec(memo, cand.val, i, cand.sup); - if (cand.sup == 0) - ready_cands.push_back(i); - } - - while (!ready_cands.empty()) { - elim_cand &cand = elim_cands[ready_cands.back()]; - ready_cands.pop_back(); - Term rep = FindRep(cand.var); - if (!eq(rep, cand.var)) - if (cand_map.find(rep) != cand_map.end()) { - int rep_pos = cand_map[rep]; - cand.val = elim_cands[rep_pos].val; - } - Term val = SubstRec(elim_map, cand.val); - if (debug_gauss) { - std::cout << "subbing " << cand.var << " --> " << val << std::endl; - } - elim_map[cand.var] = val; - std::vector &sup = sup_map[cand.var]; - for (unsigned i = 0; i < sup.size(); i++) { - int c = sup[i]; - if ((--elim_cands[c].sup) == 0) - ready_cands.push_back(c); - } - } - - for (unsigned i = 0; i < lits_in.size(); i++) { - Term lit = lits_in[i]; - lit = SubstRec(elim_map, lit); - lit = lit.simplify(); - if (eq(lit, ctx.bool_val(true))) - continue; - Term a; - if (IsPropLit(lit, a)) - if (keep.find(lit) == keep.end()) - continue; - lits_out.push_back(lit); - } - } - - // maps variables to constrains in which the occur pos, neg - hash_map la_index[2]; - hash_map la_coeffs[2]; - std::vector la_pos_vars; - bool fixing; - - void IndexLAcoeff(const Term &coeff1, const Term &coeff2, const Term &t, int id) { - Term coeff = coeff1 * coeff2; - coeff = coeff.simplify(); - Term is_pos = (coeff >= ctx.int_val(0)); - is_pos = is_pos.simplify(); - if (eq(is_pos, ctx.bool_val(true))) - IndexLA(true, coeff, t, id); - else - IndexLA(false, coeff, t, id); - } - - void IndexLAremove(const Term &t) { - if (IsVar(t)) { - la_index[0][t] = -1; // means ineligible to be eliminated - la_index[1][t] = -1; // (more that one occurrence, or occurs not in linear comb) - } - else if (t.is_app()) { - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - IndexLAremove(t.arg(i)); - } - // TODO: quantifiers? - } - - - void IndexLA(bool pos, const Term &coeff, const Term &t, int id) { - if (t.is_numeral()) - return; - if (t.is_app()) { - int nargs = t.num_args(); - switch (t.decl().get_decl_kind()) { - case Plus: - for (int i = 0; i < nargs; i++) - IndexLA(pos, coeff, t.arg(i), id); - break; - case Sub: - IndexLA(pos, coeff, t.arg(0), id); - IndexLA(!pos, coeff, t.arg(1), id); - break; - case Times: - if (t.arg(0).is_numeral()) - IndexLAcoeff(coeff, t.arg(0), t.arg(1), id); - else if (t.arg(1).is_numeral()) - IndexLAcoeff(coeff, t.arg(1), t.arg(0), id); - break; - default: - if (IsVar(t) && (fixing || la_index[pos].find(t) == la_index[pos].end())) { - la_index[pos][t] = id; - la_coeffs[pos][t] = coeff; - if (pos && !fixing) - la_pos_vars.push_back(t); // this means we only add a var once - } - else - IndexLAremove(t); - } - } - } - - void IndexLAstart(bool pos, const Term &t, int id){ - IndexLA(pos,(pos ? ctx.int_val(1) : ctx.int_val(-1)), t, id); - } - - void IndexLApred(bool pos, const Term &p, int id) { - if (p.is_app()) { - switch (p.decl().get_decl_kind()) { - case Not: - IndexLApred(!pos, p.arg(0), id); - break; - case Leq: - case Lt: - IndexLAstart(!pos, p.arg(0), id); - IndexLAstart(pos, p.arg(1), id); - break; - case Geq: - case Gt: - IndexLAstart(pos, p.arg(0), id); - IndexLAstart(!pos, p.arg(1), id); - break; - default: - IndexLAremove(p); - } - } - } - - void IndexLAfix(const Term &p, int id){ - fixing = true; - IndexLApred(true,p,id); - fixing = false; - } - - bool IsCanonIneq(const Term &lit, Term &term, Term &bound) { - // std::cout << Z3_simplify_get_help(ctx) << std::endl; - bool pos = lit.decl().get_decl_kind() != Not; - Term atom = pos ? lit : lit.arg(0); - if (atom.decl().get_decl_kind() == Leq) { - if (pos) { - bound = atom.arg(0); - term = atom.arg(1).simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - else { - bound = (atom.arg(1) + ctx.int_val(1)); - term = atom.arg(0); - // std::cout << "simplifying bound: " << bound << std::endl; - bound = bound.simplify(); - term = term.simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - return true; - } - else if (atom.decl().get_decl_kind() == Geq) { - if (pos) { - bound = atom.arg(1); // integer axiom - term = atom.arg(0).simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - return true; - } - else { - bound = -(atom.arg(1) - ctx.int_val(1)); // integer axiom - term = -atom.arg(0); - bound = bound.simplify(); - term = term.simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - return true; - } - return false; - } - - Term CanonIneqTerm(const Term &p){ - Term term,bound; - Term ps = p.simplify(); - VERIFY(IsCanonIneq(ps,term,bound)); - return term - bound; - } - - void ElimRedundantBounds(std::vector &lits) { - hash_map best_bound; - for (unsigned i = 0; i < lits.size(); i++) { - lits[i] = lits[i].simplify(simp_params); - Term term, bound; - if (IsCanonIneq(lits[i], term, bound)) { - if (best_bound.find(term) == best_bound.end()) - best_bound[term] = i; - else { - int best = best_bound[term]; - Term bterm, bbound; - IsCanonIneq(lits[best], bterm, bbound); - Term comp = bound > bbound; - comp = comp.simplify(); - if (eq(comp, ctx.bool_val(true))) { - lits[best] = ctx.bool_val(true); - best_bound[term] = i; - } - else { - lits[i] = ctx.bool_val(true); - } - } - } - } - } - - void FourierMotzkinCheap(const std::vector &lits_in, - std::vector &lits_out) { - simp_params.set(":som", true); - simp_params.set(":sort-sums", true); - fixing = false; lits_out = lits_in; - ElimRedundantBounds(lits_out); - for (unsigned i = 0; i < lits_out.size(); i++) - IndexLApred(true, lits_out[i], i); - - for (unsigned i = 0; i < la_pos_vars.size(); i++) { - Term var = la_pos_vars[i]; - if (la_index[false].find(var) != la_index[false].end()) { - int pos_idx = la_index[true][var]; - int neg_idx = la_index[false][var]; - if (pos_idx >= 0 && neg_idx >= 0) { - if (keep.find(var) != keep.end()) { - std::cout << "would have eliminated keep var\n"; - continue; - } - Term tpos = CanonIneqTerm(lits_out[pos_idx]); - Term tneg = CanonIneqTerm(lits_out[neg_idx]); - Term pos_coeff = la_coeffs[true][var]; - Term neg_coeff = -la_coeffs[false][var]; - Term comb = neg_coeff * tpos + pos_coeff * tneg; - Term ineq = ctx.int_val(0) <= comb; - ineq = ineq.simplify(); - lits_out[pos_idx] = ineq; - lits_out[neg_idx] = ctx.bool_val(true); - IndexLAfix(ineq, pos_idx); - } - } - } - } - - Term ProjectFormula(const Term &f){ - std::vector lits, new_lits1, new_lits2; - CollectConjuncts(f,lits); - timer_start("GaussElimCheap"); - GaussElimCheap(lits,new_lits1); - timer_stop("GaussElimCheap"); - timer_start("FourierMotzkinCheap"); - FourierMotzkinCheap(new_lits1,new_lits2); - timer_stop("FourierMotzkinCheap"); - return conjoin(new_lits2); - } - }; - - void Z3User::CollectConjuncts(const Term &f, std::vector &lits, bool pos) { - if (f.is_app() && f.decl().get_decl_kind() == Not) - CollectConjuncts(f.arg(0), lits, !pos); - else if (pos && f.is_app() && f.decl().get_decl_kind() == And) { - int num_args = f.num_args(); - for (int i = 0; i < num_args; i++) - CollectConjuncts(f.arg(i), lits, true); - } - else if (!pos && f.is_app() && f.decl().get_decl_kind() == Or) { - int num_args = f.num_args(); - for (int i = 0; i < num_args; i++) - CollectConjuncts(f.arg(i), lits, false); - } - else if (pos) { - if (!eq(f, ctx.bool_val(true))) - lits.push_back(f); - } - else { - if (!eq(f, ctx.bool_val(false))) - lits.push_back(!f); - } - } - - void Z3User::CollectJuncts(const Term &f, std::vector &lits, decl_kind op, bool negate) { - if (f.is_app() && f.decl().get_decl_kind() == Not) - CollectJuncts(f.arg(0), lits, (op == And) ? Or : And, !negate); - else if (f.is_app() && f.decl().get_decl_kind() == op) { - int num_args = f.num_args(); - for (int i = 0; i < num_args; i++) - CollectJuncts(f.arg(i), lits, op, negate); - } - else { - expr junct = negate ? Negate(f) : f; - lits.push_back(junct); - } - } - - struct TermLt { - bool operator()(const expr &x, const expr &y){ - unsigned xid = x.get_id(); - unsigned yid = y.get_id(); - return xid < yid; - } - }; - - void Z3User::SortTerms(std::vector &terms){ - TermLt foo; - std::sort(terms.begin(),terms.end(),foo); - } - - Z3User::Term Z3User::SortSum(const Term &t){ - if(!(t.is_app() && t.decl().get_decl_kind() == Plus)) - return t; - int nargs = t.num_args(); - if(nargs < 2) return t; - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = t.arg(i); - SortTerms(args); - if(nargs == 2) - return args[0] + args[1]; - return sum(args); - } - - - RPFP::Term RPFP::ProjectFormula(std::vector &keep_vec, const Term &f){ - VariableProjector vp(*this,keep_vec); - return vp.ProjectFormula(f); - } - - /** Compute an underapproximation of every node in a tree rooted at "root", - based on a previously computed counterexample. The underapproximation - may contain free variables that are implicitly existentially quantified. - */ - - RPFP::Term RPFP::ComputeUnderapprox(Node *root, int persist){ - /* if terminated underapprox is empty set (false) */ - bool show_model = false; - if(show_model) - std::cout << dualModel << std::endl; - if(!root->Outgoing){ - root->Underapprox.SetEmpty(); - return ctx.bool_val(true); - } - /* if not used in cex, underapprox is empty set (false) */ - if(Empty(root)){ - root->Underapprox.SetEmpty(); - return ctx.bool_val(true); - } - /* compute underapprox of children first */ - std::vector &chs = root->Outgoing->Children; - std::vector chu(chs.size()); - for(unsigned i = 0; i < chs.size(); i++) - chu[i] = ComputeUnderapprox(chs[i],persist); - - Term b; std::vector v; - RedVars(root, b, v); - /* underapproximate the edge formula */ - hash_set dont_cares; - dont_cares.insert(b); - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - Term dual = root->Outgoing->dual.null() ? ctx.bool_val(true) : root->Outgoing->dual; - Term eu = UnderapproxFormula(dual,dont_cares); - timer_stop("UnderapproxFormula"); - /* combine with children */ - chu.push_back(eu); - eu = conjoin(chu); - /* project onto appropriate variables */ - eu = ProjectFormula(v,eu); - eu = eu.simplify(); - -#if 0 - /* check the result is consistent */ - { - hash_map memo; - int res = SubtermTruth(memo, eu); - if(res != 1) - throw "inconsistent projection"; - } -#endif - - /* rename to appropriate variable names */ - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = root->Annotation.IndParams[i]; /* copy names from annotation */ - Term funder = SubstRec(memo, eu); - root->Underapprox = CreateRelation(root->Annotation.IndParams,funder); -#if 0 - if(persist) - Z3_persist_ast(ctx,root->Underapprox.Formula,persist); -#endif - return eu; - } - - void RPFP::FixCurrentState(Edge *edge){ - hash_set dont_cares; - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; - Term eu = UnderapproxFormula(dual,dont_cares); - timer_stop("UnderapproxFormula"); - ConstrainEdgeLocalized(edge,eu); - } - - void RPFP::GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under){ - if(memo[under].find(f) != memo[under].end()) - return; - memo[under].insert(f); - if (f.is_app()) { - if (!under && !f.has_quantifiers()) - return; - decl_kind k = f.decl().get_decl_kind(); - if (k == And || k == Or || k == Implies || k == Iff) { - int num_args = f.num_args(); - for (int i = 0; i < num_args; i++) - GetGroundLitsUnderQuants(memo, f.arg(i), res, under); - return; - } - } - else if (f.is_quantifier()){ -#if 0 - // treat closed quantified formula as a literal 'cause we hate nested quantifiers - if(under && IsClosedFormula(f)) - res.push_back(f); - else -#endif - GetGroundLitsUnderQuants(memo,f.body(),res,1); - return; - } - if(f.is_var()){ - // std::cout << "foo!\n"; - return; - } - if(under && f.is_ground()) - res.push_back(f); - } - - RPFP::Term RPFP::StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits){ - hash_set memo[2]; - std::vector lits; - GetGroundLitsUnderQuants(memo, f, lits, 0); - hash_set lits_hash; - for(unsigned i = 0; i < lits.size(); i++) - lits_hash.insert(lits[i]); - hash_map subst; - hash_map stt_memo; - std::vector conjuncts; - for(unsigned i = 0; i < lits.size(); i++){ - const expr &lit = lits[i]; - if(lits_hash.find(NegateLit(lit)) == lits_hash.end()){ - case_lits.push_back(lit); - bool tval = false; - expr atom = lit; - if(lit.is_app() && lit.decl().get_decl_kind() == Not){ - tval = true; - atom = lit.arg(0); - } - expr etval = ctx.bool_val(tval); - if(atom.is_quantifier()) - subst[atom] = etval; // this is a bit desperate, since we can't eval quants - else { - int b = SubtermTruth(stt_memo,atom); - if(b == (tval ? 1 : 0)) - subst[atom] = etval; - else { - if(b == 0 || b == 1){ - etval = ctx.bool_val(b ? true : false); - subst[atom] = etval; - conjuncts.push_back(b ? atom : !atom); - } - } - } - } - } - expr g = f; - if(!subst.empty()){ - g = SubstRec(subst,f); - if(conjuncts.size()) - g = g && ctx.make(And,conjuncts); - g = g.simplify(); - } -#if 1 - expr g_old = g; - g = RemoveRedundancy(g); - bool changed = !eq(g,g_old); - g = g.simplify(); - if(changed) { // a second pass can get some more simplification - g = RemoveRedundancy(g); - g = g.simplify(); - } -#else - g = RemoveRedundancy(g); - g = g.simplify(); -#endif - g = AdjustQuantifiers(g); - return g; - } - - RPFP::Term RPFP::ModelValueAsConstraint(const Term &t) { - if (t.is_array()) { - ArrayValue arr; - Term e = dualModel.eval(t); - EvalArrayTerm(e, arr); - if (arr.defined) { - std::vector cs; - for (std::map::iterator it = arr.entries.begin(), en = arr.entries.end(); it != en; ++it) { - expr foo = select(t, expr(ctx, it->first)) == expr(ctx, it->second); - cs.push_back(foo); - } - return conjoin(cs); - } - } - else { - expr r = dualModel.get_const_interp(t.decl()); - if (!r.null()) { - expr res = t == expr(ctx, r); - return res; - } - } - return ctx.bool_val(true); - } - - void RPFP::EvalNodeAsConstraint(Node *p, Transformer &res) - { - Term b; std::vector v; - RedVars(p, b, v); - std::vector args; - for(unsigned i = 0; i < v.size(); i++){ - expr val = ModelValueAsConstraint(v[i]); - if(!eq(val,ctx.bool_val(true))) - args.push_back(val); - } - expr cnst = conjoin(args); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = p->Annotation.IndParams[i]; /* copy names from annotation */ - Term funder = SubstRec(memo, cnst); - res = CreateRelation(p->Annotation.IndParams,funder); - } - -#if 0 - void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ - // verify - s.push(); - expr conj = ctx.make(And,conjuncts); - s.add(conj); - check_result res = s.check(); - if(res != unsat) - throw "should be unsat"; - s.pop(1); - - for(unsigned i = 0; i < conjuncts.size(); ){ - std::swap(conjuncts[i],conjuncts.back()); - expr save = conjuncts.back(); - conjuncts.pop_back(); - s.push(); - expr conj = ctx.make(And,conjuncts); - s.add(conj); - check_result res = s.check(); - s.pop(1); - if(res != unsat){ - conjuncts.push_back(save); - std::swap(conjuncts[i],conjuncts.back()); - i++; - } - } - } -#endif - - void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ - std::vector lits(conjuncts.size()); - for(unsigned i = 0; i < lits.size(); i++){ - func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); - lits[i] = pred(); - s.add(ctx.make(Implies,lits[i],conjuncts[i])); - } - - // verify - check_result res = s.check(lits.size(), VEC2PTR(lits)); - if(res != unsat){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - s.add(theory[i]); - for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! - if(s.check(lits.size(), VEC2PTR(lits)) == unsat) - goto is_unsat; - throw "should be unsat"; - } - is_unsat: - for(unsigned i = 0; i < conjuncts.size(); ){ - std::swap(conjuncts[i],conjuncts.back()); - std::swap(lits[i],lits.back()); - check_result res = s.check(lits.size()-1, VEC2PTR(lits)); - if(res != unsat){ - std::swap(conjuncts[i],conjuncts.back()); - std::swap(lits[i],lits.back()); - i++; - } - else { - conjuncts.pop_back(); - lits.pop_back(); - } - } - } - - void foobar(){ - } - - void RPFP::GreedyReduceNodes(std::vector &nodes){ - std::vector lits; - for(unsigned i = 0; i < nodes.size(); i++){ - Term b; std::vector v; - RedVars(nodes[i], b, v); - lits.push_back(!b); - expr bv = dualModel.eval(b); - if(eq(bv,ctx.bool_val(true))){ - check_result res = slvr_check(lits.size(), VEC2PTR(lits)); - if(res == unsat) - lits.pop_back(); - else - foobar(); - } - } - } - - check_result RPFP::CheckWithConstrainedNodes(std::vector &posnodes,std::vector &negnodes){ - timer_start("Check"); - std::vector lits; - for(unsigned i = 0; i < posnodes.size(); i++){ - Term b; std::vector v; - RedVars(posnodes[i], b, v); - lits.push_back(b); - } - for(unsigned i = 0; i < negnodes.size(); i++){ - Term b; std::vector v; - RedVars(negnodes[i], b, v); - lits.push_back(!b); - } - check_result res = slvr_check(lits.size(), VEC2PTR(lits)); - if(res == unsat && posnodes.size()){ - lits.resize(posnodes.size()); - res = slvr_check(lits.size(), VEC2PTR(lits)); - } - dualModel = slvr().get_model(); -#if 0 - if(!dualModel.null()){ - std::cout << "posnodes called:\n"; - for(unsigned i = 0; i < posnodes.size(); i++) - if(!Empty(posnodes[i])) - std::cout << posnodes[i]->Name.name() << "\n"; - std::cout << "negnodes called:\n"; - for(unsigned i = 0; i < negnodes.size(); i++) - if(!Empty(negnodes[i])) - std::cout << negnodes[i]->Name.name() << "\n"; - } -#endif - timer_stop("Check"); - return res; - } - - - void RPFP_caching::FilterCore(std::vector &core, std::vector &full_core){ - hash_set core_set; - std::copy(full_core.begin(),full_core.end(),std::inserter(core_set,core_set.begin())); - std::vector new_core; - for(unsigned i = 0; i < core.size(); i++) - if(core_set.find(core[i]) != core_set.end()) - new_core.push_back(core[i]); - core.swap(new_core); - } - - void RPFP_caching::GreedyReduceCache(std::vector &assumps, std::vector &core){ - std::vector lits = assumps, full_core; - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - - // verify - check_result res = CheckCore(lits,full_core); - if(res != unsat){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - GetAssumptionLits(theory[i],assumps); - lits = assumps; - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - - for(int k = 0; k < 4; k++) // keep trying, maybe MBQI will do something! - if((res = CheckCore(lits,full_core)) == unsat) - goto is_unsat; - throw greedy_reduce_failed(); - } - is_unsat: - FilterCore(core,full_core); - - std::vector dummy; - if(CheckCore(full_core,dummy) != unsat) - throw "should be unsat"; - - for(unsigned i = 0; i < core.size(); ){ - expr temp = core[i]; - std::swap(core[i],core.back()); - core.pop_back(); - lits.resize(assumps.size()); - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - res = CheckCore(lits,full_core); - if(res != unsat){ - core.push_back(temp); - std::swap(core[i],core.back()); - i++; - } - } - } - - expr RPFP::NegateLit(const expr &f){ - if(f.is_app() && f.decl().get_decl_kind() == Not) - return f.arg(0); - else - return !f; - } - - void RPFP::NegateLits(std::vector &lits){ - for(unsigned i = 0; i < lits.size(); i++){ - expr &f = lits[i]; - if(f.is_app() && f.decl().get_decl_kind() == Not) - f = f.arg(0); - else - f = !f; - } - } - - expr RPFP::SimplifyOr(std::vector &lits){ - if(lits.size() == 0) - return ctx.bool_val(false); - if(lits.size() == 1) - return lits[0]; - return ctx.make(Or,lits); - } - - expr RPFP::SimplifyAnd(std::vector &lits){ - if(lits.size() == 0) - return ctx.bool_val(true); - if(lits.size() == 1) - return lits[0]; - return ctx.make(And,lits); - } - - - /* This is a wrapper for a solver that is intended to compute - implicants from models. It works around a problem in Z3 with - models in the non-extensional array theory. It does this by - naming all of the store terms in a formula. That is, (store ...) - is replaced by "name" with an added constraint name = (store - ...). This allows us to determine from the model whether an array - equality is true or false (it is false if the two sides are - mapped to different function symbols, even if they have the same - contents). - */ - - struct implicant_solver { - RPFP *owner; - solver &aux_solver; - std::vector assumps, namings; - std::vector assump_stack, naming_stack; - hash_map renaming, renaming_memo; - - void add(const expr &e){ - expr t = e; - if(!aux_solver.extensional_array_theory()){ - unsigned i = namings.size(); - t = owner->ExtractStores(renaming_memo,t,namings,renaming); - for(; i < namings.size(); i++) - aux_solver.add(namings[i]); - } - assumps.push_back(t); - aux_solver.add(t); - } - - void push() { - assump_stack.push_back(assumps.size()); - naming_stack.push_back(namings.size()); - aux_solver.push(); - } - - // When we pop the solver, we have to re-add any namings that were lost - - void pop(int n) { - aux_solver.pop(n); - int new_assumps = assump_stack[assump_stack.size()-n]; - int new_namings = naming_stack[naming_stack.size()-n]; - for(unsigned i = new_namings; i < namings.size(); i++) - aux_solver.add(namings[i]); - assumps.resize(new_assumps); - namings.resize(new_namings); - assump_stack.resize(assump_stack.size()-1); - naming_stack.resize(naming_stack.size()-1); - } - - check_result check() { - return aux_solver.check(); - } - - model get_model() { - return aux_solver.get_model(); - } - - expr get_implicant() { - owner->dualModel = aux_solver.get_model(); - expr dual = owner->ctx.make(And,assumps); - bool ext = aux_solver.extensional_array_theory(); - expr eu = owner->UnderapproxFullFormula(dual,ext); - // if we renamed store terms, we have to undo - if(!ext) - eu = owner->SubstRec(renaming,eu); - return eu; - } - - implicant_solver(RPFP *_owner, solver &_aux_solver) - : owner(_owner), aux_solver(_aux_solver) - {} - }; - - // set up edge constraint in aux solver - void RPFP::AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge){ - if(!edge->dual.null()) - aux_solver.add(edge->dual); - for(unsigned i = 0; i < edge->constraints.size(); i++){ - expr tl = edge->constraints[i]; - aux_solver.add(tl); - } - } - - void RPFP::AddEdgeToSolver(Edge *edge){ - if(!edge->dual.null()) - aux_solver.add(edge->dual); - for(unsigned i = 0; i < edge->constraints.size(); i++){ - expr tl = edge->constraints[i]; - aux_solver.add(tl); - } - } - - static int by_case_counter = 0; - - void RPFP::InterpolateByCases(Node *root, Node *node){ - timer_start("InterpolateByCases"); - bool axioms_added = false; - hash_set axioms_needed; - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - axioms_needed.insert(theory[i]); - implicant_solver is(this,aux_solver); - is.push(); - AddEdgeToSolver(is,node->Outgoing); - node->Annotation.SetEmpty(); - hash_set *core = new hash_set; - core->insert(node->Outgoing->dual); - expr prev_annot = ctx.bool_val(false); - expr prev_impl = ctx.bool_val(false); - int repeated_case_count = 0; - while(1){ - by_case_counter++; - is.push(); - expr annot = !GetAnnotation(node); - Transformer old_annot = node->Annotation; - is.add(annot); - if(is.check() == unsat){ - is.pop(1); - break; - } - is.pop(1); - Push(); - expr the_impl = is.get_implicant(); - if(eq(the_impl,prev_impl)){ - // std::cout << "got old implicant\n"; - repeated_case_count++; - } - prev_impl = the_impl; - ConstrainEdgeLocalized(node->Outgoing,the_impl); - ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); //TODO: need this? - - { - check_result foo = Check(root); - if(foo != unsat){ - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw ReallyBad(); - // slvr().print("should_be_unsat.smt2"); - // throw "should be unsat"; - } - std::vector assumps, axioms_to_add; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++){ - (*core).insert(assumps[i]); - if(axioms_needed.find(assumps[i]) != axioms_needed.end()){ - axioms_to_add.push_back(assumps[i]); - axioms_needed.erase(assumps[i]); - } - } - // AddToProofCore(*core); - - try { - SolveSingleNode(root,node); - } - catch (char const *msg){ - // This happens if interpolation fails - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw msg; - } - { - expr itp = GetAnnotation(node); - dualModel = is.get_model(); // TODO: what does this mean? - std::vector case_lits; - itp = StrengthenFormulaByCaseSplitting(itp, case_lits); - SetAnnotation(node,itp); - node->Annotation.Formula = node->Annotation.Formula.simplify(); - } - - for(unsigned i = 0; i < axioms_to_add.size(); i++) - is.add(axioms_to_add[i]); - -#define TEST_BAD -#ifdef TEST_BAD - { - static int bad_count = 0, num_bads = 1; - if(bad_count >= num_bads){ - bad_count = 0; - num_bads = num_bads * 2; - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw Bad(); - } - bad_count++; - } -#endif - } - - if(node->Annotation.IsEmpty() || eq(node->Annotation.Formula,prev_annot) || (repeated_case_count > 0 && !axioms_added) || (repeated_case_count >= 10)){ - //looks_bad: - if(!axioms_added){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - is.add(theory[i]); - axioms_added = true; - } - else { - //#define KILL_ON_BAD_INTERPOLANT -#ifdef KILL_ON_BAD_INTERPOLANT - std::cout << "bad in InterpolateByCase -- core:\n"; -#if 0 - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++) - assumps[i].show(); -#endif - std::cout << "checking for inconsistency\n"; - std::cout << "model:\n"; - is.get_model().show(); - expr impl = is.get_implicant(); - std::vector conjuncts; - CollectConjuncts(impl,conjuncts,true); - std::cout << "impl:\n"; - for(unsigned i = 0; i < conjuncts.size(); i++) - conjuncts[i].show(); - std::cout << "annot:\n"; - annot.show(); - is.add(annot); - for(unsigned i = 0; i < conjuncts.size(); i++) - is.add(conjuncts[i]); - if(is.check() == unsat){ - std::cout << "inconsistent!\n"; - std::vector is_assumps; - is.aux_solver.get_proof().get_assumptions(is_assumps); - std::cout << "core:\n"; - for(unsigned i = 0; i < is_assumps.size(); i++) - is_assumps[i].show(); - } - else { - std::cout << "consistent!\n"; - is.aux_solver.print("should_be_inconsistent.smt2"); - } - std::cout << "by_case_counter = " << by_case_counter << "\n"; - throw "ack!"; -#endif - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw ReallyBad(); - } - } - Pop(1); - prev_annot = node->Annotation.Formula; - node->Annotation.UnionWith(old_annot); - } - if(proof_core) - delete proof_core; // shouldn't happen - proof_core = core; - is.pop(1); - timer_stop("InterpolateByCases"); - } - - void RPFP::Generalize(Node *root, Node *node){ - timer_start("Generalize"); - aux_solver.push(); - AddEdgeToSolver(node->Outgoing); - expr fmla = GetAnnotation(node); - std::vector conjuncts; - CollectConjuncts(fmla,conjuncts,false); - GreedyReduce(aux_solver,conjuncts); // try to remove conjuncts one at a tme - aux_solver.pop(1); - NegateLits(conjuncts); - SetAnnotation(node,SimplifyOr(conjuncts)); - timer_stop("Generalize"); - } - - RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models, bool axioms){ - edge_solver &es = edge_solvers[edge]; - uptr &p = es.slvr; - if(!p.get()){ - scoped_no_proof no_proofs_please(ctx.m()); // no proofs - p.set(new solver(ctx,true,models)); // no models - if(axioms){ - RPFP::LogicSolver *ls = edge->owner->ls; - const std::vector &axs = ls->get_axioms(); - for(unsigned i = 0; i < axs.size(); i++) - p.get()->add(axs[i]); - } - } - return es; - } - - - // caching version of above - void RPFP_caching::GeneralizeCache(Edge *edge){ - timer_start("Generalize"); - scoped_solver_for_edge ssfe(this,edge); - Node *node = edge->Parent; - std::vector assumps, core, conjuncts; - AssertEdgeCache(edge,assumps); - for(unsigned i = 0; i < edge->Children.size(); i++){ - expr as = GetAnnotation(edge->Children[i]); - std::vector clauses; - if(!as.is_true()){ - CollectConjuncts(as.arg(1),clauses); - for(unsigned j = 0; j < clauses.size(); j++) - GetAssumptionLits(as.arg(0) || clauses[j],assumps); - } - } - expr fmla = GetAnnotation(node); - std::vector lits; - if(fmla.is_true()){ - timer_stop("Generalize"); - return; - } - assumps.push_back(fmla.arg(0).arg(0)); - CollectConjuncts(!fmla.arg(1),lits); -#if 0 - for(unsigned i = 0; i < lits.size(); i++){ - const expr &lit = lits[i]; - if(lit.is_app() && lit.decl().get_decl_kind() == Equal){ - lits[i] = ctx.make(Leq,lit.arg(0),lit.arg(1)); - lits.push_back(ctx.make(Leq,lit.arg(1),lit.arg(0))); - } - } -#endif - hash_map lit_map; - for(unsigned i = 0; i < lits.size(); i++) - GetAssumptionLits(lits[i],core,&lit_map); - GreedyReduceCache(assumps,core); - for(unsigned i = 0; i < core.size(); i++) - conjuncts.push_back(lit_map[core[i]]); - NegateLits(conjuncts); - SetAnnotation(node,SimplifyOr(conjuncts)); - timer_stop("Generalize"); - } - - // caching version of above - bool RPFP_caching::PropagateCache(Edge *edge){ - timer_start("PropagateCache"); - scoped_solver_for_edge ssfe(this,edge); - bool some = false; - { - std::vector candidates, skip; - Node *node = edge->Parent; - CollectConjuncts(node->Annotation.Formula,skip); - for(unsigned i = 0; i < edge->Children.size(); i++){ - Node *child = edge->Children[i]; - if(child->map == node->map){ - CollectConjuncts(child->Annotation.Formula,candidates); - break; - } - } - if(candidates.empty()) goto done; - hash_set skip_set; - std::copy(skip.begin(),skip.end(),std::inserter(skip_set,skip_set.begin())); - std::vector new_candidates; - for(unsigned i = 0; i < candidates.size(); i++) - if(skip_set.find(candidates[i]) == skip_set.end()) - new_candidates.push_back(candidates[i]); - candidates.swap(new_candidates); - if(candidates.empty()) goto done; - std::vector assumps, core, conjuncts; - AssertEdgeCache(edge,assumps); - for(unsigned i = 0; i < edge->Children.size(); i++){ - expr ass = GetAnnotation(edge->Children[i]); - if(eq(ass,ctx.bool_val(true))) - continue; - std::vector clauses; - CollectConjuncts(ass.arg(1),clauses); - for(unsigned j = 0; j < clauses.size(); j++) - GetAssumptionLits(ass.arg(0) || clauses[j],assumps); - } - for(unsigned i = 0; i < candidates.size(); i++){ - unsigned old_size = assumps.size(); - node->Annotation.Formula = candidates[i]; - expr fmla = GetAnnotation(node); - assumps.push_back(fmla.arg(0).arg(0)); - GetAssumptionLits(!fmla.arg(1),assumps); - std::vector full_core; - check_result res = CheckCore(assumps,full_core); - if(res == unsat) - conjuncts.push_back(candidates[i]); - assumps.resize(old_size); - } - if(conjuncts.empty()) - goto done; - SetAnnotation(node,SimplifyAnd(conjuncts)); - some = true; - } - done: - timer_stop("PropagateCache"); - return some; - } - - - /** Push a scope. Assertions made after Push can be undone by Pop. */ - - void RPFP::Push() - { - stack.push_back(stack_entry()); - slvr_push(); - } - - /** Pop a scope (see Push). Note, you cannot pop axioms. */ - - void RPFP::Pop(int num_scopes) - { - slvr_pop(num_scopes); - for (int i = 0; i < num_scopes; i++) - { - stack_entry &back = stack.back(); - for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) - (*it)->dual = expr(ctx,nullptr); - for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) - (*it)->dual = expr(ctx,nullptr); - for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) - (*it).first->constraints.pop_back(); - stack.pop_back(); - } - } - - /** Erase the proof by performing a Pop, Push and re-assertion of - all the popped constraints */ - - void RPFP::PopPush(){ - slvr_pop(1); - slvr_push(); - stack_entry &back = stack.back(); - for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) - slvr_add((*it)->dual); - for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) - slvr_add((*it)->dual); - for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) - slvr_add((*it).second); - } - - - - // This returns a new FuncDel with same sort as top-level function - // of term t, but with numeric suffix appended to name. - - Z3User::FuncDecl Z3User::SuffixFuncDecl(const Term &t, int n) - { - std::string name = t.decl().name().str() + "_" + string_of_int(n); - std::vector sorts; - int nargs = t.num_args(); - sorts.reserve(nargs); - for(int i = 0; i < nargs; i++) - sorts.push_back(t.arg(i).get_sort()); - return ctx.function(name.c_str(), nargs, VEC2PTR(sorts), t.get_sort()); - } - - Z3User::FuncDecl Z3User::RenumberPred(const FuncDecl &f, int n) - { - std::string name = f.name().str(); - name = name.substr(0,name.rfind('_')) + "_" + string_of_int(n); - int arity = f.arity(); - std::vector domain; - domain.reserve(arity); - for(int i = 0; i < arity; i++) - domain.push_back(f.domain(i)); - return ctx.function(name.c_str(), arity, VEC2PTR(domain), f.range()); - } - - Z3User::FuncDecl Z3User::NumberPred(const FuncDecl &f, int n) - { - std::string name = f.name().str(); - name = name + "_" + string_of_int(n); - int arity = f.arity(); - std::vector domain; - domain.reserve(arity); - for(int i = 0; i < arity; i++) - domain.push_back(f.domain(i)); - return ctx.function(name.c_str(), arity, VEC2PTR(domain), f.range()); - } - - // Scan the clause body for occurrences of the predicate unknowns - - RPFP::Term RPFP::ScanBody(hash_map &memo, - const Term &t, - hash_map &pmap, - std::vector &parms, - std::vector &nodes) - { - if(memo.find(t) != memo.end()) - return memo[t]; - Term res(ctx); - if (t.is_app()) { - func_decl f = t.decl(); - if(pmap.find(f) != pmap.end()){ - nodes.push_back(pmap[f]); - f = SuffixFuncDecl(t,parms.size()); - parms.push_back(f); - } - int nargs = t.num_args(); - std::vector args; - args.reserve(nargs); - for(int i = 0; i < nargs; i++) - args.push_back(ScanBody(memo,t.arg(i),pmap,parms,nodes)); - res = f(nargs, VEC2PTR(args)); - } - else if (t.is_quantifier()) - res = CloneQuantifier(t,ScanBody(memo,t.body(),pmap,parms,nodes)); - else - res = t; - memo[t] = res; - return res; - } - - // return the func_del of an app if it is uninterpreted - - bool Z3User::get_relation(const Term &t, func_decl &R){ - if(!t.is_app()) - return false; - R = t.decl(); - return R.get_decl_kind() == Uninterpreted; - } - - // return true if term is an individual variable - // TODO: have to check that it is not a background symbol - - bool Z3User::is_variable(const Term &t){ - if(!t.is_app()) - return t.is_var(); - return t.decl().get_decl_kind() == Uninterpreted - && t.num_args() == 0; - } - - RPFP::Term RPFP::RemoveLabelsRec(hash_map &memo, const Term &t, - std::vector &lbls){ - if(memo.find(t) != memo.end()) - return memo[t]; - Term res(ctx); - if (t.is_app()){ - func_decl f = t.decl(); - std::vector names; - bool pos; - if(t.is_label(pos,names)){ - res = RemoveLabelsRec(memo,t.arg(0),lbls); - for(unsigned i = 0; i < names.size(); i++) - lbls.push_back(label_struct(names[i],res,pos)); - } - else { - int nargs = t.num_args(); - std::vector args; - args.reserve(nargs); - for(int i = 0; i < nargs; i++) - args.push_back(RemoveLabelsRec(memo,t.arg(i),lbls)); - res = f(nargs, VEC2PTR(args)); - } - } - else if (t.is_quantifier()) - res = CloneQuantifier(t,RemoveLabelsRec(memo,t.body(),lbls)); - else - res = t; - memo[t] = res; - return res; - } - - RPFP::Term RPFP::RemoveLabels(const Term &t, std::vector &lbls){ - hash_map memo ; - return RemoveLabelsRec(memo,t,lbls); - } - - RPFP::Term RPFP::SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo[level].insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - args.reserve(nargs); - if(nargs == 0 && f.get_decl_kind() == Uninterpreted) - ls->declare_constant(f); // keep track of background constants - for(int i = 0; i < nargs; i++) - args.push_back(SubstBoundRec(memo, subst, level, t.arg(i))); - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()){ - int bound = t.get_quantifier_num_bound(); - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstBoundRec(memo, subst, level + bound, pats[i]); - res = clone_quantifier(t, SubstBoundRec(memo, subst, level + bound, t.body()), pats); - } - else if (t.is_var()) { - int idx = t.get_index_value(); - if(idx >= level && subst.find(idx-level) != subst.end()){ - res = subst[idx-level]; - } - else res = t; - } - else res = t; - return res; - } - - RPFP::Term RPFP::SubstBound(hash_map &subst, const Term &t){ - hash_map > memo; - return SubstBoundRec(memo, subst, 0, t); - } - - // Eliminate the deBruijn indices from level to level+num-1 - Z3User::Term Z3User::DeleteBoundRec(hash_map > &memo, int level, int num, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo[level].insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - args.reserve(nargs); - for(int i = 0; i < nargs; i++) - args.push_back(DeleteBoundRec(memo, level, num, t.arg(i))); - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()){ - int bound = t.get_quantifier_num_bound(); - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = DeleteBoundRec(memo, level + bound, num, pats[i]); - res = clone_quantifier(t, DeleteBoundRec(memo, level + bound, num, t.body()), pats); - } - else if (t.is_var()) { - int idx = t.get_index_value(); - if(idx >= level){ - res = ctx.make_var(idx-num,t.get_sort()); - } - else res = t; - } - else res = t; - return res; - } - - Z3User::Term Z3User::DeleteBound(int level, int num, const Term &t){ - hash_map > memo; - return DeleteBoundRec(memo, level, num, t); - } - - int Z3User::MaxIndex(hash_map &memo, const Term &t) - { - std::pair foo(t,-1); - std::pair::iterator, bool> bar = memo.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()){ - func_decl f = t.decl(); - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - int m = MaxIndex(memo, t.arg(i)); - if(m > res) - res = m; - } - } - else if (t.is_quantifier()){ - int bound = t.get_quantifier_num_bound(); - res = MaxIndex(memo,t.body()) - bound; - } - else if (t.is_var()) { - res = t.get_index_value(); - } - return res; - } - - bool Z3User::IsClosedFormula(const Term &t){ - hash_map memo; - return MaxIndex(memo,t) < 0; - } - - - /** Convert a collection of clauses to Nodes and Edges in the RPFP. - - Predicate unknowns are uninterpreted predicates not - occurring in the background theory. - - Clauses are of the form - - B => P(t_1,...,t_k) - - where P is a predicate unknown and predicate unknowns - occur only positivey in H and only under existential - quantifiers in prenex form. - - Each predicate unknown maps to a node. Each clause maps to - an edge. Let C be a clause B => P(t_1,...,t_k) where the - sequence of predicate unknowns occurring in B (in order - of occurrence) is P_1..P_n. The clause maps to a transformer - T where: - - T.Relparams = P_1..P_n - T.Indparams = x_1...x+k - T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k - - Throws exception bad_clause(msg,i) if a clause i is - in the wrong form. - - */ - - - static bool canonical_clause(const expr &clause){ - if(clause.decl().get_decl_kind() != Implies) - return false; - expr arg = clause.arg(1); - return arg.is_app() && (arg.decl().get_decl_kind() == False || - arg.decl().get_decl_kind() == Uninterpreted); - } - -#define USE_QE_LITE - - void RPFP::FromClauses(const std::vector &unskolemized_clauses, const std::vector *bounds){ - hash_map pmap; - func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); - - std::vector clauses(unskolemized_clauses); - // first, skolemize the clauses - -#ifndef USE_QE_LITE - for(unsigned i = 0; i < clauses.size(); i++){ - expr &t = clauses[i]; - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - hash_map subst; - for(int j = 0; j < bound; j++){ - sort the_sort = t.get_quantifier_bound_sort(j); - symbol name = t.get_quantifier_bound_name(j); - expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - subst[bound-1-j] = skolem; - } - t = SubstBound(subst,t.body()); - } - } -#else - std::vector > substs(clauses.size()); -#endif - - // create the nodes from the heads of the clauses - - for(unsigned i = 0; i < clauses.size(); i++){ - Term &clause = clauses[i]; - -#ifdef USE_QE_LITE - Term &t = clause; - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - for(int j = 0; j < bound; j++){ - sort the_sort = t.get_quantifier_bound_sort(j); - symbol name = t.get_quantifier_bound_name(j); - expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - substs[i][bound-1-j] = skolem; - } - clause = t.body(); - } - -#endif - - if(clause.is_app() && clause.decl().get_decl_kind() == Uninterpreted) - clause = implies(ctx.bool_val(true),clause); - if(!canonical_clause(clause)) - clause = implies((!clause).simplify(),ctx.bool_val(false)); - Term head = clause.arg(1); - func_decl R(ctx); - bool is_query = false; - if (eq(head,ctx.bool_val(false))){ - R = fail_pred; - // R = ctx.constant("@Fail", ctx.bool_sort()).decl(); - is_query = true; - } - else if(!get_relation(head,R)) - throw bad_clause("rhs must be a predicate application",i); - if(pmap.find(R) == pmap.end()){ - - // If the node doesn't exitst, create it. The Indparams - // are arbitrary, but we use the rhs arguments if they - // are variables for mnomonic value. - - hash_set seen; - std::vector Indparams; - for(unsigned j = 0; j < head.num_args(); j++){ - Term arg = head.arg(j); - if(!is_variable(arg) || seen.find(arg) != seen.end()){ - std::string name = std::string("@a_") + string_of_int(j); - arg = ctx.constant(name.c_str(),arg.get_sort()); - } - seen.insert(arg); - Indparams.push_back(arg); - } -#ifdef USE_QE_LITE - { - hash_map > sb_memo; - for(unsigned j = 0; j < Indparams.size(); j++) - Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); - } -#endif - Node *node = CreateNode(R(Indparams.size(),VEC2PTR(Indparams))); - //nodes.push_back(node); - pmap[R] = node; - if (is_query) - node->Bound = CreateRelation(std::vector(), ctx.bool_val(false)); - node->recursion_bound = bounds ? 0 : UINT_MAX; - } - } - - bool some_labels = false; - - // create the edges - - for(unsigned i = 0; i < clauses.size(); i++){ - Term clause = clauses[i]; - Term body = clause.arg(0); - Term head = clause.arg(1); - func_decl R(ctx); - if (eq(head,ctx.bool_val(false))) - R = fail_pred; - //R = ctx.constant("@Fail", ctx.bool_sort()).decl(); - else get_relation(head,R); - Node *Parent = pmap[R]; - std::vector Indparams; - hash_set seen; - for(unsigned j = 0; j < head.num_args(); j++){ - Term arg = head.arg(j); - if(!is_variable(arg) || seen.find(arg) != seen.end()){ - std::string name = std::string("@a_") + string_of_int(j); - Term var = ctx.constant(name.c_str(),arg.get_sort()); - body = body && (arg == var); - arg = var; - } - seen.insert(arg); - Indparams.push_back(arg); - } - - // We extract the relparams positionally - - std::vector Relparams; - hash_map scan_memo; - std::vector Children; - body = ScanBody(scan_memo,body,pmap,Relparams,Children); - Term labeled = body; - std::vector lbls; // TODO: throw this away for now - body = RemoveLabels(body,lbls); - if(!eq(labeled,body)) - some_labels = true; // remember if there are labels, as we then can't do qe_lite - // body = IneqToEq(body); // UFO converts x=y to (x<=y & x >= y). Undo this. - body = body.simplify(); - -#ifdef USE_QE_LITE - std::set idxs; - if(!some_labels){ // can't do qe_lite if we have to reconstruct labels - for(unsigned j = 0; j < Indparams.size(); j++) - if(Indparams[j].is_var()) - idxs.insert(Indparams[j].get_index_value()); - body = body.qe_lite(idxs,false); - } - hash_map > sb_memo; - body = SubstBoundRec(sb_memo,substs[i],0,body); - if(some_labels) - labeled = SubstBoundRec(sb_memo,substs[i],0,labeled); - for(unsigned j = 0; j < Indparams.size(); j++) - Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); -#endif - - // Create the edge - Transformer T = CreateTransformer(Relparams,Indparams,body); - Edge *edge = CreateEdge(Parent,T,Children); - edge->labeled = labeled;; // remember for label extraction - if(bounds) - Parent->recursion_bound = std::max(Parent->recursion_bound,(*bounds)[i]); - // edges.push_back(edge); - } - - // undo hoisting of expressions out of loops - RemoveDeadNodes(); - Unhoist(); - // FuseEdges(); - } - - - // The following mess is used to undo hoisting of expressions outside loops by compilers - - expr RPFP::UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params){ - if(memo.find(w) != memo.end()) - return memo[w]; - expr res; - if(init_defs.find(w) != init_defs.end()){ - expr d = init_defs[w]; - std::vector vars; - hash_set get_vars_memo; - GetVarsRec(get_vars_memo,d,vars); - hash_map map; - for(unsigned j = 0; j < vars.size(); j++){ - expr x = vars[j]; - map[x] = UnhoistPullRec(memo,x,init_defs,const_params,const_params_inv,new_params); - } - expr defn = SubstRec(map,d); - res = defn; - } - else if(const_params_inv.find(w) == const_params_inv.end()){ - std::string old_name = w.decl().name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); - expr y = fresh(); - const_params[y] = w; - const_params_inv[w] = y; - new_params.push_back(y); - res = y; - } - else - res = const_params_inv[w]; - memo[w] = res; - return res; - } - - void RPFP::AddParamsToTransformer(Transformer &trans, const std::vector ¶ms){ - std::copy(params.begin(),params.end(),std::inserter(trans.IndParams,trans.IndParams.end())); - } - - expr RPFP::AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms){ - int n = app.num_args(); - std::vector args(n); - for (int i = 0; i < n; i++) - args[i] = app.arg(i); - std::copy(params.begin(),params.end(),std::inserter(args,args.end())); - return new_decl(args); - } - - expr RPFP::GetRelRec(hash_set &memo, const expr &t, const func_decl &rel){ - if(memo.find(t) != memo.end()) - return expr(); - memo.insert(t); - if (t.is_app()) - { - func_decl f = t.decl(); - if(f == rel) - return t; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - expr res = GetRelRec(memo,t.arg(i),rel); - if(!res.null()) - return res; - } - } - else if (t.is_quantifier()) - return GetRelRec(memo,t.body(),rel); - return expr(); - } - - expr RPFP::GetRel(Edge *edge, int child_idx){ - func_decl &rel = edge->F.RelParams[child_idx]; - hash_set memo; - return GetRelRec(memo,edge->F.Formula,rel); - } - - void RPFP::GetDefsRec(const expr &cnst, hash_map &defs){ - if(cnst.is_app()){ - switch(cnst.decl().get_decl_kind()){ - case And: { - int n = cnst.num_args(); - for(int i = 0; i < n; i++) - GetDefsRec(cnst.arg(i),defs); - break; - } - case Equal: { - expr lhs = cnst.arg(0); - expr rhs = cnst.arg(1); - if(IsVar(lhs)) - defs[lhs] = rhs; - break; - } - default: - break; - } - } - } - - void RPFP::GetDefs(const expr &cnst, hash_map &defs){ - // GetDefsRec(IneqToEq(cnst),defs); - GetDefsRec(cnst,defs); - } - - bool RPFP::IsVar(const expr &t){ - return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; - } - - void RPFP::GetVarsRec(hash_set &memo, const expr &t, std::vector &vars){ - if(memo.find(t) != memo.end()) - return; - memo.insert(t); - if (t.is_app()) - { - if(IsVar(t)){ - vars.push_back(t); - return; - } - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - GetVarsRec(memo,t.arg(i),vars); - } - } - else if (t.is_quantifier()) - GetVarsRec(memo,t.body(),vars); - } - - void RPFP::AddParamsToNode(Node *node, const std::vector ¶ms){ - int arity = node->Annotation.IndParams.size(); - std::vector domain; - domain.reserve(arity + params.size()); - for(int i = 0; i < arity; i++) - domain.push_back(node->Annotation.IndParams[i].get_sort()); - for(unsigned i = 0; i < params.size(); i++) - domain.push_back(params[i].get_sort()); - std::string old_name = node->Name.name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), domain, ctx.bool_sort()); - node->Name = fresh; - AddParamsToTransformer(node->Annotation,params); - AddParamsToTransformer(node->Bound,params); - AddParamsToTransformer(node->Underapprox,params); - } - - void RPFP::UnhoistLoop(Edge *loop_edge, Edge *init_edge){ - loop_edge->F.Formula = IneqToEq(loop_edge->F.Formula); - init_edge->F.Formula = IneqToEq(init_edge->F.Formula); - expr pre = GetRel(loop_edge,0); - if(pre.null()) - return; // this means the loop got simplified away - int nparams = loop_edge->F.IndParams.size(); - hash_map const_params, const_params_inv; - std::vector work_list; - // find the parameters that are constant in the loop - for(int i = 0; i < nparams; i++){ - if(eq(pre.arg(i),loop_edge->F.IndParams[i])){ - const_params[pre.arg(i)] = init_edge->F.IndParams[i]; - const_params_inv[init_edge->F.IndParams[i]] = pre.arg(i); - work_list.push_back(pre.arg(i)); - } - } - // get the definitions in the initialization - hash_map defs,memo,subst; - GetDefs(init_edge->F.Formula,defs); - // try to pull them inside the loop - std::vector new_params; - for(unsigned i = 0; i < work_list.size(); i++){ - expr v = work_list[i]; - expr w = const_params[v]; - expr def = UnhoistPullRec(memo,w,defs,const_params,const_params_inv,new_params); - if(!eq(def,v)) - subst[v] = def; - } - // do the substitutions - if(subst.empty()) - return; - subst[pre] = pre; // don't substitute inside the precondition itself - loop_edge->F.Formula = SubstRec(subst,loop_edge->F.Formula); - loop_edge->F.Formula = ElimIte(loop_edge->F.Formula); - init_edge->F.Formula = ElimIte(init_edge->F.Formula); - // add the new parameters - if(new_params.empty()) - return; - Node *parent = loop_edge->Parent; - AddParamsToNode(parent,new_params); - AddParamsToTransformer(loop_edge->F,new_params); - AddParamsToTransformer(init_edge->F,new_params); - std::vector &incoming = parent->Incoming; - for(unsigned i = 0; i < incoming.size(); i++){ - Edge *in_edge = incoming[i]; - std::vector &chs = in_edge->Children; - for(unsigned j = 0; j < chs.size(); j++) - if(chs[j] == parent){ - expr lit = GetRel(in_edge,j); - expr new_lit = AddParamsToApp(lit,parent->Name,new_params); - func_decl fd = SuffixFuncDecl(new_lit,j); - int nargs = new_lit.num_args(); - std::vector args; - args.reserve(nargs); - for(int k = 0; k < nargs; k++) - args.push_back(new_lit.arg(k)); - new_lit = fd(nargs, VEC2PTR(args)); - in_edge->F.RelParams[j] = fd; - hash_map map; - map[lit] = new_lit; - in_edge->F.Formula = SubstRec(map,in_edge->F.Formula); - } - } - } - - void RPFP::Unhoist(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++) - outgoing[edges[i]->Parent].push_back(edges[i]); - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - std::vector &outs = outgoing[node]; - // if we're not a simple loop with one entry, give up - if(outs.size() == 2){ - for(int j = 0; j < 2; j++){ - Edge *loop_edge = outs[j]; - Edge *init_edge = outs[1-j]; - if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent){ - UnhoistLoop(loop_edge,init_edge); - break; - } - } - } - } - } - - void RPFP::FuseEdges(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++){ - outgoing[edges[i]->Parent].push_back(edges[i]); - } - hash_set edges_to_delete; - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - std::vector &outs = outgoing[node]; - if(outs.size() > 1 && outs.size() <= 16){ - std::vector trs(outs.size()); - for(unsigned j = 0; j < outs.size(); j++) - trs[j] = &outs[j]->F; - Transformer tr = Fuse(trs); - std::vector chs; - for(unsigned j = 0; j < outs.size(); j++) - for(unsigned k = 0; k < outs[j]->Children.size(); k++) - chs.push_back(outs[j]->Children[k]); - CreateEdge(node,tr,chs); - for(unsigned j = 0; j < outs.size(); j++) - edges_to_delete.insert(outs[j]); - } - } - std::vector new_edges; - hash_set all_nodes; - for(unsigned j = 0; j < edges.size(); j++){ - if(edges_to_delete.find(edges[j]) == edges_to_delete.end()){ -#if 0 - if(all_nodes.find(edges[j]->Parent) != all_nodes.end()) - throw "help!"; - all_nodes.insert(edges[j]->Parent); -#endif - new_edges.push_back(edges[j]); - } - else - delete edges[j]; - } - edges.swap(new_edges); - } - - void RPFP::MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node){ - if(live_nodes.find(node) != live_nodes.end()) - return; - live_nodes.insert(node); - std::vector &outs = outgoing[node]; - for(unsigned i = 0; i < outs.size(); i++) - for(unsigned j = 0; j < outs[i]->Children.size(); j++) - MarkLiveNodes(outgoing, live_nodes,outs[i]->Children[j]); - } - - void RPFP::RemoveDeadNodes(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++) - outgoing[edges[i]->Parent].push_back(edges[i]); - hash_set live_nodes; - for(unsigned i = 0; i < nodes.size(); i++) - if(!nodes[i]->Bound.IsFull()) - MarkLiveNodes(outgoing,live_nodes,nodes[i]); - std::vector new_edges; - for(unsigned j = 0; j < edges.size(); j++){ - if(live_nodes.find(edges[j]->Parent) != live_nodes.end()) - new_edges.push_back(edges[j]); - else { - Edge *edge = edges[j]; - for(unsigned int i = 0; i < edge->Children.size(); i++){ - std::vector &ic = edge->Children[i]->Incoming; - for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ - if(*it == edge){ - ic.erase(it); - break; - } - } - } - delete edge; - } - } - edges.swap(new_edges); - std::vector new_nodes; - for(unsigned j = 0; j < nodes.size(); j++){ - if(live_nodes.find(nodes[j]) != live_nodes.end()) - new_nodes.push_back(nodes[j]); - else - delete nodes[j]; - } - nodes.swap(new_nodes); - } - - void RPFP::WriteSolution(std::ostream &s){ - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - Term asgn = (node->Name)(node->Annotation.IndParams) == node->Annotation.Formula; - s << asgn << std::endl; - } - } - - void RPFP::WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s) - { - std::pair foo(t,0); - std::pair::iterator, bool> bar = memo.insert(foo); - // int &res = bar.first->second; - if(!bar.second) return; - hash_map::iterator it = e->varMap.find(t); - if (it != e->varMap.end()){ - return; - } - if (t.is_app()) - { - func_decl f = t.decl(); - // int idx; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - WriteEdgeVars(e, memo, t.arg(i),s); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)){ - Term rename = HideVariable(t,e->number); - Term value = dualModel.eval(rename); - s << " (= " << t << " " << value << ")\n"; - } - } - else if (t.is_quantifier()) - WriteEdgeVars(e,memo,t.body(),s); - return; - } - - void RPFP::WriteEdgeAssignment(std::ostream &s, Edge *e){ - s << "(\n"; - hash_map memo; - WriteEdgeVars(e, memo, e->F.Formula ,s); - s << ")\n"; - } - - void RPFP::WriteCounterexample(std::ostream &s, Node *node){ - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *child = node->Outgoing->Children[i]; - if(!Empty(child)) - WriteCounterexample(s,child); - } - s << "(" << node->number << " : " << EvalNode(node) << " <- "; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *child = node->Outgoing->Children[i]; - if(!Empty(child)) - s << " " << node->Outgoing->Children[i]->number; - } - s << ")" << std::endl; - WriteEdgeAssignment(s,node->Outgoing); - } - - RPFP::Term RPFP::ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - // int idx; - std::vector args; - int nargs = t.num_args(); - args.reserve(nargs); - for(int i = 0; i < nargs; i++) - args.push_back(ToRuleRec(e, memo, t.arg(i),quants)); - hash_map::iterator rit = e->relMap.find(f); - if(rit != e->relMap.end()){ - Node* child = e->Children[rit->second]; - FuncDecl op = child->Name; - res = op(args.size(), VEC2PTR(args)); - } - else { - res = f(args.size(), VEC2PTR(args)); - if(nargs == 0 && t.decl().get_decl_kind() == Uninterpreted) - quants.push_back(t); - } - } - else if (t.is_quantifier()) - { - Term body = ToRuleRec(e,memo,t.body(),quants); - res = CloneQuantifier(t,body); - } - else res = t; - return res; - } - - - void RPFP::ToClauses(std::vector &cnsts, FileFormat format){ - cnsts.resize(edges.size()); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - SetEdgeMaps(edge); - std::vector quants; - hash_map memo; - Term lhs = ToRuleRec(edge, memo, edge->F.Formula,quants); - Term rhs = (edge->Parent->Name)(edge->F.IndParams.size(),&edge->F.IndParams[0]); - for(unsigned j = 0; j < edge->F.IndParams.size(); j++) - ToRuleRec(edge,memo,edge->F.IndParams[j],quants); // just to get quants - Term cnst = implies(lhs,rhs); -#if 0 - for(unsigned i = 0; i < quants.size(); i++){ - std::cout << expr(ctx,(Z3_ast)quants[i]) << " : " << sort(ctx,Z3_get_sort(ctx,(Z3_ast)quants[i])) << std::endl; - } -#endif - if(format != DualityFormat) - cnst= forall(quants,cnst); - cnsts[i] = cnst; - } - // int num_rules = cnsts.size(); - - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - if(!node->Bound.IsFull()){ - Term lhs = (node->Name)(node->Bound.IndParams) && !node->Bound.Formula; - Term cnst = implies(lhs,ctx.bool_val(false)); - if(format != DualityFormat){ - std::vector quants; - for(unsigned j = 0; j < node->Bound.IndParams.size(); j++) - quants.push_back(node->Bound.IndParams[j]); - if(format == HornFormat) - cnst= exists(quants,!cnst); - else - cnst= forall(quants, cnst); - } - cnsts.push_back(cnst); - } - } - - } - - - bool RPFP::proof_core_contains(const expr &e){ - return proof_core->find(e) != proof_core->end(); - } - - bool RPFP_caching::proof_core_contains(const expr &e){ - std::vector foo; - GetAssumptionLits(e,foo); - for(unsigned i = 0; i < foo.size(); i++) - if(proof_core->find(foo[i]) != proof_core->end()) - return true; - return false; - } - - bool RPFP::EdgeUsedInProof(Edge *edge){ - ComputeProofCore(); - if(!edge->dual.null() && proof_core_contains(edge->dual)) - return true; - for(unsigned i = 0; i < edge->constraints.size(); i++) - if(proof_core_contains(edge->constraints[i])) - return true; - return false; - } - - RPFP::~RPFP(){ - ClearProofCore(); - for(unsigned i = 0; i < nodes.size(); i++) - delete nodes[i]; - for(unsigned i = 0; i < edges.size(); i++) - delete edges[i]; - } -} - -#if 0 -void show_ast(expr *a){ - std::cout << *a; -} -#endif diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp deleted file mode 100644 index 7782f4a8b..000000000 --- a/src/duality/duality_solver.cpp +++ /dev/null @@ -1,3601 +0,0 @@ -/*++ - Copyright (c) 2012 Microsoft Corporation - - Module Name: - - duality_solver.h - - Abstract: - - implements relational post-fixedpoint problem - (RPFP) solver - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include "duality/duality.h" -#include "duality/duality_profiling.h" - -#include -#include -#include -#include -#include -#include - -// TODO: make these official options or get rid of them - -#define NEW_CAND_SEL -// #define LOCALIZE_CONJECTURES -// #define CANDS_FROM_UPDATES -#define CANDS_FROM_COVER_FAIL -#define DEPTH_FIRST_EXPAND -#define MINIMIZE_CANDIDATES -// #define MINIMIZE_CANDIDATES_HARDER -#define BOUNDED -// #define CHECK_CANDS_FROM_IND_SET -#define UNDERAPPROX_NODES -#define NEW_EXPAND -#define EARLY_EXPAND -// #define TOP_DOWN -// #define EFFORT_BOUNDED_STRAT -#define SKIP_UNDERAPPROX_NODES -// #define KEEP_EXPANSIONS -// #define USE_CACHING_RPFP -// #define PROPAGATE_BEFORE_CHECK -#define NEW_STRATIFIED_INLINING - -#define USE_RPFP_CLONE -#define USE_NEW_GEN_CANDS - -//#define NO_PROPAGATE -//#define NO_GENERALIZE -//#define NO_DECISIONS - -namespace Duality { - - // TODO: must be a better place for this... - static char string_of_int_buffer[20]; - - static const char *string_of_int(int n){ - sprintf(string_of_int_buffer,"%d",n); - return string_of_int_buffer; - } - - /** Generic object for producing diagnostic output. */ - - class Reporter { - protected: - RPFP *rpfp; - public: - Reporter(RPFP *_rpfp){ - rpfp = _rpfp; - } - virtual void Extend(RPFP::Node *node){} - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){} - virtual void Bound(RPFP::Node *node){} - virtual void Expand(RPFP::Edge *edge){} - virtual void AddCover(RPFP::Node *covered, std::vector &covering){} - virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){} - virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){} - virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){} - virtual void Dominates(RPFP::Node *node, RPFP::Node *other){} - virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){} - virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} - virtual void Reject(RPFP::Edge *edge, const std::vector &Children){} - virtual void Message(const std::string &msg){} - virtual void Depth(int){} - virtual ~Reporter(){} - }; - - Reporter *CreateStdoutReporter(RPFP *rpfp); - Reporter *CreateConjectureFileReporter(RPFP *rpfp, const std::string &fname); - - /** Object we throw in case of catastrophe. */ - - struct InternalError { - std::string msg; - InternalError(const std::string & _msg) - : msg(_msg) {} - }; - - - /** This is the main solver. It takes an arbitrary (possibly cyclic) - RPFP and either annotates it with a solution, or returns a - counterexample derivation in the form of an embedded RPFP tree. */ - - class Duality : public Solver { - - public: - Duality(RPFP *_rpfp) - : ctx(_rpfp->ctx), - slvr(_rpfp->slvr()), - nodes(_rpfp->nodes), - edges(_rpfp->edges) - { - rpfp = _rpfp; - reporter = nullptr; - conj_reporter = nullptr; - heuristic = nullptr; - unwinding = nullptr; - FullExpand = false; - NoConj = false; - FeasibleEdges = true; - UseUnderapprox = true; - Report = false; - StratifiedInlining = false; - RecursionBound = -1; - BatchExpand = false; - { - scoped_no_proof no_proofs_please(ctx.m()); -#ifdef USE_RPFP_CLONE - clone_rpfp = new RPFP_caching(rpfp->ls); - clone_rpfp->Clone(rpfp); -#endif -#ifdef USE_NEW_GEN_CANDS - gen_cands_rpfp = new RPFP_caching(rpfp->ls); - gen_cands_rpfp->Clone(rpfp); -#endif - } - } - - ~Duality() override { -#ifdef USE_RPFP_CLONE - delete clone_rpfp; -#endif -#ifdef USE_NEW_GEN_CANDS - delete gen_cands_rpfp; -#endif - if(unwinding) delete unwinding; - } - -#ifdef USE_RPFP_CLONE - RPFP_caching *clone_rpfp; -#endif -#ifdef USE_NEW_GEN_CANDS - RPFP_caching *gen_cands_rpfp; -#endif - - - typedef RPFP::Node Node; - typedef RPFP::Edge Edge; - - /** This struct represents a candidate for extending the - unwinding. It consists of an edge to instantiate - and a vector of children for the new instance. */ - - struct Candidate { - Edge *edge; std::vector - Children; - }; - - /** Comparison operator, allowing us to sort Nodes - by their number field. */ - - struct lnode - { - bool operator()(const Node* s1, const Node* s2) const - { - return s1->number < s2->number; - } - }; - - typedef std::set Unexpanded; // sorted set of Nodes - - /** This class provides a heuristic for expanding a derivation - tree. */ - - class Heuristic { - RPFP *rpfp; - - /** Heuristic score for unwinding nodes. Currently this - counts the number of updates. */ - struct score { - int updates; - score() : updates(0) {} - }; - hash_map scores; - - public: - Heuristic(RPFP *_rpfp){ - rpfp = _rpfp; - } - - virtual ~Heuristic(){} - - virtual void Update(RPFP::Node *node){ - scores[node].updates++; - } - - /** Heuristic choice of nodes to expand. Takes a set "choices" - and returns a subset "best". We currently choose the - nodes with the fewest updates. - */ -#if 0 - virtual void ChooseExpand(const std::set &choices, std::set &best){ - int best_score = INT_MAX; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it)->map; - int score = scores[node].updates; - best_score = std::min(best_score,score); - } - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) - if(scores[(*it)->map].updates == best_score) - best.insert(*it); - } -#else - virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority=false, bool best_only=false){ - if(high_priority) return; - int best_score = INT_MAX; - int worst_score = 0; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it)->map; - int score = scores[node].updates; - best_score = std::min(best_score,score); - worst_score = std::max(worst_score,score); - } - int cutoff = best_only ? best_score : (best_score + (worst_score-best_score)/2); - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) - if(scores[(*it)->map].updates <= cutoff) - best.insert(*it); - } -#endif - - /** Called when done expanding a tree */ - virtual void Done() {} - - /** Ask whether a node should be used/unused in the tree. Returns, - 1 if yes, -1 if no, and 0 if don't care. */ - - virtual int UseNode(Node *node){ - return 0; - } - }; - - /** The Proposer class proposes conjectures eagerly. These can come - from any source, including predicate abstraction, templates, or - previous solver runs. The proposed conjectures are checked - with low effort when the unwinding is expanded. - */ - - class Proposer { - public: - /** Given a node in the unwinding, propose some conjectures */ - virtual std::vector &Propose(Node *node) = 0; - virtual ~Proposer(){}; - }; - - - class Covering; // see below - - // These members represent the state of the algorithm. - - RPFP *rpfp; // the input RPFP - Reporter *reporter; // object for logging - Reporter *conj_reporter; // object for logging conjectures - Heuristic *heuristic; // expansion heuristic - context &ctx; // Z3 context - solver &slvr; // Z3 solver - std::vector &nodes; // Nodes of input RPFP - std::vector &edges; // Edges of input RPFP - std::vector leaves; // leaf nodes of unwinding (unused) - Unexpanded unexpanded; // unexpanded nodes - std::list candidates; // candidates for expansion - // maps children to edges in input RPFP - hash_map > edges_by_child; - // maps each node in input RPFP to its expanded instances - hash_map > insts_of_node; - // maps each node in input RPFP to all its instances - hash_map > all_of_node; - RPFP *unwinding; // the unwinding - Covering *indset; // proposed inductive subset - Counterexample cex; // counterexample - std::list to_expand; - hash_set updated_nodes; - hash_map underapprox_map; // maps underapprox nodes to the nodes they approximate - int last_decisions; - hash_set overapproxes; - std::vector proposers; - std::string ConjectureFile; - bool stratified_inlining_done; - -#ifdef BOUNDED - struct Counter { - unsigned val; - Counter(){val = 0;} - }; - typedef std::map NodeToCounter; - hash_map back_edges; // counts of back edges -#endif - - /** Solve the problem. */ - bool Solve() override { - PreSolve(); - bool res = SolveMain(); // does the actual work - PostSolve(); - return res; - } - - void PreSolve(){ - reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); - conj_reporter = ConjectureFile.empty() ? nullptr : CreateConjectureFileReporter(rpfp,ConjectureFile); -#ifndef LOCALIZE_CONJECTURES - heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); -#else - heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp)) - : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); -#endif - // determine if we are recursion bounded - for(unsigned i = 0; i < rpfp->nodes.size(); i++) - if(rpfp->nodes[i]->recursion_bound < UINT_MAX) - RecursionBound = 0; - cex.clear(); // in case we didn't use it for heuristic - if(unwinding) delete unwinding; - unwinding = new RPFP(rpfp->ls); - unwinding->HornClauses = rpfp->HornClauses; - indset = new Covering(this); - last_decisions = 0; - CreateEdgesByChildMap(); -#ifndef TOP_DOWN - CreateInitialUnwinding(); -#else - CreateLeaves(); - for(unsigned i = 0; i < leaves.size(); i++) - if(!SatisfyUpperBound(leaves[i])) - return false; -#endif - StratifiedLeafCount = -1; - stratified_inlining_done = false; - } - - void PostSolve(){ - // print_profile(std::cout); - delete indset; - delete heuristic; - // delete unwinding; // keep the unwinding for future mining of predicates - delete reporter; - if(conj_reporter) - delete conj_reporter; - for(unsigned i = 0; i < proposers.size(); i++) - delete proposers[i]; - } - - bool RecheckBounds(){ - for(unsigned i = 0; i < unwinding->nodes.size(); i++){ - Node *node = unwinding->nodes[i]; - node->Bound = node->map->Bound; - if(!SatisfyUpperBound(node)) - return false; - } - return true; - } - - void CreateInitialUnwinding(){ - if(!StratifiedInlining){ - CreateLeaves(); - if(FeasibleEdges)NullaryCandidates(); - else InstantiateAllEdges(); - } - else { -#ifdef NEW_STRATIFIED_INLINING - -#else - CreateLeaves(); -#endif - } - - } - - void Cancel() override { - // TODO - } - -#if 0 - virtual void Restart(RPFP *_rpfp){ - rpfp = _rpfp; - delete unwinding; - nodes = _rpfp->nodes; - edges = _rpfp->edges; - leaves.clear(); - unexpanded.clear(); // unexpanded nodes - candidates.clear(); // candidates for expansion - edges_by_child.clear(); - insts_of_node.clear(); - all_of_node.clear(); - to_expand.clear(); - } -#endif - - void LearnFrom(Solver *other_solver) override { - // get the counterexample as a guide - cex.swap(other_solver->GetCounterexample()); - - // propose conjectures based on old unwinding - Duality *old_duality = dynamic_cast(other_solver); - if(old_duality) - proposers.push_back(new HistoryProposer(old_duality,this)); - } - - /** Return a reference to the counterexample */ - Counterexample &GetCounterexample() override { - return cex; - } - - // options - bool FullExpand; // do not use partial expansion of derivation tree - bool NoConj; // do not use conjectures (no forced covering) - bool FeasibleEdges; // use only feasible edges in unwinding - bool UseUnderapprox; // use underapproximations - bool Report; // spew on stdout - bool StratifiedInlining; // Do stratified inlining as preprocessing step - int RecursionBound; // Recursion bound for bounded verification - bool BatchExpand; - bool EnableRestarts; - - bool SetBoolOption(bool &opt, const std::string &value){ - if(value == "0") { - opt = false; - return true; - } - if(value == "1") { - opt = true; - return true; - } - return false; - } - - bool SetIntOption(int &opt, const std::string &value){ - opt = atoi(value.c_str()); - return true; - } - - /** Set options (not currently used) */ - bool SetOption(const std::string &option, const std::string &value) override { - if(option == "full_expand"){ - return SetBoolOption(FullExpand,value); - } - if(option == "no_conj"){ - return SetBoolOption(NoConj,value); - } - if(option == "feasible_edges"){ - return SetBoolOption(FeasibleEdges,value); - } - if(option == "use_underapprox"){ - return SetBoolOption(UseUnderapprox,value); - } - if(option == "report"){ - return SetBoolOption(Report,value); - } - if(option == "stratified_inlining"){ - return SetBoolOption(StratifiedInlining,value); - } - if(option == "batch_expand"){ - return SetBoolOption(BatchExpand,value); - } - if(option == "recursion_bound"){ - return SetIntOption(RecursionBound,value); - } - if(option == "conjecture_file"){ - ConjectureFile = value; - } - if(option == "enable_restarts"){ - return SetBoolOption(EnableRestarts,value); - } - return false; - } - - /** Create an instance of a node in the unwinding. Set its - annotation to true, and mark it unexpanded. */ - Node* CreateNodeInstance(Node *node, int number = 0){ - RPFP::Node *inst = unwinding->CloneNode(node); - inst->Annotation.SetFull(); - if(number < 0) inst->number = number; - unexpanded.insert(inst); - all_of_node[node].push_back(inst); - return inst; - } - - /** Create an instance of an edge in the unwinding, with given - parent and children. */ - void CreateEdgeInstance(Edge *edge, Node *parent, const std::vector &children){ - RPFP::Edge *inst = unwinding->CreateEdge(parent,edge->F,children); - inst->map = edge; - } - - void MakeLeaf(Node *node, bool do_not_expand = false){ - node->Annotation.SetEmpty(); - Edge *e = unwinding->CreateLowerBoundEdge(node); -#ifdef TOP_DOWN - node->Annotation.SetFull(); // allow this node to cover others -#endif - if(StratifiedInlining) - node->Annotation.SetFull(); // allow this node to cover others - else - updated_nodes.insert(node); - e->map = nullptr; - reporter->Extend(node); -#ifdef EARLY_EXPAND - if(!do_not_expand) - TryExpandNode(node); -#endif - // e->F.SetEmpty(); - } - - void MakeOverapprox(Node *node){ - node->Annotation.SetFull(); - Edge *e = unwinding->CreateLowerBoundEdge(node); - overapproxes.insert(node); - e->map = nullptr; - } - - /** We start the unwinding with leaves that under-approximate - each relation with false. */ - void CreateLeaves(){ - unexpanded.clear(); - leaves.clear(); - for(unsigned i = 0; i < nodes.size(); i++){ - RPFP::Node *node = CreateNodeInstance(nodes[i]); - if(0 && nodes[i]->Outgoing->Children.size() == 0) - CreateEdgeInstance(nodes[i]->Outgoing,node,std::vector()); - else { - if(!StratifiedInlining) - MakeLeaf(node); - else { - MakeOverapprox(node); - LeafMap[nodes[i]] = node; - } - } - leaves.push_back(node); - } - } - - /** Create the map from children to edges in the input RPFP. This - is used to generate candidates for expansion. */ - void CreateEdgesByChildMap(){ - edges_by_child.clear(); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *e = edges[i]; - std::set done; - for(unsigned j = 0; j < e->Children.size(); j++){ - Node *c = e->Children[j]; - if(done.find(c) == done.end()) // avoid duplicates - edges_by_child[c].push_back(e); - done.insert(c); - } - } - } - - void NullaryCandidates(){ - for(unsigned i = 0; i < edges.size(); i++){ - RPFP::Edge *edge = edges[i]; - if(edge->Children.size() == 0){ - Candidate cand; - cand.edge = edge; - candidates.push_back(cand); - } - } - } - - void InstantiateAllEdges(){ - hash_map leaf_map; - for(unsigned i = 0; i < leaves.size(); i++){ - leaf_map[leaves[i]->map] = leaves[i]; - insts_of_node[leaves[i]->map].push_back(leaves[i]); - } - unexpanded.clear(); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - Candidate c; c.edge = edge; - c.Children.resize(edge->Children.size()); - for(unsigned j = 0; j < c.Children.size(); j++) - c.Children[j] = leaf_map[edge->Children[j]]; - Node *new_node; - Extend(c,new_node); -#ifdef EARLY_EXPAND - TryExpandNode(new_node); -#endif - } - for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it) - indset->Add(*it); - for(unsigned i = 0; i < leaves.size(); i++){ - std::vector &foo = insts_of_node[leaves[i]->map]; - foo.erase(foo.begin()); - } - } - - bool ProducedBySI(Edge *edge, std::vector &children){ - if(LeafMap.find(edge->Parent) == LeafMap.end()) return false; - Node *other = LeafMap[edge->Parent]; - if(other->Outgoing->map != edge) return false; - std::vector &ochs = other->Outgoing->Children; - for(unsigned i = 0; i < children.size(); i++) - if(ochs[i] != children[i]) return false; - return true; - } - - /** Add a candidate for expansion, but not if Stratified inlining has already - produced it */ - - void AddCandidate(Edge *edge, std::vector &children){ - if(StratifiedInlining && ProducedBySI(edge,children)) - return; - candidates.push_back(Candidate()); - candidates.back().edge = edge; - candidates.back().Children = children; - } - - /** Generate candidates for expansion, given a vector of candidate - sets for each argument position. This recursively produces - the cross product. - */ - void GenCandidatesRec(int pos, Edge *edge, - const std::vector > &vec, - std::vector &children){ - if(pos == (int)vec.size()){ - AddCandidate(edge,children); - } - else { - for(unsigned i = 0; i < vec[pos].size(); i++){ - children[pos] = vec[pos][i]; - GenCandidatesRec(pos+1,edge,vec,children); - } - } - } - - /** Setup for above recursion. */ - void GenCandidates(int pos, Edge *edge, - const std::vector > &vec){ - std::vector children(vec.size()); - GenCandidatesRec(0,edge,vec,children); - } - - /** Expand a node. We find all the candidates for expansion using - this node and other already expanded nodes. This is a little - tricky, since a node may be used for multiple argument - positions of an edge, and we don't want to produce duplicates. - */ - -#ifndef NEW_EXPAND - void ExpandNode(Node *node){ - std::vector &nedges = edges_by_child[node->map]; - for(unsigned i = 0; i < nedges.size(); i++){ - Edge *edge = nedges[i]; - for(unsigned npos = 0; npos < edge->Children.size(); ++npos){ - if(edge->Children[npos] == node->map){ - std::vector > vec(edge->Children.size()); - vec[npos].push_back(node); - for(unsigned j = 0; j < edge->Children.size(); j++){ - if(j != npos){ - std::vector &insts = insts_of_node[edge->Children[j]]; - for(unsigned k = 0; k < insts.size(); k++) - if(indset->Candidate(insts[k])) - vec[j].push_back(insts[k]); - } - if(j < npos && edge->Children[j] == node->map) - vec[j].push_back(node); - } - GenCandidates(0,edge,vec); - } - } - } - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } -#else - /** If the current proposed solution is not inductive, - use the induction failure to generate candidates for extension. */ - void ExpandNode(Node *node){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - timer_start("GenCandIndFailUsing"); - std::vector &nedges = edges_by_child[node->map]; - for(unsigned i = 0; i < nedges.size(); i++){ - Edge *edge = nedges[i]; - slvr.push(); - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerJustForEdge(edge,checker,true); - if(root){ - expr using_cond = ctx.bool_val(false); - for(unsigned npos = 0; npos < edge->Children.size(); ++npos) - if(edge->Children[npos] == node->map) - using_cond = using_cond || checker->Localize(root->Outgoing->Children[npos]->Outgoing,NodeMarker(node)); - slvr.add(using_cond); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - } - slvr.pop(1); - delete checker; - } - timer_stop("GenCandIndFailUsing"); - } -#endif - - void ExpandNodeFromOther(Node *node, Node *other){ - std::vector &in = other->Incoming; - for(unsigned i = 0; i < in.size(); i++){ - Edge *edge = in[i]; - Candidate cand; - cand.edge = edge->map; - cand.Children = edge->Children; - for(unsigned j = 0; j < cand.Children.size(); j++) - if(cand.Children[j] == other) - cand.Children[j] = node; - candidates.push_front(cand); - } - // unexpanded.erase(node); - // insts_of_node[node->map].push_back(node); - } - - /** Expand a node based on some uncovered node it dominates. - This pushes cahdidates onto the *front* of the candidate - queue, so these expansions are done depth-first. */ - bool ExpandNodeFromCoverFail(Node *node){ - if(!node->Outgoing || node->Outgoing->Children.size() == 0) - return false; - Node *other = indset->GetSimilarNode(node); - if(!other) - return false; -#ifdef UNDERAPPROX_NODES - Node *under_node = CreateUnderapproxNode(node); - underapprox_map[under_node] = node; - indset->CoverByNode(node,under_node); - ExpandNodeFromOther(under_node,other); - ExpandNode(under_node); -#else - ExpandNodeFromOther(node,other); - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); -#endif - return true; - } - - - /** Make a boolean variable to act as a "marker" for a node. */ - expr NodeMarker(Node *node){ - std::string name = std::string("@m_") + string_of_int(node->number); - return ctx.constant(name.c_str(),ctx.bool_sort()); - } - - /** Make a boolean variable to act as a "marker" for a pair of nodes. */ - expr NodeMarker(Node *node1, Node *node2){ - std::string name = std::string("@m_") + string_of_int(node1->number); - name += std::string("_") + string_of_int(node2->number); - return ctx.constant(name.c_str(),ctx.bool_sort()); - } - - /** Union the annotation of dst into src. If with_markers is - true, we conjoin the annotation formula of dst with its - marker. This allows us to discover which disjunct is - true in a satisfying assignment. */ - void UnionAnnotations(RPFP::Transformer &dst, Node *src, bool with_markers = false){ - if(!with_markers) - dst.UnionWith(src->Annotation); - else { - RPFP::Transformer t = src->Annotation; - t.Formula = t.Formula && NodeMarker(src); - dst.UnionWith(t); - } - } - - void GenNodeSolutionFromIndSet(Node *node, RPFP::Transformer &annot, bool with_markers = false){ - annot.SetEmpty(); - std::vector &insts = insts_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++) - if(indset->Contains(insts[j])) - UnionAnnotations(annot,insts[j],with_markers); - annot.Simplify(); - } - - bool NodeSolutionFromIndSetFull(Node *node){ - std::vector &insts = insts_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++) - if(indset->Contains(insts[j])) - if(insts[j]->Annotation.IsFull()) - return true; - return false; - } - - bool recursionBounded; - - /** See if the solution might be bounded. */ - void TestRecursionBounded(){ - recursionBounded = false; - if(RecursionBound == -1) - return; - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - std::vector &insts = insts_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++) - if(indset->Contains(insts[j])) - if(NodePastRecursionBound(insts[j],true)) - recursionBounded = true; - } - } - - bool IsResultRecursionBounded() override { - return recursionBounded; - } - - /** Generate a proposed solution of the input RPFP from - the unwinding, by unioning the instances of each node. */ - void GenSolutionFromIndSet(bool with_markers = false){ - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - GenNodeSolutionFromIndSet(node,node->Annotation,with_markers); - } - } - -#ifdef BOUNDED - bool NodePastRecursionBound(Node *node, bool report = false){ - if(RecursionBound < 0) return false; - NodeToCounter &backs = back_edges[node]; - for(NodeToCounter::iterator it = backs.begin(), en = backs.end(); it != en; ++it){ - if(it->second.val > it->first->recursion_bound){ - if(report){ - std::ostringstream os; - os << "cut off " << it->first->Name.name() << " at depth " << it->second.val; - reporter->Message(os.str()); - } - return true; - } - } - return false; - } -#endif - - /** Test whether a given extension candidate actually represents - an induction failure. Right now we approximate this: if - the resulting node in the unwinding could be labeled false, - it clearly is not an induction failure. */ - - bool CandidateFeasible(const Candidate &cand){ - if(!FeasibleEdges) return true; - timer_start("CandidateFeasible"); - RPFP *checker = new RPFP(rpfp->ls); - // std::cout << "Checking feasibility of extension " << cand.edge->Parent->number << std::endl; - checker->Push(); - std::vector chs(cand.Children.size()); - Node *root = checker->CloneNode(cand.edge->Parent); -#ifdef BOUNDED - for(unsigned i = 0; i < cand.Children.size(); i++) - if(NodePastRecursionBound(cand.Children[i])){ - timer_stop("CandidateFeasible"); - return false; - } -#endif -#ifdef NEW_CAND_SEL - GenNodeSolutionFromIndSet(cand.edge->Parent,root->Bound); -#else - root->Bound.SetEmpty(); -#endif - checker->AssertNode(root); - for(unsigned i = 0; i < cand.Children.size(); i++) - chs[i] = checker->CloneNode(cand.Children[i]); - Edge *e = checker->CreateEdge(root,cand.edge->F,chs); - checker->AssertEdge(e,0,true); - // std::cout << "Checking SAT: " << e->dual << std::endl; - bool res = checker->Check(root) != unsat; - // std::cout << "Result: " << res << std::endl; - if(!res)reporter->Reject(cand.edge,cand.Children); - checker->Pop(1); - delete checker; - timer_stop("CandidateFeasible"); - return res; - } - - - hash_map TopoSort; - int TopoSortCounter; - std::vector SortedEdges; - - void DoTopoSortRec(Node *node){ - if(TopoSort.find(node) != TopoSort.end()) - return; - TopoSort[node] = INT_MAX; // just to break cycles - Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge - if(edge){ - std::vector &chs = edge->Children; - for(unsigned i = 0; i < chs.size(); i++) - DoTopoSortRec(chs[i]); - } - TopoSort[node] = TopoSortCounter++; - SortedEdges.push_back(edge); - } - - void DoTopoSort(){ - TopoSort.clear(); - SortedEdges.clear(); - TopoSortCounter = 0; - for(unsigned i = 0; i < nodes.size(); i++) - DoTopoSortRec(nodes[i]); - } - - - int StratifiedLeafCount; - -#ifdef NEW_STRATIFIED_INLINING - - /** Stratified inlining builds an initial layered unwinding before - switching to the LAWI strategy. Currently the number of layers - is one. Only nodes that are the targets of back edges are - consider to be leaves. This assumes we have already computed a - topological sort. - */ - - bool DoStratifiedInlining(){ - if(stratified_inlining_done) - return true; - stratified_inlining_done = true; - DoTopoSort(); - int depth = 1; // TODO: make this an option - std::vector > unfolding_levels(depth+1); - for(int level = 1; level <= depth; level++) - for(unsigned i = 0; i < SortedEdges.size(); i++){ - Edge *edge = SortedEdges[i]; - Node *parent = edge->Parent; - std::vector &chs = edge->Children; - std::vector my_chs(chs.size()); - for(unsigned j = 0; j < chs.size(); j++){ - Node *child = chs[j]; - int ch_level = TopoSort[child] >= TopoSort[parent] ? level-1 : level; - if(unfolding_levels[ch_level].find(child) == unfolding_levels[ch_level].end()){ - if(ch_level == 0) - unfolding_levels[0][child] = CreateLeaf(child); - else - throw InternalError("in levelized unwinding"); - } - my_chs[j] = unfolding_levels[ch_level][child]; - } - Candidate cand; cand.edge = edge; cand.Children = my_chs; - Node *new_node; - bool ok = Extend(cand,new_node); - MarkExpanded(new_node); // we don't expand here -- just mark it done - if(!ok) return false; // got a counterexample - unfolding_levels[level][parent] = new_node; - } - return true; - } - - Node *CreateLeaf(Node *node){ - RPFP::Node *nchild = CreateNodeInstance(node); - MakeLeaf(nchild, /* do_not_expand = */ true); - nchild->Annotation.SetEmpty(); - return nchild; - } - - void MarkExpanded(Node *node){ - if(unexpanded.find(node) != unexpanded.end()){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } - } - -#else - - /** In stratified inlining, we build the unwinding from the bottom - down, trying to satisfy the node bounds. We do this as a pre-pass, - limiting the expansion. If we get a counterexample, we are done, - else we continue as usual expanding the unwinding upward. - */ - - bool DoStratifiedInlining(){ - timer_start("StratifiedInlining"); - DoTopoSort(); - for(unsigned i = 0; i < leaves.size(); i++){ - Node *node = leaves[i]; - bool res = SatisfyUpperBound(node); - if(!res){ - timer_stop("StratifiedInlining"); - return false; - } - } - // don't leave any dangling nodes! -#ifndef EFFORT_BOUNDED_STRAT - for(unsigned i = 0; i < leaves.size(); i++) - if(!leaves[i]->Outgoing) - MakeLeaf(leaves[i],true); -#endif - timer_stop("StratifiedInlining"); - return true; - } - -#endif - - /** Here, we do the downward expansion for stratified inlining */ - - hash_map LeafMap, StratifiedLeafMap; - - Edge *GetNodeOutgoing(Node *node, int last_decs = 0){ - if(overapproxes.find(node) == overapproxes.end()) return node->Outgoing; /* already expanded */ - overapproxes.erase(node); -#ifdef EFFORT_BOUNDED_STRAT - if(last_decs > 5000){ - // RPFP::Transformer save = node->Annotation; - node->Annotation.SetEmpty(); - Edge *e = unwinding->CreateLowerBoundEdge(node); - // node->Annotation = save; - insts_of_node[node->map].push_back(node); - // std::cout << "made leaf: " << node->number << std::endl; - return e; - } -#endif - Edge *edge = node->map->Outgoing; - std::vector &chs = edge->Children; - - // make sure we don't create a covered node in this process! - - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - if(TopoSort[child] < TopoSort[node->map]){ - Node *leaf = LeafMap[child]; - if(!indset->Contains(leaf)){ - node->Outgoing->F.Formula = ctx.bool_val(false); // make this a proper leaf, else bogus cex - return node->Outgoing; - } - } - } - - std::vector nchs(chs.size()); - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - if(TopoSort[child] < TopoSort[node->map]){ - Node *leaf = LeafMap[child]; - nchs[i] = leaf; - if(unexpanded.find(leaf) != unexpanded.end()){ - unexpanded.erase(leaf); - insts_of_node[child].push_back(leaf); - } - } - else { - if(StratifiedLeafMap.find(child) == StratifiedLeafMap.end()){ - RPFP::Node *nchild = CreateNodeInstance(child,StratifiedLeafCount--); - MakeLeaf(nchild); - nchild->Annotation.SetEmpty(); - StratifiedLeafMap[child] = nchild; - indset->SetDominated(nchild); - } - nchs[i] = StratifiedLeafMap[child]; - } - } - CreateEdgeInstance(edge,node,nchs); - reporter->Extend(node); - return node->Outgoing; - } - - void SetHeuristicOldNode(Node *node){ - LocalHeuristic *h = dynamic_cast(heuristic); - if(h) - h->SetOldNode(node); - } - - bool SolveMain(){ - timer_start("SolveMain"); - bool res = SolveMainInt(); // does the actual work - timer_stop("SolveMain"); - return res; - } - - /** This does the actual solving work. We try to generate - candidates for extension. If we succed, we extend the - unwinding. If we fail, we have a solution. */ - bool SolveMainInt(){ - if(StratifiedInlining && !DoStratifiedInlining()) - return false; -#ifdef BOUNDED - DoTopoSort(); -#endif - while(true){ - timer_start("ProduceCandidatesForExtension"); - ProduceCandidatesForExtension(); - timer_stop("ProduceCandidatesForExtension"); - if(candidates.empty()){ - GenSolutionFromIndSet(); - TestRecursionBounded(); - return true; - } - Candidate cand = candidates.front(); - candidates.pop_front(); - if(CandidateFeasible(cand)){ - Node *new_node; - if(!Extend(cand,new_node)) - return false; -#ifdef EARLY_EXPAND - TryExpandNode(new_node); -#endif - } - } - } - - // hack: put something local into the underapproximation formula - // without this, interpolants can be pretty bad - void AddThing(expr &conj){ - std::string name = "@thing"; - expr thing = ctx.constant(name.c_str(),ctx.bool_sort()); - if(conj.is_app() && conj.decl().get_decl_kind() == And){ - std::vector conjs(conj.num_args()+1); - for(unsigned i = 0; i+1 < conjs.size(); i++) - conjs[i] = conj.arg(i); - conjs[conjs.size()-1] = thing; - conj = rpfp->conjoin(conjs); - } - } - - Node *CreateUnderapproxNode(Node *node){ - // cex.get_tree()->ComputeUnderapprox(cex.get_root(),0); - RPFP::Node *under_node = CreateNodeInstance(node->map /* ,StratifiedLeafCount-- */); - under_node->Annotation.IntersectWith(cex.get_root()->Underapprox); - AddThing(under_node->Annotation.Formula); - Edge *e = unwinding->CreateLowerBoundEdge(under_node); - under_node->Annotation.SetFull(); // allow this node to cover others - back_edges[under_node] = back_edges[node]; - e->map = nullptr; - reporter->Extend(under_node); - return under_node; - } - - /** Try to prove a conjecture about a node. If successful - update the unwinding annotation appropriately. */ - bool ProveConjecture(Node *node, const RPFP::Transformer &t,Node *other = nullptr, Counterexample *_cex = nullptr){ - reporter->Conjecture(node,t); - timer_start("ProveConjecture"); - RPFP::Transformer save = node->Bound; - node->Bound.IntersectWith(t); - -#ifndef LOCALIZE_CONJECTURES - bool ok = SatisfyUpperBound(node); -#else - SetHeuristicOldNode(other); - bool ok = SatisfyUpperBound(node); - SetHeuristicOldNode(0); -#endif - - if(ok){ - timer_stop("ProveConjecture"); - return true; - } -#ifdef UNDERAPPROX_NODES - if(UseUnderapprox && last_decisions > 500){ - std::cout << "making an underapprox\n"; - ExpandNodeFromCoverFail(node); - } -#endif - if(_cex) (*_cex).swap(cex); // return the cex if asked - cex.clear(); // throw away the useless cex - node->Bound = save; // put back original bound - timer_stop("ProveConjecture"); - return false; - } - - /** If a node is part of the inductive subset, expand it. - We ask the inductive subset to exclude the node if possible. - */ - void TryExpandNode(RPFP::Node *node){ - if(indset->Close(node)) return; - if(!NoConj && indset->Conjecture(node)){ -#ifdef UNDERAPPROX_NODES - /* TODO: temporary fix. this prevents an infinite loop in case - the node is covered by multiple others. This should be - removed when covering by a set is implemented. - */ - if(indset->Contains(node)){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } -#endif - return; - } -#ifdef UNDERAPPROX_NODES - if(!indset->Contains(node)) - return; // could be covered by an underapprox node -#endif - indset->Add(node); -#if defined(CANDS_FROM_COVER_FAIL) && !defined(UNDERAPPROX_NODES) - if(ExpandNodeFromCoverFail(node)) - return; -#endif - ExpandNode(node); - } - - /** Make the conjunction of markers for all (expanded) instances of - a node in the input RPFP. */ - expr AllNodeMarkers(Node *node){ - expr res = ctx.bool_val(true); - std::vector &insts = insts_of_node[node]; - for(int k = insts.size()-1; k >= 0; k--) - res = res && NodeMarker(insts[k]); - return res; - } - - void RuleOutNodesPastBound(Node *node, RPFP::Transformer &t){ -#ifdef BOUNDED - if(RecursionBound < 0)return; - std::vector &insts = insts_of_node[node]; - for(unsigned i = 0; i < insts.size(); i++) - if(NodePastRecursionBound(insts[i])) - t.Formula = t.Formula && !NodeMarker(insts[i]); -#endif - } - - - void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction, Node *other_node){ -#ifdef BOUNDED - if(RecursionBound >= 0 && NodePastRecursionBound(node)) - return; -#endif - RPFP::Transformer temp = node->Annotation; - expr marker = (!other_node) ? NodeMarker(node) : NodeMarker(node, other_node); - temp.Formula = (!marker || temp.Formula); - annot.IntersectWith(temp); - marker_disjunction = marker_disjunction || marker; - } - - bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false, Node *other_node = nullptr){ - bool res = false; - annot.SetFull(); - expr marker_disjunction = ctx.bool_val(false); - std::vector &insts = expanded_only ? insts_of_node[node] : all_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++){ - Node *node = insts[j]; - if(indset->Contains(insts[j])){ - GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction, other_node); res = true; - } - } - annot.Formula = annot.Formula && marker_disjunction; - annot.Simplify(); - return res; - } - - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerJustForEdge(Edge *edge, RPFP *checker, bool expanded_only = false){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); - if(root->Bound.IsFull()) - return nullptr; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - if(!GenNodeSolutionWithMarkers(oc,nc->Annotation,expanded_only)) - return nullptr; - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - -#ifndef MINIMIZE_CANDIDATES_HARDER - -#if 0 - /** Make a checker to detheermine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - root->Bound = edge->Parent->Annotation; - root->Bound.Formula = (!AllNodeMarkers(edge->Parent)) || root->Bound.Formula; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - nc->Annotation = oc->Annotation; - RuleOutNodesPastBound(oc,nc->Annotation); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - -#else - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); -#if 0 - if(root->Bound.IsFull()) - return = 0; -#endif - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } -#endif - - /** If an edge is not satisfied, produce an extension candidate - using instances of its children that violate the parent annotation. - We find these using the marker predicates. */ - void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ - candidate.edge = edge; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *node = root->Outgoing->Children[j]; - Edge *lb = node->Outgoing; - std::vector &insts = insts_of_node[edge->Children[j]]; -#ifndef MINIMIZE_CANDIDATES - for(int k = insts.size()-1; k >= 0; k--) -#else - for(unsigned k = 0; k < insts.size(); k++) -#endif - { - Node *inst = insts[k]; - if(indset->Contains(inst)){ - if(checker->Empty(node) || - eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst,node)),ctx.bool_val(true))){ - candidate.Children.push_back(inst); - goto next_child; - } - } - } - throw InternalError("No candidate from induction failure"); - next_child:; - } - } -#else - - - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); - if(root->Bound.IsFull()) - return = 0; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - - /** If an edge is not satisfied, produce an extension candidate - using instances of its children that violate the parent annotation. - We find these using the marker predicates. */ - void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ - candidate.edge = edge; - std::vector assumps; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Edge *lb = root->Outgoing->Children[j]->Outgoing; - std::vector &insts = insts_of_node[edge->Children[j]]; - for(unsigned k = 0; k < insts.size(); k++) - { - Node *inst = insts[k]; - expr marker = NodeMarker(inst); - if(indset->Contains(inst)){ - if(checker->Empty(lb->Parent) || - eq(checker->Eval(lb,marker),ctx.bool_val(true))){ - candidate.Children.push_back(inst); - assumps.push_back(checker->Localize(lb,marker)); - goto next_child; - } - assumps.push_back(checker->Localize(lb,marker)); - if(checker->CheckUpdateModel(root,assumps) != unsat){ - candidate.Children.push_back(inst); - goto next_child; - } - assumps.pop_back(); - } - } - throw InternalError("No candidate from induction failure"); - next_child:; - } - } - -#endif - - - Node *CheckerForEdgeClone(Edge *edge, RPFP_caching *checker){ - Edge *gen_cands_edge = checker->GetEdgeClone(edge); - Node *root = gen_cands_edge->Parent; - root->Outgoing = gen_cands_edge; - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); -#if 0 - if(root->Bound.IsFull()) - return = 0; -#endif - checker->AssertNode(root); - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = gen_cands_edge->Children[j]; - GenNodeSolutionWithMarkers(oc,nc->Annotation,true,nc); - } - checker->AssertEdge(gen_cands_edge,1,true); - return root; - } - - /** If the current proposed solution is not inductive, - use the induction failure to generate candidates for extension. */ - void GenCandidatesFromInductionFailure(bool full_scan = false){ - timer_start("GenCandIndFail"); - GenSolutionFromIndSet(true /* add markers */); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - if(!full_scan && updated_nodes.find(edge->Parent) == updated_nodes.end()) - continue; -#ifndef USE_NEW_GEN_CANDS - slvr.push(); - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerForEdge(edge,checker); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - slvr.pop(1); - delete checker; -#else - if(!NodeSolutionFromIndSetFull(edge->Parent)){ - RPFP_caching::scoped_solver_for_edge ssfe(gen_cands_rpfp,edge,true /* models */, true /*axioms*/); - gen_cands_rpfp->Push(); - Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); - if(gen_cands_rpfp->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,gen_cands_rpfp,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - gen_cands_rpfp->Pop(1); - } -#endif - } - updated_nodes.clear(); - timer_stop("GenCandIndFail"); -#ifdef CHECK_CANDS_FROM_IND_SET - for(std::list::iterator it = candidates.begin(), en = candidates.end(); it != en; ++it){ - if(!CandidateFeasible(*it)) - throw "produced infeasible candidate"; - } -#endif - if(!full_scan && candidates.empty()){ - reporter->Message("No candidates from updates. Trying full scan."); - GenCandidatesFromInductionFailure(true); - } - } - -#ifdef CANDS_FROM_UPDATES - /** If the given edge is not inductive in the current proposed solution, - use the induction failure to generate candidates for extension. */ - void GenCandidatesFromEdgeInductionFailure(RPFP::Edge *edge){ - GenSolutionFromIndSet(true /* add markers */); - for(unsigned i = 0; i < edges.size(); i++){ - slvr.push(); - Edge *edge = edges[i]; - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerForEdge(edge,checker); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - slvr.pop(1); - delete checker; - } - } -#endif - - /** Find the unexpanded nodes in the inductive subset. */ - void FindNodesToExpand(){ - for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it){ - Node *node = *it; - if(indset->Candidate(node)) - to_expand.push_back(node); - } - } - - /** Try to create some extension candidates from the unexpanded - nodes. */ - void ProduceSomeCandidates(){ - while(candidates.empty() && !to_expand.empty()){ - Node *node = to_expand.front(); - to_expand.pop_front(); - TryExpandNode(node); - } - } - - std::list postponed_candidates; - - /** Try to produce some extension candidates, first from unexpanded - nides, and if this fails, from induction failure. */ - void ProduceCandidatesForExtension(){ - if(candidates.empty()) - ProduceSomeCandidates(); - while(candidates.empty()){ - FindNodesToExpand(); - if(to_expand.empty()) break; - ProduceSomeCandidates(); - } - if(candidates.empty()){ -#ifdef DEPTH_FIRST_EXPAND - if(postponed_candidates.empty()){ - GenCandidatesFromInductionFailure(); - postponed_candidates.swap(candidates); - } - if(!postponed_candidates.empty()){ - candidates.push_back(postponed_candidates.front()); - postponed_candidates.pop_front(); - } -#else - GenCandidatesFromInductionFailure(); -#endif - } - } - - bool Update(Node *node, const RPFP::Transformer &fact, bool eager=false){ - if(!node->Annotation.SubsetEq(fact)){ - reporter->Update(node,fact,eager); - if(conj_reporter) - conj_reporter->Update(node,fact,eager); - indset->Update(node,fact); - updated_nodes.insert(node->map); - node->Annotation.IntersectWith(fact); - return true; - } - return false; - } - - bool UpdateNodeToNode(Node *node, Node *top){ - return Update(node,top->Annotation); - } - - /** Update the unwinding solution, using an interpolant for the - derivation tree. */ - void UpdateWithInterpolant(Node *node, RPFP *tree, Node *top){ - if(top->Outgoing) - for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) - UpdateWithInterpolant(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); - UpdateNodeToNode(node, top); - heuristic->Update(node); - } - - /** Update unwinding lower bounds, using a counterexample. */ - - void UpdateWithCounterexample(Node *node, RPFP *tree, Node *top){ - if(top->Outgoing) - for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) - UpdateWithCounterexample(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); - if(!top->Underapprox.SubsetEq(node->Underapprox)){ - reporter->UpdateUnderapprox(node,top->Underapprox); - // indset->Update(node,top->Annotation); - node->Underapprox.UnionWith(top->Underapprox); - heuristic->Update(node); - } - } - - /** Try to update the unwinding to satisfy the upper bound of a - node. */ - bool SatisfyUpperBound(Node *node){ - if(node->Bound.IsFull()) return true; -#ifdef PROPAGATE_BEFORE_CHECK - Propagate(); -#endif - reporter->Bound(node); - int start_decs = rpfp->CumulativeDecisions(); - DerivationTree *dtp = new DerivationTreeSlow(this,unwinding,reporter,heuristic,FullExpand); - DerivationTree &dt = *dtp; - bool res = dt.Derive(unwinding,node,UseUnderapprox); - int end_decs = rpfp->CumulativeDecisions(); - // std::cout << "decisions: " << (end_decs - start_decs) << std::endl; - last_decisions = end_decs - start_decs; - if(res){ - cex.set(dt.tree,dt.top); // note tree is now owned by cex - if(UseUnderapprox){ - UpdateWithCounterexample(node,dt.tree,dt.top); - } - } - else { - UpdateWithInterpolant(node,dt.tree,dt.top); - delete dt.tree; - } - delete dtp; - return !res; - } - - /* For a given nod in the unwinding, get conjectures from the - proposers and check them locally. Update the node with any true - conjectures. - */ - - void DoEagerDeduction(Node *node){ - for(unsigned i = 0; i < proposers.size(); i++){ - const std::vector &conjectures = proposers[i]->Propose(node); - for(unsigned j = 0; j < conjectures.size(); j++){ - const RPFP::Transformer &conjecture = conjectures[j]; - RPFP::Transformer bound(conjecture); - std::vector conj_vec; - unwinding->CollectConjuncts(bound.Formula,conj_vec); - for(unsigned k = 0; k < conj_vec.size(); k++){ - bound.Formula = conj_vec[k]; - if(CheckEdgeCaching(node->Outgoing,bound) == unsat) - Update(node,bound, /* eager = */ true); - //else - //std::cout << "conjecture failed\n"; - } - } - } - } - - - check_result CheckEdge(RPFP *checker, Edge *edge){ - Node *root = edge->Parent; - checker->Push(); - checker->AssertNode(root); - checker->AssertEdge(edge,1,true); - check_result res = checker->Check(root); - checker->Pop(1); - return res; - } - - check_result CheckEdgeCaching(Edge *unwinding_edge, const RPFP::Transformer &bound){ - - // use a dedicated solver for this edge - // TODO: can this mess be hidden somehow? - - RPFP_caching *checker = gen_cands_rpfp; // TODO: a good choice? - Edge *edge = unwinding_edge->map; // get the edge in the original RPFP - RPFP_caching::scoped_solver_for_edge ssfe(checker,edge,true /* models */, true /*axioms*/); - Edge *checker_edge = checker->GetEdgeClone(edge); - - // copy the annotations and bound to the clone - Node *root = checker_edge->Parent; - root->Bound = bound; - for(unsigned j = 0; j < checker_edge->Children.size(); j++){ - Node *oc = unwinding_edge->Children[j]; - Node *nc = checker_edge->Children[j]; - nc->Annotation = oc->Annotation; - } - - return CheckEdge(checker,checker_edge); - } - - - /* If the counterexample derivation is partial due to - use of underapproximations, complete it. */ - - void BuildFullCex(Node *node){ - DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); - bool res = dt.Derive(unwinding,node,UseUnderapprox,true); // build full tree - if(!res) throw "Duality internal error in BuildFullCex"; - cex.set(dt.tree,dt.top); - } - - void UpdateBackEdges(Node *node){ -#ifdef BOUNDED - std::vector &chs = node->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - bool is_back = TopoSort[child->map] >= TopoSort[node->map]; - NodeToCounter &nov = back_edges[node]; - NodeToCounter chv = back_edges[child]; - if(is_back) - chv[child->map].val++; - for(NodeToCounter::iterator it = chv.begin(), en = chv.end(); it != en; ++it){ - Node *back = it->first; - Counter &c = nov[back]; - c.val = std::max(c.val,it->second.val); - } - } -#endif - } - - /** Extend the unwinding, keeping it solved. */ - bool Extend(Candidate &cand, Node *&node){ - timer_start("Extend"); - node = CreateNodeInstance(cand.edge->Parent); - CreateEdgeInstance(cand.edge,node,cand.Children); - UpdateBackEdges(node); - reporter->Extend(node); - DoEagerDeduction(node); // first be eager... - bool res = SatisfyUpperBound(node); // then be lazy - if(res) indset->CloseDescendants(node); - else { -#ifdef UNDERAPPROX_NODES - ExpandUnderapproxNodes(cex.get_tree(), cex.get_root()); -#endif - if(UseUnderapprox) BuildFullCex(node); - timer_stop("Extend"); - return res; - } - timer_stop("Extend"); - return res; - } - - void ExpandUnderapproxNodes(RPFP *tree, Node *root){ - Node *node = root->map; - if(underapprox_map.find(node) != underapprox_map.end()){ - RPFP::Transformer cnst = root->Annotation; - tree->EvalNodeAsConstraint(root, cnst); - cnst.Complement(); - Node *orig = underapprox_map[node]; - RPFP::Transformer save = orig->Bound; - orig->Bound = cnst; - DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); - bool res = dt.Derive(unwinding,orig,UseUnderapprox,true,tree); - if(!res){ - UpdateWithInterpolant(orig,dt.tree,dt.top); - throw "bogus underapprox!"; - } - ExpandUnderapproxNodes(tree,dt.top); - } - else if(root->Outgoing){ - std::vector &chs = root->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++) - ExpandUnderapproxNodes(tree,chs[i]); - } - } - - // Propagate conjuncts up the unwinding - void Propagate(){ - reporter->Message("beginning propagation"); - timer_start("Propagate"); - std::vector sorted_nodes = unwinding->nodes; - std::sort(sorted_nodes.begin(),sorted_nodes.end(),std::less()); // sorts by sequence number - hash_map > facts; - for(unsigned i = 0; i < sorted_nodes.size(); i++){ - Node *node = sorted_nodes[i]; - std::set &node_facts = facts[node->map]; - if(!(node->Outgoing && indset->Contains(node))) - continue; - std::vector conj_vec; - unwinding->CollectConjuncts(node->Annotation.Formula,conj_vec); - std::set conjs; - std::copy(conj_vec.begin(),conj_vec.end(),std::inserter(conjs,conjs.begin())); - if(!node_facts.empty()){ - RPFP *checker = new RPFP(rpfp->ls); - slvr.push(); - Node *root = checker->CloneNode(node); - Edge *edge = node->Outgoing; - // checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - nc->Annotation = oc->Annotation; // is this needed? - cs.push_back(nc); - } - Edge *checker_edge = checker->CreateEdge(root,edge->F,cs); - checker->AssertEdge(checker_edge, 0, true, false); - std::vector propagated; - for(std::set ::iterator it = node_facts.begin(), en = node_facts.end(); it != en;){ - const expr &fact = *it; - if(conjs.find(fact) == conjs.end()){ - root->Bound.Formula = fact; - slvr.push(); - checker->AssertNode(root); - check_result res = checker->Check(root); - slvr.pop(); - if(res != unsat){ - std::set ::iterator victim = it; - ++it; - node_facts.erase(victim); // if it ain't true, nix it - continue; - } - propagated.push_back(fact); - } - ++it; - } - slvr.pop(); - for(unsigned i = 0; i < propagated.size(); i++){ - root->Annotation.Formula = propagated[i]; - UpdateNodeToNode(node,root); - } - delete checker; - } - for(std::set ::iterator it = conjs.begin(), en = conjs.end(); it != en; ++it){ - expr foo = *it; - node_facts.insert(foo); - } - } - timer_stop("Propagate"); - } - - - /** This class represents a derivation tree. */ - class DerivationTree { - public: - - virtual ~DerivationTree(){} - - DerivationTree(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) - : slvr(rpfp->slvr()), - ctx(rpfp->ctx) - { - duality = _duality; - reporter = _reporter; - heuristic = _heuristic; - full_expand = _full_expand; - } - - Duality *duality; - Reporter *reporter; - Heuristic *heuristic; - solver &slvr; - context &ctx; - RPFP *tree; - RPFP::Node *top; - std::list leaves; - bool full_expand; - bool underapprox; - bool constrained; - bool false_approx; - std::vector underapprox_core; - int start_decs, last_decs; - - /* We build derivation trees in one of three modes: - - 1) In normal mode, we build the full tree without considering - underapproximations. - - 2) In underapprox mode, we use underapproximations to cut off - the tree construction. THis means the resulting tree may not - be complete. - - 3) In constrained mode, we build the full tree but use - underapproximations as upper bounds. This mode is used to - complete the partial derivation constructed in underapprox - mode. - */ - - bool Derive(RPFP *rpfp, RPFP::Node *root, bool _underapprox, bool _constrained = false, RPFP *_tree = nullptr){ - underapprox = _underapprox; - constrained = _constrained; - false_approx = true; - timer_start("Derive"); -#ifndef USE_CACHING_RPFP - tree = _tree ? _tree : new RPFP(rpfp->ls); -#else - RPFP::LogicSolver *cache_ls = new RPFP::iZ3LogicSolver(ctx); - cache_ls->slvr->push(); - tree = _tree ? _tree : new RPFP_caching(cache_ls); -#endif - tree->HornClauses = rpfp->HornClauses; - tree->Push(); // so we can clear out the solver later when finished - top = CreateApproximatedInstance(root); - tree->AssertNode(top); // assert the negation of the top-level spec - timer_start("Build"); - bool res = Build(); - heuristic->Done(); - timer_stop("Build"); - timer_start("Pop"); - tree->Pop(1); - timer_stop("Pop"); -#ifdef USE_CACHING_RPFP - cache_ls->slvr->pop(1); - delete cache_ls; - tree->ls = rpfp->ls; -#endif - timer_stop("Derive"); - return res; - } - -#define WITH_CHILDREN - - void InitializeApproximatedInstance(RPFP::Node *to){ - to->Annotation = to->map->Annotation; -#ifndef WITH_CHILDREN - tree->CreateLowerBoundEdge(to); -#endif - leaves.push_back(to); - } - - Node *CreateApproximatedInstance(RPFP::Node *from){ - Node *to = tree->CloneNode(from); - InitializeApproximatedInstance(to); - return to; - } - - bool CheckWithUnderapprox(){ - timer_start("CheckWithUnderapprox"); - std::vector leaves_vector(leaves.size()); - std::copy(leaves.begin(),leaves.end(),leaves_vector.begin()); - check_result res = tree->Check(top,leaves_vector); - timer_stop("CheckWithUnderapprox"); - return res != unsat; - } - - virtual bool Build(){ -#ifdef EFFORT_BOUNDED_STRAT - start_decs = tree->CumulativeDecisions(); -#endif - while(ExpandSomeNodes(true)); // do high-priority expansions - while (true) - { -#ifndef WITH_CHILDREN - timer_start("asserting leaves"); - timer_start("pushing"); - tree->Push(); - timer_stop("pushing"); - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - tree->AssertEdge((*it)->Outgoing,1); // assert the overapproximation, and keep it past pop - timer_stop("asserting leaves"); - lbool res = tree->Solve(top, 2); // incremental solve, keep interpolants for two pops - timer_start("popping leaves"); - tree->Pop(1); - timer_stop("popping leaves"); -#else - lbool res; - if((underapprox || false_approx) && top->Outgoing && CheckWithUnderapprox()){ - if(constrained) goto expand_some_nodes; // in constrained mode, keep expanding - goto we_are_sat; // else if underapprox is sat, we stop - } - // tree->Check(top); - res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop -#endif - if (res == l_false) - return false; - - expand_some_nodes: - if(ExpandSomeNodes()) - continue; - - we_are_sat: - if(underapprox && !constrained){ - timer_start("ComputeUnderapprox"); - tree->ComputeUnderapprox(top,1); - timer_stop("ComputeUnderapprox"); - } - else { -#ifdef UNDERAPPROX_NODES -#ifndef SKIP_UNDERAPPROX_NODES - timer_start("ComputeUnderapprox"); - tree->ComputeUnderapprox(top,1); - timer_stop("ComputeUnderapprox"); -#endif -#endif - } - return true; - } - } - - virtual void ExpandNode(RPFP::Node *p){ - // tree->RemoveEdge(p->Outgoing); - Edge *ne = p->Outgoing; - if(ne) { - // reporter->Message("Recycling edge..."); - std::vector &cs = ne->Children; - for(unsigned i = 0; i < cs.size(); i++) - InitializeApproximatedInstance(cs[i]); - // ne->dual = expr(); - } - else { - Edge *edge = duality->GetNodeOutgoing(p->map,last_decs); - std::vector &cs = edge->Children; - std::vector children(cs.size()); - for(unsigned i = 0; i < cs.size(); i++) - children[i] = CreateApproximatedInstance(cs[i]); - ne = tree->CreateEdge(p, p->map->Outgoing->F, children); - ne->map = p->map->Outgoing->map; - } -#ifndef WITH_CHILDREN - tree->AssertEdge(ne); // assert the edge in the solver -#else - tree->AssertEdge(ne,0,!full_expand,(underapprox || false_approx)); // assert the edge in the solver -#endif - reporter->Expand(ne); - } - -#define UNDERAPPROXCORE -#ifndef UNDERAPPROXCORE - void ExpansionChoices(std::set &best){ - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - if (!tree->Empty(*it)) // if used in the counter-model - choices.insert(*it); - heuristic->ChooseExpand(choices, best); - } -#else -#if 0 - - void ExpansionChoices(std::set &best){ - std::vector unused_set, used_set; - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ - Node *n = *it; - if (!tree->Empty(n)) - used_set.push_back(n); - else - unused_set.push_back(n); - } - if(tree->Check(top,unused_set) == unsat) - throw "error in ExpansionChoices"; - for(unsigned i = 0; i < used_set.size(); i++){ - Node *n = used_set[i]; - unused_set.push_back(n); - if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ - unused_set.pop_back(); - choices.insert(n); - } - else - std::cout << "Using underapprox of " << n->number << std::endl; - } - heuristic->ChooseExpand(choices, best); - } -#else - void ExpansionChoicesFull(std::set &best, bool high_priority, bool best_only = false){ - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - if (high_priority || !tree->Empty(*it)) // if used in the counter-model - choices.insert(*it); - heuristic->ChooseExpand(choices, best, high_priority, best_only); - } - - void ExpansionChoicesRec(std::vector &unused_set, std::vector &used_set, - std::set &choices, int from, int to){ - if(from == to) return; - int orig_unused = unused_set.size(); - unused_set.resize(orig_unused + (to - from)); - std::copy(used_set.begin()+from,used_set.begin()+to,unused_set.begin()+orig_unused); - if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ - unused_set.resize(orig_unused); - if(to - from == 1){ -#if 1 - std::cout << "Not using underapprox of " << used_set[from] ->number << std::endl; -#endif - choices.insert(used_set[from]); - } - else { - int mid = from + (to - from)/2; - ExpansionChoicesRec(unused_set, used_set, choices, from, mid); - ExpansionChoicesRec(unused_set, used_set, choices, mid, to); - } - } - else { -#if 1 - std::cout << "Using underapprox of "; - for(int i = from; i < to; i++){ - std::cout << used_set[i]->number << " "; - if(used_set[i]->map->Underapprox.IsEmpty()) - std::cout << "(false!) "; - } - std::cout << std::endl; -#endif - } - } - - std::set old_choices; - - void ExpansionChoices(std::set &best, bool high_priority, bool best_only = false){ - if(!underapprox || constrained || high_priority){ - ExpansionChoicesFull(best, high_priority,best_only); - return; - } - std::vector unused_set, used_set; - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ - Node *n = *it; - if (!tree->Empty(n)){ - if(old_choices.find(n) != old_choices.end() || n->map->Underapprox.IsEmpty()) - choices.insert(n); - else - used_set.push_back(n); - } - else - unused_set.push_back(n); - } - if(tree->Check(top,unused_set) == unsat) - throw "error in ExpansionChoices"; - ExpansionChoicesRec(unused_set, used_set, choices, 0, used_set.size()); - old_choices = choices; - heuristic->ChooseExpand(choices, best, high_priority); - } -#endif -#endif - - bool ExpandSomeNodes(bool high_priority = false, int max = INT_MAX){ -#ifdef EFFORT_BOUNDED_STRAT - last_decs = tree->CumulativeDecisions() - start_decs; -#endif - timer_start("ExpandSomeNodes"); - timer_start("ExpansionChoices"); - std::set choices; - ExpansionChoices(choices,high_priority,max != INT_MAX); - timer_stop("ExpansionChoices"); - std::list leaves_copy = leaves; // copy so can modify orig - leaves.clear(); - int count = 0; - for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ - if(choices.find(*it) != choices.end() && count < max){ - count++; - ExpandNode(*it); - } - else leaves.push_back(*it); - } - timer_stop("ExpandSomeNodes"); - return !choices.empty(); - } - - void RemoveExpansion(RPFP::Node *p){ - Edge *edge = p->Outgoing; - Node *parent = edge->Parent; -#ifndef KEEP_EXPANSIONS - std::vector cs = edge->Children; - tree->DeleteEdge(edge); - for(unsigned i = 0; i < cs.size(); i++) - tree->DeleteNode(cs[i]); -#endif - leaves.push_back(parent); - } - - // remove all the descendants of tree root (but not root itself) - void RemoveTree(RPFP *tree, RPFP::Node *root){ - Edge *edge = root->Outgoing; - std::vector cs = edge->Children; - tree->DeleteEdge(edge); - for(unsigned i = 0; i < cs.size(); i++){ - RemoveTree(tree,cs[i]); - tree->DeleteNode(cs[i]); - } - } - }; - - class DerivationTreeSlow : public DerivationTree { - public: - - struct stack_entry { - unsigned level; // SMT solver stack level - std::vector expansions; - }; - - std::vector stack; - - hash_map updates; - - int restart_interval; - - DerivationTreeSlow(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) - : DerivationTree(_duality, rpfp, _reporter, _heuristic, _full_expand) { - stack.push_back(stack_entry()); - } - - struct DoRestart {}; - - bool Build() override { - restart_interval = 3; - while (true) { - try { - return BuildMain(); - } - catch (const DoRestart &) { - // clear the statck and try again - updated_nodes.clear(); - while(stack.size() > 1) - PopLevel(); - reporter->Message("restarted"); - restart_interval += 1; - } - } - } - - - // When we check, try to use the same children that were used in the - // previous counterexample. - check_result Check(){ -#if 0 - std::vector posnodes, negnodes; - std::vector &expansions = stack.back().expansions; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - std::vector &chs = node->Outgoing->Children; - for(unsigned j = 0; j < chs.size(); j++){ - Node *ch = chs[j]; - int use = heuristic->UseNode(ch); - if(use == 1) - posnodes.push_back(ch); - else if (use == -1) - negnodes.push_back(ch); - } - } - if(!(posnodes.empty() && negnodes.empty())){ - check_result res = tree->CheckWithConstrainedNodes(posnodes,negnodes); - if(res != unsat){ - reporter->Message("matched previous counterexample"); - return res; - } - } -#endif - return tree->Check(top); - } - - bool BuildMain(){ - - stack.back().level = tree->slvr().get_scope_level(); - bool was_sat = true; - int update_failures = 0; - int total_updates = 0; - - while (true) - { - lbool res; - - unsigned slvr_level = tree->slvr().get_scope_level(); - if(slvr_level != stack.back().level) - throw "stacks out of sync!"; - reporter->Depth(stack.size()); - - // res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop - check_result foo = Check(); - res = foo == unsat ? l_false : l_true; - - if (res == l_false) { - if (stack.empty()) // should never happen - return false; - - { - std::vector &expansions = stack.back().expansions; - int update_count = 0; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - try { - tree->SolveSingleNode(top,node); -#ifdef NO_GENERALIZE - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); -#else - if(expansions.size() == 1 && NodeTooComplicated(node)) - SimplifyNode(node); - else - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); - Generalize(node); -#endif - } - catch(const RPFP::Bad &){ - // bad interpolants can get us here - throw DoRestart(); - } - catch(const RPFP::ReallyBad &){ - // this could be caused by incompleteness - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - node->map->Annotation.SetFull(); - std::vector &chs = node->map->Outgoing->Children; - for(unsigned j = 0; j < chs.size(); j++) - chs[j]->Annotation.SetFull(); - reporter->Message("incompleteness: cleared annotation and child annotations"); - } - throw DoRestart(); - } - catch(char const *msg){ - // bad interpolants can get us here - reporter->Message(std::string("interpolation failure:") + msg); - throw DoRestart(); - } - catch(const RPFP::greedy_reduce_failed &){ - // if we couldn't reduce, just continue (maybe should restart?) - reporter->Message("interpolant verification failed"); - } - if(RecordUpdate(node)){ - update_count++; - total_updates++; - } - else - heuristic->Update(node->map); // make it less likely to expand this node in future - } -#if 1 - if(duality->EnableRestarts) - if(total_updates >= restart_interval) - throw DoRestart(); -#endif - if(update_count == 0){ - if(was_sat){ - update_failures++; - if(update_failures > 10){ - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - node->map->Annotation.SetFull(); - reporter->Message("incompleteness: cleared annotation"); - } - throw DoRestart(); - } - } - reporter->Message("backtracked without learning"); - } - else update_failures = 0; - } - tree->ComputeProofCore(); // need to compute the proof core before popping solver - bool propagated = false; - while(1) { - bool prev_level_used = LevelUsedInProof(stack.size()-2); // need to compute this before pop - PopLevel(); - if(stack.size() == 1)break; - if(prev_level_used){ - Node *node = stack.back().expansions[0]; -#ifndef NO_PROPAGATE - if(!Propagate(node)) break; -#endif - if(!RecordUpdate(node)) break; // shouldn't happen! - RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list - propagated = true; - continue; - } - if(propagated) break; // propagation invalidates the proof core, so disable non-chron backtrack - RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list - std::vector &unused_ex = stack.back().expansions; - for(unsigned i = 0; i < unused_ex.size(); i++) - heuristic->Update(unused_ex[i]->map); // make it less likely to expand this node in future - } - HandleUpdatedNodes(); - if(stack.size() == 1){ - if(top->Outgoing) - tree->DeleteEdge(top->Outgoing); // in case we kept the tree - return false; - } - was_sat = false; - } - else { - was_sat = true; - tree->Push(); - std::vector &expansions = stack.back().expansions; -#ifndef NO_DECISIONS -#if 0 - if(expansions.size() > 0) - tree->GreedyReduceNodes(expansions[0]->Outgoing->Children); // try to reduce number of children -#endif - for(unsigned i = 0; i < expansions.size(); i++){ - tree->FixCurrentState(expansions[i]->Outgoing); - } -#endif -#if 0 - if(tree->slvr().check() == unsat) - throw "help!"; -#endif - int expand_max = 1; - if(0&&duality->BatchExpand){ - int thing = stack.size() / 10; // * 0.1; - expand_max = std::max(1,thing); - if(expand_max > 1) - std::cout << "foo!\n"; - } - - if(ExpandSomeNodes(false,expand_max)) - continue; - tree->Pop(1); - node_order.clear(); - while(stack.size() > 1){ - tree->Pop(1); - std::vector &expansions = stack.back().expansions; - for(unsigned i = 0; i < expansions.size(); i++) - node_order.push_back(expansions[i]); - stack.pop_back(); - } -#if 0 - Reduce(); -#endif - return true; - } - } - } - - std::vector node_order; - - void Reduce(){ - tree->Push(); - // tree->AssertNode(top); // assert the negation of the top-level spec - for(int i = node_order.size()-1; i >= 0; --i){ - Edge *edge = node_order[i]->Outgoing; - if(edge){ - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *ch = edge->Children[j]; - if(!ch->Outgoing) - ch->Annotation.SetEmpty(); - } - tree->AssertEdge(edge,0,true); - } - } - tree->GreedyReduceNodes(node_order); // try to reduce the counterexample size - tree->Pop(1); - } - - void PopLevel(){ - std::vector &expansions = stack.back().expansions; - tree->Pop(1); - hash_set leaves_to_remove; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - // if(node != top) - //tree->ConstrainParent(node->Incoming[0],node); - std::vector &cs = node->Outgoing->Children; - for(unsigned i = 0; i < cs.size(); i++){ - leaves_to_remove.insert(cs[i]); - UnmapNode(cs[i]); - if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) - throw "help!"; - } - } - RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - RemoveExpansion(node); - } - stack.pop_back(); - } - - bool NodeTooComplicated(Node *node){ - int ops = tree->CountOperators(node->Annotation.Formula); - if(ops > 10) return true; - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); - return tree->CountOperators(node->Annotation.Formula) > 3; - } - - void SimplifyNode(Node *node){ - // have to destroy the old proof to get a new interpolant - timer_start("SimplifyNode"); - tree->PopPush(); - try { - tree->InterpolateByCases(top,node); - } - catch(const RPFP::Bad&){ - timer_stop("SimplifyNode"); - throw RPFP::Bad(); - } - timer_stop("SimplifyNode"); - } - - bool LevelUsedInProof(unsigned level){ - std::vector &expansions = stack[level].expansions; - for(unsigned i = 0; i < expansions.size(); i++) - if(tree->EdgeUsedInProof(expansions[i]->Outgoing)) - return true; - return false; - } - - void RemoveUpdateNodesAtCurrentLevel() { - for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ - Node *node = *it; - if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ - std::list::iterator victim = it; - ++it; - updated_nodes.erase(victim); - } - else - ++it; - } - } - - void RemoveLeaves(hash_set &leaves_to_remove){ - std::list leaves_copy; - leaves_copy.swap(leaves); - for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ - if(leaves_to_remove.find(*it) == leaves_to_remove.end()) - leaves.push_back(*it); - } - } - - hash_map > node_map; - std::list updated_nodes; - - void ExpandNode(RPFP::Node *p) override { - stack.push_back(stack_entry()); - stack.back().level = tree->slvr().get_scope_level(); - stack.back().expansions.push_back(p); - DerivationTree::ExpandNode(p); - std::vector &new_nodes = p->Outgoing->Children; - for(unsigned i = 0; i < new_nodes.size(); i++){ - Node *n = new_nodes[i]; - node_map[n->map].push_back(n); - } - } - - bool RecordUpdate(Node *node){ - bool res = duality->UpdateNodeToNode(node->map,node); - if(res){ - std::vector to_update = node_map[node->map]; - for(unsigned i = 0; i < to_update.size(); i++){ - Node *node2 = to_update[i]; - // maintain invariant that no nodes on updated list are created at current stack level - if(node2 == node || !(node->Incoming.size() > 0 && AtCurrentStackLevel(node2->Incoming[0]->Parent))){ - updated_nodes.push_back(node2); - if(node2 != node) - node2->Annotation = node->Annotation; - } - } - } - return res; - } - - void HandleUpdatedNodes(){ - for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ - Node *node = *it; - node->Annotation = node->map->Annotation; - if(node->Incoming.size() > 0) - tree->ConstrainParent(node->Incoming[0],node); - if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ - std::list::iterator victim = it; - ++it; - updated_nodes.erase(victim); - } - else - ++it; - } - } - - bool AtCurrentStackLevel(Node *node){ - std::vector vec = stack.back().expansions; - for(unsigned i = 0; i < vec.size(); i++) - if(vec[i] == node) - return true; - return false; - } - - void UnmapNode(Node *node){ - std::vector &vec = node_map[node->map]; - for(unsigned i = 0; i < vec.size(); i++){ - if(vec[i] == node){ - std::swap(vec[i],vec.back()); - vec.pop_back(); - return; - } - } - throw "can't unmap node"; - } - - void Generalize(Node *node){ -#ifndef USE_RPFP_CLONE - tree->Generalize(top,node); -#else - RPFP_caching *clone_rpfp = duality->clone_rpfp; - if(!node->Outgoing->map) return; - Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); - Node *clone_node = clone_edge->Parent; - clone_node->Annotation = node->Annotation; - for(unsigned i = 0; i < clone_edge->Children.size(); i++) - clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; - clone_rpfp->GeneralizeCache(clone_edge); - node->Annotation = clone_node->Annotation; -#endif - } - - bool Propagate(Node *node){ -#ifdef USE_RPFP_CLONE - RPFP_caching *clone_rpfp = duality->clone_rpfp; - Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); - Node *clone_node = clone_edge->Parent; - clone_node->Annotation = node->map->Annotation; - for(unsigned i = 0; i < clone_edge->Children.size(); i++) - clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; - bool res = clone_rpfp->PropagateCache(clone_edge); - if(res) - node->Annotation = clone_node->Annotation; - return res; -#else - return false; -#endif - } - - }; - - - class Covering { - - struct cover_info { - Node *covered_by; - std::list covers; - bool dominated; - std::set dominates; - cover_info(){ - covered_by = nullptr; - dominated = false; - } - }; - - typedef hash_map cover_map; - cover_map cm; - Duality *parent; - bool some_updates; - -#define NO_CONJ_ON_SIMPLE_LOOPS -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - hash_set simple_loops; -#endif - - Node *&covered_by(Node *node){ - return cm[node].covered_by; - } - - std::list &covers(Node *node){ - return cm[node].covers; - } - - std::vector &insts_of_node(Node *node){ - return parent->insts_of_node[node]; - } - - Reporter *reporter(){ - return parent->reporter; - } - - std::set &dominates(Node *x){ - return cm[x].dominates; - } - - bool dominates(Node *x, Node *y){ - std::set &d = cm[x].dominates; - return d.find(y) != d.end(); - } - - bool &dominated(Node *x){ - return cm[x].dominated; - } - - public: - - Covering(Duality *_parent){ - parent = _parent; - some_updates = false; - -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - hash_map > outgoing; - for(unsigned i = 0; i < parent->rpfp->edges.size(); i++) - outgoing[parent->rpfp->edges[i]->Parent].push_back(parent->rpfp->edges[i]); - for(unsigned i = 0; i < parent->rpfp->nodes.size(); i++){ - Node * node = parent->rpfp->nodes[i]; - std::vector &outs = outgoing[node]; - if(outs.size() == 2){ - for(int j = 0; j < 2; j++){ - Edge *loop_edge = outs[j]; - if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent) - simple_loops.insert(node); - } - } - } -#endif - - } - - bool IsCoveredRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return false; - memo.insert(node); - if(covered_by(node)) return true; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) - if(IsCoveredRec(memo,node->Outgoing->Children[i])) - return true; - return false; - } - - bool IsCovered(Node *node){ - hash_set memo; - return IsCoveredRec(memo,node); - } - -#ifndef UNDERAPPROX_NODES - void RemoveCoveringsBy(Node *node){ - std::list &cs = covers(node); - for(std::list::iterator it = cs.begin(), en = cs.end(); it != en; it++){ - covered_by(*it) = 0; - reporter()->RemoveCover(*it,node); - } - cs.clear(); - } -#else - void RemoveCoveringsBy(Node *node){ - std::vector &cs = parent->all_of_node[node->map]; - for(std::vector::iterator it = cs.begin(), en = cs.end(); it != en; it++){ - Node *other = *it; - if(covered_by(other) && CoverOrder(node,other)){ - covered_by(other) = nullptr; - reporter()->RemoveCover(*it,node); - } - } - } -#endif - - void RemoveAscendantCoveringsRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return; - memo.insert(node); - RemoveCoveringsBy(node); - for(std::vector::iterator it = node->Incoming.begin(), en = node->Incoming.end(); it != en; ++it) - RemoveAscendantCoveringsRec(memo,(*it)->Parent); - } - - void RemoveAscendantCoverings(Node *node){ - hash_set memo; - RemoveAscendantCoveringsRec(memo,node); - } - - bool CoverOrder(Node *covering, Node *covered){ -#ifdef UNDERAPPROX_NODES - if(parent->underapprox_map.find(covered) != parent->underapprox_map.end()) - return false; - if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) - return covering->number < covered->number || parent->underapprox_map[covering] == covered; -#endif - return covering->number < covered->number; - } - - bool CheckCover(Node *covered, Node *covering){ - return - CoverOrder(covering,covered) - && covered->Annotation.SubsetEq(covering->Annotation) - && !IsCovered(covering); - } - - bool CoverByNode(Node *covered, Node *covering){ - if(CheckCover(covered,covering)){ - covered_by(covered) = covering; - covers(covering).push_back(covered); - std::vector others; others.push_back(covering); - reporter()->AddCover(covered,others); - RemoveAscendantCoverings(covered); - return true; - } - else - return false; - } - -#ifdef UNDERAPPROX_NODES - bool CoverByAll(Node *covered){ - RPFP::Transformer all = covered->Annotation; - all.SetEmpty(); - std::vector &insts = parent->insts_of_node[covered->map]; - std::vector others; - for(unsigned i = 0; i < insts.size(); i++){ - Node *covering = insts[i]; - if(CoverOrder(covering,covered) && !IsCovered(covering)){ - others.push_back(covering); - all.UnionWith(covering->Annotation); - } - } - if(others.size() && covered->Annotation.SubsetEq(all)){ - covered_by(covered) = covered; // anything non-null will do - reporter()->AddCover(covered,others); - RemoveAscendantCoverings(covered); - return true; - } - else - return false; - } -#endif - - bool Close(Node *node){ - if(covered_by(node)) - return true; -#ifndef UNDERAPPROX_NODES - std::vector &insts = insts_of_node(node->map); - for(unsigned i = 0; i < insts.size(); i++) - if(CoverByNode(node,insts[i])) - return true; -#else - if(CoverByAll(node)) - return true; -#endif - return false; - } - - bool CloseDescendantsRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return false; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) - if(CloseDescendantsRec(memo,node->Outgoing->Children[i])) - return true; - if(Close(node)) - return true; - memo.insert(node); - return false; - } - - bool CloseDescendants(Node *node){ - timer_start("CloseDescendants"); - hash_set memo; - bool res = CloseDescendantsRec(memo,node); - timer_stop("CloseDescendants"); - return res; - } - - bool Contains(Node *node){ - timer_start("Contains"); - bool res = !IsCovered(node); - timer_stop("Contains"); - return res; - } - - bool Candidate(Node *node){ - timer_start("Candidate"); - bool res = !IsCovered(node) && !dominated(node); - timer_stop("Candidate"); - return res; - } - - void SetDominated(Node *node){ - dominated(node) = true; - } - - bool CouldCover(Node *covered, Node *covering){ -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - // Forsimple loops, we rely on propagation, not covering - if(simple_loops.find(covered->map) != simple_loops.end()) - return false; -#endif -#ifdef UNDERAPPROX_NODES - // if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) - // return parent->underapprox_map[covering] == covered; -#endif - if(CoverOrder(covering,covered) - && !IsCovered(covering)){ - RPFP::Transformer f(covering->Annotation); f.SetEmpty(); -#if defined(TOP_DOWN) || defined(EFFORT_BOUNDED_STRAT) - if(parent->StratifiedInlining) - return true; -#endif - return !covering->Annotation.SubsetEq(f); - } - return false; - } - - bool ContainsCex(Node *node, Counterexample &cex){ - expr val = cex.get_tree()->Eval(cex.get_root()->Outgoing,node->Annotation.Formula); - return eq(val,parent->ctx.bool_val(true)); - } - - /** We conjecture that the annotations of similar nodes may be - true of this one. We start with later nodes, on the - principle that their annotations are likely weaker. We save - a counterexample -- if annotations of other nodes are true - in this counterexample, we don't need to check them. - */ - -#ifndef UNDERAPPROX_NODES - bool Conjecture(Node *node){ - std::vector &insts = insts_of_node(node->map); - Counterexample cex; - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CouldCover(node,other)){ - reporter()->Forcing(node,other); - if(cex.get_tree() && !ContainsCex(other,cex)) - continue; - cex.clear(); - if(parent->ProveConjecture(node,other->Annotation,other,&cex)) - if(CloseDescendants(node)) - return true; - } - } - cex.clear(); - return false; - } -#else - bool Conjecture(Node *node){ - std::vector &insts = insts_of_node(node->map); - Counterexample cex; - RPFP::Transformer Bound = node->Annotation; - Bound.SetEmpty(); - bool some_other = false; - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CouldCover(node,other)){ - reporter()->Forcing(node,other); - Bound.UnionWith(other->Annotation); - some_other = true; - } - } - if(some_other && parent->ProveConjecture(node,Bound)){ - CloseDescendants(node); - return true; - } - return false; - } -#endif - - void Update(Node *node, const RPFP::Transformer &update){ - RemoveCoveringsBy(node); - some_updates = true; - } - -#ifndef UNDERAPPROX_NODES - Node *GetSimilarNode(Node *node){ - if(!some_updates) - return 0; - std::vector &insts = insts_of_node(node->map); - for(int i = insts.size()-1; i >= 0; i--){ - Node *other = insts[i]; - if(dominates(node,other)) - if(CoverOrder(other,node) - && !IsCovered(other)) - return other; - } - return 0; - } -#else - Node *GetSimilarNode(Node *node){ - if(!some_updates) - return nullptr; - std::vector &insts = insts_of_node(node->map); - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CoverOrder(other,node) - && !IsCovered(other)) - return other; - } - return nullptr; - } -#endif - - bool Dominates(Node * node, Node *other){ - if(node == other) return false; - if(other->Outgoing->map == nullptr) return true; - if(node->Outgoing->map == other->Outgoing->map){ - assert(node->Outgoing->Children.size() == other->Outgoing->Children.size()); - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *nc = node->Outgoing->Children[i]; - Node *oc = other->Outgoing->Children[i]; - if(!(nc == oc || oc->Outgoing->map ==nullptr || dominates(nc,oc))) - return false; - } - return true; - } - return false; - } - - void Add(Node *node){ - std::vector &insts = insts_of_node(node->map); - for(unsigned i = 0; i < insts.size(); i++){ - Node *other = insts[i]; - if(Dominates(node,other)){ - cm[node].dominates.insert(other); - cm[other].dominated = true; - reporter()->Dominates(node, other); - } - } - } - - }; - - /* This expansion heuristic makes use of a previuosly obtained - counterexample as a guide. This is for use in abstraction - refinement schemes.*/ - - class ReplayHeuristic : public Heuristic { - - Counterexample old_cex; - public: - ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) - : Heuristic(_rpfp) - { - old_cex.swap(_old_cex); // take ownership from caller - } - - ~ReplayHeuristic() override { - } - - // Maps nodes of derivation tree into old cex - hash_map cex_map; - - void Done() override { - cex_map.clear(); - old_cex.clear(); - } - - void ShowNodeAndChildren(Node *n){ - std::cout << n->Name.name() << ": "; - std::vector &chs = n->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++) - std::cout << chs[i]->Name.name() << " " ; - std::cout << std::endl; - } - - // HACK: When matching relation names, we drop suffixes used to - // make the names unique between runs. For compatibility - // with boggie, we drop suffixes beginning with @@ - std::string BaseName(const std::string &name){ - int pos = name.find("@@"); - if(pos >= 1) - return name.substr(0,pos); - return name; - } - - Node *MatchNode(Node *node){ - if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node - Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! - if(cex_map.find(parent) == cex_map.end()) - throw "catastrophe in ReplayHeuristic::ChooseExpand"; - Node *old_parent = cex_map[parent]; - std::vector &chs = parent->Outgoing->Children; - if(old_parent && old_parent->Outgoing){ - std::vector &old_chs = old_parent->Outgoing->Children; - for(unsigned i = 0, j=0; i < chs.size(); i++){ - if(j < old_chs.size() && BaseName(chs[i]->Name.name().str()) == BaseName(old_chs[j]->Name.name().str())) - cex_map[chs[i]] = old_chs[j++]; - else { - std::cerr << "WARNING: duality: unmatched child: " << chs[i]->Name.name() << std::endl; - cex_map[chs[i]] = 0; - } - } - goto matching_done; - } - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = 0; - } - matching_done: - return cex_map[node]; - } - - int UseNode(Node *node) override { - if (!old_cex.get_tree()) - return 0; - Node *old_node = MatchNode(node); - if(!old_node) - return 0; - return old_cex.get_tree()->Empty(old_node) ? -1 : 1; - } - - void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only) override { - if(cex_map.empty()) - cex_map[*(choices.begin())] = old_cex.get_root(); // match the root nodes - if(!high_priority || !old_cex.get_tree()){ - Heuristic::ChooseExpand(choices,best,false); - return; - } - // first, try to match the derivatino tree nodes to the old cex - std::set matched, unmatched; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it); - Node *old_node = MatchNode(node); - if(!old_node) - unmatched.insert(node); - else if(old_cex.get_tree()->Empty(old_node)) - unmatched.insert(node); - else - matched.insert(node); - } - if (matched.empty() && !high_priority) - Heuristic::ChooseExpand(unmatched,best,false); - else - Heuristic::ChooseExpand(matched,best,false); - } - }; - - - class LocalHeuristic : public Heuristic { - - RPFP::Node *old_node; - public: - LocalHeuristic(RPFP *_rpfp) - : Heuristic(_rpfp) - { - old_node = nullptr; - } - - void SetOldNode(RPFP::Node *_old_node){ - old_node = _old_node; - cex_map.clear(); - } - - // Maps nodes of derivation tree into old subtree - hash_map cex_map; - - void ChooseExpand(const std::set &choices, std::set &best, bool, bool) override { - if(old_node == nullptr){ - Heuristic::ChooseExpand(choices,best); - return; - } - // first, try to match the derivatino tree nodes to the old cex - std::set matched, unmatched; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it); - if(cex_map.empty()) - cex_map[node] = old_node; // match the root nodes - if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node - Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! - if(cex_map.find(parent) == cex_map.end()) - throw "catastrophe in ReplayHeuristic::ChooseExpand"; - Node *old_parent = cex_map[parent]; - std::vector &chs = parent->Outgoing->Children; - if(old_parent && old_parent->Outgoing){ - std::vector &old_chs = old_parent->Outgoing->Children; - if(chs.size() == old_chs.size()){ - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = old_chs[i]; - goto matching_done; - } - else - std::cout << "derivation tree does not match old cex" << std::endl; - } - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = 0; - } - matching_done: - Node *old_node = cex_map[node]; - if(!old_node) - unmatched.insert(node); - else if(old_node != node->map) - unmatched.insert(node); - else - matched.insert(node); - } - Heuristic::ChooseExpand(unmatched,best); - } - }; - - /** This proposer class generates conjectures based on the - unwinding generated by a previous solver. The assumption is - that the provious solver was working on a different - abstraction of the same system. The trick is to adapt the - annotations in the old unwinding to the new predicates. We - start by generating a map from predicates and parameters in - the old problem to the new. - - HACK: mapping is done by cheesy name comparison. - */ - - class HistoryProposer : public Proposer - { - Duality *old_solver; - Duality *new_solver; - hash_map > conjectures; - - public: - /** Construct a history solver. */ - HistoryProposer(Duality *_old_solver, Duality *_new_solver) - : old_solver(_old_solver), new_solver(_new_solver) { - - // tricky: names in the axioms may have changed -- map them - hash_set &old_constants = old_solver->unwinding->ls->get_constants(); - hash_set &new_constants = new_solver->rpfp->ls->get_constants(); - hash_map cmap; - for(hash_set::iterator it = new_constants.begin(), en = new_constants.end(); it != en; ++it) - cmap[GetKey(*it)] = *it; - hash_map bckg_map; - for(hash_set::iterator it = old_constants.begin(), en = old_constants.end(); it != en; ++it){ - func_decl f = new_solver->ctx.translate(*it); // move to new context - if(cmap.find(GetKey(f)) != cmap.end()) - bckg_map[f] = cmap[GetKey(f)]; - // else - // std::cout << "constant not matched\n"; - } - - RPFP *old_unwinding = old_solver->unwinding; - hash_map > pred_match; - - // index all the predicates in the old unwinding - for(unsigned i = 0; i < old_unwinding->nodes.size(); i++){ - Node *node = old_unwinding->nodes[i]; - std::string key = GetKey(node); - pred_match[key].push_back(node); - } - - // match with predicates in the new RPFP - RPFP *rpfp = new_solver->rpfp; - for(unsigned i = 0; i < rpfp->nodes.size(); i++){ - Node *node = rpfp->nodes[i]; - std::string key = GetKey(node); - std::vector &matches = pred_match[key]; - for(unsigned j = 0; j < matches.size(); j++) - MatchNodes(node,matches[j],bckg_map); - } - } - - std::vector &Propose(Node *node) override { - return conjectures[node->map]; - } - - ~HistoryProposer() override { - }; - - private: - void MatchNodes(Node *new_node, Node *old_node, hash_map &bckg_map){ - if(old_node->Annotation.IsFull()) - return; // don't conjecture true! - hash_map var_match; - std::vector &new_params = new_node->Annotation.IndParams; - // Index the new parameters by their keys - for(unsigned i = 0; i < new_params.size(); i++) - var_match[GetKey(new_params[i])] = new_params[i]; - RPFP::Transformer &old = old_node->Annotation; - std::vector from_params = old.IndParams; - for(unsigned j = 0; j < from_params.size(); j++) - from_params[j] = new_solver->ctx.translate(from_params[j]); // get in new context - std::vector to_params = from_params; - for(unsigned j = 0; j < to_params.size(); j++){ - std::string key = GetKey(to_params[j]); - if(var_match.find(key) == var_match.end()){ - // std::cout << "unmatched parameter!\n"; - return; - } - to_params[j] = var_match[key]; - } - expr fmla = new_solver->ctx.translate(old.Formula); // get in new context - fmla = new_solver->rpfp->SubstParams(old.IndParams,to_params,fmla); // substitute parameters - hash_map memo; - fmla = new_solver->rpfp->SubstRec(memo,bckg_map,fmla); // substitute background constants - RPFP::Transformer new_annot = new_node->Annotation; - new_annot.Formula = fmla; - conjectures[new_node].push_back(new_annot); - } - - // We match names by removing suffixes beginning with double at sign - - std::string GetKey(Node *node){ - return GetKey(node->Name); - } - - std::string GetKey(const expr &var){ - return GetKey(var.decl()); - } - - std::string GetKey(const func_decl &f){ - std::string name = f.name().str(); - int idx = name.find("@@"); - if(idx >= 0) - name.erase(idx); - return name; - } - }; - }; - - static int stop_event = -1; - - class StreamReporter : public Reporter { - std::ostream &s; - public: - StreamReporter(RPFP *_rpfp, std::ostream &_s) - : Reporter(_rpfp), s(_s) {event = 0; depth = -1;} - int event; - int depth; - void ev(){ - if(stop_event == event){ - std::cout << "stop!\n"; - } - s << "[" << event++ << "]" ; - } - void Extend(RPFP::Node *node) override { - ev(); s << "node " << node->number << ": " << node->Name.name(); - std::vector &rps = node->Outgoing->Children; - for(unsigned i = 0; i < rps.size(); i++) - s << " " << rps[i]->number; - s << std::endl; - } - void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager) override { - ev(); s << "update " << node->number << " " << node->Name.name() << ": "; - rpfp->Summarize(update.Formula); - if(depth > 0) s << " (depth=" << depth << ")"; - if(eager) s << " (eager)"; - s << std::endl; - } - void Bound(RPFP::Node *node) override { - ev(); s << "check " << node->number << std::endl; - } - void Expand(RPFP::Edge *edge) override { - RPFP::Node *node = edge->Parent; - ev(); s << "expand " << node->map->number << " " << node->Name.name(); - if(depth > 0) s << " (depth=" << depth << ")"; - s << std::endl; - } - void Depth(int d) override { - depth = d; - } - void AddCover(RPFP::Node *covered, std::vector &covering) override { - ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by "; - for(unsigned i = 0; i < covering.size(); i++) - s << covering[i]->number << " "; - s << std::endl; - } - void RemoveCover(RPFP::Node *covered, RPFP::Node *covering) override { - ev(); s << "uncover " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; - } - void Forcing(RPFP::Node *covered, RPFP::Node *covering) override { - ev(); s << "forcing " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; - } - void Conjecture(RPFP::Node *node, const RPFP::Transformer &t) override { - ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": "; - rpfp->Summarize(t.Formula); - s << std::endl; - } - void Dominates(RPFP::Node *node, RPFP::Node *other) override { - ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl; - } - void InductionFailure(RPFP::Edge *edge, const std::vector &children) override { - ev(); s << "induction failure: " << edge->Parent->Name.name() << ", children ="; - for(unsigned i = 0; i < children.size(); i++) - s << " " << children[i]->number; - s << std::endl; - } - void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update) override { - ev(); s << "underapprox " << node->number << " " << node->Name.name() << ": " << update.Formula << std::endl; - } - void Reject(RPFP::Edge *edge, const std::vector &children) override { - ev(); s << "reject " << edge->Parent->number << " " << edge->Parent->Name.name() << ": "; - for(unsigned i = 0; i < children.size(); i++) - s << " " << children[i]->number; - s << std::endl; - } - void Message(const std::string &msg) override { - ev(); s << "msg " << msg << std::endl; - } - - }; - - - class DualityDepthBounded : public Solver { - - Duality *duality; - context &ctx; // Z3 context - solver &slvr; // Z3 solver - - public: - DualityDepthBounded(RPFP *_rpfp) : - ctx(_rpfp->ctx), - slvr(_rpfp->slvr()){ - rpfp = _rpfp; - DepthBoundRPFP(); - duality = alloc(Duality,drpfp); - } - - ~DualityDepthBounded() override { - dealloc(duality); - delete drpfp; - } - - bool Solve() override { - int depth_bound = 10; - bool res; - SetMaxDepthRPFP(depth_bound); - duality->PreSolve(); - while(true){ - res = duality->SolveMain(); - if(!res || GetSolution()) - break; - depth_bound++; - SetMaxDepthRPFP(depth_bound); - res = duality->RecheckBounds(); - if(!res) - break; - } - duality->PostSolve(); - if(!res) - ConvertCex(); - return res; - } - - Counterexample &GetCounterexample() override { - return cex; - } - - bool SetOption(const std::string &option, const std::string &value) override { - return duality->SetOption(option,value); - } - - void LearnFrom(Solver *old_solver) override { - DualityDepthBounded *old = dynamic_cast(old_solver); - if(old){ - duality->LearnFrom(old->duality); - } - } - - bool IsResultRecursionBounded() override { - return duality->IsResultRecursionBounded(); - } - - void Cancel() override { - duality->Cancel(); - } - - typedef RPFP::Node Node; - typedef RPFP::Edge Edge; - RPFP *rpfp, *drpfp; - hash_map db_map, db_rev_map; - hash_map db_edge_rev_map; - std::vector db_saved_bounds; - Counterexample cex; - - expr AddParamToRels(hash_map &memo, hash_map &rmap, const expr &p, const expr &t) { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - expr &res = bar.first->second; - if(!bar.second) return res; - - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - args.reserve(nargs); - for(int i = 0; i < nargs; i++) - args.push_back(AddParamToRels(memo, rmap, p, t.arg(i))); - hash_map::iterator rit = rmap.find(f); - if(rit != rmap.end()){ - args.push_back(p); - res = (rit->second)(args); - res = ctx.make(And,res,ctx.make(Geq,p,ctx.int_val(0))); - } - else - res = f(args); - } - else if (t.is_quantifier()) - { - expr body = AddParamToRels(memo, rmap, p, t.body()); - res = clone_quantifier(t, body); - } - else res = t; - return res; - } - - - void DepthBoundRPFP(){ - drpfp = new RPFP(rpfp->ls); - expr dvar = ctx.int_const("@depth"); - expr dmax = ctx.int_const("@depth_max"); - for(unsigned i = 0; i < rpfp->nodes.size(); i++){ - Node *node = rpfp->nodes[i]; - std::vector arg_sorts; - const std::vector ¶ms = node->Annotation.IndParams; - for(unsigned j = 0; j < params.size(); j++) - arg_sorts.push_back(params[j].get_sort()); - arg_sorts.push_back(ctx.int_sort()); - std::string new_name = std::string("@db@") + node->Name.name().str(); - func_decl f = ctx.function(new_name.c_str(),arg_sorts.size(), VEC2PTR(arg_sorts),ctx.bool_sort()); - std::vector args = params; - args.push_back(dvar); - expr pat = f(args); - Node *dnode = drpfp->CreateNode(pat); - db_map[node] = dnode; - db_rev_map[dnode] = node; - expr bound_fmla = node->Bound.Formula; - if(!eq(bound_fmla,ctx.bool_val(true))){ - bound_fmla = implies(dvar == dmax,bound_fmla); - dnode->Bound.Formula = bound_fmla; - } - db_saved_bounds.push_back(bound_fmla); - // dnode->Annotation.Formula = ctx.make(And,node->Annotation.Formula,ctx.make(Geq,dvar,ctx.int_val(0))); - } - for(unsigned i = 0; i < rpfp->edges.size(); i++){ - Edge *edge = rpfp->edges[i]; - std::vector new_children; - std::vector new_relparams; - hash_map rmap; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *ch = edge->Children[j]; - Node *nch = db_map[ch]; - func_decl f = nch->Name; - func_decl sf = drpfp->NumberPred(f,j); - new_children.push_back(nch); - new_relparams.push_back(sf); - rmap[edge->F.RelParams[j]] = sf; - } - std::vector new_indparams = edge->F.IndParams; - new_indparams.push_back(dvar); - hash_map memo; - expr new_fmla = AddParamToRels(memo,rmap,ctx.make(Sub,dvar,ctx.int_val(1)),edge->F.Formula); - RPFP::Transformer new_t = drpfp->CreateTransformer(new_relparams,new_indparams,new_fmla); - Node *new_parent = db_map[edge->Parent]; - db_edge_rev_map[drpfp->CreateEdge(new_parent,new_t,new_children)] = edge; - } - } - - void SetMaxDepthRPFP(int depth){ - hash_map subst; - expr dmax = ctx.int_const("@depth_max"); - subst[dmax] = ctx.int_val(depth); - for(unsigned i = 0; i < drpfp->nodes.size(); i++){ - Node *node = drpfp->nodes[i]; - expr fmla = db_saved_bounds[i]; - fmla = drpfp->SubstRec(subst,fmla); - node->Bound.Formula = fmla; - } - } - - void ConvertCex(){ - cex.clear(); - RPFP *tree = new RPFP(rpfp->ls); - Node *root; - Counterexample &dctx = duality->GetCounterexample(); - hash_map ctx_node_map; - for(unsigned i = 0; i < dctx.get_tree()->nodes.size(); i++){ - Node *dnode = dctx.get_tree()->nodes[i]; - Node *onode = db_rev_map[dnode->map->map]; - Node *node = tree->CloneNode(onode); - node->number = dnode->number; // numbers have to match for model to make sense - ctx_node_map[dnode] = node; - if(dnode == dctx.get_root()) - root = node; - } - for(unsigned i = 0; i < dctx.get_tree()->edges.size(); i++){ - Edge *dedge = dctx.get_tree()->edges[i]; - Edge *oedge = db_edge_rev_map[dedge->map]; - Node *parent = ctx_node_map[dedge->Parent]; - std::vector chs; - for(unsigned j = 0; j < dedge->Children.size(); j++) - chs.push_back(ctx_node_map[dedge->Children[j]]); - Edge *edge = tree->CreateEdge(parent,oedge->F,chs); - edge->number = dedge->number; // numbers have to match for model to make sense - edge->map = oedge; - } - tree->dualModel = dctx.get_tree()->dualModel; - cex.set(tree,root); - } - - bool GetSolution(){ - for(unsigned i = 0; i < rpfp->nodes.size(); i++) - if(!drpfp->nodes[i]->Annotation.SubsetEq(rpfp->nodes[i]->Bound)) - return false; - expr dvar = ctx.int_const("@depth"); - hash_map subst; - subst[dvar] = ctx.int_val(INT_MAX); - for(unsigned i = 0; i < rpfp->nodes.size(); i++){ - expr fmla = drpfp->nodes[i]->Annotation.Formula; - fmla = drpfp->SubstRec(subst,fmla); - fmla = fmla.simplify(); - rpfp->nodes[i]->Annotation.Formula = fmla; - } - return true; - } - - void UndoDepthBoundRPFP(){ -#if 0 - if(cex.get_tree()){ - // here, need to map the cex back... - } - // also need to map the proof back, but we don't... -#endif - } - }; - - Solver *Solver::Create(const std::string &solver_class, RPFP *rpfp){ - // Solver *s = alloc(DualityDepthBounded,rpfp); - Solver *s = alloc(Duality,rpfp); - return s; - } - - Reporter *CreateStdoutReporter(RPFP *rpfp){ - return new StreamReporter(rpfp, std::cout); - } - - class ConjectureFileReporter : public Reporter { - std::ofstream s; - public: - ConjectureFileReporter(RPFP *_rpfp, const std::string &fname) - : Reporter(_rpfp), s(fname.c_str()) {} - void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager) override { - s << "(define-fun " << node->Name.name() << " ("; - for(unsigned i = 0; i < update.IndParams.size(); i++){ - if(i != 0) - s << " "; - s << "(" << update.IndParams[i] << " " << update.IndParams[i].get_sort() << ")"; - } - s << ") Bool \n"; - s << update.Formula << ")\n"; - s << std::endl; - } - }; - - Reporter *CreateConjectureFileReporter(RPFP *rpfp, const std::string &fname){ - return new ConjectureFileReporter(rpfp, fname); - } - -} - diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp deleted file mode 100644 index 17adbdcea..000000000 --- a/src/duality/duality_wrapper.cpp +++ /dev/null @@ -1,744 +0,0 @@ -/*++ - Copyright (c) 2012 Microsoft Corporation - - Module Name: - - wrapper.cpp - - Abstract: - - wrap various objects in the style expected by duality - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "duality/duality_wrapper.h" -#include -#include "smt/smt_solver.h" -#include "interp/iz3interp.h" -#include "util/statistics.h" -#include "ast/expr_abstract.h" -#include "util/stopwatch.h" -#include "model/model_smt2_pp.h" -#include "qe/qe_lite.h" - -namespace Duality { - - solver::solver(Duality::context& c, bool _extensional, bool models) : object(c), the_model(c) { - params_ref p; - p.set_bool("proof", true); // this is currently useless - if(models) - p.set_bool("model", true); - p.set_bool("unsat_core", true); - bool mbqi = c.get_config().get().get_bool("mbqi",true); - p.set_bool("mbqi",mbqi); // just to test - p.set_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants - p.set_uint("mbqi.max_iterations",1); // use mbqi for quantifiers in interpolants - extensional = mbqi && (true || _extensional); - if(extensional) - p.set_bool("array.extensional",true); - scoped_ptr sf = mk_smt_solver_factory(); - m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); - m_solver->updt_params(p); // why do we have to do this? - canceled = false; - m_mode = m().proof_mode(); - } - - expr context::constant(const std::string &name, const sort &ty){ - symbol s = str_symbol(name.c_str()); - return cook(m().mk_const(m().mk_const_decl(s, ty))); - } - - expr context::make(decl_kind op, int n, ::expr **args){ - switch(op) { - case True: return mki(m_basic_fid,OP_TRUE,n,args); - case False: return mki(m_basic_fid,OP_FALSE,n,args); - case Equal: return mki(m_basic_fid,OP_EQ,n,args); - case Distinct: return mki(m_basic_fid,OP_DISTINCT,n,args); - case Ite: return mki(m_basic_fid,OP_ITE,n,args); - case And: return mki(m_basic_fid,OP_AND,n,args); - case Or: return mki(m_basic_fid,OP_OR,n,args); - case Iff: return mki(m_basic_fid,OP_IFF,n,args); - case Xor: return mki(m_basic_fid,OP_XOR,n,args); - case Not: return mki(m_basic_fid,OP_NOT,n,args); - case Implies: return mki(m_basic_fid,OP_IMPLIES,n,args); - case Oeq: return mki(m_basic_fid,OP_OEQ,n,args); - case Interp: return mki(m_basic_fid,OP_INTERP,n,args); - case Leq: return mki(m_arith_fid,OP_LE,n,args); - case Geq: return mki(m_arith_fid,OP_GE,n,args); - case Lt: return mki(m_arith_fid,OP_LT,n,args); - case Gt: return mki(m_arith_fid,OP_GT,n,args); - case Plus: return mki(m_arith_fid,OP_ADD,n,args); - case Sub: return mki(m_arith_fid,OP_SUB,n,args); - case Uminus: return mki(m_arith_fid,OP_UMINUS,n,args); - case Times: return mki(m_arith_fid,OP_MUL,n,args); - case Div: return mki(m_arith_fid,OP_DIV,n,args); - case Idiv: return mki(m_arith_fid,OP_IDIV,n,args); - case Rem: return mki(m_arith_fid,OP_REM,n,args); - case Mod: return mki(m_arith_fid,OP_MOD,n,args); - case Power: return mki(m_arith_fid,OP_POWER,n,args); - case ToReal: return mki(m_arith_fid,OP_TO_REAL,n,args); - case ToInt: return mki(m_arith_fid,OP_TO_INT,n,args); - case IsInt: return mki(m_arith_fid,OP_IS_INT,n,args); - case Store: return mki(m_array_fid,OP_STORE,n,args); - case Select: return mki(m_array_fid,OP_SELECT,n,args); - case ConstArray: return mki(m_array_fid,OP_CONST_ARRAY,n,args); - case ArrayDefault: return mki(m_array_fid,OP_ARRAY_DEFAULT,n,args); - case ArrayMap: return mki(m_array_fid,OP_ARRAY_MAP,n,args); - case SetUnion: return mki(m_array_fid,OP_SET_UNION,n,args); - case SetIntersect: return mki(m_array_fid,OP_SET_INTERSECT,n,args); - case SetDifference: return mki(m_array_fid,OP_SET_DIFFERENCE,n,args); - case SetComplement: return mki(m_array_fid,OP_SET_COMPLEMENT,n,args); - case SetSubSet: return mki(m_array_fid,OP_SET_SUBSET,n,args); - case AsArray: return mki(m_array_fid,OP_AS_ARRAY,n,args); - default: - assert(0); - return expr(*this); - } - } - - expr context::mki(family_id fid, ::decl_kind dk, int n, ::expr **args){ - return cook(m().mk_app(fid, dk, 0, nullptr, n, (::expr **)args)); - } - - expr context::make(decl_kind op, const std::vector &args){ - static std::vector< ::expr*> a(10); - if(a.size() < args.size()) - a.resize(args.size()); - for(unsigned i = 0; i < args.size(); i++) - a[i] = to_expr(args[i].raw()); - return make(op,args.size(), args.size() ? VEC2PTR(a) : nullptr); - } - - expr context::make(decl_kind op){ - return make(op,0,nullptr); - } - - expr context::make(decl_kind op, const expr &arg0){ - ::expr *a = to_expr(arg0.raw()); - return make(op,1,&a); - } - - expr context::make(decl_kind op, const expr &arg0, const expr &arg1){ - ::expr *args[2]; - args[0] = to_expr(arg0.raw()); - args[1] = to_expr(arg1.raw()); - return make(op,2,args); - } - - expr context::make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2){ - ::expr *args[3]; - args[0] = to_expr(arg0.raw()); - args[1] = to_expr(arg1.raw()); - args[2] = to_expr(arg2.raw()); - return make(op,3,args); - } - - expr context::make_quant(decl_kind op, const std::vector &bvs, const expr &body){ - if(bvs.size() == 0) return body; - std::vector< ::expr *> foo(bvs.size()); - - - std::vector< ::symbol> names; - std::vector< ::sort *> types; - std::vector< ::expr *> bound_asts; - unsigned num_bound = bvs.size(); - - for (unsigned i = 0; i < num_bound; ++i) { - app* a = to_app(bvs[i].raw()); - ::symbol s(to_app(a)->get_decl()->get_name()); - names.push_back(s); - types.push_back(m().get_sort(a)); - bound_asts.push_back(a); - } - expr_ref abs_body(m()); - expr_abstract(m(), 0, num_bound, VEC2PTR(bound_asts), to_expr(body.raw()), abs_body); - expr_ref result(m()); - result = m().mk_quantifier( - op == Forall, - names.size(), VEC2PTR(types), VEC2PTR(names), abs_body.get(), - 0, - ::symbol(), - ::symbol(), - 0, nullptr, - 0, nullptr - ); - return cook(result.get()); - } - - expr context::make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body){ - if(_sorts.size() == 0) return body; - - - std::vector< ::symbol> names; - std::vector< ::sort *> types; - std::vector< ::expr *> bound_asts; - unsigned num_bound = _sorts.size(); - - for (unsigned i = 0; i < num_bound; ++i) { - names.push_back(_names[i]); - types.push_back(to_sort(_sorts[i].raw())); - } - expr_ref result(m()); - result = m().mk_quantifier( - op == Forall, - names.size(), VEC2PTR(types), VEC2PTR(names), to_expr(body.raw()), - 0, - ::symbol(), - ::symbol(), - 0, nullptr, - 0, nullptr - ); - return cook(result.get()); - } - - - decl_kind func_decl::get_decl_kind() const { - return ctx().get_decl_kind(*this); - } - - decl_kind context::get_decl_kind(const func_decl &t){ - ::func_decl *d = to_func_decl(t.raw()); - if (null_family_id == d->get_family_id()) - return Uninterpreted; - // return (opr)d->get_decl_kind(); - if (m_basic_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_TRUE: return True; - case OP_FALSE: return False; - case OP_EQ: return Equal; - case OP_DISTINCT: return Distinct; - case OP_ITE: return Ite; - case OP_AND: return And; - case OP_OR: return Or; - case OP_IFF: return Iff; - case OP_XOR: return Xor; - case OP_NOT: return Not; - case OP_IMPLIES: return Implies; - case OP_OEQ: return Oeq; - case OP_INTERP: return Interp; - default: - return OtherBasic; - } - } - if (m_arith_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_LE: return Leq; - case OP_GE: return Geq; - case OP_LT: return Lt; - case OP_GT: return Gt; - case OP_ADD: return Plus; - case OP_SUB: return Sub; - case OP_UMINUS: return Uminus; - case OP_MUL: return Times; - case OP_DIV: return Div; - case OP_IDIV: return Idiv; - case OP_REM: return Rem; - case OP_MOD: return Mod; - case OP_POWER: return Power; - case OP_TO_REAL: return ToReal; - case OP_TO_INT: return ToInt; - case OP_IS_INT: return IsInt; - default: - return OtherArith; - } - } - if (m_array_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_STORE: return Store; - case OP_SELECT: return Select; - case OP_CONST_ARRAY: return ConstArray; - case OP_ARRAY_DEFAULT: return ArrayDefault; - case OP_ARRAY_MAP: return ArrayMap; - case OP_SET_UNION: return SetUnion; - case OP_SET_INTERSECT: return SetIntersect; - case OP_SET_DIFFERENCE: return SetDifference; - case OP_SET_COMPLEMENT: return SetComplement; - case OP_SET_SUBSET: return SetSubSet; - case OP_AS_ARRAY: return AsArray; - default: - return OtherArray; - } - } - - return Other; - } - - - sort_kind context::get_sort_kind(const sort &s){ - family_id fid = to_sort(s.raw())->get_family_id(); - ::decl_kind k = to_sort(s.raw())->get_decl_kind(); - if (m().is_uninterp(to_sort(s.raw()))) { - return UninterpretedSort; - } - else if (fid == m_basic_fid && k == BOOL_SORT) { - return BoolSort; - } - else if (fid == m_arith_fid && k == INT_SORT) { - return IntSort; - } - else if (fid == m_arith_fid && k == REAL_SORT) { - return RealSort; - } - else if (fid == m_array_fid && k == ARRAY_SORT) { - return ArraySort; - } - else { - return UnknownSort; - } - } - - expr func_decl::operator()(unsigned n, expr const * args) const { - std::vector< ::expr *> _args(n); - for(unsigned i = 0; i < n; i++) - _args[i] = to_expr(args[i].raw()); - return ctx().cook(m().mk_app(to_func_decl(raw()),n,VEC2PTR(_args))); - } - - int solver::get_num_decisions(){ - ::statistics st; - m_solver->collect_statistics(st); - std::ostringstream ss; - st.display(ss); - std::string stats = ss.str(); - int pos = stats.find("decisions:"); - if(pos < 0) return 0; // for some reason, decisions are not reported if there are none - pos += 10; - int end = stats.find('\n',pos); - std::string val = stats.substr(pos,end-pos); - return atoi(val.c_str()); - } - - void context::print_expr(std::ostream &s, const ast &e){ - s << mk_pp(e.raw(), m()); - } - - - expr expr::simplify(const params &_p) const { - ::expr * a = to_expr(raw()); - params_ref p = _p.get(); - th_rewriter m_rw(m(), p); - expr_ref result(m()); - m_rw(a, result); - return ctx().cook(result); - } - - expr expr::simplify() const { - params p; - return simplify(p); - } - - expr context::make_var(int idx, const sort &s){ - ::sort * a = to_sort(s.raw()); - return cook(m().mk_var(idx,a)); - } - - - expr expr::qe_lite() const { - ::qe_lite qe(m(), params_ref()); - expr_ref result(to_expr(raw()),m()); - proof_ref pf(m()); - qe(result,pf); - return ctx().cook(result); - } - - expr expr::qe_lite(const std::set &idxs, bool index_of_bound) const { - ::qe_lite qe(m(), params_ref()); - expr_ref result(to_expr(raw()),m()); - proof_ref pf(m()); - uint_set uis; - for(std::set::const_iterator it=idxs.begin(), en = idxs.end(); it != en; ++it) - uis.insert(*it); - qe(uis,index_of_bound,result); - return ctx().cook(result); - } - - expr clone_quantifier(const expr &q, const expr &b){ - return q.ctx().cook(q.m().update_quantifier(to_quantifier(q.raw()), to_expr(b.raw()))); - } - - expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns){ - quantifier *thing = to_quantifier(q.raw()); - bool is_forall = thing->is_forall(); - unsigned num_patterns = patterns.size(); - std::vector< ::expr *> _patterns(num_patterns); - for(unsigned i = 0; i < num_patterns; i++) - _patterns[i] = to_expr(patterns[i].raw()); - return q.ctx().cook(q.m().update_quantifier(thing, is_forall, num_patterns, VEC2PTR(_patterns), to_expr(b.raw()))); - } - - expr clone_quantifier(decl_kind dk, const expr &q, const expr &b){ - quantifier *thing = to_quantifier(q.raw()); - bool is_forall = dk == Forall; - return q.ctx().cook(q.m().update_quantifier(thing, is_forall, to_expr(b.raw()))); - } - - void expr::get_patterns(std::vector &pats) const { - quantifier *thing = to_quantifier(raw()); - unsigned num_patterns = thing->get_num_patterns(); - :: expr * const *it = thing->get_patterns(); - pats.resize(num_patterns); - for(unsigned i = 0; i < num_patterns; i++) - pats[i] = expr(ctx(),it[i]); - } - - - unsigned func_decl::arity() const { - return (to_func_decl(raw())->get_arity()); - } - - sort func_decl::domain(unsigned i) const { - return sort(ctx(),(to_func_decl(raw())->get_domain(i))); - } - - sort func_decl::range() const { - return sort(ctx(),(to_func_decl(raw())->get_range())); - } - - func_decl context::fresh_func_decl(char const * prefix, const std::vector &domain, sort const & range){ - std::vector < ::sort * > _domain(domain.size()); - for(unsigned i = 0; i < domain.size(); i++) - _domain[i] = to_sort(domain[i].raw()); - ::func_decl* d = m().mk_fresh_func_decl(prefix, - _domain.size(), - VEC2PTR(_domain), - to_sort(range.raw())); - return func_decl(*this,d); - } - - func_decl context::fresh_func_decl(char const * prefix, sort const & range){ - ::func_decl* d = m().mk_fresh_func_decl(prefix, - 0, - nullptr, - to_sort(range.raw())); - return func_decl(*this,d); - } - - - -#if 0 - - - lbool interpolating_solver::interpolate( - const std::vector &assumptions, - std::vector &interpolants, - model &model, - Z3_literals &labels, - bool incremental) - { - Z3_model _model = 0; - Z3_literals _labels = 0; - Z3_lbool lb; - std::vector _assumptions(assumptions.size()); - std::vector _interpolants(assumptions.size()-1); - for(unsigned i = 0; i < assumptions.size(); i++) - _assumptions[i] = assumptions[i]; - std::vector _theory(theory.size()); - for(unsigned i = 0; i < theory.size(); i++) - _theory[i] = theory[i]; - - lb = Z3_interpolate( - ctx(), - _assumptions.size(), - VEC2PTR(_assumptions), - 0, - 0, - VEC2PTR(_interpolants), - &_model, - &_labels, - incremental, - _theory.size(), - VEC2PTR(_theory)); - - if(lb == Z3_L_FALSE){ - interpolants.resize(_interpolants.size()); - for (unsigned i = 0; i < _interpolants.size(); ++i) { - interpolants[i] = expr(ctx(),_interpolants[i]); - } - } - - if (_model) { - model = iz3wrapper::model(ctx(), _model); - } - - if(_labels){ - labels = _labels; - } - - return lb; - } - -#endif - - static int linearize_assumptions(int num, - TermTree *assumptions, - std::vector > &linear_assumptions, - std::vector &parents){ - for(unsigned i = 0; i < assumptions->getChildren().size(); i++) - num = linearize_assumptions(num, assumptions->getChildren()[i], linear_assumptions, parents); - // linear_assumptions[num].push_back(assumptions->getTerm()); - for(unsigned i = 0; i < assumptions->getChildren().size(); i++) - parents[assumptions->getChildren()[i]->getNumber()] = num; - parents[num] = SHRT_MAX; // in case we have no parent - linear_assumptions[num].push_back(assumptions->getTerm()); - std::vector &ts = assumptions->getTerms(); - for(unsigned i = 0; i < ts.size(); i++) - linear_assumptions[num].push_back(ts[i]); - return num + 1; - } - - static int unlinearize_interpolants(int num, - TermTree* assumptions, - const std::vector &interpolant, - TermTree * &tree_interpolant) - { - std::vector chs(assumptions->getChildren().size()); - for(unsigned i = 0; i < assumptions->getChildren().size(); i++) - num = unlinearize_interpolants(num, assumptions->getChildren()[i], interpolant,chs[i]); - expr f; - if(num < (int)interpolant.size()) // last interpolant is missing, presumed false - f = interpolant[num]; - tree_interpolant = new TermTree(f,chs); - return num + 1; - } - - - lbool interpolating_solver::interpolate_tree(TermTree *assumptions, - TermTree *&interpolant, - model &model, - literals &labels, - bool incremental - ) - - { - int size = assumptions->number(0); - std::vector > linear_assumptions(size); - std::vector parents(size); - linearize_assumptions(0,assumptions,linear_assumptions,parents); - - ptr_vector< ::ast> _interpolants(size-1); - vector >_assumptions(size); - for(int i = 0; i < size; i++) - for(unsigned j = 0; j < linear_assumptions[i].size(); j++) - _assumptions[i].push_back(linear_assumptions[i][j]); - ::vector _parents; _parents.resize(parents.size()); - for(unsigned i = 0; i < parents.size(); i++) - _parents[i] = parents[i]; - ptr_vector< ::ast> _theory(theory.size()); - for(unsigned i = 0; i < theory.size(); i++) - _theory[i] = theory[i]; - - - if(!incremental){ - push(); - for(unsigned i = 0; i < linear_assumptions.size(); i++) - for(unsigned j = 0; j < linear_assumptions[i].size(); j++) - add(linear_assumptions[i][j]); - } - - check_result res = unsat; - - if(!m_solver->get_proof()) - res = check(); - - if(res == unsat){ - - interpolation_options_struct opts; - if(weak_mode) - opts.set("weak","1"); - - ::ast *proof = m_solver->get_proof(); - try { - iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,&opts); - } - // If there's an interpolation bug, throw a char * - // exception so duality can catch it and restart. - catch (const interpolation_failure &f) { - throw f.msg(); - } - - std::vector linearized_interpolants(_interpolants.size()); - for(unsigned i = 0; i < _interpolants.size(); i++) - linearized_interpolants[i] = expr(ctx(),_interpolants[i]); - - // since iz3interpolant returns interpolants with one ref count, we decrement here - for(unsigned i = 0; i < _interpolants.size(); i++) - m().dec_ref(_interpolants[i]); - - unlinearize_interpolants(0,assumptions,linearized_interpolants,interpolant); - interpolant->setTerm(ctx().bool_val(false)); - } - - model_ref _m; - m_solver->get_model(_m); - model = Duality::model(ctx(),_m.get()); - -#if 0 - if(_labels){ - labels = _labels; - } -#endif - - if(!incremental) - pop(); - - return (res == unsat) ? l_false : ((res == sat) ? l_true : l_undef); - - } - - - void interpolating_solver::SetWeakInterpolants(bool weak){ - weak_mode = weak; - } - - - void interpolating_solver::SetPrintToFile(const std::string &filename){ - print_filename = filename; - } - - void interpolating_solver::AssertInterpolationAxiom(const expr & t){ - add(t); - theory.push_back(t); - } - - - void interpolating_solver::RemoveInterpolationAxiom(const expr & t){ - // theory.remove(t); - } - - - const char *interpolating_solver::profile(){ - // return Z3_interpolation_profile(ctx()); - return ""; - } - - - static void get_assumptions_rec(stl_ext::hash_set &memo, const proof &pf, std::vector &assumps){ - if(memo.find(pf) != memo.end())return; - memo.insert(pf); - pfrule dk = pf.rule(); - if(dk == PR_ASSERTED){ - expr con = pf.conc(); - assumps.push_back(con); - } - else { - unsigned nprems = pf.num_prems(); - for(unsigned i = 0; i < nprems; i++){ - proof arg = pf.prem(i); - get_assumptions_rec(memo,arg,assumps); - } - } - } - - void proof::get_assumptions(std::vector &assumps){ - stl_ext::hash_set memo; - get_assumptions_rec(memo,*this,assumps); - } - - - void ast::show() const{ - std::cout << mk_pp(raw(), m()) << std::endl; - } - - void model::show() const { - model_smt2_pp(std::cout, m(), *m_model, 0); - std::cout << std::endl; - } - - void model::show_hash() const { - std::ostringstream ss; - model_smt2_pp(ss, m(), *m_model, 0); - hash_space::hash hasher; - unsigned h = hasher(ss.str()); - std::cout << "model hash: " << h << "\n"; - } - - void solver::show() { - unsigned n = m_solver->get_num_assertions(); - if(!n) - return; - ast_smt_pp pp(m()); - for (unsigned i = 0; i < n-1; ++i) - pp.add_assumption(m_solver->get_assertion(i)); - pp.display_smt2(std::cout, m_solver->get_assertion(n-1)); - } - - void solver::print(const char *filename) { - std::ofstream f(filename); - unsigned n = m_solver->get_num_assertions(); - if(!n) - return; - ast_smt_pp pp(m()); - for (unsigned i = 0; i < n-1; ++i) - pp.add_assumption(m_solver->get_assertion(i)); - pp.display_smt2(f, m_solver->get_assertion(n-1)); - } - - - void solver::show_assertion_ids() { -#if 0 - unsigned n = m_solver->get_num_assertions(); - std::cerr << "assertion ids: "; - for (unsigned i = 0; i < n-1; ++i) - std::cerr << " " << m_solver->get_assertion(i)->get_id(); - std::cerr << "\n"; -#else - unsigned n = m_solver->get_num_assertions(); - std::cerr << "assertion ids hash: "; - unsigned h = 0; - for (unsigned i = 0; i < n-1; ++i) - h += m_solver->get_assertion(i)->get_id(); - std::cerr << h << "\n"; -#endif - } - - void include_ast_show(ast &a){ - a.show(); - } - - void include_model_show(model &a){ - a.show(); - } - - void show_ast(::ast *a, ast_manager &m) { - std::cout << mk_pp(a, m) << std::endl; - } - - bool expr::is_label (bool &pos,std::vector &names) const { - buffer< ::symbol> _names; - bool res = m().is_label(to_expr(raw()),pos,_names); - if(res) - for(unsigned i = 0; i < _names.size(); i++) - names.push_back(symbol(ctx(),_names[i])); - return res; - } - - double current_time() - { - static stopwatch sw; - static bool started = false; - if(!started){ - sw.start(); - started = true; - } - return sw.get_current_seconds(); - } - -} - - - - diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h deleted file mode 100644 index 69f821a08..000000000 --- a/src/duality/duality_wrapper.h +++ /dev/null @@ -1,1489 +0,0 @@ -/*++ - Copyright (c) 2012 Microsoft Corporation - - Module Name: - - duality_wrapper.h - - Abstract: - - wrap various Z3 classes in the style expected by duality - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ -#ifndef DUALITY_WRAPPER_H_ -#define DUALITY_WRAPPER_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "util/version.h" -#include - -#include "interp/iz3hash.h" -#include "model/model.h" -#include "solver/solver.h" - -#include "ast/well_sorted.h" -#include "ast/arith_decl_plugin.h" -#include "ast/bv_decl_plugin.h" -#include "ast/datatype_decl_plugin.h" -#include "ast/array_decl_plugin.h" -#include "ast/ast_translation.h" -#include "ast/ast_pp.h" -#include "ast/ast_ll_pp.h" -#include "ast/ast_smt_pp.h" -#include "ast/ast_smt2_pp.h" -#include "ast/rewriter/th_rewriter.h" -#include "ast/rewriter/var_subst.h" -#include "ast/expr_substitution.h" -#include "ast/pp.h" -#include "util/scoped_ctrl_c.h" -#include "util/cancel_eh.h" -#include "util/scoped_timer.h" -#include "ast/scoped_proof.h" - -namespace Duality { - - class exception; - class config; - class context; - class symbol; - class params; - class ast; - class sort; - class func_decl; - class expr; - class solver; - class goal; - class tactic; - class probe; - class model; - class func_interp; - class func_entry; - class statistics; - class apply_result; - class fixedpoint; - class literals; - - /** - Duality global configuration object. - */ - - class config { - params_ref m_params; - config & operator=(config const & s); - public: - config(config const & s) : m_params(s.m_params) {} - config(const params_ref &_params) : m_params(_params) {} - config() { } // TODO: create a new params object here - ~config() { } - void set(char const * param, char const * value) { m_params.set_str(param,value); } - void set(char const * param, bool value) { m_params.set_bool(param,value); } - void set(char const * param, int value) { m_params.set_uint(param,value); } - params_ref &get() {return m_params;} - const params_ref &get() const {return m_params;} - }; - - enum decl_kind { - True, - False, - And, - Or, - Not, - Iff, - Ite, - Equal, - Implies, - Distinct, - Xor, - Oeq, - Interp, - Leq, - Geq, - Lt, - Gt, - Plus, - Sub, - Uminus, - Times, - Div, - Idiv, - Rem, - Mod, - Power, - ToReal, - ToInt, - IsInt, - Select, - Store, - ConstArray, - ArrayDefault, - ArrayMap, - SetUnion, - SetIntersect, - SetDifference, - SetComplement, - SetSubSet, - AsArray, - Numeral, - Forall, - Exists, - Variable, - Uninterpreted, - OtherBasic, - OtherArith, - OtherArray, - Other - }; - - enum sort_kind {BoolSort,IntSort,RealSort,ArraySort,UninterpretedSort,UnknownSort}; - - /** - A context has an ast manager global configuration options, etc. - */ - - class context { - protected: - ast_manager &mgr; - config m_config; - - family_id m_basic_fid; - family_id m_array_fid; - family_id m_arith_fid; - family_id m_bv_fid; - family_id m_dt_fid; - family_id m_datalog_fid; - arith_util m_arith_util; - - public: - context(ast_manager &_manager, const config &_config) : mgr(_manager), m_config(_config), m_arith_util(_manager) { - m_basic_fid = m().get_basic_family_id(); - m_arith_fid = m().mk_family_id("arith"); - m_bv_fid = m().mk_family_id("bv"); - m_array_fid = m().mk_family_id("array"); - m_dt_fid = m().mk_family_id("datatype"); - m_datalog_fid = m().mk_family_id("datalog_relation"); - } - ~context() { } - - ast_manager &m() const {return *(ast_manager *)&mgr;} - - void set(char const * param, char const * value) { m_config.set(param,value); } - void set(char const * param, bool value) { m_config.set(param,value); } - void set(char const * param, int value) { m_config.set(param,value); } - config &get_config() {return m_config;} - - symbol str_symbol(char const * s); - symbol int_symbol(int n); - - sort bool_sort(); - sort int_sort(); - sort real_sort(); - sort bv_sort(unsigned sz); - sort array_sort(const sort & d, const sort & r); - - 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); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); - func_decl fresh_func_decl(char const * name, const std::vector &domain, sort const & range); - func_decl fresh_func_decl(char const * name, sort const & range); - - expr constant(symbol const & name, sort const & s); - expr constant(char const * name, sort const & s); - expr constant(const std::string &name, sort const & s); - expr bool_const(char const * name); - expr int_const(char const * name); - expr real_const(char const * name); - expr bv_const(char const * name, unsigned sz); - - expr bool_val(bool b); - - expr int_val(int n); - expr int_val(unsigned n); - expr int_val(char const * n); - - expr real_val(int n, int d); - expr real_val(int n); - expr real_val(unsigned n); - expr real_val(char const * n); - - expr bv_val(int n, unsigned sz); - expr bv_val(unsigned n, unsigned sz); - expr bv_val(char const * n, unsigned sz); - - expr num_val(int n, sort const & s); - - expr mki(family_id fid, ::decl_kind dk, int n, ::expr **args); - expr make(decl_kind op, int n, ::expr **args); - expr make(decl_kind op, const std::vector &args); - expr make(decl_kind op); - expr make(decl_kind op, const expr &arg0); - expr make(decl_kind op, const expr &arg0, const expr &arg1); - expr make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2); - - expr make_quant(decl_kind op, const std::vector &bvs, const expr &body); - expr make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body); - expr make_var(int idx, const sort &s); - - decl_kind get_decl_kind(const func_decl &t); - - sort_kind get_sort_kind(const sort &s); - - expr translate(const expr &e); - func_decl translate(const func_decl &); - - void print_expr(std::ostream &s, const ast &e); - - fixedpoint mk_fixedpoint(); - - expr cook(::expr *a); - std::vector cook(ptr_vector< ::expr> v); - ::expr *uncook(const expr &a); - }; - - template - class z3array { - T * m_array; - unsigned m_size; - z3array(z3array const & s); - z3array & operator=(z3array const & s); - public: - z3array(unsigned sz):m_size(sz) { m_array = new T[sz]; } - ~z3array() { 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 const * ptr() const { return m_array; } - T * ptr() { return m_array; } - }; - - class object { - protected: - context * m_ctx; - public: - object(): m_ctx((context *)nullptr) {} - object(context & c):m_ctx(&c) {} - object(object const & s):m_ctx(s.m_ctx) {} - context & ctx() const { return *m_ctx; } - friend void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } - ast_manager &m() const {return m_ctx->m();} - }; - - class symbol : public object { - ::symbol m_sym; - public: - symbol(context & c, ::symbol s):object(c), m_sym(s) {} - symbol(symbol const & s):object(s), m_sym(s.m_sym) {} - symbol & operator=(symbol const & s) { m_ctx = s.m_ctx; m_sym = s.m_sym; return *this; } - operator ::symbol() const {return m_sym;} - std::string str() const { - if (m_sym.is_numerical()) { - std::ostringstream buffer; - buffer << m_sym.get_num(); - return buffer.str(); - } - else { - return m_sym.bare_str(); - } - } - friend std::ostream & operator<<(std::ostream & out, symbol const & s) { - return out << s.str(); - } - friend bool operator==(const symbol &x, const symbol &y) { - return x.m_sym == y.m_sym; - } - }; - - class params : public config {}; - - /** Wrapper around an ast pointer */ - class ast_i : public object { - protected: - ::ast *_ast; - public: - ::ast * const &raw() const {return _ast;} - ast_i(context & c, ::ast *a = nullptr) : object(c) {_ast = a;} - - ast_i(){_ast = nullptr;} - bool eq(const ast_i &other) const { - return _ast == other._ast; - } - bool lt(const ast_i &other) const { - return _ast < other._ast; - } - friend bool operator==(const ast_i &x, const ast_i&y){ - return x.eq(y); - } - friend bool operator!=(const ast_i &x, const ast_i&y){ - return !x.eq(y); - } - friend bool operator<(const ast_i &x, const ast_i&y){ - return x.lt(y); - } - size_t hash() const {return (size_t)_ast;} - bool null() const {return !_ast;} - }; - - /** Reference counting verison of above */ - class ast : public ast_i { - public: - operator ::ast*() const { return raw(); } - friend bool eq(ast const & a, ast const & b) { return a.raw() == b.raw(); } - - - ast(context &c, ::ast *a = nullptr) : ast_i(c,a) { - if(_ast) - m().inc_ref(a); - } - - ast() {} - - ast(const ast &other) : ast_i(other) { - if(_ast) - m().inc_ref(_ast); - } - - ast &operator=(const ast &other) { - if(_ast) - m().dec_ref(_ast); - _ast = other._ast; - m_ctx = other.m_ctx; - if(_ast) - m().inc_ref(_ast); - return *this; - } - - ~ast(){ - if(_ast) - m().dec_ref(_ast); - } - - void show() const; - - }; - - class sort : public ast { - public: - sort(context & c):ast(c) {} - sort(context & c, ::sort *s):ast(c, s) {} - sort(sort const & s):ast(s) {} - operator ::sort*() const { return to_sort(raw()); } - sort & operator=(sort const & s) { return static_cast(ast::operator=(s)); } - - bool is_bool() const { return m().is_bool(*this); } - bool is_int() const { return ctx().get_sort_kind(*this) == IntSort; } - bool is_real() const { return ctx().get_sort_kind(*this) == RealSort; } - bool is_arith() const; - bool is_array() const { return ctx().get_sort_kind(*this) == ArraySort; } - bool is_datatype() const; - bool is_relation() const; - bool is_finite_domain() const; - - - sort array_domain() const; - sort array_range() const; - - friend std::ostream & operator<<(std::ostream & out, sort const & m){ - m.ctx().print_expr(out,m); - return out; - } - }; - - - class func_decl : public ast { - public: - func_decl() : ast() {} - func_decl(context & c):ast(c) {} - func_decl(context & c, ::func_decl *n):ast(c, n) {} - func_decl(func_decl const & s):ast(s) {} - operator ::func_decl*() const { return to_func_decl(*this); } - func_decl & operator=(func_decl const & s) { return static_cast(ast::operator=(s)); } - - unsigned arity() const; - sort domain(unsigned i) const; - sort range() const; - symbol name() const {return symbol(ctx(),to_func_decl(raw())->get_name());} - decl_kind get_decl_kind() const; - - bool is_const() const { return arity() == 0; } - - expr operator()(unsigned n, expr const * args) const; - expr operator()(const std::vector &args) const; - expr operator()() const; - expr operator()(expr const & a) const; - expr operator()(int a) const; - expr operator()(expr const & a1, expr const & a2) const; - expr operator()(expr const & a1, int a2) const; - expr operator()(int a1, expr const & a2) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const; - - func_decl get_func_decl_parameter(unsigned idx){ - return func_decl(ctx(),to_func_decl(to_func_decl(raw())->get_parameters()[idx].get_ast())); - } - - }; - - class expr : public ast { - public: - expr() : ast() {} - expr(context & c):ast(c) {} - expr(context & c, ::ast *n):ast(c, n) {} - expr(expr const & n):ast(n) {} - expr & operator=(expr const & n) { return static_cast(ast::operator=(n)); } - operator ::expr*() const { return to_expr(raw()); } - unsigned get_id() const {return to_expr(raw())->get_id();} - - sort get_sort() const { return sort(ctx(),m().get_sort(to_expr(raw()))); } - - bool is_bool() const { return get_sort().is_bool(); } - bool is_int() const { return get_sort().is_int(); } - bool is_real() const { return get_sort().is_real(); } - bool is_arith() const { return get_sort().is_arith(); } - bool is_array() const { return get_sort().is_array(); } - bool is_datatype() const { return get_sort().is_datatype(); } - bool is_relation() const { return get_sort().is_relation(); } - bool is_finite_domain() const { return get_sort().is_finite_domain(); } - bool is_true() const {return is_app() && decl().get_decl_kind() == True; } - - bool is_numeral() const { - return is_app() && decl().get_decl_kind() == OtherArith && m().is_unique_value(to_expr(raw())); - } - bool is_app() const {return raw()->get_kind() == AST_APP;} - bool is_quantifier() const {return raw()->get_kind() == AST_QUANTIFIER;} - bool is_var() const {return raw()->get_kind() == AST_VAR;} - bool is_label (bool &pos,std::vector &names) const ; - bool is_ground() const {return to_app(raw())->is_ground();} - bool has_quantifiers() const {return to_app(raw())->has_quantifiers();} - bool has_free(int idx) const { - used_vars proc; - proc.process(to_expr(raw())); - return proc.contains(idx); - } - unsigned get_max_var_idx_plus_1() const { - used_vars proc; - proc.process(to_expr(raw())); - return proc.get_max_found_var_idx_plus_1(); - } - - // operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } - func_decl decl() const {return func_decl(ctx(),to_app(raw())->get_decl());} - unsigned num_args() const { - ast_kind dk = raw()->get_kind(); - switch(dk){ - case AST_APP: - return to_app(raw())->get_num_args(); - case AST_QUANTIFIER: - return 1; - case AST_VAR: - return 0; - default:; - } - SASSERT(0); - return 0; - } - expr arg(unsigned i) const { - ast_kind dk = raw()->get_kind(); - switch(dk){ - case AST_APP: - return ctx().cook(to_app(raw())->get_arg(i)); - case AST_QUANTIFIER: - return ctx().cook(to_quantifier(raw())->get_expr()); - default:; - } - assert(0); - return expr(); - } - - expr body() const { - return ctx().cook(to_quantifier(raw())->get_expr()); - } - - friend expr operator!(expr const & a) { - // ::expr *e = a; - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_NOT,a)); - } - - friend expr operator&&(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_AND,a,b)); - } - - friend expr operator||(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_OR,a,b)); - } - - friend expr implies(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_IMPLIES,a,b)); - } - - friend expr operator==(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_EQ,a,b)); - } - - friend expr operator!=(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DISTINCT,a,b)); - } - - friend expr operator+(expr const & a, expr const & b) { - return a.ctx().make(Plus,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_ADD,a,b)); - } - - friend expr operator*(expr const & a, expr const & b) { - return a.ctx().make(Times,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_MUL,a,b)); - } - - friend expr operator/(expr const & a, expr const & b) { - return a.ctx().make(Div,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DIV,a,b)); - } - - friend expr operator-(expr const & a) { - return a.ctx().make(Uminus,a); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_UMINUS,a)); - } - - friend expr operator-(expr const & a, expr const & b) { - return a.ctx().make(Sub,a,b); // expr(a.ctx(),a.m().mk_app(a.ctx().m_arith_fid,OP_SUB,a,b)); - } - - friend expr operator<=(expr const & a, expr const & b) { - return a.ctx().make(Leq,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LE,a,b)); - } - - friend expr operator>=(expr const & a, expr const & b) { - return a.ctx().make(Geq,a,b); //expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GE,a,b)); - } - - friend expr operator<(expr const & a, expr const & b) { - return a.ctx().make(Lt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LT,a,b)); - } - - friend expr operator>(expr const & a, expr const & b) { - return a.ctx().make(Gt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GT,a,b)); - } - - expr simplify() const; - - expr simplify(params const & p) const; - - expr qe_lite() const; - - expr qe_lite(const std::set &idxs, bool index_of_bound) const; - - friend expr clone_quantifier(const expr &, const expr &); - - friend expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns); - - friend expr clone_quantifier(decl_kind, const expr &, const expr &); - - friend std::ostream & operator<<(std::ostream & out, expr const & m){ - m.ctx().print_expr(out,m); - return out; - } - - void get_patterns(std::vector &pats) const ; - - unsigned get_quantifier_num_bound() const { - return to_quantifier(raw())->get_num_decls(); - } - - unsigned get_index_value() const { - var* va = to_var(raw()); - return va->get_idx(); - } - - bool is_quantifier_forall() const { - return to_quantifier(raw())->is_forall(); - } - - sort get_quantifier_bound_sort(unsigned n) const { - return sort(ctx(),to_quantifier(raw())->get_decl_sort(n)); - } - - symbol get_quantifier_bound_name(unsigned n) const { - return symbol(ctx(),to_quantifier(raw())->get_decl_names()[n]); - } - - friend expr forall(const std::vector &quants, const expr &body); - - friend expr exists(const std::vector &quants, const expr &body); - - }; - - - typedef ::decl_kind pfrule; - - class proof : public ast { - public: - proof(context & c):ast(c) {} - proof(context & c, ::proof *s):ast(c, s) {} - proof(proof const & s):ast(s) {} - operator ::proof*() const { return to_app(raw()); } - proof & operator=(proof const & s) { return static_cast(ast::operator=(s)); } - - pfrule rule() const { - ::func_decl *d = to_app(raw())->get_decl(); - return d->get_decl_kind(); - } - - unsigned num_prems() const { - return to_app(raw())->get_num_args() - 1; - } - - expr conc() const { - return ctx().cook(to_app(raw())->get_arg(num_prems())); - } - - proof prem(unsigned i) const { - return proof(ctx(),to_app(to_app(raw())->get_arg(i))); - } - - void get_assumptions(std::vector &assumps); - }; - -#if 0 - -#if Z3_MAJOR_VERSION > 4 || Z3_MAJOR_VERSION == 4 && Z3_MINOR_VERSION >= 3 - template - class ast_vector_tpl : public object { - Z3_ast_vector m_vector; - void init(Z3_ast_vector v) { Z3_ast_vector_inc_ref(ctx(), v); m_vector = v; } - public: - ast_vector_tpl(context & c):object(c) { init(Z3_mk_ast_vector(c)); } - ast_vector_tpl(context & c, Z3_ast_vector v):object(c) { init(v); } - ast_vector_tpl(ast_vector_tpl const & s):object(s), m_vector(s.m_vector) { Z3_ast_vector_inc_ref(ctx(), m_vector); } - ~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); } - 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); } - void pop_back() { assert(size() > 0); resize(size() - 1); } - bool empty() const { return size() == 0; } - ast_vector_tpl & operator=(ast_vector_tpl const & s) { - Z3_ast_vector_inc_ref(s.ctx(), s.m_vector); - // Z3_ast_vector_dec_ref(ctx(), m_vector); - m_ctx = s.m_ctx; - m_vector = s.m_vector; - return *this; - } - 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; - -#endif - -#endif - - class func_interp : public object { - ::func_interp * m_interp; - void init(::func_interp * e) { - m_interp = e; - } - public: - func_interp(context & c, ::func_interp * e):object(c) { init(e); } - func_interp(func_interp const & s):object(s) { init(s.m_interp); } - ~func_interp() { } - operator ::func_interp *() const { return m_interp; } - func_interp & operator=(func_interp const & s) { - m_ctx = s.m_ctx; - m_interp = s.m_interp; - return *this; - } - unsigned num_entries() const { return m_interp->num_entries(); } - expr get_arg(unsigned ent, unsigned arg) const { - return expr(ctx(),m_interp->get_entry(ent)->get_arg(arg)); - } - expr get_value(unsigned ent) const { - return expr(ctx(),m_interp->get_entry(ent)->get_result()); - } - expr else_value() const { - return expr(ctx(),m_interp->get_else()); - } - }; - - - - class model : public object { - model_ref m_model; - void init(::model *m) { - m_model = m; - } - public: - model(context & c, ::model * m = nullptr):object(c), m_model(m) { } - model(model const & s):object(s), m_model(s.m_model) { } - ~model() { } - operator ::model *() const { return m_model.get(); } - model & operator=(model const & s) { - // ::model *_inc_ref(s.ctx(), s.m_model); - // ::model *_dec_ref(ctx(), m_model); - m_ctx = s.m_ctx; - m_model = s.m_model.get(); - return *this; - } - model & operator=(::model *s) { - m_model = s; - return *this; - } - bool null() const {return !m_model;} - - expr eval(expr const & n, bool model_completion=true) const { - ::model * _m = m_model.get(); - expr_ref result(ctx().m()); - _m->eval(n, result, model_completion); - return expr(ctx(), result); - } - - void show() const; - void show_hash() const; - - unsigned num_consts() const {return m_model.get()->get_num_constants();} - unsigned num_funcs() const {return m_model.get()->get_num_functions();} - func_decl get_const_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_constant(i));} - func_decl get_func_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_function(i));} - unsigned size() const; - func_decl operator[](unsigned i) const; - - expr get_const_interp(const func_decl & f) const { - return ctx().cook(m_model->get_const_interp(to_func_decl(f.raw()))); - } - - func_interp get_func_interp(const func_decl & f) const { - return func_interp(ctx(),m_model->get_func_interp(to_func_decl(f.raw()))); - } - -#if 0 - friend std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; } -#endif - }; - -#if 0 - class stats : public object { - Z3_stats m_stats; - void init(Z3_stats e) { - m_stats = e; - Z3_stats_inc_ref(ctx(), m_stats); - } - public: - stats(context & c):object(c), m_stats(0) {} - stats(context & c, Z3_stats e):object(c) { init(e); } - stats(stats const & s):object(s) { init(s.m_stats); } - ~stats() { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } - operator Z3_stats() const { return m_stats; } - stats & operator=(stats const & s) { - Z3_stats_inc_ref(s.ctx(), s.m_stats); - if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); - m_ctx = s.m_ctx; - m_stats = s.m_stats; - return *this; - } - unsigned size() const { return Z3_stats_size(ctx(), m_stats); } - std::string key(unsigned i) const { Z3_string s = Z3_stats_get_key(ctx(), m_stats, i); check_error(); return s; } - bool is_uint(unsigned i) const { Z3_bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r != 0; } - bool is_double(unsigned i) const { Z3_bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r != 0; } - unsigned uint_value(unsigned i) const { unsigned r = Z3_stats_get_uint_value(ctx(), m_stats, i); check_error(); return r; } - double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } - friend std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } - }; -#endif - - enum check_result { - unsat, sat, unknown - }; - - class fixedpoint : public object { - - public: - void get_rules(std::vector &rules); - void get_assertions(std::vector &rules); - void register_relation(const func_decl &rela); - void add_rule(const expr &clause, const symbol &name); - void assert_cnst(const expr &cnst); - }; - - inline std::ostream & operator<<(std::ostream & out, check_result r) { - if (r == unsat) out << "unsat"; - else if (r == sat) out << "sat"; - else out << "unknown"; - return out; - } - - inline check_result to_check_result(lbool l) { - if (l == l_true) return sat; - else if (l == l_false) return unsat; - return unknown; - } - - class solver : public object { - protected: - ::solver *m_solver; - model the_model; - bool canceled; - proof_gen_mode m_mode; - bool extensional; - public: - solver(context & c, bool extensional = false, bool models = true); - solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} - solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver; canceled = false;} - ~solver() { - if(m_solver) - dealloc(m_solver); - } - operator ::solver*() const { return m_solver; } - solver & operator=(solver const & s) { - m_ctx = s.m_ctx; - m_solver = s.m_solver; - the_model = s.the_model; - m_mode = s.m_mode; - return *this; - } - struct cancel_exception {}; - void checkpoint(){ - if(canceled) - throw(cancel_exception()); - } - // void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } - void push() { scoped_proof_mode spm(m(),m_mode); m_solver->push(); } - void pop(unsigned n = 1) { scoped_proof_mode spm(m(),m_mode); m_solver->pop(n); } - // void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } - void add(expr const & e) { scoped_proof_mode spm(m(),m_mode); m_solver->assert_expr(e); } - check_result check() { - scoped_proof_mode spm(m(),m_mode); - checkpoint(); - lbool r = m_solver->check_sat(0,nullptr); - model_ref m; - m_solver->get_model(m); - the_model = m.get(); - return to_check_result(r); - } - check_result check_keep_model(unsigned n, expr * const assumptions, unsigned *core_size = nullptr, expr *core = nullptr) { - scoped_proof_mode spm(m(),m_mode); - model old_model(the_model); - check_result res = check(n,assumptions,core_size,core); - if(the_model == nullptr) - the_model = old_model; - return res; - } - check_result check(unsigned n, expr * const assumptions, unsigned *core_size = nullptr, expr *core = nullptr) { - scoped_proof_mode spm(m(),m_mode); - checkpoint(); - std::vector< ::expr *> _assumptions(n); - for (unsigned i = 0; i < n; i++) { - _assumptions[i] = to_expr(assumptions[i]); - } - the_model = nullptr; - lbool r = m_solver->check_sat(n, VEC2PTR(_assumptions)); - - if(core_size && core){ - ptr_vector< ::expr> _core; - m_solver->get_unsat_core(_core); - *core_size = _core.size(); - for(unsigned i = 0; i < *core_size; i++) - core[i] = expr(ctx(),_core[i]); - } - - model_ref m; - m_solver->get_model(m); - the_model = m.get(); - - return to_check_result(r); - } -#if 0 - check_result check(expr_vector assumptions) { - scoped_proof_mode spm(m(),m_mode); - unsigned n = assumptions.size(); - z3array _assumptions(n); - for (unsigned i = 0; i < n; i++) { - check_context(*this, assumptions[i]); - _assumptions[i] = assumptions[i]; - } - Z3_lbool r = Z3_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); - check_error(); - return to_check_result(r); - } -#endif - model get_model() const { return model(ctx(), the_model); } - // std::string reason_unknown() const { Z3_string r = Z3_solver_get_reason_unknown(ctx(), m_solver); check_error(); return r; } - // stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } -#if 0 - expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } - expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } -#endif - // expr proof() const { Z3_ast r = Z3_solver_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } - // friend std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } - int get_num_decisions(); - - void cancel(){ - scoped_proof_mode spm(m(),m_mode); - canceled = true; - m().limit().cancel(); - } - - unsigned get_scope_level(){ scoped_proof_mode spm(m(),m_mode); return m_solver->get_scope_level();} - - void show(); - void print(const char *filename); - void show_assertion_ids(); - - proof get_proof(){ - scoped_proof_mode spm(m(),m_mode); - return proof(ctx(),m_solver->get_proof()); - } - - bool extensional_array_theory() {return extensional;} - }; - -#if 0 - class goal : public object { - Z3_goal m_goal; - void init(Z3_goal s) { - m_goal = s; - Z3_goal_inc_ref(ctx(), s); - } - public: - goal(context & c, bool models=true, bool unsat_cores=false, bool proofs=false):object(c) { init(Z3_mk_goal(c, models, unsat_cores, proofs)); } - goal(context & c, Z3_goal s):object(c) { init(s); } - goal(goal const & s):object(s) { init(s.m_goal); } - ~goal() { Z3_goal_dec_ref(ctx(), m_goal); } - operator Z3_goal() const { return m_goal; } - goal & operator=(goal const & s) { - Z3_goal_inc_ref(s.ctx(), s.m_goal); - Z3_goal_dec_ref(ctx(), m_goal); - m_ctx = s.m_ctx; - m_goal = s.m_goal; - return *this; - } - 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); } - 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); } - void reset() { Z3_goal_reset(ctx(), m_goal); } - unsigned num_exprs() const { 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; } - friend std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } - }; - - class apply_result : public object { - Z3_apply_result m_apply_result; - void init(Z3_apply_result s) { - m_apply_result = s; - Z3_apply_result_inc_ref(ctx(), s); - } - public: - apply_result(context & c, Z3_apply_result s):object(c) { init(s); } - apply_result(apply_result const & s):object(s) { init(s.m_apply_result); } - ~apply_result() { Z3_apply_result_dec_ref(ctx(), m_apply_result); } - operator Z3_apply_result() const { return m_apply_result; } - apply_result & operator=(apply_result const & s) { - Z3_apply_result_inc_ref(s.ctx(), s.m_apply_result); - Z3_apply_result_dec_ref(ctx(), m_apply_result); - m_ctx = s.m_ctx; - m_apply_result = s.m_apply_result; - 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)); } - 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); - check_error(); - return model(ctx(), new_m); - } - friend std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } - }; - - class tactic : public object { - Z3_tactic m_tactic; - void init(Z3_tactic s) { - m_tactic = s; - Z3_tactic_inc_ref(ctx(), s); - } - public: - tactic(context & c, char const * name):object(c) { Z3_tactic r = Z3_mk_tactic(c, name); check_error(); init(r); } - tactic(context & c, Z3_tactic s):object(c) { init(s); } - tactic(tactic const & s):object(s) { init(s.m_tactic); } - ~tactic() { Z3_tactic_dec_ref(ctx(), m_tactic); } - operator Z3_tactic() const { return m_tactic; } - tactic & operator=(tactic const & s) { - Z3_tactic_inc_ref(s.ctx(), s.m_tactic); - Z3_tactic_dec_ref(ctx(), m_tactic); - m_ctx = s.m_ctx; - m_tactic = s.m_tactic; - return *this; - } - solver mk_solver() const { Z3_solver r = Z3_mk_solver_from_tactic(ctx(), m_tactic); check_error(); return solver(ctx(), r); } - apply_result apply(goal const & g) const { - check_context(*this, g); - Z3_apply_result r = Z3_tactic_apply(ctx(), m_tactic, g); - check_error(); - return apply_result(ctx(), r); - } - apply_result operator()(goal const & g) const { - return apply(g); - } - std::string help() const { char const * r = Z3_tactic_get_help(ctx(), m_tactic); check_error(); return r; } - friend tactic operator&(tactic const & t1, tactic const & t2) { - check_context(t1, t2); - Z3_tactic r = Z3_tactic_and_then(t1.ctx(), t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - friend tactic operator|(tactic const & t1, tactic const & t2) { - check_context(t1, t2); - Z3_tactic r = Z3_tactic_or_else(t1.ctx(), t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - friend tactic repeat(tactic const & t, unsigned max=UINT_MAX) { - Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); - t.check_error(); - return tactic(t.ctx(), r); - } - friend tactic with(tactic const & t, params const & p) { - Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); - t.check_error(); - return tactic(t.ctx(), r); - } - friend tactic try_for(tactic const & t, unsigned ms) { - Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); - t.check_error(); - return tactic(t.ctx(), r); - } - }; - - class probe : public object { - Z3_probe m_probe; - void init(Z3_probe s) { - m_probe = s; - Z3_probe_inc_ref(ctx(), s); - } - public: - probe(context & c, char const * name):object(c) { Z3_probe r = Z3_mk_probe(c, name); check_error(); init(r); } - probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } - probe(context & c, Z3_probe s):object(c) { init(s); } - probe(probe const & s):object(s) { init(s.m_probe); } - ~probe() { Z3_probe_dec_ref(ctx(), m_probe); } - operator Z3_probe() const { return m_probe; } - probe & operator=(probe const & s) { - Z3_probe_inc_ref(s.ctx(), s.m_probe); - Z3_probe_dec_ref(ctx(), m_probe); - m_ctx = s.m_ctx; - m_probe = s.m_probe; - return *this; - } - double apply(goal const & g) const { double r = Z3_probe_apply(ctx(), m_probe, g); check_error(); return r; } - double operator()(goal const & g) const { return apply(g); } - friend probe operator<=(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_le(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator<=(probe const & p1, double p2) { return p1 <= probe(p1.ctx(), p2); } - friend probe operator<=(double p1, probe const & p2) { return probe(p2.ctx(), p1) <= p2; } - friend probe operator>=(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_ge(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator>=(probe const & p1, double p2) { return p1 >= probe(p1.ctx(), p2); } - friend probe operator>=(double p1, probe const & p2) { return probe(p2.ctx(), p1) >= p2; } - friend probe operator<(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_lt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator<(probe const & p1, double p2) { return p1 < probe(p1.ctx(), p2); } - friend probe operator<(double p1, probe const & p2) { return probe(p2.ctx(), p1) < p2; } - friend probe operator>(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_gt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator>(probe const & p1, double p2) { return p1 > probe(p1.ctx(), p2); } - friend probe operator>(double p1, probe const & p2) { return probe(p2.ctx(), p1) > p2; } - friend probe operator==(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_eq(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator==(probe const & p1, double p2) { return p1 == probe(p1.ctx(), p2); } - friend probe operator==(double p1, probe const & p2) { return probe(p2.ctx(), p1) == p2; } - friend probe operator&&(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_and(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator||(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_or(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator!(probe const & p) { - Z3_probe r = Z3_probe_not(p.ctx(), p); p.check_error(); return probe(p.ctx(), r); - } - }; - - inline tactic fail_if(probe const & p) { - Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); - p.check_error(); - return tactic(p.ctx(), r); - } - inline tactic when(probe const & p, tactic const & t) { - check_context(p, t); - Z3_tactic r = Z3_tactic_when(t.ctx(), p, t); - t.check_error(); - return tactic(t.ctx(), r); - } - inline tactic cond(probe const & p, tactic const & t1, tactic const & t2) { - check_context(p, t1); check_context(p, t2); - Z3_tactic r = Z3_tactic_cond(t1.ctx(), p, t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - -#endif - - inline expr context::bool_val(bool b){return b ? make(True) : make(False);} - - inline symbol context::str_symbol(char const * s) { ::symbol r = ::symbol(s); return symbol(*this, r); } - inline symbol context::int_symbol(int n) { ::symbol r = ::symbol(n); return symbol(*this, r); } - - inline sort context::bool_sort() { - ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); - return sort(*this, s); - } - inline sort context::int_sort() { - ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); - return sort(*this, s); - } - inline sort context::real_sort() { - ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); - return sort(*this, s); - } - inline sort context::array_sort(const sort & d, const sort & r) { - parameter params[2] = { parameter(d), parameter(to_sort(r)) }; - ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); - return sort(*this, s); - } - - - inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { - std::vector< ::sort *> sv(arity); - for(unsigned i = 0; i < arity; i++) - sv[i] = domain[i]; - ::func_decl* d = m().mk_func_decl(name,arity, VEC2PTR(sv),range); - return func_decl(*this,d); - } - - inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) { - return function(str_symbol(name), arity, domain, range); - } - - inline func_decl context::function(char const * name, sort const & domain, sort const & range) { - sort args[1] = { domain }; - return function(name, 1, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & range) { - sort args[2] = { d1, d2 }; - return function(name, 2, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { - sort args[3] = { d1, d2, d3 }; - return function(name, 3, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { - sort args[4] = { d1, d2, d3, d4 }; - return function(name, 4, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { - sort args[5] = { d1, d2, d3, d4, d5 }; - return function(name, 5, args, range); - } - - - inline expr context::constant(symbol const & name, sort const & s) { - ::expr *r = m().mk_const(m().mk_const_decl(name, s)); - return expr(*this, r); - } - inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); } - inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); } - inline expr context::int_const(char const * name) { return constant(name, int_sort()); } - inline expr context::real_const(char const * name) { return constant(name, real_sort()); } - inline expr context::bv_const(char const * name, unsigned sz) { return constant(name, bv_sort(sz)); } - - inline expr func_decl::operator()(const std::vector &args) const { - return operator()(args.size(), VEC2PTR(args)); - } - inline expr func_decl::operator()() const { - return operator()(0,nullptr); - } - inline expr func_decl::operator()(expr const & a) const { - return operator()(1,&a); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2) const { - expr args[2] = {a1,a2}; - return operator()(2,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3) const { - expr args[3] = {a1,a2,a3}; - return operator()(3,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const { - expr args[4] = {a1,a2,a3,a4}; - return operator()(4,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const { - expr args[5] = {a1,a2,a3,a4,a5}; - return operator()(5,args); - } - - - inline expr select(expr const & a, expr const & i) { return a.ctx().make(Select,a,i); } - inline expr store(expr const & a, expr const & i, expr const & v) { return a.ctx().make(Store,a,i,v); } - - inline expr forall(const std::vector &quants, const expr &body){ - return body.ctx().make_quant(Forall,quants,body); - } - - inline expr exists(const std::vector &quants, const expr &body){ - return body.ctx().make_quant(Exists,quants,body); - } - - inline expr context::int_val(int n){ - :: sort *r = m().mk_sort(m_arith_fid, INT_SORT); - return cook(m_arith_util.mk_numeral(rational(n),r)); - } - - - class literals : public object { - }; - - class TermTree { - public: - - TermTree(const expr &_term){ - term = _term; - } - - TermTree(const expr &_term, const std::vector &_children){ - term = _term; - children = _children; - } - - inline expr getTerm(){return term;} - - inline std::vector &getTerms(){return terms;} - - inline std::vector &getChildren(){ - return children; - } - - inline int number(int from){ - for(unsigned i = 0; i < children.size(); i++) - from = children[i]->number(from); - num = from; - return from + 1; - } - - inline int getNumber(){ - return num; - } - - inline void setTerm(const expr &t){term = t;} - - inline void addTerm(const expr &t){terms.push_back(t);} - - inline void setChildren(const std::vector & _children){ - children = _children; - } - - inline void setNumber(int _num){ - num = _num; - } - - ~TermTree(){ - for(unsigned i = 0; i < children.size(); i++) - delete children[i]; - } - - private: - expr term; - std::vector terms; - std::vector children; - int num; - }; - - typedef context interpolating_context; - - class interpolating_solver : public solver { - public: - interpolating_solver(context &ctx, bool models = true) - : solver(ctx, true, models) - { - weak_mode = false; - } - - public: - lbool interpolate(const std::vector &assumptions, - std::vector &interpolants, - model &_model, - literals &lits, - bool incremental - ); - - lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - literals &lits, - bool incremental - ); - - bool read_interpolation_problem(const std::string &file_name, - std::vector &assumptions, - std::vector &theory, - std::string &error_message - ); - - void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &theory - ); - - void AssertInterpolationAxiom(const expr &expr); - void RemoveInterpolationAxiom(const expr &expr); - - void SetWeakInterpolants(bool weak); - void SetPrintToFile(const std::string &file_name); - - const std::vector &GetInterpolationAxioms() {return theory;} - const char *profile(); - - private: - bool weak_mode; - std::string print_filename; - std::vector theory; - }; - - - inline expr context::cook(::expr *a) {return expr(*this,a);} - - inline std::vector context::cook(ptr_vector< ::expr> v) { - std::vector _v(v.size()); - for(unsigned i = 0; i < v.size(); i++) - _v[i] = cook(v[i]); - return _v; - } - - inline ::expr *context::uncook(const expr &a) { - m().inc_ref(a.raw()); - return to_expr(a.raw()); - } - - inline expr context::translate(const expr &e) { - ::expr *f = to_expr(e.raw()); - if(&e.ctx().m() != &m()) // same ast manager -> no translation - throw "ast manager mismatch"; - return cook(f); - } - - inline func_decl context::translate(const func_decl &e) { - ::func_decl *f = to_func_decl(e.raw()); - if(&e.ctx().m() != &m()) // same ast manager -> no translation - throw "ast manager mismatch"; - return func_decl(*this,f); - } - - typedef double clock_t; - clock_t current_time(); - inline void output_time(std::ostream &os, clock_t time){os << time;} - - template class uptr { - public: - X *ptr; - uptr(){ptr = nullptr;} - void set(X *_ptr){ - if(ptr) delete ptr; - ptr = _ptr; - } - X *get(){ return ptr;} - ~uptr(){ - if(ptr) delete ptr; - } - }; - -}; - -// to make Duality::ast hashable -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::ast &s) const { - return s.raw()->get_id(); - } - }; -} - - -// to make Duality::ast usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::ast &s, const Duality::ast &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// to make Duality::ast usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::expr &s, const Duality::expr &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// to make Duality::func_decl hashable -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::func_decl &s) const { - return s.raw()->get_id(); - } - }; -} - - -// to make Duality::func_decl usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::func_decl &s, const Duality::func_decl &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -#endif diff --git a/src/interp/CMakeLists.txt b/src/interp/CMakeLists.txt deleted file mode 100644 index c3d8e3d5e..000000000 --- a/src/interp/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -z3_add_component(interp - SOURCES - iz3base.cpp - iz3checker.cpp - iz3interp.cpp - iz3mgr.cpp - iz3pp.cpp - iz3profiling.cpp - iz3proof.cpp - iz3proof_itp.cpp - iz3scopes.cpp - iz3translate.cpp - iz3translate_direct.cpp - COMPONENT_DEPENDENCIES - solver - PYG_FILES - interp_params.pyg -) diff --git a/src/interp/interp_params.pyg b/src/interp/interp_params.pyg deleted file mode 100644 index 3116a18db..000000000 --- a/src/interp/interp_params.pyg +++ /dev/null @@ -1,6 +0,0 @@ -def_module_params('interp', - description='interpolation parameters', - export=True, - params=(('profile', BOOL, False, '(INTERP) profile interpolation'), - ('check', BOOL, False, '(INTERP) check interpolants'), - )) diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp deleted file mode 100644 index 43e7bdff8..000000000 --- a/src/interp/iz3base.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3base.cpp - - Abstract: - - Base class for interpolators. Includes an AST manager and a scoping - object as bases. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3base.h" -#include -#include -#include -#include -#include "solver/solver.h" -#include "../smt/smt_solver.h" - - -using namespace stl_ext; - - -iz3base::range &iz3base::ast_range(ast t){ - return ast_ranges_hash[t].rng; -} - -iz3base::range &iz3base::sym_range(symb d){ - return sym_range_hash[d]; -} - -void iz3base::add_frame_range(int frame, ast t){ - range &rng = ast_range(t); - if(!in_range(frame,rng)){ - range_add(frame,rng); - for(int i = 0, n = num_args(t); i < n; ++i) - add_frame_range(frame,arg(t,i)); - if(op(t) == Uninterpreted) - range_add(frame,sym_range(sym(t))); - } -} - -#if 1 -iz3base::range &iz3base::ast_scope(ast t){ - ranges &rngs = ast_ranges_hash[t]; - range &rng = rngs.scp; - if(!rngs.scope_computed){ // not computed yet - rng = range_full(); - for(int i = 0, n = num_args(t); i < n; ++i) - rng = range_glb(rng,ast_scope(arg(t,i))); - if(op(t) == Uninterpreted) - if(parents.empty() || num_args(t) == 0) // in tree mode, all function syms are global - rng = range_glb(rng,sym_range(sym(t))); - rngs.scope_computed = true; - } - return rng; -} -#else -iz3base::range &iz3base::ast_scope(ast t){ - ranges &rngs = ast_ranges_hash[t]; - if(rngs.scope_computed) return rngs.scp; - range rng = range_full(); - for(int i = 0, n = num_args(t); i < n; ++i) - rng = range_glb(rng,ast_scope(arg(t,i))); - if(op(t) == Uninterpreted) - if(parents.empty() || num_args(t) == 0) // in tree mode, all function syms are global - rng = range_glb(rng,sym_range(sym(t))); - rngs = ast_ranges_hash[t]; - rngs.scope_computed = true; - rngs.scp = rng; - return rngs.scp; -} -#endif - -void iz3base::print(const std::string &filename){ - ast t = make(And,cnsts); - std::ofstream f(filename.c_str()); - print_sat_problem(f,t); - f.close(); -} - - -void iz3base::gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo){ - if(memo.find(n) == memo.end()){ - memo.insert(n); - if(op(n) == And){ - int nargs = num_args(n); - for(int i = 0; i < nargs; i++) - gather_conjuncts_rec(arg(n,i),conjuncts,memo); - } - else - conjuncts.push_back(n); - } -} - -void iz3base::gather_conjuncts(ast n, std::vector &conjuncts){ - hash_set memo; - gather_conjuncts_rec(n,conjuncts,memo); -} - -bool iz3base::is_literal(ast n){ - if(is_not(n))n = arg(n,0); - if(is_true(n) || is_false(n)) return false; - if(op(n) == And) return false; - return true; -} - -iz3base::ast iz3base::simplify_and(std::vector &conjuncts){ - hash_set memo; - for(unsigned i = 0; i < conjuncts.size(); i++){ - if(is_false(conjuncts[i])) - return conjuncts[i]; - if(is_true(conjuncts[i]) || memo.find(conjuncts[i]) != memo.end()){ - std::swap(conjuncts[i],conjuncts.back()); - conjuncts.pop_back(); - } - else if(memo.find(mk_not(conjuncts[i])) != memo.end()) - return mk_false(); // contradiction! - else - memo.insert(conjuncts[i]); - } - if(conjuncts.empty())return mk_true(); - return make(And,conjuncts); -} - -iz3base::ast iz3base::simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth){ - if(is_not(n))return mk_not(simplify_with_lit_rec(mk_not(n),lit,memo,depth)); - if(n == lit) return mk_true(); - ast not_lit = mk_not(lit); - if(n == not_lit) return mk_false(); - if(op(n) != And || depth <= 0) return n; - std::pair foo(n,ast()); - std::pair::iterator,bool> bar = memo.insert(foo); - ast &res = bar.first->second; - if(!bar.second) return res; - int nargs = num_args(n); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = simplify_with_lit_rec(arg(n,i),lit,memo,depth-1); - res = simplify_and(args); - return res; -} - -iz3base::ast iz3base::simplify_with_lit(ast n, ast lit){ - hash_map memo; - return simplify_with_lit_rec(n,lit,memo,1); -} - -iz3base::ast iz3base::simplify(ast n){ - if(is_not(n)) return mk_not(simplify(mk_not(n))); - std::pair memo_obj(n,ast()); - std::pair::iterator,bool> memo = simplify_memo.insert(memo_obj); - ast &res = memo.first->second; - if(!memo.second) return res; - switch(op(n)){ - case And: { - std::vector conjuncts; - gather_conjuncts(n,conjuncts); - for(unsigned i = 0; i < conjuncts.size(); i++) - conjuncts[i] = simplify(conjuncts[i]); -#if 0 - for(unsigned i = 0; i < conjuncts.size(); i++) - if(is_literal(conjuncts[i])) - for(unsigned j = 0; j < conjuncts.size(); j++) - if(j != i) - conjuncts[j] = simplify_with_lit(conjuncts[j],conjuncts[i]); -#endif - res = simplify_and(conjuncts); - } - break; - case Equal: { - ast x = arg(n,0); - ast y = arg(n,1); - if(ast_id(x) > ast_id(y)) - std::swap(x,y); - res = make(Equal,x,y); - break; - } - default: - res = n; - } - return res; -} - -void iz3base::initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory){ - cnsts = _parts; - theory = _theory; - for(unsigned i = 0; i < cnsts.size(); i++) - add_frame_range(i, cnsts[i]); - for(unsigned i = 0; i < _theory.size(); i++){ - add_frame_range(SHRT_MIN, _theory[i]); - add_frame_range(SHRT_MAX, _theory[i]); - } - for(unsigned i = 0; i < cnsts.size(); i++) - frame_map[cnsts[i]] = i; - for(unsigned i = 0; i < theory.size(); i++) - frame_map[theory[i]] = INT_MAX; -} - -void iz3base::initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory){ - cnsts.resize(_parts.size()); - theory = _theory; - for(unsigned i = 0; i < _parts.size(); i++) - for(unsigned j = 0; j < _parts[i].size(); j++){ - cnsts[i] = make(And,_parts[i]); - add_frame_range(i, _parts[i][j]); - frame_map[_parts[i][j]] = i; - } - for(unsigned i = 0; i < _theory.size(); i++){ - add_frame_range(SHRT_MIN, _theory[i]); - add_frame_range(SHRT_MAX, _theory[i]); - frame_map[theory[i]] = INT_MAX; - } -} - -void iz3base::check_interp(const std::vector &itps, std::vector &theory){ -#if 0 - Z3_config config = Z3_mk_config(); - Z3_context vctx = Z3_mk_context(config); - int frames = cnsts.size(); - std::vector foocnsts(cnsts); - for(unsigned i = 0; i < frames; i++) - foocnsts[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),cnsts[i]); - Z3_write_interpolation_problem(ctx,frames,&foocnsts[0],0, "temp_lemma.smt", theory.size(), &theory[0]); - int vframes,*vparents; - Z3_ast *vcnsts; - const char *verror; - bool ok = Z3_read_interpolation_problem(vctx,&vframes,&vcnsts,0,"temp_lemma.smt",&verror); - assert(ok); - std::vector vvcnsts(vframes); - std::copy(vcnsts,vcnsts+vframes,vvcnsts.begin()); - std::vector vitps(itps.size()); - for(unsigned i = 0; i < itps.size(); i++) - vitps[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),itps[i]); - Z3_write_interpolation_problem(ctx,itps.size(),&vitps[0],0,"temp_interp.smt"); - int iframes,*iparents; - Z3_ast *icnsts; - const char *ierror; - ok = Z3_read_interpolation_problem(vctx,&iframes,&icnsts,0,"temp_interp.smt",&ierror); - assert(ok); - const char *error = 0; - bool iok = Z3_check_interpolant(vctx, frames, &vvcnsts[0], parents.size() ? &parents[0] : 0, icnsts, &error); - assert(iok); -#endif -} - -bool iz3base::is_sat(const std::vector &q, ast &_proof, std::vector &vars){ - - params_ref p; - p.set_bool("proof", true); // this is currently useless - p.set_bool("model", true); - p.set_bool("unsat_core", true); - scoped_ptr sf = mk_smt_solver_factory(); - scoped_ptr< ::solver > solver = (*sf)(m(), p, true, true, true, ::symbol::null); - ::solver &s = *solver.get(); - - for(unsigned i = 0; i < q.size(); i++) - s.assert_expr(to_expr(q[i].raw())); - lbool res = s.check_sat(0,nullptr); - if (m().canceled()) { - throw iz3_exception(Z3_CANCELED_MSG); - } - if(res == l_false){ - ::ast *proof = s.get_proof(); - _proof = cook(proof); - } - else if(vars.size()) { - model_ref(_m); - s.get_model(_m); - if (!_m.get()) { - SASSERT(l_undef == res); - throw iz3_exception("interpolation cannot proceed without a model"); - } - for(unsigned i = 0; i < vars.size(); i++){ - expr_ref r(m()); - _m.get()->eval(to_expr(vars[i].raw()),r,true); - vars[i] = cook(r.get()); - } - } - solver = nullptr; - return res != l_false; -} - - -void iz3base::find_children(const stl_ext::hash_set &cnsts_set, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &conjuncts, - std::vector &children, - std::vector &pos_map, - bool merge - ){ - std::vector my_children; - std::vector my_conjuncts; - if(op(tree) == Interp){ // if we've hit an interpolation position... - find_children(cnsts_set,arg(tree,0),cnsts,parents,my_conjuncts,my_children,pos_map,merge); - if(my_conjuncts.empty()) - my_conjuncts.push_back(mk_true()); // need at least one conjunct - int root = cnsts.size() + my_conjuncts.size() - 1; - for(unsigned i = 0; i < my_conjuncts.size(); i++){ - parents.push_back(root); - cnsts.push_back(my_conjuncts[i]); - } - for(unsigned i = 0; i < my_children.size(); i++) - parents[my_children[i]] = root; - children.push_back(root); - pos_map.push_back(root); - } - else { - if(op(tree) == And){ - int nargs = num_args(tree); - for(int i = 0; i < nargs; i++) - find_children(cnsts_set,arg(tree,i),cnsts,parents,my_conjuncts,my_children,pos_map,merge); - } - if(cnsts_set.find(tree) != cnsts_set.end()){ - if(merge && !my_conjuncts.empty()) - my_conjuncts.back() = mk_and(my_conjuncts.back(),tree); - else - my_conjuncts.push_back(tree); - } - for(unsigned i = 0; i < my_children.size(); i++) - children.push_back(my_children[i]); - for(unsigned i = 0; i < my_conjuncts.size(); i++) - conjuncts.push_back(my_conjuncts[i]); - } -} - -void iz3base::to_parents_vec_representation(const std::vector &_cnsts, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &theory, - std::vector &pos_map, - bool merge - ){ - std::vector my_children; - std::vector my_conjuncts; - hash_set cnsts_set; - for(unsigned i = 0; i < _cnsts.size(); i++) - cnsts_set.insert(_cnsts[i]); - ast _tree = (op(tree) != Interp) ? make(Interp,tree) : tree; - find_children(cnsts_set,_tree,cnsts,parents,my_conjuncts,my_children,pos_map,merge); - if(op(tree) != Interp) pos_map.pop_back(); - parents[parents.size()-1] = SHRT_MAX; - - // rest of the constraints are the background theory - - hash_set used_set; - for(unsigned i = 0; i < cnsts.size(); i++) - used_set.insert(cnsts[i]); - for(unsigned i = 0; i < _cnsts.size(); i++) - if(used_set.find(_cnsts[i]) == used_set.end()) - theory.push_back(_cnsts[i]); -} - diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h deleted file mode 100755 index 15f613730..000000000 --- a/src/interp/iz3base.h +++ /dev/null @@ -1,195 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3base.h - - Abstract: - - Base class for interpolators. Includes an AST manager and a scoping - object as bases. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3BASE_H -#define IZ3BASE_H - -#include "interp/iz3mgr.h" -#include "interp/iz3scopes.h" - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(func_decl * const &s) const { - return (size_t) s; - } - }; -} - -/* Base class for interpolators. Includes an AST manager and a scoping - object as bases. */ - -class iz3base : public iz3mgr, public scopes { - - public: - - /** Get the range in which an expression occurs. This is the - smallest subtree containing all occurrences of the - expression. */ - range &ast_range(ast); - - /** Get the scope of an expression. This is the set of tree nodes in - which all of the expression's symbols are in scope. */ - range &ast_scope(ast); - - /** Get the range of a symbol. This is the smallest subtree containing - all occurrences of the symbol. */ - range &sym_range(symb); - - /** Is an expression local (in scope in some frame)? */ - - bool is_local(ast node){ - return !range_is_empty(ast_scope(node)); - } - - /** Simplify an expression */ - - ast simplify(ast); - - /** Constructor */ - - iz3base(ast_manager &_m_manager, - const std::vector &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3mgr(_m_manager), scopes(_parents) { - initialize(_cnsts,_parents,_theory); - weak = false; - } - - iz3base(const iz3mgr& other, - const std::vector &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3mgr(other), scopes(_parents) { - initialize(_cnsts,_parents,_theory); - weak = false; - } - - iz3base(const iz3mgr& other, - const std::vector > &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3mgr(other), scopes(_parents) { - initialize(_cnsts,_parents,_theory); - weak = false; - } - - iz3base(const iz3mgr& other) - : iz3mgr(other), scopes() { - weak = false; - } - - /* Set our options */ - void set_option(const std::string &name, const std::string &value){ - if(name == "weak" && value == "1") weak = true; - } - - /* Are we doing weak interpolants? */ - bool weak_mode(){return weak;} - - /** Print interpolation problem to an SMTLIB format file */ - void print(const std::string &filename); - - /** Check correctness of a solutino to this problem. */ - void check_interp(const std::vector &itps, std::vector &theory); - - /** For convenience -- is this formula SAT? */ - bool is_sat(const std::vector &consts, ast &_proof, std::vector &vars); - - /** Interpolator for clauses, to be implemented */ - virtual void interpolate_clause(std::vector &lits, std::vector &itps){ - throw iz3_exception("no interpolator"); - } - - ast get_proof_check_assump(range &rng){ - std::vector cs(theory); - cs.push_back(cnsts[rng.hi]); - return make(And,cs); - } - - int frame_of_assertion(const ast &ass){ - stl_ext::hash_map::iterator it = frame_map.find(ass); - if(it == frame_map.end()) - throw iz3_exception("unknown assertion"); - return it->second; - } - - - void to_parents_vec_representation(const std::vector &_cnsts, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &theory, - std::vector &pos_map, - bool merge = false - ); - - protected: - std::vector cnsts; - std::vector theory; - - private: - - struct ranges { - range rng; - range scp; - bool scope_computed; - ranges(){scope_computed = false;} - }; - - stl_ext::hash_map sym_range_hash; - stl_ext::hash_map ast_ranges_hash; - stl_ext::hash_map simplify_memo; - stl_ext::hash_map frame_map; // map assertions to frames - - // int frames; // number of frames - - protected: - void add_frame_range(int frame, ast t); - - private: - void initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory); - - void initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory); - - bool is_literal(ast n); - void gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo); - void gather_conjuncts(ast n, std::vector &conjuncts); - ast simplify_and(std::vector &conjuncts); - ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth); - ast simplify_with_lit(ast n, ast lit); - void find_children(const stl_ext::hash_set &cnsts_set, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &conjuncts, - std::vector &children, - std::vector &pos_map, - bool merge - ); - bool weak; - -}; - - - -#endif diff --git a/src/interp/iz3checker.cpp b/src/interp/iz3checker.cpp deleted file mode 100644 index 511342819..000000000 --- a/src/interp/iz3checker.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3checker.cpp - - Abstract: - - check correctness of interpolant - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3base.h" -#include "interp/iz3checker.h" - -#include -#include -#include -#include -#include -#include -#include - - -using namespace stl_ext; - -struct iz3checker : iz3base { - - /* HACK: for tree interpolants, we assume that uninterpreted functions - are global. This is because in the current state of the tree interpolation - code, symbols that appear in sibling sub-trees have to be global, and - we have no way to eliminate such function symbols. When tree interpolation is - fixed, we can tree function symbols the same as constant symbols. */ - - bool is_tree; - - void support(const ast &t, std::set &res, hash_set &memo){ - if(memo.find(t) != memo.end()) return; - memo.insert(t); - - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - support(arg(t,i),res,memo); - - switch(op(t)){ - case Uninterpreted: - if(nargs == 0 || !is_tree) { - std::string name = string_of_symbol(sym(t)); - res.insert(name); - } - break; - case Forall: - case Exists: - support(get_quantifier_body(t),res,memo); - break; - default:; - } - } - - bool check(solver *s, std::ostream &err, - const std::vector &cnsts, - const std::vector &parents, - const std::vector &itp, - const std::vector &theory){ - - is_tree = !parents.empty(); - int num = cnsts.size(); - std::vector > children(num); - - for(int i = 0; i < num-1; i++){ - if(parents.size()) - children[parents[i]].push_back(i); - else - children[i+1].push_back(i); - } - - for(int i = 0; i < num; i++){ - s->push(); - for(unsigned j = 0; j < theory.size(); j++) - s->assert_expr(to_expr(theory[j].raw())); - s->assert_expr(to_expr(cnsts[i].raw())); - std::vector &cs = children[i]; - for(unsigned j = 0; j < cs.size(); j++) - s->assert_expr(to_expr(itp[cs[j]].raw())); - if(i != num-1) - s->assert_expr(to_expr(mk_not(itp[i]).raw())); - lbool result = s->check_sat(0,nullptr); - if(result != l_false){ - err << "interpolant " << i << " is incorrect"; - - s->pop(1); - for(unsigned j = 0; j < theory.size(); j++) - s->assert_expr(to_expr(theory[j].raw())); - for(unsigned j = 0; j < cnsts.size(); j++) - if(in_range(j,range_downward(i))) - s->assert_expr(to_expr(cnsts[j].raw())); - if(i != num-1) - s->assert_expr(to_expr(mk_not(itp[i]).raw())); - lbool result = s->check_sat(0,nullptr); - if(result != l_false) - err << "interpolant " << i << " is not implied by its downeard closurn"; - - return false; - } - s->pop(1); - } - - std::vector > supports(num); - for(int i = 0; i < num; i++){ - hash_set memo; - support(cnsts[i],supports[i],memo); - } - for(int i = 0; i < num-1; i++){ - std::vector Bside(num); - for(int j = num-1; j >= 0; j--) - Bside[j] = j != i; - for(int j = num-1; j >= 0; j--) - if(!Bside[j]){ - std::vector &cs = children[i]; - for(unsigned k = 0; k < cs.size(); k++) - Bside[cs[k]] = false; - } - std::set Asup, Bsup,common,Isup,bad; - for(int j = num-1; j >= 0; j--){ - std::set &side = Bside[j] ? Bsup : Asup; - side.insert(supports[j].begin(),supports[j].end()); - } - std::set_intersection(Asup.begin(),Asup.end(),Bsup.begin(),Bsup.end(),std::inserter(common,common.begin())); - { - hash_set tmemo; - for(unsigned j = 0; j < theory.size(); j++) - support(theory[j],common,tmemo); // all theory symbols allowed in interps - } - hash_set memo; - support(itp[i],Isup,memo); - std::set_difference(Isup.begin(),Isup.end(),common.begin(),common.end(),std::inserter(bad,bad.begin())); - if(!bad.empty()){ - err << "bad symbols in interpolant " << i << ":"; - std::copy(bad.begin(),bad.end(),std::ostream_iterator(err,",")); - return false; - } - } - return true; - } - - bool check(solver *s, std::ostream &err, - const std::vector &_cnsts, - const ast &tree, - const std::vector &itp){ - - std::vector pos_map; - - // convert to the parents vector representation - - to_parents_vec_representation(_cnsts, tree, cnsts, parents, theory, pos_map); - - //use the parents vector representation to compute interpolant - return check(s,err,cnsts,parents,itp,theory); - } - - iz3checker(ast_manager &_m) - : iz3base(_m) { - } - - iz3checker(iz3mgr &_m) - : iz3base(_m) { - } - -}; - -template -std::vector to_std_vector(const ::vector &v){ - std::vector _v(v.size()); - for(unsigned i = 0; i < v.size(); i++) - _v[i] = v[i]; - return _v; -} - - -bool iz3check(ast_manager &_m_manager, - solver *s, - std::ostream &err, - const ptr_vector &cnsts, - const ::vector &parents, - const ptr_vector &interps, - const ptr_vector &theory) -{ - iz3checker chk(_m_manager); - return chk.check(s,err,chk.cook(cnsts),to_std_vector(parents),chk.cook(interps),chk.cook(theory)); -} - -bool iz3check(iz3mgr &mgr, - solver *s, - std::ostream &err, - const std::vector &cnsts, - const std::vector &parents, - const std::vector &interps, - const std::vector &theory) -{ - iz3checker chk(mgr); - return chk.check(s,err,cnsts,parents,interps,theory); -} - -bool iz3check(ast_manager &_m_manager, - solver *s, - std::ostream &err, - const ptr_vector &_cnsts, - ast *tree, - const ptr_vector &interps) -{ - iz3checker chk(_m_manager); - return chk.check(s,err,chk.cook(_cnsts),chk.cook(tree),chk.cook(interps)); -} diff --git a/src/interp/iz3checker.h b/src/interp/iz3checker.h deleted file mode 100644 index d89db3011..000000000 --- a/src/interp/iz3checker.h +++ /dev/null @@ -1,49 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3checker.h - - Abstract: - - check correctness of an interpolant - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3_CHECKER_H -#define IZ3_CHECKER_H - -#include "interp/iz3mgr.h" -#include "solver/solver.h" - -bool iz3check(ast_manager &_m_manager, - solver *s, - std::ostream &err, - const ptr_vector &cnsts, - const ::vector &parents, - const ptr_vector &interps, - const ptr_vector &theory); - -bool iz3check(ast_manager &_m_manager, - solver *s, - std::ostream &err, - const ptr_vector &cnsts, - ast *tree, - const ptr_vector &interps); - -bool iz3check(iz3mgr &mgr, - solver *s, - std::ostream &err, - const std::vector &cnsts, - const std::vector &parents, - const std::vector &interps, - const ptr_vector &theory); - -#endif diff --git a/src/interp/iz3exception.h b/src/interp/iz3exception.h deleted file mode 100644 index b3f841565..000000000 --- a/src/interp/iz3exception.h +++ /dev/null @@ -1,28 +0,0 @@ -/*++ -Copyright (c) 2015 Microsoft Corporation - -Module Name: - - iz3exception.h - -Abstract: - - Base class for exceptions raised by interpolation routines - -Author: - -Notes: - ---*/ -#ifndef _IZ3EXCEPTION_H_ -#define _IZ3EXCEPTION_H_ - -#include "util/z3_exception.h" -#include "util/error_codes.h" - -class iz3_exception: public default_exception { -public: - iz3_exception(const std::string &msg): default_exception(msg) {} -}; - -#endif diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h deleted file mode 100644 index f85242ed1..000000000 --- a/src/interp/iz3hash.h +++ /dev/null @@ -1,479 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3hash.h - - Abstract: - - Simple implementation of bucket-list hash tables conforming to SGI - hash_map and hash_set interfaces. Just enough members are - implemented to support iz3 and duality. - - iz3 and duality need this package because they assume that insert - preserves iterators and references to elements, which is not true - of the hashtable packages in util. - - This package lives in namespace hash_space. Specializations of - class "hash" should be made in this namespace. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3_HASH_H -#define IZ3_HASH_H - -#ifdef _WINDOWS -#pragma warning(disable:4267) -#endif - -#include -#include -#include -#include "util/hash.h" - -#define stl_ext hash_space - -namespace hash_space { - - template class hash {}; - - - template <> - class hash { - public: - size_t operator()(const int &s) const { - return s; - } - }; - - template <> - class hash { - public: - size_t operator()(const std::string &s) const { - return string_hash(s.c_str(), s.size(), 0); - } - }; - - template <> - class hash > { - public: - size_t operator()(const std::pair &p) const { - return p.first + p.second; - } - }; - - template - class hash > { - public: - size_t operator()(const std::pair &p) const { - return (size_t)p.first + (size_t)p.second; - } - }; - - enum { num_primes = 29 }; - - static const unsigned long primes[num_primes] = - { - 7ul, - 53ul, - 97ul, - 193ul, - 389ul, - 769ul, - 1543ul, - 3079ul, - 6151ul, - 12289ul, - 24593ul, - 49157ul, - 98317ul, - 196613ul, - 393241ul, - 786433ul, - 1572869ul, - 3145739ul, - 6291469ul, - 12582917ul, - 25165843ul, - 50331653ul, - 100663319ul, - 201326611ul, - 402653189ul, - 805306457ul, - 1610612741ul, - 3221225473ul, - 4294967291ul - }; - - inline unsigned long next_prime(unsigned long n) { - const unsigned long* to = primes + (int)num_primes; - for(const unsigned long* p = primes; p < to; p++) - if(*p >= n) return *p; - return primes[num_primes-1]; - } - - template - class hashtable - { - public: - - typedef Value &reference; - typedef const Value &const_reference; - - struct Entry - { - Entry* next; - Value val; - - Entry(const Value &_val) : val(_val) {next = nullptr;} - }; - - - struct iterator - { - Entry* ent; - hashtable* tab; - - typedef std::forward_iterator_tag iterator_category; - typedef Value value_type; - typedef std::ptrdiff_t difference_type; - typedef size_t size_type; - typedef Value& reference; - typedef Value* pointer; - - iterator(Entry* _ent, hashtable* _tab) : ent(_ent), tab(_tab) { } - - iterator() { } - - Value &operator*() const { return ent->val; } - - Value *operator->() const { return &(operator*()); } - - iterator &operator++() { - Entry *old = ent; - ent = ent->next; - if (!ent) { - size_t bucket = tab->get_bucket(old->val); - while (!ent && ++bucket < tab->buckets.size()) - ent = tab->buckets[bucket]; - } - return *this; - } - - iterator operator++(int) { - iterator tmp = *this; - operator++(); - return tmp; - } - - - bool operator==(const iterator& it) const { - return ent == it.ent; - } - - bool operator!=(const iterator& it) const { - return ent != it.ent; - } - }; - - struct const_iterator - { - const Entry* ent; - const hashtable* tab; - - typedef std::forward_iterator_tag iterator_category; - typedef Value value_type; - typedef std::ptrdiff_t difference_type; - typedef size_t size_type; - typedef const Value& reference; - typedef const Value* pointer; - - const_iterator(const Entry* _ent, const hashtable* _tab) : ent(_ent), tab(_tab) { } - - const_iterator() { } - - const Value &operator*() const { return ent->val; } - - const Value *operator->() const { return &(operator*()); } - - const_iterator &operator++() { - Entry *old = ent; - ent = ent->next; - if (!ent) { - size_t bucket = tab->get_bucket(old->val); - while (!ent && ++bucket < tab->buckets.size()) - ent = tab->buckets[bucket]; - } - return *this; - } - - const_iterator operator++(int) { - const_iterator tmp = *this; - operator++(); - return tmp; - } - - - bool operator==(const const_iterator& it) const { - return ent == it.ent; - } - - bool operator!=(const const_iterator& it) const { - return ent != it.ent; - } - }; - - private: - - typedef std::vector Table; - - Table buckets; - size_t entries; - HashFun hash_fun ; - GetKey get_key; - KeyEqFun key_eq_fun; - - public: - - hashtable(size_t init_size) : buckets(init_size,(Entry *)nullptr) { - entries = 0; - } - - hashtable(const hashtable& other) { - dup(other); - } - - hashtable& operator= (const hashtable& other) { - if (&other != this) - dup(other); - return *this; - } - - ~hashtable() { - clear(); - } - - size_t size() const { - return entries; - } - - bool empty() const { - return size() == 0; - } - - void swap(hashtable& other) { - buckets.swap(other.buckets); - std::swap(entries, other.entries); - } - - iterator begin() { - for (size_t i = 0; i < buckets.size(); ++i) - if (buckets[i]) - return iterator(buckets[i], this); - return end(); - } - - iterator end() { - return iterator(nullptr, this); - } - - const_iterator begin() const { - for (size_t i = 0; i < buckets.size(); ++i) - if (buckets[i]) - return const_iterator(buckets[i], this); - return end(); - } - - const_iterator end() const { - return const_iterator(nullptr, this); - } - - size_t get_bucket(const Value& val, size_t n) const { - return hash_fun(get_key(val)) % n; - } - - size_t get_key_bucket(const Key& key) const { - return hash_fun(key) % buckets.size(); - } - - size_t get_bucket(const Value& val) const { - return get_bucket(val,buckets.size()); - } - - Entry *lookup(const Value& val, bool ins = false) - { - resize(entries + 1); - - size_t n = get_bucket(val); - Entry* from = buckets[n]; - - for (Entry* ent = from; ent; ent = ent->next) - if (key_eq_fun(get_key(ent->val), get_key(val))) - return ent; - - if(!ins) return nullptr; - - Entry* tmp = new Entry(val); - tmp->next = from; - buckets[n] = tmp; - ++entries; - return tmp; - } - - Entry *lookup_key(const Key& key) const - { - size_t n = get_key_bucket(key); - Entry* from = buckets[n]; - - for (Entry* ent = from; ent; ent = ent->next) - if (key_eq_fun(get_key(ent->val), key)) - return ent; - - return nullptr; - } - - const_iterator find(const Key& key) const { - return const_iterator(lookup_key(key),this); - } - - iterator find(const Key& key) { - return iterator(lookup_key(key),this); - } - - std::pair insert(const Value& val){ - size_t old_entries = entries; - Entry *ent = lookup(val,true); - return std::pair(iterator(ent,this),entries > old_entries); - } - - iterator insert(const iterator &it, const Value& val){ - Entry *ent = lookup(val,true); - return iterator(ent,this); - } - - size_t erase(const Key& key) - { - Entry** p = &(buckets[get_key_bucket(key)]); - size_t count = 0; - while(*p){ - Entry *q = *p; - if (key_eq_fun(get_key(q->val), key)) { - ++count; - *p = q->next; - delete q; - } - else - p = &(q->next); - } - entries -= count; - return count; - } - - void resize(size_t new_size) { - const size_t old_n = buckets.size(); - if (new_size <= old_n) return; - const size_t n = next_prime(new_size); - if (n <= old_n) return; - Table tmp(n, (Entry*)nullptr); - for (size_t i = 0; i < old_n; ++i) { - Entry* ent = buckets[i]; - while (ent) { - size_t new_bucket = get_bucket(ent->val, n); - buckets[i] = ent->next; - ent->next = tmp[new_bucket]; - tmp[new_bucket] = ent; - ent = buckets[i]; - } - } - buckets.swap(tmp); - } - - void clear() - { - for (size_t i = 0; i < buckets.size(); ++i) { - for (Entry* ent = buckets[i]; ent != nullptr;) { - Entry* next = ent->next; - delete ent; - ent = next; - } - buckets[i] = nullptr; - } - entries = 0; - } - - void dup(const hashtable& other) - { - buckets.resize(other.buckets.size()); - for (size_t i = 0; i < other.buckets.size(); ++i) { - Entry** to = &buckets[i]; - for (Entry* from = other.buckets[i]; from; from = from->next) - to = &((*to = new Entry(from->val))->next); - } - entries = other.entries; - } - }; - - template - class equal { - public: - bool operator()(const T& x, const T &y) const { - return x == y; - } - }; - - template - class identity { - public: - const T &operator()(const T &x) const { - return x; - } - }; - - template - class proj1 { - public: - const T &operator()(const std::pair &x) const { - return x.first; - } - }; - - template , - class EqFun = equal > - class hash_set - : public hashtable,EqFun> { - - public: - - typedef Element value_type; - - hash_set() - : hashtable,EqFun>(7) {} - }; - - template , - class EqFun = equal > - class hash_map - : public hashtable,Key,HashFun,proj1,EqFun> { - - public: - - hash_map() - : hashtable,Key,HashFun,proj1,EqFun>(7) {} - - Value &operator[](const Key& key) { - std::pair kvp(key,Value()); - return - hashtable,Key,HashFun,proj1,EqFun>:: - lookup(kvp,true)->val.second; - } - }; - -} -#endif diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp deleted file mode 100644 index 41c968bd8..000000000 --- a/src/interp/iz3interp.cpp +++ /dev/null @@ -1,578 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3interp.cpp - - Abstract: - - Interpolation based on proof translation. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -/* Copyright 2011 Microsoft Research. */ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include -#include -#include -#include -#include -#include - -#include "interp/iz3profiling.h" -#include "interp/iz3translate.h" -#include "interp/iz3proof.h" -#include "interp/iz3hash.h" -#include "interp/iz3interp.h" - -#include "ast/scoped_proof.h" - - -using namespace stl_ext; - - - -#if 1 - -struct frame_reducer : public iz3mgr { - - int frames; - hash_map frame_map; - std::vector assertions_map; - std::vector orig_parents_copy; - std::vector used_frames; - - - frame_reducer(const iz3mgr &other) - : iz3mgr(other) {} - - void get_proof_assumptions_rec(z3pf proof, hash_set &memo, std::vector &used_frames){ - if(memo.find(proof) != memo.end())return; - memo.insert(proof); - pfrule dk = pr(proof); - if(dk == PR_ASSERTED){ - ast con = conc(proof); - if(frame_map.find(con) != frame_map.end()){ // false for theory facts - int frame = frame_map[con]; - used_frames[frame] = true; - } - } - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - z3pf arg = prem(proof,i); - get_proof_assumptions_rec(arg,memo,used_frames); - } - } - } - - void get_frames(const std::vector >&z3_preds, - const std::vector &orig_parents, - std::vector >&assertions, - std::vector &parents, - z3pf proof){ - frames = z3_preds.size(); - orig_parents_copy = orig_parents; - for(unsigned i = 0; i < z3_preds.size(); i++) - for(unsigned j = 0; j < z3_preds[i].size(); j++) - frame_map[z3_preds[i][j]] = i; - used_frames.resize(frames); - hash_set memo; - get_proof_assumptions_rec(proof,memo,used_frames); - std::vector assertions_back_map(frames); - - // if multiple children of a tree node are used, we can't delete it - std::vector used_children; - used_children.reserve(frames); - for(int i = 0; i < frames; i++) - used_children.push_back(0); - for(int i = 0; i < frames; i++) - if(orig_parents[i] != SHRT_MAX) - if(used_frames[i] || used_children[i]){ - if(used_children[i] > 1) - used_frames[i] = true; - used_children[orig_parents[i]]++; - } - - for(unsigned i = 0; i < z3_preds.size(); i++) - if(used_frames[i] || i == z3_preds.size() - 1){ - assertions.push_back(z3_preds[i]); - assertions_map.push_back(i); - assertions_back_map[i] = assertions.size() - 1; - } - - if(orig_parents.size()){ - parents.resize(assertions.size()); - for(unsigned i = 0; i < assertions.size(); i++){ - int p = orig_parents[assertions_map[i]]; - while(p != SHRT_MAX && !used_frames[p]) - p = orig_parents[p]; - parents[i] = p == SHRT_MAX ? p : assertions_back_map[p]; - } - } - - // std::cout << "used frames = " << frames << "\n"; - } - - void fix_interpolants(std::vector &interpolants){ - std::vector unfixed = interpolants; - interpolants.resize(frames - 1); - for(int i = 0; i < frames - 1; i++) - interpolants[i] = mk_true(); - for(unsigned i = 0; i < unfixed.size(); i++) - interpolants[assertions_map[i]] = unfixed[i]; - for(int i = 0; i < frames-2; i++){ - int p = orig_parents_copy.size() == 0 ? i+1 : orig_parents_copy[i]; - if(p < frames - 1 && !used_frames[p]) - interpolants[p] = mk_and(interpolants[i],interpolants[p]); - } - } -}; - -#else -struct frame_reducer { - - - - frame_reducer(context _ctx){ - } - - void get_frames(const std::vector &z3_preds, - const std::vector &orig_parents, - std::vector &assertions, - std::vector &parents, - ast proof){ - assertions = z3_preds; - parents = orig_parents; - } - - void fix_interpolants(std::vector &interpolants){ - } -}; - -#endif - - - -template -struct killme { - T *p; - killme(){p = nullptr;} - void set(T *_p) {p = _p;} - ~killme(){ - if(p) - delete p; - } -}; - - -class iz3interp : public iz3base { -public: - - killme sp_killer; - killme tr_killer; - - bool is_linear(std::vector &parents){ - for(int i = 0; i < ((int)parents.size())-1; i++) - if(parents[i] != i+1) - return false; - return true; - } - - void test_secondary(const std::vector &cnsts, - const std::vector &parents, - std::vector &interps - ){ - throw iz3_exception("secondary interpolating prover not supported"); - } - - void proof_to_interpolant(z3pf proof, - const std::vector > &cnsts, - const std::vector &parents, - std::vector &interps, - const std::vector &theory, - interpolation_options_struct *options = nullptr - ){ -#if 0 - test_secondary(cnsts,parents,interps); - return; -#endif - - profiling::timer_start("Interpolation prep"); - - // get rid of frames not used in proof - - std::vector > cnsts_vec; - std::vector parents_vec; - frame_reducer fr(*this); - fr.get_frames(cnsts,parents,cnsts_vec,parents_vec,proof); - - int num = cnsts_vec.size(); - std::vector interps_vec(num-1); - - // if this is really a sequence problem, we can make it easier - if(is_linear(parents_vec)) - parents_vec.clear(); - - // secondary prover no longer supported - iz3secondary *sp = nullptr; - -#define BINARY_INTERPOLATION -#ifndef BINARY_INTERPOLATION - // create a translator - iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec,parents_vec,theory); - tr_killer.set(tr); - - // set the translation options, if needed - if(options) - for(hash_map::iterator it = options->map.begin(), en = options->map.end(); it != en; ++it) - tr->set_option(it->first, it->second); - - // create a proof object to hold the translation - iz3proof pf(tr); - - profiling::timer_stop("Interpolation prep"); - - // translate into an interpolatable proof - profiling::timer_start("Proof translation"); - try { - tr->translate(proof,pf); - } - catch (const char *msg) { - throw interpolation_failure(msg); - } - catch (const iz3translation::unsupported & ex) { - TRACE("iz3", tout << "unsupported " << "\n";); - throw interpolation_error(); - } - catch (const iz3proof::proof_error & ex) { - TRACE("iz3", tout << "proof error " << "\n";); - throw interpolation_error(); - } - profiling::timer_stop("Proof translation"); - - // translate the proof into interpolants - profiling::timer_start("Proof interpolation"); - for(int i = 0; i < num-1; i++){ - interps_vec[i] = pf.interpolate(tr->range_downward(i),tr->weak_mode()); - interps_vec[i] = tr->quantify(interps_vec[i],tr->range_downward(i)); - } - profiling::timer_stop("Proof interpolation"); -#else - iz3base the_base(*this,cnsts_vec,parents_vec,theory); - - profiling::timer_stop("Interpolation prep"); - - for(int i = 0; i < num-1; i++){ - range rng = the_base.range_downward(i); - std::vector > cnsts_vec_vec(2); - for(unsigned j = 0; j < cnsts_vec.size(); j++){ - bool is_A = the_base.in_range(j,rng); - for(unsigned k = 0; k < cnsts_vec[j].size(); k++) - cnsts_vec_vec[is_A ? 0 : 1].push_back(cnsts_vec[j][k]); - } - - killme tr_killer_i; - iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec_vec,std::vector(),theory); - tr_killer_i.set(tr); - - // set the translation options, if needed - if(options) - for(hash_map::iterator it = options->map.begin(), en = options->map.end(); it != en; ++it) - tr->set_option(it->first, it->second); - - // create a proof object to hold the translation - iz3proof pf(tr); - - // translate into an interpolatable proof - profiling::timer_start("Proof translation"); - try { - tr->translate(proof,pf); - } - catch (const char *msg) { - throw interpolation_failure(msg); - } - catch (const iz3translation::unsupported & ex) { - TRACE("iz3", tout << "unsupported " << "\n";); - throw interpolation_error(); - } - catch (const iz3proof::proof_error &) { - TRACE("iz3", tout << "proof error\n";); - throw interpolation_error(); - } - profiling::timer_stop("Proof translation"); - - // translate the proof into interpolants - profiling::timer_start("Proof interpolation"); - interps_vec[i] = pf.interpolate(tr->range_downward(0),tr->weak_mode()); - interps_vec[i] = tr->quantify(interps_vec[i],tr->range_downward(0)); - profiling::timer_stop("Proof interpolation"); - } -#endif - // put back in the removed frames - fr.fix_interpolants(interps_vec); - - interps = interps_vec; - - } - - - void proof_to_interpolant(z3pf proof, - std::vector &cnsts, - const std::vector &parents, - std::vector &interps, - const std::vector &theory, - interpolation_options_struct *options = nullptr - ){ - std::vector > cnsts_vec(cnsts.size()); - for(unsigned i = 0; i < cnsts.size(); i++) - cnsts_vec[i].push_back(cnsts[i]); - proof_to_interpolant(proof,cnsts_vec,parents,interps,theory,options); - } - - // same as above, but represents the tree using an ast - - void proof_to_interpolant(const z3pf &proof, - const std::vector &_cnsts, - const ast &tree, - std::vector &interps, - interpolation_options_struct *options = nullptr - ){ - std::vector pos_map; - - // convert to the parents vector representation - - to_parents_vec_representation(_cnsts, tree, cnsts, parents, theory, pos_map); - - //use the parents vector representation to compute interpolant - proof_to_interpolant(proof,cnsts,parents,interps,theory,options); - - // get the interps for the tree positions - std::vector _interps = interps; - interps.resize(pos_map.size()); - for(unsigned i = 0; i < pos_map.size(); i++){ - unsigned j = pos_map[i]; - interps[i] = j < _interps.size() ? _interps[j] : mk_false(); - } - } - - bool has_interp(hash_map &memo, const ast &t){ - if(memo.find(t) != memo.end()) - return memo[t]; - bool res = false; - if(op(t) == Interp) - res = true; - else if(op(t) == And){ - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - res |= has_interp(memo, arg(t,i)); - } - memo[t] = res; - return res; - } - - void collect_conjuncts(std::vector &cnsts, hash_map &memo, const ast &t){ - if(!has_interp(memo,t)) - cnsts.push_back(t); - else { - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - collect_conjuncts(cnsts, memo, arg(t,i)); - } - } - - void assert_conjuncts(solver &s, std::vector &cnsts, const ast &t){ - hash_map memo; - collect_conjuncts(cnsts,memo,t); - for(unsigned i = 0; i < cnsts.size(); i++) - s.assert_expr(to_expr(cnsts[i].raw())); - } - - void get_proof_assumptions(z3pf proof, std::vector &cnsts, hash_set &memo){ - if(memo.find(proof) != memo.end())return; - memo.insert(proof); - pfrule dk = pr(proof); - if(dk == PR_ASSERTED) - cnsts.push_back(conc(proof)); - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - z3pf arg = prem(proof,i); - get_proof_assumptions(arg,cnsts,memo); - } - } - } - - iz3interp(ast_manager &_m_manager) - : iz3base(_m_manager) {} -}; - - - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ptr_vector &cnsts, - const ::vector &parents, - ptr_vector &interps, - const ptr_vector &theory, - interpolation_options_struct * options) -{ - iz3interp itp(_m_manager); - if(options) - options->apply(itp); - std::vector _cnsts(cnsts.size()); - std::vector _parents(parents.size()); - std::vector _interps; - std::vector _theory(theory.size()); - for(unsigned i = 0; i < cnsts.size(); i++) - _cnsts[i] = itp.cook(cnsts[i]); - for(unsigned i = 0; i < parents.size(); i++) - _parents[i] = parents[i]; - for(unsigned i = 0; i < theory.size(); i++) - _theory[i] = itp.cook(theory[i]); - iz3mgr::ast _proof = itp.cook(proof); - itp.proof_to_interpolant(_proof,_cnsts,_parents,_interps,_theory,options); - interps.resize(_interps.size()); - for(unsigned i = 0; i < interps.size(); i++) - interps[i] = itp.uncook(_interps[i]); -} - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ::vector > &cnsts, - const ::vector &parents, - ptr_vector &interps, - const ptr_vector &theory, - interpolation_options_struct * options) -{ - iz3interp itp(_m_manager); - if(options) - options->apply(itp); - std::vector > _cnsts(cnsts.size()); - std::vector _parents(parents.size()); - std::vector _interps; - std::vector _theory(theory.size()); - for(unsigned i = 0; i < cnsts.size(); i++) - for(unsigned j = 0; j < cnsts[i].size(); j++) - _cnsts[i].push_back(itp.cook(cnsts[i][j])); - for(unsigned i = 0; i < parents.size(); i++) - _parents[i] = parents[i]; - for(unsigned i = 0; i < theory.size(); i++) - _theory[i] = itp.cook(theory[i]); - iz3mgr::ast _proof = itp.cook(proof); - itp.proof_to_interpolant(_proof,_cnsts,_parents,_interps,_theory,options); - interps.resize(_interps.size()); - for(unsigned i = 0; i < interps.size(); i++) - interps[i] = itp.uncook(_interps[i]); -} - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ptr_vector &cnsts, - ast *tree, - ptr_vector &interps, - interpolation_options_struct * options) -{ - iz3interp itp(_m_manager); - if(options) - options->apply(itp); - std::vector _cnsts(cnsts.size()); - std::vector _interps; - for(unsigned i = 0; i < cnsts.size(); i++) - _cnsts[i] = itp.cook(cnsts[i]); - iz3mgr::ast _proof = itp.cook(proof); - iz3mgr::ast _tree = itp.cook(tree); - - // if consts isn't provided, we can reconstruct it - if(_cnsts.empty()){ - hash_set memo; - itp.get_proof_assumptions(_proof,_cnsts,memo); - } - - itp.proof_to_interpolant(_proof,_cnsts,_tree,_interps,options); - interps.resize(_interps.size()); - for(unsigned i = 0; i < interps.size(); i++) - interps[i] = itp.uncook(_interps[i]); -} - -lbool iz3interpolate(ast_manager &_m_manager, - solver &s, - ast *tree, - ptr_vector &cnsts, - ptr_vector &interps, - model_ref &m, - interpolation_options_struct * options) -{ - iz3interp itp(_m_manager); - if(options) - options->apply(itp); - iz3mgr::ast _tree = itp.cook(tree); - std::vector _cnsts; - itp.assert_conjuncts(s,_cnsts,_tree); - profiling::timer_start("solving"); - lbool res = s.check_sat(0,nullptr); - profiling::timer_stop("solving"); - if(res == l_false){ - ast *proof = s.get_proof(); - iz3mgr::ast _proof = itp.cook(proof); - std::vector _interps; - itp.proof_to_interpolant(_proof,_cnsts,_tree,_interps,options); - interps.resize(_interps.size()); - for(unsigned i = 0; i < interps.size(); i++) - interps[i] = itp.uncook(_interps[i]); - } - else if(m){ - s.get_model(m); - } - cnsts.resize(_cnsts.size()); - for(unsigned i = 0; i < cnsts.size(); i++) - cnsts[i] = itp.uncook(_cnsts[i]); - return res; -} - - - -void interpolation_options_struct::apply(iz3base &b){ - for(stl_ext::hash_map::iterator it = map.begin(), en = map.end(); - it != en; - ++it) - b.set_option((*it).first,(*it).second); -} - -// On linux and mac, unlimit stack space so we get recursion - -#if defined(_WINDOWS) || defined(_CYGWIN) || defined(_MINGW) - -#else - -#include -#include - -class iz3stack_unlimiter { -public: - iz3stack_unlimiter() { - struct rlimit rl = {RLIM_INFINITY, RLIM_INFINITY}; - setrlimit(RLIMIT_STACK, &rl); - // nothing to be done if above fails - } -}; - -// initializing this will unlimit stack - -iz3stack_unlimiter the_iz3stack_unlimiter; - -#endif diff --git a/src/interp/iz3interp.h b/src/interp/iz3interp.h deleted file mode 100644 index 909703bed..000000000 --- a/src/interp/iz3interp.h +++ /dev/null @@ -1,123 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3interp.h - - Abstract: - - Interpolation based on proof translation. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3_INTERP_H -#define IZ3_INTERP_H - -#include "interp/iz3hash.h" -#include "interp/iz3exception.h" -#include "solver/solver.h" - -class iz3base; - -struct interpolation_options_struct { - stl_ext::hash_map map; -public: - void set(const std::string &name, const std::string &value){ - map[name] = value; - } - void apply(iz3base &b); -}; - -/** This object is thrown if a tree interpolation problem is mal-formed */ -struct iz3_bad_tree: public iz3_exception { - iz3_bad_tree(): iz3_exception("iz3_bad_tree") {} -}; - -/** This object is thrown when iz3 fails due to an incompleteness in - the secondary solver. */ -struct iz3_incompleteness: public iz3_exception { - iz3_incompleteness(): iz3_exception("iz3_incompleteness") {} -}; - -// This is thrown if there is some bug in the -// interpolation procedure -class interpolation_failure : public default_exception { - public: - interpolation_failure(const char *msg) - : default_exception(msg) - { - } -}; - -// This is thrown if we cannot derive an interpolant from a proof -// because it contains unsupported theories or if the proof contains -// errors -class interpolation_error : public default_exception { - public: - interpolation_error() - : default_exception("theory not supported by interpolation or bad proof" ) - { - } -}; - -typedef interpolation_options_struct *interpolation_options; - -/* Compute an interpolant from a proof. This version uses the parents vector - representation, for compatibility with the old API. */ - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ptr_vector &cnsts, - const ::vector &parents, - ptr_vector &interps, - const ptr_vector &theory, - interpolation_options_struct * options = nullptr); - -/* Same as above, but each constraint is a vector of formulas. */ - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const vector > &cnsts, - const ::vector &parents, - ptr_vector &interps, - const ptr_vector &theory, - interpolation_options_struct * options = nullptr); - -/* Compute an interpolant from a proof. This version uses the ast - representation, for compatibility with the new API. Here, cnsts is - a vector of all the assertions in the proof. This can be - over-approximated by the set of all assertions in the - solver. However, if it is empty it will be reconstructed from the - proof, so it can be considered a hint. */ - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ptr_vector &cnsts, - ast *tree, - ptr_vector &interps, - interpolation_options_struct * options); - - -/* Compute an interpolant from an ast representing an interpolation - problem, if unsat, else return a model (if enabled). Uses the - given solver to produce the proof/model. Also returns a vector - of the constraints in the problem, helpful for checking correctness. -*/ - -lbool iz3interpolate(ast_manager &_m_manager, - solver &s, - ast *tree, - ptr_vector &cnsts, - ptr_vector &interps, - model_ref &m, - interpolation_options_struct * options); - - -#endif diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp deleted file mode 100644 index bb37d7f48..000000000 --- a/src/interp/iz3mgr.cpp +++ /dev/null @@ -1,969 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3mgr.cpp - - Abstract: - - A wrapper around an ast manager, providing convenience methods. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#pragma warning(disable:4805) -#pragma warning(disable:4800) -#endif - -#include "interp/iz3mgr.h" - -#include -#include -#include -#include -#include - -#include "ast/expr_abstract.h" -#include "util/params.h" -#include "ast/used_vars.h" - - -using namespace stl_ext; - - -std::ostream &operator <<(std::ostream &s, const iz3mgr::ast &a){ - return s; -} - - -iz3mgr::ast iz3mgr::make_var(const std::string &name, type ty){ - symbol s = symbol(name.c_str()); - return cook(m().mk_const(m().mk_const_decl(s, ty))); -} - -iz3mgr::ast iz3mgr::make(opr op, int n, raw_ast **args){ - switch(op) { - case True: return mki(m_basic_fid,OP_TRUE,n,args); - case False: return mki(m_basic_fid,OP_FALSE,n,args); - case Equal: return mki(m_basic_fid,OP_EQ,n,args); - case Distinct: return mki(m_basic_fid,OP_DISTINCT,n,args); - case Ite: return mki(m_basic_fid,OP_ITE,n,args); - case And: return mki(m_basic_fid,OP_AND,n,args); - case Or: return mki(m_basic_fid,OP_OR,n,args); - case Iff: return mki(m_basic_fid,OP_IFF,n,args); - case Xor: return mki(m_basic_fid,OP_XOR,n,args); - case Not: return mki(m_basic_fid,OP_NOT,n,args); - case Implies: return mki(m_basic_fid,OP_IMPLIES,n,args); - case Oeq: return mki(m_basic_fid,OP_OEQ,n,args); - case Interp: return mki(m_basic_fid,OP_INTERP,n,args); - case Leq: return mki(m_arith_fid,OP_LE,n,args); - case Geq: return mki(m_arith_fid,OP_GE,n,args); - case Lt: return mki(m_arith_fid,OP_LT,n,args); - case Gt: return mki(m_arith_fid,OP_GT,n,args); - case Plus: return mki(m_arith_fid,OP_ADD,n,args); - case Sub: return mki(m_arith_fid,OP_SUB,n,args); - case Uminus: return mki(m_arith_fid,OP_UMINUS,n,args); - case Times: return mki(m_arith_fid,OP_MUL,n,args); - case Div: return mki(m_arith_fid,OP_DIV,n,args); - case Idiv: return mki(m_arith_fid,OP_IDIV,n,args); - case Rem: return mki(m_arith_fid,OP_REM,n,args); - case Mod: return mki(m_arith_fid,OP_MOD,n,args); - case Power: return mki(m_arith_fid,OP_POWER,n,args); - case ToReal: return mki(m_arith_fid,OP_TO_REAL,n,args); - case ToInt: return mki(m_arith_fid,OP_TO_INT,n,args); - case IsInt: return mki(m_arith_fid,OP_IS_INT,n,args); - case Store: return mki(m_array_fid,OP_STORE,n,args); - case Select: return mki(m_array_fid,OP_SELECT,n,args); - case ConstArray: return mki(m_array_fid,OP_CONST_ARRAY,n,args); - case ArrayDefault: return mki(m_array_fid,OP_ARRAY_DEFAULT,n,args); - case ArrayMap: return mki(m_array_fid,OP_ARRAY_MAP,n,args); - case SetUnion: return mki(m_array_fid,OP_SET_UNION,n,args); - case SetIntersect: return mki(m_array_fid,OP_SET_INTERSECT,n,args); - case SetDifference: return mki(m_array_fid,OP_SET_DIFFERENCE,n,args); - case SetComplement: return mki(m_array_fid,OP_SET_COMPLEMENT,n,args); - case SetSubSet: return mki(m_array_fid,OP_SET_SUBSET,n,args); - case AsArray: return mki(m_array_fid,OP_AS_ARRAY,n,args); - default: - assert(0); - return ast(); - } -} - -iz3mgr::ast iz3mgr::mki(family_id fid, decl_kind dk, int n, raw_ast **args){ - return cook(m().mk_app(fid, dk, 0, nullptr, n, (expr **)args)); -} - -iz3mgr::ast iz3mgr::make(opr op, const std::vector &args){ - static std::vector a(10); - if(a.size() < args.size()) - a.resize(args.size()); - for(unsigned i = 0; i < args.size(); i++) - a[i] = args[i].raw(); - return make(op,args.size(), args.size() ? &a[0] : nullptr); -} - -iz3mgr::ast iz3mgr::make(opr op){ - return make(op,0,nullptr); -} - -iz3mgr::ast iz3mgr::make(opr op, const ast &arg0){ - raw_ast *a = arg0.raw(); - return make(op,1,&a); -} - -iz3mgr::ast iz3mgr::make(opr op, const ast &arg0, const ast &arg1){ - raw_ast *args[2]; - args[0] = arg0.raw(); - args[1] = arg1.raw(); - return make(op,2,args); -} - -iz3mgr::ast iz3mgr::make(opr op, const ast &arg0, const ast &arg1, const ast &arg2){ - raw_ast *args[3]; - args[0] = arg0.raw(); - args[1] = arg1.raw(); - args[2] = arg2.raw(); - return make(op,3,args); -} - -iz3mgr::ast iz3mgr::make(symb sym, int n, raw_ast **args){ - return cook(m().mk_app(sym, n, (expr **) args)); -} - -iz3mgr::ast iz3mgr::make(symb sym, const std::vector &args){ - static std::vector a(10); - if(a.size() < args.size()) - a.resize(args.size()); - for(unsigned i = 0; i < args.size(); i++) - a[i] = args[i].raw(); - return make(sym,args.size(), args.size() ? &a[0] : nullptr); -} - -iz3mgr::ast iz3mgr::make(symb sym){ - return make(sym,0,nullptr); -} - -iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0){ - raw_ast *a = arg0.raw(); - return make(sym,1,&a); -} - -iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0, const ast &arg1){ - raw_ast *args[2]; - args[0] = arg0.raw(); - args[1] = arg1.raw(); - return make(sym,2,args); -} - -iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0, const ast &arg1, const ast &arg2){ - raw_ast *args[3]; - args[0] = arg0.raw(); - args[1] = arg1.raw(); - args[2] = arg2.raw(); - return make(sym,3,args); -} - -iz3mgr::ast iz3mgr::make_quant(opr op, const std::vector &bvs, ast &body){ - if(bvs.size() == 0) return body; - std::vector foo(bvs.size()); - - - std::vector names; - std::vector types; - std::vector bound_asts; - unsigned num_bound = bvs.size(); - - for (unsigned i = 0; i < num_bound; ++i) { - app* a = to_app(bvs[i].raw()); - symbol s(to_app(a)->get_decl()->get_name()); - names.push_back(s); - types.push_back(m().get_sort(a)); - bound_asts.push_back(a); - } - expr_ref abs_body(m()); - expr_abstract(m(), 0, num_bound, &bound_asts[0], to_expr(body.raw()), abs_body); - expr_ref result(m()); - result = m().mk_quantifier( - op == Forall, - names.size(), &types[0], &names[0], abs_body.get(), - 0, - symbol("itp"), - symbol(), - 0, nullptr, - 0, nullptr - ); - return cook(result.get()); -} - -// FIXME replace this with existing Z3 functionality - -iz3mgr::ast iz3mgr::clone(const ast &t, const std::vector &_args){ - if(_args.size() == 0) - return t; - - ast_manager& m = m_manager; - expr* a = to_expr(t.raw()); - static std::vector rargs(10); - if(rargs.size() < _args.size()) - rargs.resize(_args.size()); - for(unsigned i = 0; i < _args.size(); i++) - rargs[i] = _args[i].raw(); - expr* const* args = (expr **)&rargs[0]; - switch(a->get_kind()) { - case AST_APP: { - app* e = to_app(a); - if (e->get_num_args() != _args.size()) { - assert(0); - } - else { - a = m.mk_app(e->get_decl(), _args.size(), args); - } - break; - } - case AST_QUANTIFIER: { - if (_args.size() != 1) { - assert(0); - } - else { - a = m.update_quantifier(to_quantifier(a), args[0]); - } - break; - } - default: - break; - } - return cook(a); -} - - -void iz3mgr::show(ast t){ - if(t.null()){ - std::cout << "(null)" << std::endl; - } - params_ref p; - p.set_bool("flat_assoc",false); - std::cout << mk_pp(t.raw(), m(), p) << std::endl; -} - -void iz3mgr::show_symb(symb s){ - std::cout << mk_pp(s, m()) << std::endl; -} - -void iz3mgr::print_expr(std::ostream &s, const ast &e){ - params_ref p; - p.set_bool("flat_assoc",false); - s << mk_pp(e.raw(), m(), p); -} - - -void iz3mgr::print_clause(std::ostream &s, std::vector &cls){ - s << "("; - for(unsigned i = 0; i < cls.size(); i++){ - if(i > 0) s << ","; - print_expr(s,cls[i]); - } - s << ")"; -} - -void iz3mgr::show_clause(std::vector &cls){ - print_clause(std::cout,cls); - std::cout << std::endl; -} - -void iz3mgr::print_lit(ast lit){ - ast abslit = is_not(lit) ? arg(lit,0) : lit; - int f = op(abslit); - if(f == And || f == Or || f == Iff){ - if(is_not(lit)) std::cout << "~"; - std::cout << "[" << abslit << "]"; - } - else - std::cout << lit; -} - - -static int pretty_cols = 79; -static int pretty_indent_chars = 2; - -static int pretty_find_delim(const std::string &s, int pos){ - int level = 0; - int end = s.size(); - for(; pos < end; pos++){ - int ch = s[pos]; - if(ch == '(')level++; - if(ch == ')')level--; - if(level < 0 || (level == 0 && ch == ','))break; - } - return pos; -} - -static void pretty_newline(std::ostream &f, int indent){ - f << std::endl; - for(int i = 0; i < indent; i++) - f << " "; -} - -void iz3mgr::pretty_print(std::ostream &f, const std::string &s){ - int cur_indent = 0; - int indent = 0; - int col = 0; - int pos = 0; - while(pos < (int)s.size()){ - int delim = pretty_find_delim(s,pos); - if(s[pos] != ')' && s[pos] != ',' && cur_indent > indent){ - pretty_newline(f,indent); - cur_indent = indent; - col = indent; - continue; - } - if (col + delim - pos > pretty_cols) { - if (col > indent) { - pretty_newline(f,indent); - cur_indent = indent; - col = indent; - continue; - } - int paren = s.find('(',pos); - if(paren != (int)std::string::npos){ - int chars = paren - pos + 1; - f << s.substr(pos,chars); - indent += pretty_indent_chars; - if(col) pretty_newline(f,indent); - cur_indent = indent; - pos += chars; - col = indent; - continue; - } - } - int chars = delim - pos + 1; - f << s.substr(pos,chars); - pos += chars; - col += chars; - if(s[delim] == ')') - indent -= pretty_indent_chars; - } -} - - -iz3mgr::opr iz3mgr::op(const ast &t){ - ast_kind dk = t.raw()->get_kind(); - switch(dk){ - case AST_APP: { - expr * e = to_expr(t.raw()); - func_decl *d = to_app(t.raw())->get_decl(); - if (null_family_id == d->get_family_id()) - return Uninterpreted; - // return (opr)d->get_decl_kind(); - if (m_basic_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_TRUE: return True; - case OP_FALSE: return False; - case OP_EQ: return Equal; - case OP_DISTINCT: return Distinct; - case OP_ITE: return Ite; - case OP_AND: return And; - case OP_OR: return Or; - case OP_IFF: return Iff; - case OP_XOR: return Xor; - case OP_NOT: return Not; - case OP_IMPLIES: return Implies; - case OP_OEQ: return Oeq; - case OP_INTERP: return Interp; - default: - return Other; - } - } - if (m_arith_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_LE: return Leq; - case OP_GE: return Geq; - case OP_LT: return Lt; - case OP_GT: return Gt; - case OP_ADD: return Plus; - case OP_SUB: return Sub; - case OP_UMINUS: return Uminus; - case OP_MUL: return Times; - case OP_DIV: return Div; - case OP_IDIV: return Idiv; - case OP_REM: return Rem; - case OP_MOD: return Mod; - case OP_POWER: return Power; - case OP_TO_REAL: return ToReal; - case OP_TO_INT: return ToInt; - case OP_IS_INT: return IsInt; - default: - if (m().is_unique_value(e)) - return Numeral; - return Other; - } - } - if (m_array_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_STORE: return Store; - case OP_SELECT: return Select; - case OP_CONST_ARRAY: return ConstArray; - case OP_ARRAY_DEFAULT: return ArrayDefault; - case OP_ARRAY_MAP: return ArrayMap; - case OP_SET_UNION: return SetUnion; - case OP_SET_INTERSECT: return SetIntersect; - case OP_SET_DIFFERENCE: return SetDifference; - case OP_SET_COMPLEMENT: return SetComplement; - case OP_SET_SUBSET: return SetSubSet; - case OP_AS_ARRAY: return AsArray; - default: - return Other; - } - } - - return Other; - } - - - case AST_QUANTIFIER: - return to_quantifier(t.raw())->is_forall() ? Forall : Exists; - case AST_VAR: - return Variable; - default:; - } - return Other; -} - - -iz3mgr::pfrule iz3mgr::pr(const ast &t){ - func_decl *d = to_app(t.raw())->get_decl(); - assert(m_basic_fid == d->get_family_id()); - return d->get_decl_kind(); -} - -void iz3mgr::print_sat_problem(std::ostream &out, const ast &t){ - ast_smt_pp pp(m()); - pp.set_simplify_implies(false); - pp.display_smt2(out, to_expr(t.raw())); -} - -iz3mgr::ast iz3mgr::z3_simplify(const ast &e){ - ::expr * a = to_expr(e.raw()); - params_ref p; - th_rewriter m_rw(m(), p); - expr_ref result(m()); - m_rw(a, result); - return cook(result); -} - -iz3mgr::ast iz3mgr::z3_really_simplify(const ast &e){ - ::expr * a = to_expr(e.raw()); - params_ref simp_params; - simp_params.set_bool(":som",true); - simp_params.set_bool(":sort-sums",true); - th_rewriter m_rw(m(), simp_params); - expr_ref result(m()); - m_rw(a, result); - return cook(result); -} - - -#if 0 -static rational lcm(const rational &x, const rational &y){ - int a = x.numerator(); - int b = y.numerator(); - return rational(a * b / gcd(a, b)); -} -#endif - -static rational extract_lcd(std::vector &res){ - if(res.size() == 0) return rational(1); // shouldn't happen - rational lcd = denominator(res[0]); - for(unsigned i = 1; i < res.size(); i++) - lcd = lcm(lcd,denominator(res[i])); - for(unsigned i = 0; i < res.size(); i++) - res[i] *= lcd; - return lcd; -} - -void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& coeffs){ - std::vector rats; - get_farkas_coeffs(proof,rats); - coeffs.resize(rats.size()); - for(unsigned i = 0; i < rats.size(); i++){ - class sort *is = m().mk_sort(m_arith_fid, INT_SORT); - ast coeff = cook(m_arith_util.mk_numeral(rats[i],is)); - coeffs[i] = coeff; - } -} - -static void abs_rat(std::vector &rats){ - // check that they are all non-neg -- if neg, take abs val and warn! - for(unsigned i = 0; i < rats.size(); i++) - if(rats[i].is_neg()){ - // std::cout << "negative Farkas coeff!\n"; - rats[i] = -rats[i]; - } -} - -bool iz3mgr::is_farkas_coefficient_negative(const ast &proof, int n){ - rational r; - symb s = sym(proof); - bool ok = s->get_parameter(n+2).is_rational(r); - if(!ok) - throw iz3_exception("Bad Farkas coefficient"); - return r.is_neg(); -} - -void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ - symb s = sym(proof); - int numps = s->get_num_parameters(); - rats.resize(numps-2); -#if 0 - if(num_prems(proof) < numps-2){ - std::cout << "bad farkas rule: " << num_prems(proof) << " premises should be " << numps-2 << "\n"; - } -#endif - for(int i = 2; i < numps; i++){ - rational r; - bool ok = s->get_parameter(i).is_rational(r); - if(!ok) - throw iz3_exception("Bad Farkas coefficient"); -#if 0 - { - ast con = conc(prem(proof,i-2)); - ast temp = make_real(r); // for debugging - opr o = is_not(con) ? op(arg(con,0)) : op(con); - if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) - r = -r; - } -#endif - rats[i-2] = r; - } -#if 0 - if(rats.size() != 0 && rats[0].is_neg()){ - for(unsigned i = 0; i < rats.size(); i++){ - assert(rats[i].is_neg()); - rats[i] = -rats[i]; - } - } -#endif - abs_rat(rats); - extract_lcd(rats); -} - -void iz3mgr::get_broken_gcd_test_coeffs(const ast &proof, std::vector& rats){ - symb s = sym(proof); - int numps = s->get_num_parameters(); - rats.resize(numps-2); - for(int i = 2; i < numps; i++){ - rational r; - bool ok = s->get_parameter(i).is_rational(r); - if(!ok) - throw "Bad Farkas coefficient"; - rats[i-2] = r; - } - extract_lcd(rats); -} - -void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& coeffs){ - std::vector rats; - get_assign_bounds_coeffs(proof,rats); - coeffs.resize(rats.size()); - for(unsigned i = 0; i < rats.size(); i++){ - coeffs[i] = make_int(rats[i]); - } -} - -void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& rats){ - symb s = sym(proof); - int numps = s->get_num_parameters(); - rats.resize(numps-1); - rats[0] = rational(1); - ast conseq = arg(conc(proof),0); - opr conseq_o = is_not(conseq) ? op(arg(conseq,0)) : op(conseq); - bool conseq_neg = is_not(conseq) ? (conseq_o == Leq || conseq_o == Lt) : (conseq_o == Geq || conseq_o == Gt); - for(int i = 2; i < numps; i++){ - rational r; - bool ok = s->get_parameter(i).is_rational(r); - if(!ok) - throw iz3_exception("Bad Farkas coefficient"); - { - ast con = arg(conc(proof),i-1); - ast temp = make_real(r); // for debugging - opr o = is_not(con) ? op(arg(con,0)) : op(con); - if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) - r = -r; - if(conseq_neg) - r = -r; - } - rats[i-1] = r; - } -#if 0 - if(rats[1].is_neg()){ // work around bug -- if all coeffs negative, negate them - for(unsigned i = 1; i < rats.size(); i++){ - if(!rats[i].is_neg()) - throw iz3_exception("Bad Farkas coefficients"); - rats[i] = -rats[i]; - } - } -#endif - abs_rat(rats); - extract_lcd(rats); -} - -void iz3mgr::get_gomory_cut_coeffs(const ast &proof, std::vector& coeffs){ - std::vector rats; - get_gomory_cut_coeffs(proof,rats); - coeffs.resize(rats.size()); - for(unsigned i = 0; i < rats.size(); i++){ - coeffs[i] = make_int(rats[i]); - } -} - -void iz3mgr::get_gomory_cut_coeffs(const ast &proof, std::vector& rats){ - symb s = sym(proof); - int numps = s->get_num_parameters(); - rats.resize(numps-2); - for(int i = 2; i < numps; i++){ - rational r; - bool ok = s->get_parameter(i).is_rational(r); - if(!ok) - throw "Bad Farkas coefficient"; - rats[i-2] = r; - } - abs_rat(rats); - extract_lcd(rats); -} - -void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector& coeffs){ - std::vector rats; - get_assign_bounds_rule_coeffs(proof,rats); - coeffs.resize(rats.size()); - for(unsigned i = 0; i < rats.size(); i++){ - coeffs[i] = make_int(rats[i]); - } -} - -void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats){ - symb s = sym(proof); - int numps = s->get_num_parameters(); - rats.resize(numps-1); - rats[0] = rational(1); - ast conseq = arg(conc(proof),0); - opr conseq_o = is_not(conseq) ? op(arg(conseq,0)) : op(conseq); - bool conseq_neg = is_not(conseq) ? (conseq_o == Leq || conseq_o == Lt) : (conseq_o == Geq || conseq_o == Gt); - for(int i = 2; i < numps; i++){ - rational r; - bool ok = s->get_parameter(i).is_rational(r); - if(!ok) - throw iz3_exception("Bad Farkas coefficient"); - { - ast con = conc(prem(proof,i-2)); - ast temp = make_real(r); // for debugging - opr o = is_not(con) ? op(arg(con,0)) : op(con); - if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) - r = -r; - if(conseq_neg) - r = -r; - } - rats[i-1] = r; - } -#if 0 - if(rats[1].is_neg()){ // work around bug -- if all coeffs negative, negate them - for(unsigned i = 1; i < rats.size(); i++){ - if(!rats[i].is_neg()) - throw iz3_exception("Bad Farkas coefficients"); - rats[i] = -rats[i]; - } - } -#endif - abs_rat(rats); - extract_lcd(rats); -} - -/** Set P to P + cQ, where P and Q are linear inequalities. Assumes P is 0 <= y or 0 < y. */ - -void iz3mgr::linear_comb(ast &P, const ast &c, const ast &Q, bool round_off){ - ast Qrhs; - bool qstrict = false; - if(is_not(Q)){ - ast nQ = arg(Q,0); - switch(op(nQ)){ - case Gt: - Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); - break; - case Lt: - Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); - break; - case Geq: - Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); - qstrict = true; - break; - case Leq: - Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); - qstrict = true; - break; - default: - throw iz3_exception("not an inequality"); - } - } - else { - switch(op(Q)){ - case Leq: - Qrhs = make(Sub,arg(Q,1),arg(Q,0)); - break; - case Geq: - Qrhs = make(Sub,arg(Q,0),arg(Q,1)); - break; - case Lt: - Qrhs = make(Sub,arg(Q,1),arg(Q,0)); - qstrict = true; - break; - case Gt: - Qrhs = make(Sub,arg(Q,0),arg(Q,1)); - qstrict = true; - break; - default: - throw iz3_exception("not an inequality"); - } - } - bool pstrict = op(P) == Lt; - if (round_off && get_type(Qrhs) != int_type()) - round_off = false; - if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ - Qrhs = make(Sub,Qrhs,make_int(rational(1))); - qstrict = false; - } - Qrhs = make(Times,c,Qrhs); - bool strict = pstrict || qstrict; - if(strict) - P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); - else - P = make(Leq,arg(P,0),make(Plus,arg(P,1),Qrhs)); -} - -iz3mgr::ast iz3mgr::sum_inequalities(const std::vector &coeffs, const std::vector &ineqs, bool round_off){ - ast zero = make_int("0"); - ast thing = make(Leq,zero,zero); - for(unsigned i = 0; i < ineqs.size(); i++){ - linear_comb(thing,coeffs[i],ineqs[i], round_off); - } - thing = simplify_ineq(thing); - return thing; -} - -void iz3mgr::mk_idiv(const ast& t, const rational &d, ast &whole, ast &frac){ - opr o = op(t); - if(o == Plus){ - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - mk_idiv(arg(t,i),d,whole,frac); - return; - } - else if(o == Times){ - rational coeff; - if(is_numeral(arg(t,0),coeff)){ - if(gcd(coeff,d) == d){ - whole = make(Plus,whole,make(Times,make_int(coeff/d),arg(t,1))); - return; - } - } - } - frac = make(Plus,frac,t); -} - -iz3mgr::ast iz3mgr::mk_idiv(const ast& q, const rational &d){ - ast t = z3_simplify(q); - if(d == rational(1)) - return t; - else { - ast whole = make_int("0"); - ast frac = whole; - mk_idiv(t,d,whole,frac); - return z3_simplify(make(Plus,whole,make(Idiv,z3_simplify(frac),make_int(d)))); - } -} - -iz3mgr::ast iz3mgr::mk_idiv(const ast& t, const ast &d){ - rational r; - if(is_numeral(d,r)) - return mk_idiv(t,r); - return make(Idiv,t,d); -} - - -// does variable occur in expression? -int iz3mgr::occurs_in1(stl_ext::hash_map &occurs_in_memo,ast var, ast e){ - std::pair foo(e,false); - std::pair::iterator,bool> bar = occurs_in_memo.insert(foo); - bool &res = bar.first->second; - if(bar.second){ - if(e == var) res = true; - int nargs = num_args(e); - for(int i = 0; i < nargs; i++) - res |= occurs_in1(occurs_in_memo,var,arg(e,i)); - } - return res; -} - -int iz3mgr::occurs_in(ast var, ast e){ - hash_map memo; - return occurs_in1(memo,var,e); -} - - -bool iz3mgr::solve_arith(const ast &v, const ast &x, const ast &y, ast &res){ - if(occurs_in(v,y)) - return false; - if(op(x) == Plus){ - int n = num_args(x); - for(int i = 0; i < n; i++){ - if(arg(x,i) == v){ - res = z3_simplify(make(Sub, y, make(Sub, x, v))); - return true; - } - } - } - return false; -} - -// find a controlling equality for a given variable v in a term -// a controlling equality is of the form v = t, which, being -// false would force the formula to have the specifid truth value -// returns t, or null if no such - -iz3mgr::ast iz3mgr::cont_eq(stl_ext::hash_set &cont_eq_memo, bool truth, ast v, ast e){ - if(is_not(e)) return cont_eq(cont_eq_memo, !truth,v,arg(e,0)); - if(cont_eq_memo.find(e) != cont_eq_memo.end()) - return ast(); - cont_eq_memo.insert(e); - if(!truth && op(e) == Equal){ - if(arg(e,0) == v && !occurs_in(v,arg(e,1))) return(arg(e,1)); - if(arg(e,1) == v && !occurs_in(v,arg(e,0))) return(arg(e,0)); - ast res; - if(solve_arith(v,arg(e,0),arg(e,1),res)) return res; - if(solve_arith(v,arg(e,1),arg(e,0),res)) return res; - } - if((!truth && op(e) == And) || (truth && op(e) == Or)){ - int nargs = num_args(e); - for(int i = 0; i < nargs; i++){ - ast res = cont_eq(cont_eq_memo, truth, v, arg(e,i)); - if(!res.null()) return res; - } - } - if(truth && op(e) == Implies){ - ast res = cont_eq(cont_eq_memo, !truth, v, arg(e,0)); - if(!res.null()) return res; - res = cont_eq(cont_eq_memo, truth, v, arg(e,1)); - if(!res.null()) return res; - } - return ast(); -} - -// substitute a term t for unbound occurrences of variable v in e - -iz3mgr::ast iz3mgr::subst(stl_ext::hash_map &subst_memo, ast var, ast t, ast e){ - if(e == var) return t; - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = subst_memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - int nargs = num_args(e); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = subst(subst_memo,var,t,arg(e,i)); - opr f = op(e); - if(f == Equal && args[0] == args[1]) res = mk_true(); - else res = clone(e,args); - } - return res; -} - -iz3mgr::ast iz3mgr::subst(ast var, ast t, ast e){ - hash_map memo; - return subst(memo,var,t,e); -} - -iz3mgr::ast iz3mgr::subst(stl_ext::hash_map &subst_memo,ast e){ - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = subst_memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - int nargs = num_args(e); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = subst(subst_memo,arg(e,i)); - opr f = op(e); - if(f == Equal && args[0] == args[1]) res = mk_true(); - else res = clone(e,args); - } - return res; -} - -// apply a quantifier to a formula, with some optimizations -// 1) bound variable does not occur -> no quantifier -// 2) bound variable must be equal to some term -> substitute - -iz3mgr::ast iz3mgr::apply_quant(opr quantifier, ast var, ast e){ - if((quantifier == Forall && op(e) == And) - || (quantifier == Exists && op(e) == Or)){ - int n = num_args(e); - std::vector args(n); - for(int i = 0; i < n; i++) - args[i] = apply_quant(quantifier,var,arg(e,i)); - return make(op(e),args); - } - if(!occurs_in(var,e))return e; - hash_set cont_eq_memo; - ast cterm = cont_eq(cont_eq_memo, quantifier == Forall, var, e); - if(!cterm.null()){ - return subst(var,cterm,e); - } - std::vector bvs; bvs.push_back(var); - return make_quant(quantifier,bvs,e); -} - -#if 0 -void iz3mgr::get_bound_substitutes(stl_ext::hash_map &memo, const ast &e, const ast &var, std::vector &substs){ - std::pair foo(e,false); - std::pair::iterator,bool> bar = memo.insert(foo); - if(bar.second){ - if(op(e) == - } - - } -#endif - -unsigned iz3mgr::num_free_variables(const ast &e){ - used_vars uv; - uv(to_expr(e.raw())); - return uv.get_num_vars(); -} - -iz3mgr::ast iz3mgr::close_universally (ast e){ - used_vars uv; - uv(to_expr(e.raw())); - std::vector bvs; - stl_ext::hash_map subst_memo; - for (unsigned i = 0; i < uv.get_max_found_var_idx_plus_1(); i++){ - if (uv.get(i)) { - std::ostringstream os; - os << "%%" << i; - ast c = make_var(os.str(),uv.get(i)); - ast v = cook(m().mk_var(i,uv.get(i))); - subst_memo[v] = c; - bvs.push_back(c); - } - } - e = subst(subst_memo,e); - for (unsigned i = 0; i < bvs.size(); i++) - e = apply_quant(Forall,bvs[i],e); - return e; -} diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h deleted file mode 100644 index 49552f92f..000000000 --- a/src/interp/iz3mgr.h +++ /dev/null @@ -1,738 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3mgr.h - - Abstract: - - A wrapper around an ast manager, providing convenience methods. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3MGR_H -#define IZ3MGR_H - - -#include -#include -#include - -#include "interp/iz3hash.h" -#include "interp/iz3exception.h" - -#include "ast/well_sorted.h" -#include "ast/arith_decl_plugin.h" -#include "ast/bv_decl_plugin.h" -#include "ast/datatype_decl_plugin.h" -#include "ast/array_decl_plugin.h" -#include "ast/ast_translation.h" -#include "ast/ast_pp.h" -#include "ast/ast_ll_pp.h" -#include "ast/ast_smt_pp.h" -#include "ast/ast_smt2_pp.h" -#include "ast/rewriter/th_rewriter.h" -#include "ast/rewriter/var_subst.h" -#include "ast/expr_substitution.h" -#include "ast/pp.h" -#include "util/scoped_ctrl_c.h" -#include "util/cancel_eh.h" -#include "util/scoped_timer.h" - -/* A wrapper around an ast manager, providing convenience methods. */ - -/** Shorthands for some built-in operators. */ - - - -// rename this to keep it accessible, as we use ast for something else -typedef ast raw_ast; - -/** Wrapper around an ast pointer */ -class ast_i { -protected: - raw_ast *_ast; -public: - raw_ast * const &raw() const {return _ast;} - ast_i(raw_ast *a){_ast = a;} - - ast_i(){_ast = nullptr;} - bool eq(const ast_i &other) const { - return _ast == other._ast; - } - bool lt(const ast_i &other) const { - return _ast->get_id() < other._ast->get_id(); - } - friend bool operator==(const ast_i &x, const ast_i&y){ - return x.eq(y); - } - friend bool operator!=(const ast_i &x, const ast_i&y){ - return !x.eq(y); - } - friend bool operator<(const ast_i &x, const ast_i&y){ - return x.lt(y); - } - size_t hash() const {return _ast->get_id();} - bool null() const {return !_ast;} -}; - -/** Reference counting verison of above */ -class ast_r : public ast_i { - ast_manager *_m; -public: - ast_r(ast_manager *m, raw_ast *a) : ast_i(a) { - _m = m; - m->inc_ref(a); - } - - ast_r() {_m = nullptr;} - - ast_r(const ast_r &other) : ast_i(other) { - _m = other._m; - if (_m) _m->inc_ref(_ast); - } - - ast_r &operator=(const ast_r &other) { - if(_ast) - _m->dec_ref(_ast); - _ast = other._ast; - _m = other._m; - if (_m) _m->inc_ref(_ast); - return *this; - } - - ~ast_r() { - if(_ast) - _m->dec_ref(_ast); - } - - ast_manager *mgr() const {return _m;} - -}; - -// to make ast_r hashable -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const ast_r &s) const { - return s.raw()->get_id(); - } - }; -} - - -// to make ast_r usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const ast_r &s, const ast_r &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - - -/** Wrapper around an AST manager, providing convenience methods. */ - -class iz3mgr { - - public: - typedef ast_r ast; - // typedef decl_kind opr; - typedef func_decl *symb; - typedef sort *type; - typedef ast_r z3pf; - typedef decl_kind pfrule; - - enum opr { - True, - False, - And, - Or, - Not, - Iff, - Ite, - Equal, - Implies, - Distinct, - Xor, - Oeq, - Interp, - Leq, - Geq, - Lt, - Gt, - Plus, - Sub, - Uminus, - Times, - Div, - Idiv, - Rem, - Mod, - Power, - ToReal, - ToInt, - IsInt, - Select, - Store, - ConstArray, - ArrayDefault, - ArrayMap, - SetUnion, - SetIntersect, - SetDifference, - SetComplement, - SetSubSet, - AsArray, - Numeral, - Forall, - Exists, - Variable, - Uninterpreted, - Other - }; - - opr op(const ast &t); - - unsigned ast_id(const ast &x) - { - return to_expr(x.raw())->get_id(); - } - - /** Overloads for constructing ast. */ - - ast make_var(const std::string &name, type ty); - ast make(opr op, const std::vector &args); - ast make(opr op); - ast make(opr op, const ast &arg0); - ast make(opr op, const ast &arg0, const ast &arg1); - ast make(opr op, const ast &arg0, const ast &arg1, const ast &arg2); - ast make(symb sym, const std::vector &args); - ast make(symb sym); - ast make(symb sym, const ast &arg0); - ast make(symb sym, const ast &arg0, const ast &arg1); - ast make(symb sym, const ast &arg0, const ast &arg1, const ast &arg2); - ast make_quant(opr op, const std::vector &bvs, ast &body); - ast clone(const ast &t, const std::vector &args); - - ast_manager &m() {return m_manager;} - - ast cook(raw_ast *a) {return ast(&m_manager,a);} - - std::vector cook(ptr_vector v) { - std::vector _v(v.size()); - for(unsigned i = 0; i < v.size(); i++) - _v[i] = cook(v[i]); - return _v; - } - - raw_ast *uncook(const ast &a) { - m_manager.inc_ref(a.raw()); - return a.raw(); - } - - /** Methods for destructing ast. */ - - - int num_args(const ast& t){ - ast_kind dk = t.raw()->get_kind(); - switch(dk){ - case AST_APP: - return to_app(t.raw())->get_num_args(); - case AST_QUANTIFIER: - return 1; - case AST_VAR: - return 0; - default:; - } - assert(0); - return 0; - } - - ast arg(const ast &t, int i){ - ast_kind dk = t.raw()->get_kind(); - switch(dk){ - case AST_APP: - return cook(to_app(t.raw())->get_arg(i)); - case AST_QUANTIFIER: - return cook(to_quantifier(t.raw())->get_expr()); - default:; - } - assert(0); - return ast(); - } - - void get_args(const ast &t, std::vector &res){ - res.resize(num_args(t)); - for(unsigned i = 0; i < res.size(); i++) - res[i] = arg(t,i); - } - - std::vector args(const ast &t){ - std::vector res; - get_args(t,res); - return res; - } - - symb sym(const ast& t){ - raw_ast *_ast = t.raw(); - return is_app(_ast) ? to_app(_ast)->get_decl() : nullptr; - } - - std::string string_of_symbol(symb s){ - symbol _s = s->get_name(); - if (_s.is_numerical()) { - std::ostringstream buffer; - buffer << _s.get_num(); - return buffer.str(); - } - else { - return _s.bare_str(); - } - } - - type get_type(const ast& t){ - return m().get_sort(to_expr(t.raw())); - } - - std::string string_of_numeral(const ast& t){ - rational r; - expr* e = to_expr(t.raw()); - assert(e); - if (m_arith_util.is_numeral(e, r)) - return r.to_string(); - assert(0); - return "NaN"; - } - - bool is_numeral(const ast& t, rational &r){ - expr* e = to_expr(t.raw()); - assert(e); - return m_arith_util.is_numeral(e, r); - } - - rational get_coeff(const ast& t){ - rational res; - if(op(t) == Times && is_numeral(arg(t,0),res)) - return res; - return rational(1); - } - - ast get_linear_var(const ast& t){ - rational res; - if(op(t) == Times && is_numeral(arg(t,0),res)) - return arg(t,1); - return t; - } - - int get_quantifier_num_bound(const ast &t) { - return to_quantifier(t.raw())->get_num_decls(); - } - - std::string get_quantifier_bound_name(const ast &t, unsigned i) { - return to_quantifier(t.raw())->get_decl_names()[i].bare_str(); - } - - type get_quantifier_bound_type(const ast &t, unsigned i) { - return to_quantifier(t.raw())->get_decl_sort(i); - } - - ast get_quantifier_body(const ast &t) { - return cook(to_quantifier(t.raw())->get_expr()); - } - - unsigned get_variable_index_value(const ast &t) { - var* va = to_var(t.raw()); - return va->get_idx(); - } - - bool is_bool_type(type t){ - family_id fid = to_sort(t)->get_family_id(); - decl_kind k = to_sort(t)->get_decl_kind(); - return fid == m().get_basic_family_id() && k == BOOL_SORT; - } - - bool is_array_type(type t){ - family_id fid = to_sort(t)->get_family_id(); - decl_kind k = to_sort(t)->get_decl_kind(); - return fid == m_array_fid && k == ARRAY_SORT; - } - - type get_range_type(symb s){ - return to_func_decl(s)->get_range(); - } - - int get_num_parameters(const symb &s){ - return to_func_decl(s)->get_num_parameters(); - } - - ast get_ast_parameter(const symb &s, int idx){ - return cook(to_func_decl(s)->get_parameters()[idx].get_ast()); - } - - enum lemma_theory {ArithTheory,ArrayTheory,UnknownTheory}; - - lemma_theory get_theory_lemma_theory(const ast &proof){ - symb s = sym(proof); - ::symbol p0; - bool ok = s->get_parameter(0).is_symbol(p0); - if(!ok) return UnknownTheory; - std::string foo(p0.bare_str()); - if(foo == "arith") - return ArithTheory; - if(foo == "array") - return ArrayTheory; - return UnknownTheory; - } - - enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,EqPropagateKind,GomoryCutKind,ArithMysteryKind,UnknownKind}; - - lemma_kind get_theory_lemma_kind(const ast &proof){ - symb s = sym(proof); - if(s->get_num_parameters() < 2) { - return ArithMysteryKind; // Bad -- Z3 hasn't told us - } - ::symbol p0; - bool ok = s->get_parameter(1).is_symbol(p0); - if(!ok) return UnknownKind; - std::string foo(p0.bare_str()); - if(foo == "farkas") - return FarkasKind; - if(foo == "triangle-eq") - return is_not(arg(conc(proof),0)) ? Eq2LeqKind : Leq2EqKind; - if(foo == "gcd-test") - return GCDTestKind; - if(foo == "assign-bounds") - return AssignBoundsKind; - if(foo == "eq-propagate") - return EqPropagateKind; - if(foo == "gomory-cut") - return GomoryCutKind; - return UnknownKind; - } - - void get_farkas_coeffs(const ast &proof, std::vector& coeffs); - - void get_farkas_coeffs(const ast &proof, std::vector& rats); - - void get_broken_gcd_test_coeffs(const ast &proof, std::vector& rats); - - void get_assign_bounds_coeffs(const ast &proof, std::vector& rats); - - void get_assign_bounds_coeffs(const ast &proof, std::vector& rats); - - void get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats); - - void get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats); - - void get_gomory_cut_coeffs(const ast &proof, std::vector& rats); - - void get_gomory_cut_coeffs(const ast &proof, std::vector& rats); - - bool is_farkas_coefficient_negative(const ast &proof, int n); - - bool is_true(const ast& t){ - return op(t) == True; - } - - bool is_false(const ast& t){ - return op(t) == False; - } - - bool is_iff(const ast& t){ - return op(t) == Iff; - } - - bool is_or(const ast& t){ - return op(t) == Or; - } - - bool is_not(const ast& t){ - return op(t) == Not; - } - - /** Simplify an expression using z3 simplifier */ - - ast z3_simplify(const ast& e); - - /** Simplify, sorting sums */ - ast z3_really_simplify(const ast &e); - - - // Some constructors that simplify things - - ast mk_not(const ast& x){ - opr o = op(x); - if(o == True) return make(False); - if(o == False) return make(True); - if(o == Not) return arg(x,0); - return make(Not,x); - } - - ast mk_and(const ast& x, const ast& y){ - opr ox = op(x); - opr oy = op(y); - if(ox == True) return y; - if(oy == True) return x; - if(ox == False) return x; - if(oy == False) return y; - if(x == y) return x; - return make(And,x,y); - } - - ast mk_or(const ast& x, const ast& y){ - opr ox = op(x); - opr oy = op(y); - if(ox == False) return y; - if(oy == False) return x; - if(ox == True) return x; - if(oy == True) return y; - if(x == y) return x; - return make(Or,x,y); - } - - ast mk_implies(const ast& x, const ast& y){ - opr ox = op(x); - opr oy = op(y); - if(ox == True) return y; - if(oy == False) return mk_not(x); - if(ox == False) return mk_true(); - if(oy == True) return y; - if(x == y) return mk_true(); - return make(Implies,x,y); - } - - ast mk_or(const std::vector &x){ - ast res = mk_false(); - for(unsigned i = 0; i < x.size(); i++) - res = mk_or(res,x[i]); - return res; - } - - ast mk_and(const std::vector &x){ - std::vector conjs; - for(unsigned i = 0; i < x.size(); i++){ - const ast &e = x[i]; - opr o = op(e); - if(o == False) - return mk_false(); - if(o != True) - conjs.push_back(e); - } - if(conjs.size() == 0) - return mk_true(); - if(conjs.size() == 1) - return conjs[0]; - return make(And,conjs); - } - - ast mk_equal(const ast& x, const ast& y){ - if(x == y) return make(True); - opr ox = op(x); - opr oy = op(y); - if(ox == True) return y; - if(oy == True) return x; - if(ox == False) return mk_not(y); - if(oy == False) return mk_not(x); - if(ox == False && oy == True) return make(False); - if(oy == False && ox == True) return make(False); - return make(Equal,x,y); - } - - ast z3_ite(const ast& x, const ast& y, const ast& z){ - opr ox = op(x); - opr oy = op(y); - opr oz = op(z); - if(ox == True) return y; - if(ox == False) return z; - if(y == z) return y; - if(oy == True && oz == False) return x; - if(oz == True && oy == False) return mk_not(x); - return make(Ite,x,y,z); - } - - ast make_int(const std::string &s) { - sort *r = m().mk_sort(m_arith_fid, INT_SORT); - return cook(m_arith_util.mk_numeral(rational(s.c_str()),r)); - } - - ast make_int(const rational &s) { - sort *r = m().mk_sort(m_arith_fid, INT_SORT); - return cook(m_arith_util.mk_numeral(s,r)); - } - - ast make_real(const std::string &s) { - sort *r = m().mk_sort(m_arith_fid, REAL_SORT); - return cook(m_arith_util.mk_numeral(rational(s.c_str()),r)); - } - - ast make_real(const rational &s) { - sort *r = m().mk_sort(m_arith_fid, REAL_SORT); - return cook(m_arith_util.mk_numeral(s,r)); - } - - ast mk_false() { return make(False); } - - ast mk_true() { return make(True); } - - ast mk_fresh_constant(char const * prefix, type s){ - return cook(m().mk_fresh_const(prefix, s)); - } - - type bool_type() { - ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); - return s; - } - - type int_type() { - ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); - return s; - } - - type real_type() { - ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); - return s; - } - - type array_type(type d, type r) { - parameter params[2] = { parameter(d), parameter(to_sort(r)) }; - ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); - return s; - } - - symb function(const std::string &str_name, unsigned arity, type *domain, type range) { - ::symbol name = ::symbol(str_name.c_str()); - std::vector< ::sort *> sv(arity); - for(unsigned i = 0; i < arity; i++) - sv[i] = domain[i]; - ::func_decl* d = m().mk_func_decl(name,arity,&sv[0],range); - return d; - } - - void linear_comb(ast &P, const ast &c, const ast &Q, bool round_off = false); - - ast sum_inequalities(const std::vector &coeffs, const std::vector &ineqs, bool round_off = false); - - ast simplify_ineq(const ast &ineq){ - ast res = make(op(ineq),arg(ineq,0),z3_simplify(arg(ineq,1))); - return res; - } - - void mk_idiv(const ast& t, const rational &d, ast &whole, ast &frac); - - ast mk_idiv(const ast& t, const rational &d); - - ast mk_idiv(const ast& t, const ast &d); - - /** methods for destructing proof terms */ - - pfrule pr(const z3pf &t); - - int num_prems(const z3pf &t){return to_app(t.raw())->get_num_args()-1;} - - z3pf prem(const z3pf &t, int n){return arg(t,n);} - - z3pf conc(const z3pf &t){return arg(t,num_prems(t));} - - - /* quantifier handling */ - - // substitute a term t for unbound occurrences of variable v in e - - ast subst(ast var, ast t, ast e); - - // apply a substitution defined by a map - ast subst(stl_ext::hash_map &map, ast e); - - // apply a quantifier to a formula, with some optimizations - // 1) bound variable does not occur -> no quantifier - // 2) bound variable must be equal to some term -> substitute - - ast apply_quant(opr quantifier, ast var, ast e); - - // Universally quantify all the free variables in a formula. - // Makes up names for the quntifiers. - - ast close_universally (ast e); - - unsigned num_free_variables(const ast &e); - - /** For debugging */ - void show(ast); - - void show_symb(symb s); - - /** Constructor */ - - void print_lit(ast lit); - - void print_expr(std::ostream &s, const ast &e); - - void print_clause(std::ostream &s, std::vector &cls); - - void print_sat_problem(std::ostream &out, const ast &t); - - void show_clause(std::vector &cls); - - static void pretty_print(std::ostream &f, const std::string &s); - - iz3mgr(ast_manager &_m_manager) - : m_manager(_m_manager), - m_arith_util(_m_manager) - { - m_basic_fid = m().get_basic_family_id(); - m_arith_fid = m().mk_family_id("arith"); - m_bv_fid = m().mk_family_id("bv"); - m_array_fid = m().mk_family_id("array"); - m_dt_fid = m().mk_family_id("datatype"); - m_datalog_fid = m().mk_family_id("datalog_relation"); - } - - iz3mgr(const iz3mgr& other) - : m_manager(other.m_manager), - m_arith_util(other.m_manager) - { - m_basic_fid = m().get_basic_family_id(); - m_arith_fid = m().mk_family_id("arith"); - m_bv_fid = m().mk_family_id("bv"); - m_array_fid = m().mk_family_id("array"); - m_dt_fid = m().mk_family_id("datatype"); - m_datalog_fid = m().mk_family_id("datalog_relation"); - } - - protected: - ast_manager &m_manager; - int occurs_in(ast var, ast e); - - private: - ast mki(family_id fid, decl_kind sk, int n, raw_ast **args); - ast make(opr op, int n, raw_ast **args); - ast make(symb sym, int n, raw_ast **args); - int occurs_in1(stl_ext::hash_map &occurs_in_memo, ast var, ast e); - bool solve_arith(const ast &v, const ast &x, const ast &y, ast &res); - ast cont_eq(stl_ext::hash_set &cont_eq_memo, bool truth, ast v, ast e); - ast subst(stl_ext::hash_map &subst_memo, ast var, ast t, ast e); - - - family_id m_basic_fid; - family_id m_array_fid; - family_id m_arith_fid; - family_id m_bv_fid; - family_id m_dt_fid; - family_id m_datalog_fid; - arith_util m_arith_util; -}; - -#endif - diff --git a/src/interp/iz3pp.cpp b/src/interp/iz3pp.cpp deleted file mode 100644 index 787fa4ec7..000000000 --- a/src/interp/iz3pp.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/*++ - Copyright (c) 2013 Microsoft Corporation - - Module Name: - - iz3pp.cpp - - Abstract: - - Pretty-print interpolation problems - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -/* Copyright 2011 Microsoft Research. */ -#include -#include -#include -#include -#include -#include -#include - -#include "interp/iz3mgr.h" -#include "interp/iz3pp.h" -#include "ast/func_decl_dependencies.h" -#include "ast/for_each_expr.h" -#include "ast/ast_smt_pp.h" -#include "ast/ast_smt2_pp.h" -#include "ast/expr_functors.h" -#include "ast/expr_abstract.h" - - -using namespace stl_ext; - -// We promise not to use this for hash_map with range destructor -namespace stl_ext { - template <> - class hash { - public: - size_t operator()(const expr *p) const { - return (size_t) p; - } - }; -} - - -// TBD: algebraic data-types declarations will not be printed. -class free_func_visitor { - ast_manager& m; - func_decl_set m_funcs; - obj_hashtable m_sorts; -public: - free_func_visitor(ast_manager& m): m(m) {} - void operator()(var * n) { } - void operator()(app * n) { - m_funcs.insert(n->get_decl()); - class sort* s = m.get_sort(n); - if (s->get_family_id() == null_family_id) { - m_sorts.insert(s); - } - } - void operator()(quantifier * n) { } - func_decl_set& funcs() { return m_funcs; } - obj_hashtable& sorts() { return m_sorts; } -}; - -class iz3pp_helper : public iz3mgr { -public: - - void print_tree(const ast &tree, hash_map &cnames, std::ostream &out){ - hash_map::iterator foo = cnames.find(to_expr(tree.raw())); - if(foo != cnames.end()){ - symbol nm = foo->second; - if (is_smt2_quoted_symbol(nm)) { - out << mk_smt2_quoted_symbol(nm); - } - else { - out << nm; - } - } - else if(op(tree) == And){ - out << "(and"; - int nargs = num_args(tree); - for(int i = 0; i < nargs; i++){ - out << " "; - print_tree(arg(tree,i), cnames, out); - } - out << ")"; - } - else if(op(tree) == Interp){ - out << "(interp "; - print_tree(arg(tree,0), cnames, out); - out << ")"; - } - else throw iz3pp_bad_tree(); - } - - - iz3pp_helper(ast_manager &_m_manager) - : iz3mgr(_m_manager) {} -}; - -void iz3pp(ast_manager &m, - const ptr_vector &cnsts_vec, - expr *tree, - std::ostream& out) { - - unsigned sz = cnsts_vec.size(); - expr* const* cnsts = &cnsts_vec[0]; - - out << "(set-option :produce-interpolants true)\n"; - - free_func_visitor visitor(m); - expr_mark visited; - bool print_low_level = true; // m_params.print_low_level_smt2(); - -#define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env); - - smt2_pp_environment_dbg env(m); - - for (unsigned i = 0; i < sz; ++i) { - expr* e = cnsts[i]; - for_each_expr(visitor, visited, e); - } - - // name all the constraints - hash_map cnames; - int ctr = 1; - for(unsigned i = 0; i < sz; i++){ - symbol nm; - std::ostringstream s; - s << "f!" << (ctr++); - cnames[cnsts[i]] = symbol(s.str().c_str()); - } - - func_decl_set &funcs = visitor.funcs(); - func_decl_set::iterator it = funcs.begin(), end = funcs.end(); - - obj_hashtable& sorts = visitor.sorts(); - obj_hashtable::iterator sit = sorts.begin(), send = sorts.end(); - - - - for (; sit != send; ++sit) { - PP(*sit); - } - - for (; it != end; ++it) { - func_decl* f = *it; - if(f->get_family_id() == null_family_id){ - PP(f); - out << "\n"; - } - } - - for (unsigned i = 0; i < sz; ++i) { - out << "(assert "; - expr* r = cnsts[i]; - symbol nm = cnames[r]; - out << "(! "; - PP(r); - out << " :named "; - if (is_smt2_quoted_symbol(nm)) { - out << mk_smt2_quoted_symbol(nm); - } - else { - out << nm; - } - out << ")"; - out << ")\n"; - } - out << "(check-sat)\n"; - out << "(get-interpolant "; - iz3pp_helper pp(m); - pp.print_tree(pp.cook(tree),cnames,out); - out << ")\n"; -} - - diff --git a/src/interp/iz3pp.h b/src/interp/iz3pp.h deleted file mode 100644 index 7b3405f9b..000000000 --- a/src/interp/iz3pp.h +++ /dev/null @@ -1,36 +0,0 @@ -/*++ - Copyright (c) 2013 Microsoft Corporation - - Module Name: - - iz3pp.cpp - - Abstract: - - Pretty-print interpolation problems - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3_PP_H -#define IZ3_PP_H - -#include "interp/iz3mgr.h" - -/** Exception thrown in case of mal-formed tree interpoloation - specification */ - -struct iz3pp_bad_tree: public iz3_exception { - iz3pp_bad_tree(): iz3_exception("iz3pp_bad_tree") {} -}; - -void iz3pp(ast_manager &m, - const ptr_vector &cnsts_vec, - expr *tree, - std::ostream& out); -#endif diff --git a/src/interp/iz3profiling.cpp b/src/interp/iz3profiling.cpp deleted file mode 100644 index ba4fd0206..000000000 --- a/src/interp/iz3profiling.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3profiling.h - - Abstract: - - Some routines for measuring performance. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3profiling.h" - -#include -#include -#include -#include -#include -#include "util/stopwatch.h" - - -// FIXME fill in these stubs - -#define clock_t double - -static double current_time() -{ - static stopwatch sw; - static bool started = false; - if(!started){ - sw.start(); - started = true; - } - return sw.get_current_seconds(); -} - -static void output_time(std::ostream &os, clock_t time){ - os << time; -} - - -namespace profiling { - - void show_time(){ - output_time(std::cout,current_time()); - std::cout << "\n"; - } - - typedef std::map nmap; - - struct node { - std::string name; - clock_t time; - clock_t start_time; - nmap sub; - struct node *parent; - - node(); - } top; - - node::node(){ - time = 0; - parent = nullptr; - } - - struct node *current; - - struct init { - init(){ - top.name = "TOTAL"; - current = ⊤ - } - } initializer; - - struct time_entry { - clock_t t; - time_entry(){t = 0;}; - void add(clock_t incr){t += incr;} - }; - - struct ltstr - { - bool operator()(const char* s1, const char* s2) const - { - return strcmp(s1, s2) < 0; - } - }; - - typedef std::map tmap; - - static std::ostream *pfs; - - void print_node(node &top, int indent, tmap &totals){ - for(int i = 0; i < indent; i++) (*pfs) << " "; - (*pfs) << top.name; - int dots = 70 - 2 * indent - top.name.size(); - for(int i = 0; i second,indent+1,totals); - } - - void print(std::ostream &os) { - pfs = &os; - top.time = 0; - for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++) - top.time += it->second.time; - tmap totals; - print_node(top,0,totals); - (*pfs) << "TOTALS:" << std::endl; - for(tmap::iterator it = totals.begin(); it != totals.end(); it++){ - (*pfs) << (it->first) << " "; - output_time(*pfs, it->second.t); - (*pfs) << std::endl; - } - } - - void timer_start(const char *name){ - node &child = current->sub[name]; - if(child.name.empty()){ // a new node - child.parent = current; - child.name = name; - } - child.start_time = current_time(); - current = &child; - } - - void timer_stop(const char *name){ - if(current->name != name || !current->parent){ - std::cerr << "imbalanced timer_start and timer_stop"; - exit(1); - } - current->time += (current_time() - current->start_time); - current = current->parent; - } -} diff --git a/src/interp/iz3profiling.h b/src/interp/iz3profiling.h deleted file mode 100755 index 6b9b07f25..000000000 --- a/src/interp/iz3profiling.h +++ /dev/null @@ -1,37 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3profiling.h - - Abstract: - - Some routines for measuring performance. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3PROFILING_H -#define IZ3PROFILING_H - -#include - -namespace profiling { - /** Start a timer with given name */ - void timer_start(const char *); - /** Stop a timer with given name */ - void timer_stop(const char *); - /** Print out timings */ - void print(std::ostream &s); - /** Show the current time. */ - void show_time(); -} - -#endif - diff --git a/src/interp/iz3proof.cpp b/src/interp/iz3proof.cpp deleted file mode 100755 index bc046ceff..000000000 --- a/src/interp/iz3proof.cpp +++ /dev/null @@ -1,628 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3proof.cpp - - Abstract: - - This class defines a simple interpolating proof system. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3proof.h" -#include "interp/iz3profiling.h" - -#include -#include -#include -#include - -// #define FACTOR_INTERPS -// #define CHECK_PROOFS - - -void iz3proof::resolve(ast pivot, std::vector &cls1, const std::vector &cls2){ -#ifdef CHECK_PROOFS - std::vector orig_cls1 = cls1; -#endif - ast neg_pivot = pv->mk_not(pivot); - bool found_pivot1 = false, found_pivot2 = false; - for(unsigned i = 0; i < cls1.size(); i++){ - if(cls1[i] == neg_pivot){ - cls1[i] = cls1.back(); - cls1.pop_back(); - found_pivot1 = true; - break; - } - } - { - std::set memo; - memo.insert(cls1.begin(),cls1.end()); - for(unsigned j = 0; j < cls2.size(); j++){ - if(cls2[j] == pivot) - found_pivot2 = true; - else - if(memo.find(cls2[j]) == memo.end()) - cls1.push_back(cls2[j]); - } - } - if(found_pivot1 && found_pivot2) - return; - -#ifdef CHECK_PROOFS - std::cerr << "resolution anomaly: " << nodes.size()-1 << "\n"; -#if 0 - std::cerr << "pivot: "; {pv->print_lit(pivot); std::cout << "\n";} - std::cerr << "left clause:\n"; - for(unsigned i = 0; i < orig_cls1.size(); i++) - {pv->print_lit(orig_cls1[i]); std::cout << "\n";} - std::cerr << "right clause:\n"; - for(unsigned i = 0; i < cls2.size(); i++) - {pv->print_lit(cls2[i]); std::cout << "\n";} - throw proof_error(); -#endif -#endif -} - -iz3proof::node iz3proof::make_resolution(ast pivot, node premise1, node premise2) -{ - if(nodes[premise1].rl == Hypothesis) return premise2; // resolve with hyp is noop - if(nodes[premise2].rl == Hypothesis) return premise1; - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Resolution; - n.aux = pivot; - n.premises.resize(2); - n.premises[0] = (premise1); - n.premises[1] = (premise2); -#ifdef CHECK_PROOFS - n.conclusion = nodes[premise1].conclusion; - resolve(pivot,n.conclusion,nodes[premise2].conclusion); - n.frame = 1; -#else - n.frame = 0; // compute conclusion lazily -#endif - return res; -} - -iz3proof::node iz3proof::resolve_lemmas(ast pivot, node premise1, node premise2) -{ - std::vector lits(nodes[premise1].conclusion), itp; // no interpolant - resolve(pivot,lits,nodes[premise2].conclusion); - return make_lemma(lits,itp); -} - - -iz3proof::node iz3proof::make_assumption(int frame, const std::vector &assumption){ -#if 0 - std::cout << "assumption: \n"; - for(unsigned i = 0; i < assumption.size(); i++) - pv->show(assumption[i]); - std::cout << "\n"; -#endif - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Assumption; - n.conclusion.resize(1); - n.conclusion = assumption; - n.frame = frame; - return res; -} - -iz3proof::node iz3proof::make_hypothesis(ast hypothesis){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Hypothesis; - n.conclusion.resize(2); - n.conclusion[0] = hypothesis; - n.conclusion[1] = pv->mk_not(hypothesis); - return res; -} - -iz3proof::node iz3proof::make_theory(const std::vector &conclusion, std::vector premises){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Theory; - n.conclusion = conclusion; - n.premises = premises; - return res; -} - -iz3proof::node iz3proof::make_axiom(const std::vector &conclusion){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Axiom; - n.conclusion = conclusion; - return res; -} - -iz3proof::node iz3proof::make_contra(node prem, const std::vector &conclusion){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Contra; - n.conclusion = conclusion; -#ifdef CHECK_PROOFS - //if(!(conclusion == nodes[prem].conclusion)){ - //std::cerr << "internal error: proof error\n"; - //assert(0 && "proof error"); - //} -#endif - n.premises.push_back(prem); - return res; -} - - -iz3proof::node iz3proof::make_lemma(const std::vector &conclusion, const std::vector &interpolation){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Lemma; - n.conclusion = conclusion; - n.frame = interps.size(); - interps.push_back(interpolation); - return res; -} - -/** Make a Reflexivity node. This rule produces |- x = x */ - -iz3proof::node iz3proof::make_reflexivity(ast con){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Reflexivity; - n.conclusion.push_back(con); - return res; -} - -/** Make a Symmetry node. This takes a derivation of |- x = y and - produces | y = x */ - -iz3proof::node iz3proof::make_symmetry(ast con, node prem){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Reflexivity; - n.conclusion.push_back(con); - n.premises.push_back(prem); - return res; -} - -/** Make a transitivity node. This takes derivations of |- x = y - and |- y = z produces | x = z */ - -iz3proof::node iz3proof::make_transitivity(ast con, node prem1, node prem2){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Transitivity; - n.conclusion.push_back(con); - n.premises.push_back(prem1); - n.premises.push_back(prem2); - return res; -} - - -/** Make a congruence node. This takes derivations of |- x_i = y_i - and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ - -iz3proof::node iz3proof::make_congruence(ast con, const std::vector &prems){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Congruence; - n.conclusion.push_back(con); - n.premises = prems; - return res; -} - - -/** Make an equality contradicition node. This takes |- x = y - and |- !(x = y) and produces false. */ - -iz3proof::node iz3proof::make_eqcontra(node prem1, node prem2){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = EqContra; - n.premises.push_back(prem1); - n.premises.push_back(prem2); - return res; -} - -iz3proof::node iz3proof::copy_rec(stl_ext::hash_map &memo, iz3proof &src, node n){ - stl_ext::hash_map::iterator it = memo.find(n); - if(it != memo.end()) return (*it).second; - node_struct &ns = src.nodes[n]; - std::vector prems(ns.premises.size()); - for(unsigned i = 0; i < prems.size(); i++) - prems[i] = copy_rec(memo,src,ns.premises[i]); - nodes.push_back(ns); - nodes.back().premises.swap(prems); - if(ns.rl == Lemma){ - nodes.back().frame = interps.size(); - interps.push_back(src.interps[ns.frame]); - } - int res = nodes.size()-1; - memo[n] = res; - return res; -} - -iz3proof::node iz3proof::copy(iz3proof &src, node n){ - stl_ext::hash_map memo; - return copy_rec(memo, src, n); -} - -bool iz3proof::pred_in_A(ast id){ - return weak - ? pv->ranges_intersect(pv->ast_range(id),rng) : - pv->range_contained(pv->ast_range(id),rng); -} - -bool iz3proof::term_in_B(ast id){ - prover::range r = pv->ast_scope(id); - if(weak) { - if(pv->range_min(r) == SHRT_MIN) - return !pv->range_contained(r,rng); - else - return !pv->ranges_intersect(r,rng); - } - else - return !pv->range_contained(r,rng); -} - -bool iz3proof::frame_in_A(int frame){ - return pv->in_range(frame,rng); -} - -bool iz3proof::lit_in_B(ast lit){ - return - b_lits.find(lit) != b_lits.end() - || b_lits.find(pv->mk_not(lit)) != b_lits.end(); -} - -iz3proof::ast iz3proof::my_or(ast x, ast y){ - return pv->mk_not(pv->mk_and(pv->mk_not(x),pv->mk_not(y))); -} - -iz3proof::ast iz3proof::get_A_lits(std::vector &cls){ - ast foo = pv->mk_false(); - for(unsigned i = 0; i < cls.size(); i++){ - ast lit = cls[i]; - if(b_lits.find(pv->mk_not(lit)) == b_lits.end()){ - if(pv->range_max(pv->ast_scope(lit)) == pv->range_min(pv->ast_scope(lit))){ - std::cout << "bad lit: " << pv->range_max(rng) << " : " << pv->range_max(pv->ast_scope(lit)) << " : " << (pv->ast_id(lit)) << " : "; - pv->show(lit); - } - foo = my_or(foo,lit); - } - } - return foo; -} - -iz3proof::ast iz3proof::get_B_lits(std::vector &cls){ - ast foo = pv->mk_false(); - for(unsigned i = 0; i < cls.size(); i++){ - ast lit = cls[i]; - if(b_lits.find(pv->mk_not(lit)) != b_lits.end()) - foo = my_or(foo,lit); - } - return foo; -} - -void iz3proof::set_of_B_lits(std::vector &cls, std::set &res){ - for(unsigned i = 0; i < cls.size(); i++){ - ast lit = cls[i]; - if(b_lits.find(pv->mk_not(lit)) != b_lits.end()) - res.insert(lit); - } -} - -void iz3proof::set_of_A_lits(std::vector &cls, std::set &res){ - for(unsigned i = 0; i < cls.size(); i++){ - ast lit = cls[i]; - if(b_lits.find(pv->mk_not(lit)) == b_lits.end()) - res.insert(lit); - } -} - -void iz3proof::find_B_lits(){ - b_lits.clear(); - for(unsigned i = 0; i < nodes.size(); i++){ - node_struct &n = nodes[i]; - std::vector &cls = n.conclusion; - if(n.rl == Assumption){ - if(weak) goto lemma; - if(!frame_in_A(n.frame)) - for(unsigned j = 0; j < cls.size(); j++) - b_lits.insert(cls[j]); - } - else if(n.rl == Lemma) { - lemma: - for(unsigned j = 0; j < cls.size(); j++) - if(term_in_B(cls[j])) - b_lits.insert(cls[j]); - } - } -} - -iz3proof::ast iz3proof::disj_of_set(std::set &s){ - ast res = pv->mk_false(); - for(std::set::iterator it = s.begin(), en = s.end(); it != en; ++it) - res = my_or(*it,res); - return res; -} - -void iz3proof::mk_and_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs){ -#ifdef FACTOR_INTERPS - std::set &d1 = disjs[p1]; - std::set &d2 = disjs[p2]; - if(!weak){ - if(pv->is_true(itps[p1])){ - itps[i] = itps[p2]; - disjs[i] = disjs[p2]; - } - else if(pv->is_true(itps[p2])){ - itps[i] = itps[p1]; - disjs[i] = disjs[p1]; - } - else { - std::set p1only,p2only; - std::insert_iterator > p1o(p1only,p1only.begin()); - std::insert_iterator > p2o(p2only,p2only.begin()); - std::insert_iterator > dio(disjs[i],disjs[i].begin()); - std::set_difference(d1.begin(),d1.end(),d2.begin(),d2.end(),p1o); - std::set_difference(d2.begin(),d2.end(),d1.begin(),d1.end(),p2o); - std::set_intersection(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); - ast p1i = my_or(itps[p1],disj_of_set(p1only)); - ast p2i = my_or(itps[p2],disj_of_set(p2only)); - itps[i] = pv->mk_and(p1i,p2i); - } - } - else { - itps[i] = pv->mk_and(itps[p1],itps[p2]); - std::insert_iterator > dio(disjs[i],disjs[i].begin()); - std::set_union(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); - } -#endif -} - -void iz3proof::mk_or_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs){ -#ifdef FACTOR_INTERPS - std::set &d1 = disjs[p1]; - std::set &d2 = disjs[p2]; - if(weak){ - if(pv->is_false(itps[p1])){ - itps[i] = itps[p2]; - disjs[i] = disjs[p2]; - } - else if(pv->is_false(itps[p2])){ - itps[i] = itps[p1]; - disjs[i] = disjs[p1]; - } - else { - std::set p1only,p2only; - std::insert_iterator > p1o(p1only,p1only.begin()); - std::insert_iterator > p2o(p2only,p2only.begin()); - std::insert_iterator > dio(disjs[i],disjs[i].begin()); - std::set_difference(d1.begin(),d1.end(),d2.begin(),d2.end(),p1o); - std::set_difference(d2.begin(),d2.end(),d1.begin(),d1.end(),p2o); - std::set_intersection(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); - ast p1i = pv->mk_and(itps[p1],pv->mk_not(disj_of_set(p1only))); - ast p2i = pv->mk_and(itps[p2],pv->mk_not(disj_of_set(p2only))); - itps[i] = my_or(p1i,p2i); - } - } - else { - itps[i] = my_or(itps[p1],itps[p2]); - std::insert_iterator > dio(disjs[i],disjs[i].begin()); - std::set_union(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); - } -#endif -} - -void iz3proof::interpolate_lemma(node_struct &n){ - if(interps[n.frame].size()) - return; // already computed - pv->interpolate_clause(n.conclusion,interps[n.frame]); -} - -iz3proof::ast iz3proof::interpolate(const prover::range &_rng, bool _weak -#ifdef CHECK_PROOFS - , ast assump - , std::vector *parents -#endif - ){ - // std::cout << "proof size: " << nodes.size() << "\n"; - rng = _rng; - weak = _weak; -#ifdef CHECK_PROOFS - if(nodes[nodes.size()-1].conclusion.size() != 0) - std::cerr << "internal error: proof conclusion is not empty clause\n"; - if(!child_interps.size()){ - child_interps.resize(nodes.size()); - for(unsigned j = 0; j < nodes.size(); j++) - child_interps[j] = pv->mk_true(); - } -#endif - std::vector itps(nodes.size()); -#ifdef FACTOR_INTERPS - std::vector > disjs(nodes.size()); -#endif - profiling::timer_start("Blits"); - find_B_lits(); - profiling::timer_stop("Blits"); - profiling::timer_start("interp_proof"); - // strengthen(); - for(unsigned i = 0; i < nodes.size(); i++){ - node_struct &n = nodes[i]; - ast &q = itps[i]; - switch(n.rl){ - case Assumption: { - - if(frame_in_A(n.frame)){ - /* HypC-A */ - if(!weak) -#ifdef FACTOR_INTERPS - { - q = pv->mk_false(); - set_of_B_lits(n.conclusion,disjs[i]); - } -#else - q = get_B_lits(n.conclusion); -#endif - else - q = pv->mk_false(); - } - else { - /* HypEq-B */ - if(!weak) - q = pv->mk_true(); - else -#ifdef FACTOR_INTERPS - { - q = pv->mk_true(); - set_of_A_lits(n.conclusion,disjs[i]); - } -#else - q = pv->mk_not(get_A_lits(n.conclusion)); -#endif - } - break; - } - case Resolution: { - ast p = n.aux; - p = pv->is_not(p) ? pv->mk_not(p) : p; // should be positive, but just in case - if(lit_in_B(p)) -#ifdef FACTOR_INTERPS - mk_and_factor(n.premises[0],n.premises[1],i,itps,disjs); -#else - q = pv->mk_and(itps[n.premises[0]],itps[n.premises[1]]); -#endif - else -#ifdef FACTOR_INTERPS - mk_or_factor(n.premises[0],n.premises[1],i,itps,disjs); -#else - q = my_or(itps[n.premises[0]],itps[n.premises[1]]); -#endif - break; - } - case Lemma: { - interpolate_lemma(n); // make sure lemma interpolants have been computed - q = interps[n.frame][pv->range_max(rng)]; - break; - } - case Contra: { - q = itps[n.premises[0]]; -#ifdef FACTOR_INTERPS - disjs[i] = disjs[n.premises[0]]; -#endif - break; - } - default: - assert(0 && "rule not allowed in interpolated proof"); - } -#ifdef CHECK_PROOFS - int this_frame = pv->range_max(rng); - if(0 && this_frame == 39) { - std::vector alits; - ast s = pv->mk_true(); - for(unsigned j = 0; j < n.conclusion.size(); j++) - if(pred_in_A(n.conclusion[j])){ - int scpmax = pv->range_max(pv->ast_scope(n.conclusion[j])); - if(scpmax == this_frame) - s = pv->mk_and(s,pv->mk_not(n.conclusion[j])); - } - ast ci = child_interps[i]; - s = pv->mk_and(pv->mk_and(s,pv->mk_and(assump,pv->mk_not(q))),ci); - if(pv->is_sat(s)){ - std::cout << "interpolation invariant violated at step " << i << "\n"; - assert(0 && "interpolation invariant violated"); - } - } - if((*parents)[this_frame] == 39) - child_interps[i] = pv->mk_and(child_interps[i],q); -#endif - } - ast &bar = itps[nodes.size()-1]; -#ifdef FACTOR_INTERPS - if(!weak) - bar = my_or(bar,disj_of_set(disjs[nodes.size()-1])); - else - bar = pv->mk_and(bar,pv->mk_not(disj_of_set(disjs[nodes.size()-1]))); -#endif - profiling::timer_stop("interp_proof"); - profiling::timer_start("simplifying"); - bar = pv->simplify(bar); - profiling::timer_stop("simplifying"); - return bar; -} - - -void iz3proof::print(std::ostream &s, int id){ - node_struct &n = nodes[id]; - switch(n.rl){ - case Assumption: - s << "Assumption("; - pv->print_clause(s,n.conclusion); - s << ")"; - break; - case Hypothesis: - s << "Hyp("; pv->print_expr(s,n.conclusion[0]); s << ")"; break; - case Reflexivity: - s << "Refl("; pv->print_expr(s,n.conclusion[0]); s << ")"; break; - case Symmetry: - s << "Symm("; print(s,n.premises[0]); s << ")"; break; - case Transitivity: - s << "Trans("; print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; break; - case Congruence: - s << "Cong("; pv->print_expr(s,n.conclusion[0]); - for(unsigned i = 0; i < n.premises.size(); i++){ - s << ","; - print(s,n.premises[i]); - } - s << ")"; break; - case EqContra: - s << "EqContra("; print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; break; - case Resolution: - s << "Res("; - pv->print_expr(s,n.aux); s << ","; - print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; - break; - case Lemma: - s << "Lemma("; - pv->print_clause(s,n.conclusion); - for(unsigned i = 0; i < n.premises.size(); i++){ - s << ","; - print(s,n.premises[i]); - } - s << ")"; - break; - case Contra: - s << "Contra("; - print(s,n.premises[0]); - s << ")"; - break; - default:; - } -} - - -void iz3proof::show(int id){ - std::ostringstream ss; - print(ss,id); - iz3base::pretty_print(std::cout,ss.str()); - // std::cout << ss.str(); - std::cout << "\n"; -} - - diff --git a/src/interp/iz3proof.h b/src/interp/iz3proof.h deleted file mode 100644 index a7dcb9b75..000000000 --- a/src/interp/iz3proof.h +++ /dev/null @@ -1,274 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3proof.h - - Abstract: - - This class defines a simple interpolating proof system. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3PROOF_H -#define IZ3PROOF_H - -#include - -#include "interp/iz3base.h" -#include "interp/iz3secondary.h" - -// #define CHECK_PROOFS - -/** This class defines a simple proof system. - - A proof is a dag consisting of "nodes". The children of each node - are its "premises". Each node has a "conclusion" that is a clause, - represented as a vector of literals. - - The literals are represented by abstract syntax trees. Operations - on these, including computation of scopes are provided by iz3base. - - A proof can be interpolated, provided it is restricted to the - rules Resolution, Assumption, Contra and Lemma, and that all - clauses are strict (i.e., each literal in each clause is local). - -*/ - -class iz3proof { - public: - /** The type of proof nodes (nodes in the derivation tree). */ - typedef int node; - - /** Enumeration of proof rules. */ - enum rule {Resolution,Assumption,Hypothesis,Theory,Axiom,Contra,Lemma,Reflexivity,Symmetry,Transitivity,Congruence,EqContra}; - - /** Interface to prover. */ - typedef iz3base prover; - - /** Ast type. */ - typedef prover::ast ast; - - /** Object thrown in case of a proof error. */ - struct proof_error: public iz3_exception { - proof_error(): iz3_exception("proof_error") {} - }; - - /* Null proof node */ - static const node null = -1; - - /** Make a resolution node with given pivot liter and premises. - The conclusion of premise1 should contain the negation of the - pivot literal, while the conclusion of premise2 should containe the - pivot literal. - */ - node make_resolution(ast pivot, node premise1, node premise2); - - /** Make an assumption node. The given clause is assumed in the given frame. */ - node make_assumption(int frame, const std::vector &assumption); - - /** Make a hypothesis node. If phi is the hypothesis, this is - effectively phi |- phi. */ - node make_hypothesis(ast hypothesis); - - /** Make a theory node. This can be any inference valid in the theory. */ - node make_theory(const std::vector &conclusion, std::vector premises); - - /** Make an axiom node. The conclusion must be an instance of an axiom. */ - node make_axiom(const std::vector &conclusion); - - /** Make a Contra node. This rule takes a derivation of the form - Gamma |- False and produces |- \/~Gamma. */ - - node make_contra(node prem, const std::vector &conclusion); - - /** Make a lemma node. A lemma node must have an interpolation. */ - node make_lemma(const std::vector &conclusion, const std::vector &interpolation); - - /** Make a Reflexivity node. This rule produces |- x = x */ - - node make_reflexivity(ast con); - - /** Make a Symmetry node. This takes a derivation of |- x = y and - produces | y = x */ - - node make_symmetry(ast con, node prem); - - /** Make a transitivity node. This takes derivations of |- x = y - and |- y = z produces | x = z */ - - node make_transitivity(ast con, node prem1, node prem2); - - /** Make a congruence node. This takes derivations of |- x_i = y_i - and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ - - node make_congruence(ast con, const std::vector &prems); - - /** Make an equality contradicition node. This takes |- x = y - and |- !(x = y) and produces false. */ - - node make_eqcontra(node prem1, node prem2); - - /** Get the rule of a node in a proof. */ - rule get_rule(node n){ - return nodes[n].rl; - } - - /** Get the pivot of a resolution node. */ - ast get_pivot(node n){ - return nodes[n].aux; - } - - /** Get the frame of an assumption node. */ - int get_frame(node n){ - return nodes[n].frame; - } - - /** Get the number of literals of the conclusion of a node. */ - int get_num_conclusion_lits(node n){ - return get_conclusion(n).size(); - } - - /** Get the nth literal of the conclusion of a node. */ - ast get_nth_conclusion_lit(node n, int i){ - return get_conclusion(n)[i]; - } - - /** Get the conclusion of a node. */ - void get_conclusion(node n, std::vector &result){ - result = get_conclusion(n); - } - - /** Get the number of premises of a node. */ - int get_num_premises(node n){ - return nodes[n].premises.size(); - } - - /** Get the nth premise of a node. */ - int get_nth_premise(node n, int i){ - return nodes[n].premises[i]; - } - - /** Get all the premises of a node. */ - void get_premises(node n, std::vector &result){ - result = nodes[n].premises; - } - - /** Create a new proof node, replacing the premises of an old - one. */ - - node clone(node n, std::vector &premises){ - if(premises == nodes[n].premises) - return n; - nodes.push_back(nodes[n]); - nodes.back().premises = premises; - return nodes.size()-1; - } - - /** Copy a proof node from src */ - node copy(iz3proof &src, node n); - - /** Resolve two lemmas on a given literal. */ - - node resolve_lemmas(ast pivot, node left, node right); - - /** Swap two proofs. */ - void swap(iz3proof &other){ - std::swap(pv,other.pv); - nodes.swap(other.nodes); - interps.swap(other.interps); - } - - /** Compute an interpolant for a proof, where the "A" side is defined by - the given range of frames. Parameter "weak", when true, uses different - interpolation system that resutls in generally weaker interpolants. - */ - ast interpolate(const prover::range &_rng, bool weak = false -#ifdef CHECK_PROOFS - , Z3_ast assump = (Z3_ast)0, std::vector *parents = 0 - -#endif - ); - - /** print proof node to a stream */ - - void print(std::ostream &s, node n); - - /** show proof node on stdout */ - void show(node n); - - /** Construct a proof, with a given prover. */ - iz3proof(prover *p){ - pv = p; - } - - /** Default constructor */ - iz3proof(){pv = nullptr;} - - - protected: - - struct node_struct { - rule rl; - ast aux; - int frame; - std::vector conclusion; - std::vector premises; - }; - - std::vector nodes; - std::vector > interps; // interpolations of lemmas - prover *pv; - - node make_node(){ - nodes.push_back(node_struct()); - return nodes.size()-1; - } - - void resolve(ast pivot, std::vector &cls1, const std::vector &cls2); - - node copy_rec(stl_ext::hash_map &memo, iz3proof &src, node n); - - void interpolate_lemma(node_struct &n); - - // lazily compute the result of resolution - // the node member "frame" indicates result is computed - const std::vector &get_conclusion(node x){ - node_struct &n = nodes[x]; - if(n.rl == Resolution && !n.frame){ - n.conclusion = get_conclusion(n.premises[0]); - resolve(n.aux,n.conclusion,get_conclusion(n.premises[1])); - n.frame = 1; - } - return n.conclusion; - } - - prover::range rng; - bool weak; - stl_ext::hash_set b_lits; - ast my_or(ast x, ast y); -#ifdef CHECK_PROOFS - std::vector child_interps; -#endif - bool pred_in_A(ast id); - bool term_in_B(ast id); - bool frame_in_A(int frame); - bool lit_in_B(ast lit); - ast get_A_lits(std::vector &cls); - ast get_B_lits(std::vector &cls); - void find_B_lits(); - ast disj_of_set(std::set &s); - void mk_or_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs); - void mk_and_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs); - void set_of_B_lits(std::vector &cls, std::set &res); - void set_of_A_lits(std::vector &cls, std::set &res); -}; - -#endif diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp deleted file mode 100755 index bbeb8d072..000000000 --- a/src/interp/iz3proof_itp.cpp +++ /dev/null @@ -1,3117 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3proof.cpp - - Abstract: - - This class defines a simple interpolating proof system. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3proof_itp.h" - -using namespace stl_ext; - -// #define INVARIANT_CHECKING - -class iz3proof_itp_impl : public iz3proof_itp { - - prover *pv; - prover::range rng; - bool weak; - - enum LitType {LitA,LitB,LitMixed}; - - hash_map placeholders; - - // These symbols represent deduction rules - - /* This symbol represents a proof by contradiction. That is, - contra(p,l1 /\ ... /\ lk) takes a proof p of - - l1,...,lk |- false - - and returns a proof of - - |- ~l1,...,~l2 - */ - symb contra; - - /* The summation rule. The term sum(p,c,i) takes a proof p of an - inequality i', an integer coefficient c and an inequality i, and - yields a proof of i' + ci. */ - symb sum; - - /* Proof rotation. The proof term rotate(q,p) takes a - proof p of: - - Gamma, q |- false - - and yields a proof of: - - Gamma |- ~q - */ - symb rotate_sum; - - /* Inequalities to equality. leq2eq(p, q, r) takes a proof - p of ~x=y, a proof q of x <= y and a proof r of y <= x - and yields a proof of false. */ - symb leq2eq; - - /* Equality to inequality. eq2leq(p, q) takes a proof p of x=y, and - a proof q ~(x <= y) and yields a proof of false. */ - symb eq2leq; - - /* Proof term cong(p,q) takes a proof p of x=y and a proof - q of t != t and returns a proof of false. */ - symb cong; - - - /* Excluded middle. exmid(phi,p,q) takes a proof p of phi and a - proof q of ~\phi and returns a proof of false. */ - symb exmid; - - /* Symmetry. symm(p) takes a proof p of x=y and produces - a proof of y=x. */ - symb symm; - - /* Modus ponens. modpon(p,e,q) takes proofs p of P, e of P=Q - and q of ~Q and returns a proof of false. */ - symb modpon; - - /* This oprerator represents a concatenation of rewrites. The term - a=b;c=d represents an A rewrite from a to b, followed by a B - rewrite from b to c, followed by an A rewrite from c to d. - */ - symb concat; - - /* This represents a lack of a proof */ - ast no_proof; - - // This is used to represent an infinitessimal value - ast epsilon; - - // Represents the top position of a term - ast top_pos; - - // add_pos(i,pos) represents position pos if the ith argument - symb add_pos; - - // rewrite proof rules - - /* rewrite_A(pos,cond,x=y) derives A |- cond => t[x]_p = t[y]_p - where t is an arbitrary term */ - symb rewrite_A; - - /* rewrite_B(pos,cond,x=y) derives B |- cond => t[x]_p = t[y]_p, - where t is an arbitrary term */ - symb rewrite_B; - - /* a normalization step is of the form (lhs=rhs) : proof, where "proof" - is a proof of lhs=rhs and lhs is a mixed term. If rhs is a mixed term - then it must have a greater index than lhs. */ - symb normal_step; - - /* A chain of normalization steps is either "true" (the null chain) - or normal_chain( ), where step is a normalization step - and tail is a normalization chain. The lhs of must have - a less term index than any lhs in the chain. Moreover, the rhs of - may not occur as the lhs of step in . If we wish to - add lhs=rhs to the beginning of and rhs=rhs' occurs in - we must apply transitivity, transforming to lhs=rhs'. */ - - symb normal_chain; - - /* If p is a proof of Q and c is a normalization chain, then normal(p,c) - is a proof of Q(c) (that is, Q with all substitutions in c performed). */ - - symb normal; - - /** Stand-ins for quantifiers */ - - symb sforall, sexists; - - - ast get_placeholder(ast t){ - hash_map::iterator it = placeholders.find(t); - if(it != placeholders.end()) - return it->second; - ast &res = placeholders[t]; - res = mk_fresh_constant("@p",get_type(t)); -#if 0 - std::cout << "placeholder "; - print_expr(std::cout,res); - std::cout << " = "; - print_expr(std::cout,t); - std::cout << std::endl; -#endif - return res; - } - - ast make_contra_node(const ast &pf, const std::vector &lits, int pfok = -1){ - if(lits.size() == 0) - return pf; - std::vector reslits; - reslits.push_back(make(contra,pf,mk_false())); - for(unsigned i = 0; i < lits.size(); i++){ - ast bar; - if(pfok & (1 << i)) bar = make(rotate_sum,lits[i],pf); - else bar = no_proof; - ast foo = make(contra,bar,lits[i]); - reslits.push_back(foo); - } - return make(And,reslits); - } - - LitType get_term_type(const ast &lit){ - prover::range r = pv->ast_scope(lit); - if(pv->range_is_empty(r)) - return LitMixed; - if(weak) { - if(pv->range_min(r) == SHRT_MIN) - return pv->range_contained(r,rng) ? LitA : LitB; - else - return pv->ranges_intersect(r,rng) ? LitA : LitB; - } - else - return pv->range_contained(r,rng) ? LitA : LitB; - } - - bool term_common(const ast &t){ - prover::range r = pv->ast_scope(t); - return pv->ranges_intersect(r,rng) && !pv->range_contained(r,rng); - } - - bool term_in_vocab(LitType ty, const ast &lit){ - prover::range r = pv->ast_scope(lit); - if(ty == LitA){ - return pv->ranges_intersect(r,rng); - } - return !pv->range_contained(r,rng); - } - - /** Make a resolution node with given pivot literal and premises. - The conclusion of premise1 should contain the negation of the - pivot literal, while the conclusion of premise2 should contain the - pivot literal. - */ - node make_resolution(ast pivot, const std::vector &conc, node premise1, node premise2) override { - LitType lt = get_term_type(pivot); - if(lt == LitA) - return my_or(premise1,premise2); - if(lt == LitB) - return my_and(premise1,premise2); - - /* the mixed case is a bit complicated */ - - static int non_local_count = 0; - ast res = resolve_arith(pivot,conc,premise1,premise2); -#ifdef INVARIANT_CHECKING - check_contra(conc,res); -#endif - non_local_count++; - return res; - } - - - /* Handles the case of resolution on a mixed arith atom. */ - - ast resolve_arith(const ast &pivot, const std::vector &conc, node premise1, node premise2){ - ast atom = get_lit_atom(pivot); - hash_map memo; - ast neg_pivot_lit = mk_not(atom); - if(op(pivot) != Not) - std::swap(premise1,premise2); - if(op(pivot) == Equal && op(arg(pivot,0)) == Select && op(arg(pivot,1)) == Select){ - neg_pivot_lit = mk_not(neg_pivot_lit); - std::swap(premise1,premise2); - } - return resolve_arith_rec1(memo, neg_pivot_lit, premise1, premise2); - } - - - ast apply_coeff(const ast &coeff, const ast &t){ -#if 0 - rational r; - if(!is_integer(coeff,r)) - throw iz3_exception("ack!"); - ast n = make_int(r.numerator()); - ast res = make(Times,n,t); - if(!r.is_int()) { - ast d = make_int(r.numerator()); - res = mk_idiv(res,d); - } - return res; -#endif - return make(Times,coeff,t); - } - - ast sum_ineq(const ast &coeff1, const ast &ineq1, const ast &coeff2, const ast &ineq2){ - opr sum_op = Leq; - if(op(ineq1) == Lt || op(ineq2) == Lt) - sum_op = Lt; - ast sum_sides[2]; - for(int i = 0; i < 2; i++){ - sum_sides[i] = make(Plus,apply_coeff(coeff1,arg(ineq1,i)),apply_coeff(coeff2,arg(ineq2,i))); - sum_sides[i] = z3_simplify(sum_sides[i]); - } - return make(sum_op,sum_sides[0],sum_sides[1]); - } - - - void collect_contra_resolvents(int from, const ast &pivot1, const ast &pivot, const ast &conj, std::vector &res){ - int nargs = num_args(conj); - for(int i = from; i < nargs; i++){ - ast f = arg(conj,i); - if(!(f == pivot)){ - ast ph = get_placeholder(mk_not(arg(pivot1,1))); - ast pf = arg(pivot1,0); - ast thing = pf == no_proof ? no_proof : subst_term_and_simp(ph,pf,arg(f,0)); - ast newf = make(contra,thing,arg(f,1)); - res.push_back(newf); - } - } - } - - bool is_negative_equality(const ast &e){ - if(op(e) == Not){ - opr o = op(arg(e,0)); - return o == Equal || o == Iff; - } - return false; - } - - int count_negative_equalities(const std::vector &resolvent){ - int res = 0; - for(unsigned i = 0; i < resolvent.size(); i++) - if(is_negative_equality(arg(resolvent[i],1))) - res++; - return res; - } - - ast resolve_contra_nf(const ast &pivot1, const ast &conj1, - const ast &pivot2, const ast &conj2){ - std::vector resolvent; - collect_contra_resolvents(0,pivot1,pivot2,conj2,resolvent); - collect_contra_resolvents(1,pivot2,pivot1,conj1,resolvent); - if(count_negative_equalities(resolvent) > 1) - throw proof_error(); - if(resolvent.size() == 1) // we have proved a contradiction - return simplify(arg(resolvent[0],0)); // this is the proof -- get interpolant - return make(And,resolvent); - } - - ast resolve_contra(const ast &pivot1, const ast &conj1, - const ast &pivot2, const ast &conj2){ - if(arg(pivot1,0) != no_proof) - return resolve_contra_nf(pivot1, conj1, pivot2, conj2); - if(arg(pivot2,0) != no_proof) - return resolve_contra_nf(pivot2, conj2, pivot1, conj1); - return resolve_with_quantifier(pivot1, conj1, pivot2, conj2); - } - - - bool is_contra_itp(const ast &pivot1, ast itp2, ast &pivot2){ - if(op(itp2) == And){ - int nargs = num_args(itp2); - for(int i = 1; i < nargs; i++){ - ast foo = arg(itp2,i); - if(op(foo) == Uninterpreted && sym(foo) == contra){ - if(arg(foo,1) == pivot1){ - pivot2 = foo; - return true; - } - } - else break; - } - } - return false; - } - - ast resolve_arith_rec2(hash_map &memo, const ast &pivot1, const ast &conj1, const ast &itp2){ - ast &res = memo[itp2]; - if(!res.null()) - return res; - - ast pivot2; - if(is_contra_itp(mk_not(arg(pivot1,1)),itp2,pivot2)) - res = resolve_contra(pivot1,conj1,pivot2,itp2); - else { - switch(op(itp2)){ - case Or: - case And: - case Implies: { - unsigned nargs = num_args(itp2); - std::vector args; args.resize(nargs); - for(unsigned i = 0; i < nargs; i++) - args[i] = resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,i)); - ast foo = itp2; // get rid of const - res = clone(foo,args); - break; - } - default: - { - opr o = op(itp2); - if(o == Uninterpreted){ - symb s = sym(itp2); - if(s == sforall || s == sexists) - res = make(s,arg(itp2,0),resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,1))); - else - res = itp2; - } - else { - res = itp2; - } - } - } - } - return res; - } - - - ast resolve_arith_rec1(hash_map &memo, const ast &neg_pivot_lit, const ast &itp1, const ast &itp2){ - ast &res = memo[itp1]; - if(!res.null()) - return res; - ast pivot1; - if(is_contra_itp(neg_pivot_lit,itp1,pivot1)){ - hash_map memo2; - res = resolve_arith_rec2(memo2,pivot1,itp1,itp2); - } - else { - switch(op(itp1)){ - case Or: - case And: - case Implies: { - unsigned nargs = num_args(itp1); - std::vector args; args.resize(nargs); - for(unsigned i = 0; i < nargs; i++) - args[i] = resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,i), itp2); - ast foo = itp1; // get rid of const - res = clone(foo,args); - break; - } - default: - { - opr o = op(itp1); - if(o == Uninterpreted){ - symb s = sym(itp1); - if(s == sforall || s == sexists) - res = make(s,arg(itp1,0),resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,1), itp2)); - else - res = itp1; - } - else { - res = itp1; - } - } - } - } - return res; - } - - void check_contra(hash_set &memo, hash_set &neg_lits, const ast &foo){ - if(memo.find(foo) != memo.end()) - return; - memo.insert(foo); - if(op(foo) == Uninterpreted && sym(foo) == contra){ - ast neg_lit = arg(foo,1); - if(!is_false(neg_lit) && neg_lits.find(neg_lit) == neg_lits.end()) - throw iz3_exception("lost a literal"); - return; - } - else { - switch(op(foo)){ - case Or: - case And: - case Implies: { - unsigned nargs = num_args(foo); - std::vector args; args.resize(nargs); - for(unsigned i = 0; i < nargs; i++) - check_contra(memo, neg_lits, arg(foo,i)); - break; - } - default: break; - } - } - } - - void check_contra(const std::vector &neg_lits, const ast &foo){ - hash_set memo; - hash_set neg_lits_set; - for(unsigned i = 0; i < neg_lits.size(); i++) - if(get_term_type(neg_lits[i]) == LitMixed) - neg_lits_set.insert(mk_not(neg_lits[i])); - check_contra(memo,neg_lits_set,foo); - } - - hash_map subst_memo; // memo of subst function - - ast subst_term_and_simp(const ast &var, const ast &t, const ast &e){ - subst_memo.clear(); - return subst_term_and_simp_rec(var,t,e); - } - - ast subst_term_and_simp_rec(const ast &var, const ast &t, const ast &e){ - if(e == var) return t; - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = subst_memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - if(op(e) == Uninterpreted){ - symb g = sym(e); - if(g == rotate_sum){ - if(var == get_placeholder(arg(e,0))){ - res = e; - } - else - res = make(rotate_sum,arg(e,0),subst_term_and_simp_rec(var,t,arg(e,1))); - return res; - } - if(g == concat){ - res = e; - return res; - } - } - int nargs = num_args(e); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = subst_term_and_simp_rec(var,t,arg(e,i)); - opr f = op(e); - if(f == Equal && args[0] == args[1]) res = mk_true(); - else if(f == And) res = my_and(args); - else if(f == Or) res = my_or(args); - else if(f == Idiv) res = mk_idiv(args[0],args[1]); - else res = clone(e,args); - } - return res; - } - - /* This is where the real work happens. Here, we simplify the - proof obtained by cut elimination, obtaining an interpolant. */ - - struct cannot_simplify: public iz3_exception { - cannot_simplify(): iz3_exception("cannot_simplify") {} - }; - hash_map simplify_memo; - - ast simplify(const ast &t){ - ast res = normalize(simplify_rec(t)); -#ifdef BOGUS_QUANTS - if(localization_vars.size()) - res = add_quants(z3_simplify(res)); -#endif - return res; - } - - ast simplify_rec(const ast &e){ - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = simplify_memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - int nargs = num_args(e); - std::vector args(nargs); - bool placeholder_arg = false; - symb g = sym(e); - if(g == concat){ - res = e; - return res; - } - for(int i = 0; i < nargs; i++){ - if(i == 0 && g == rotate_sum) - args[i] = arg(e,i); - else - args[i] = simplify_rec(arg(e,i)); - placeholder_arg |= is_placeholder(args[i]); - } - try { - TRACE("duality", print_expr(tout, e); tout << "\n";); - opr f = op(e); - if(f == Equal && args[0] == args[1]) res = mk_true(); - else if(f == And) res = my_and(args); - else if(f == Or) - res = my_or(args); - else if(f == Idiv) res = mk_idiv(args[0],args[1]); - else if(f == Uninterpreted && !placeholder_arg){ - if(g == rotate_sum) res = simplify_rotate(args); - else if(g == symm) res = simplify_symm(args); - else if(g == modpon) res = simplify_modpon(args); - else if(g == sum) res = simplify_sum(args); - else if(g == exmid) res = simplify_exmid(args); - else if(g == cong) res = simplify_cong(args); -#if 0 - else if(g == modpon) res = simplify_modpon(args); - else if(g == leq2eq) res = simplify_leq2eq(args); - else if(g == eq2leq) res = simplify_eq2leq(args); -#endif - else res = clone(e,args); - } - else res = clone(e,args); - } - catch (const cannot_simplify &){ - if(g == sum) - res = clone(e,args); - else - throw "interpolation failure"; - } - } - return res; - } - - - ast simplify_rotate(const std::vector &args){ - const ast &pf = args[1]; - ast pl = get_placeholder(args[0]); - if(op(pf) == Uninterpreted){ - symb g = sym(pf); - if(g == sum) return simplify_rotate_sum(pl,pf); - if(g == leq2eq) return simplify_rotate_leq2eq(pl,args[0],pf); - if(g == eq2leq) return simplify_rotate_eq2leq(pl,args[0],pf); - if(g == cong) return simplify_rotate_cong(pl,args[0],pf); - if(g == modpon) return simplify_rotate_modpon(pl,args[0],pf); - // if(g == symm) return simplify_rotate_symm(pl,args[0],pf); - } - if(op(pf) == Leq) - throw iz3_exception("foo!"); - throw cannot_simplify(); - } - - bool is_normal_ineq(const ast &ineq){ - if(sym(ineq) == normal) - return is_ineq(arg(ineq,0)); - return is_ineq(ineq); - } - - ast destruct_cond_ineq(const ast &ineq, ast &Aproves, ast &Bproves){ - ast res = ineq; - opr o = op(res); - if(o == And){ - Aproves = my_and(Aproves,arg(res,0)); - res = arg(res,1); - o = op(res); - } - if(o == Implies){ - Bproves = my_and(Bproves,arg(res,0)); - res = arg(res,1); - } - return res; - } - - ast distribute_coeff(const ast &coeff, const ast &s){ - if(sym(s) == sum){ - if(sym(arg(s,2)) == sum) - return make(sum, - distribute_coeff(coeff,arg(s,0)), - make_int(rational(1)), - distribute_coeff(make(Times,coeff,arg(s,1)), arg(s,2))); - else - return make(sum, - distribute_coeff(coeff,arg(s,0)), - make(Times,coeff,arg(s,1)), - arg(s,2)); - } - if(op(s) == Leq && arg(s,1) == make_int(rational(0)) && arg(s,2) == make_int(rational(0))) - return s; - return make(sum,make(Leq,make_int(rational(0)),make_int(rational(0))),coeff,s); - } - - ast simplify_sum(std::vector &args){ - if(args[1] != make_int(rational(1))){ - if(sym(args[2]) == sum) - return make(sum,args[0],make_int(rational(1)),distribute_coeff(args[1],args[2])); - } - ast Aproves = mk_true(), Bproves = mk_true(); - ast ineq = destruct_cond_ineq(args[0],Aproves,Bproves); - if(!is_normal_ineq(ineq)) throw cannot_simplify(); - sum_cond_ineq(ineq,args[1],args[2],Aproves,Bproves); - return my_and(Aproves,my_implies(Bproves,ineq)); - } - - ast simplify_rotate_sum(const ast &pl, const ast &pf){ - ast Aproves = mk_true(), Bproves = mk_true(); - ast ineq = make(Leq,make_int("0"),make_int("0")); - ineq = rotate_sum_rec(pl,pf,Aproves,Bproves,ineq); - if(is_true(Aproves) && is_true(Bproves)) - return ineq; - return my_and(Aproves,my_implies(Bproves,ineq)); - } - - bool is_rewrite_chain(const ast &chain){ - return sym(chain) == concat; - } - -#if 0 - ast ineq_from_chain_simple(const ast &chain, ast &cond){ - if(is_true(chain)) - return chain; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(is_true(rest) && is_rewrite_side(LitA,last) - && is_true(rewrite_lhs(last))){ - cond = my_and(cond,rewrite_cond(last)); - return rewrite_rhs(last); - } - if(is_rewrite_side(LitB,last) && is_true(rewrite_cond(last))) - return ineq_from_chain_simple(rest,cond); - return chain; - } -#endif - - ast ineq_from_chain(const ast &chain, ast &Aproves, ast &Bproves){ - if(is_rewrite_chain(chain)) - return rewrite_chain_to_normal_ineq(chain,Aproves,Bproves); - return chain; - } - - - void sum_cond_ineq(ast &ineq, const ast &coeff2, const ast &ineq2, ast &Aproves, ast &Bproves){ - opr o = op(ineq2); - if(o == And){ - sum_cond_ineq(ineq,coeff2,arg(ineq2,1),Aproves,Bproves); - Aproves = my_and(Aproves,arg(ineq2,0)); - } - else if(o == Implies){ - sum_cond_ineq(ineq,coeff2,arg(ineq2,1),Aproves,Bproves); - Bproves = my_and(Bproves,arg(ineq2,0)); - } - else { - ast the_ineq = ineq_from_chain(ineq2,Aproves,Bproves); - if(sym(ineq) == normal || sym(the_ineq) == normal){ - sum_normal_ineq(ineq,coeff2,the_ineq,Aproves,Bproves); - return; - } - if(is_ineq(the_ineq)) - linear_comb(ineq,coeff2,the_ineq); - else - throw cannot_simplify(); - } - } - - void destruct_normal(const ast &pf, ast &p, ast &n){ - if(sym(pf) == normal){ - p = arg(pf,0); - n = arg(pf,1); - } - else { - p = pf; - n = mk_true(); - } - } - - void sum_normal_ineq(ast &ineq, const ast &coeff2, const ast &ineq2, ast &Aproves, ast &Bproves){ - ast in1,in2,n1,n2; - destruct_normal(ineq,in1,n1); - destruct_normal(ineq2,in2,n2); - ast dummy1, dummy2; - sum_cond_ineq(in1,coeff2,in2,dummy1,dummy2); - n1 = merge_normal_chains(n1,n2, Aproves, Bproves); - ineq = is_true(n1) ? in1 : make_normal(in1,n1); - } - - bool is_ineq(const ast &ineq){ - opr o = op(ineq); - if(o == Not) o = op(arg(ineq,0)); - return o == Leq || o == Lt || o == Geq || o == Gt; - } - - // divide both sides of inequality by a non-negative integer divisor - ast idiv_ineq(const ast &ineq1, const ast &divisor){ - if(sym(ineq1) == normal){ - ast in1,n1; - destruct_normal(ineq1,in1,n1); - in1 = idiv_ineq(in1,divisor); - return make_normal(in1,n1); - } - if(divisor == make_int(rational(1))) - return ineq1; - ast ineq = ineq1; - if(op(ineq) == Lt) - ineq = simplify_ineq(make(Leq,arg(ineq,0),make(Sub,arg(ineq,1),make_int("1")))); - return make(op(ineq),mk_idiv(arg(ineq,0),divisor),mk_idiv(arg(ineq,1),divisor)); - } - - ast rotate_sum_rec(const ast &pl, const ast &pf, ast &Aproves, ast &Bproves, ast &ineq){ - if(pf == pl){ - if(sym(ineq) == normal) - return ineq; - return simplify_ineq(ineq); - } - if(op(pf) == Uninterpreted && sym(pf) == sum){ - if(arg(pf,2) == pl){ - sum_cond_ineq(ineq,make_int("1"),arg(pf,0),Aproves,Bproves); - ineq = idiv_ineq(ineq,arg(pf,1)); - return ineq; - } - sum_cond_ineq(ineq,arg(pf,1),arg(pf,2),Aproves,Bproves); - return rotate_sum_rec(pl,arg(pf,0),Aproves,Bproves,ineq); - } - throw cannot_simplify(); - } - - ast simplify_rotate_leq2eq(const ast &pl, const ast &neg_equality, const ast &pf){ - if(pl == arg(pf,0)){ - ast equality = arg(neg_equality,0); - ast x = arg(equality,0); - ast y = arg(equality,1); - ast Aproves1 = mk_true(), Bproves1 = mk_true(); - ast pf1 = destruct_cond_ineq(arg(pf,1), Aproves1, Bproves1); - ast pf2 = destruct_cond_ineq(arg(pf,2), Aproves1, Bproves1); - ast xleqy = round_ineq(ineq_from_chain(pf1,Aproves1,Bproves1)); - ast yleqx = round_ineq(ineq_from_chain(pf2,Aproves1,Bproves1)); - ast ineq1 = make(Leq,make_int("0"),make_int("0")); - sum_cond_ineq(ineq1,make_int("-1"),xleqy,Aproves1,Bproves1); - sum_cond_ineq(ineq1,make_int("-1"),yleqx,Aproves1,Bproves1); - ast Acond = my_implies(Aproves1,my_and(Bproves1,z3_simplify(ineq1))); - ast Aproves2 = mk_true(), Bproves2 = mk_true(); - ast ineq2 = make(Leq,make_int("0"),make_int("0")); - sum_cond_ineq(ineq2,make_int("1"),xleqy,Aproves2,Bproves2); - sum_cond_ineq(ineq2,make_int("1"),yleqx,Aproves2,Bproves2); - ast Bcond = my_implies(Bproves1,my_and(Aproves1,z3_simplify(ineq2))); - // if(!is_true(Aproves1) || !is_true(Bproves1)) - // std::cout << "foo!\n";; - if(y == make_int(rational(0)) && op(x) == Plus && num_args(x) == 2){ - if(get_term_type(arg(x,0)) == LitA){ - ast iter = z3_simplify(make(Plus,arg(x,0),get_ineq_rhs(xleqy))); - ast rewrite1 = make_rewrite(LitA,pos_add(0,top_pos),Acond,make(Equal,arg(x,0),iter)); - iter = make(Plus,iter,arg(x,1)); - ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); - return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); - } - if(get_term_type(arg(x,1)) == LitA){ - ast iter = z3_simplify(make(Plus,arg(x,1),get_ineq_rhs(xleqy))); - ast rewrite1 = make_rewrite(LitA,pos_add(1,top_pos),Acond,make(Equal,arg(x,1),iter)); - iter = make(Plus,arg(x,0),iter); - ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); - return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); - } - } - if(get_term_type(x) == LitA){ - ast iter = z3_simplify(make(Plus,x,get_ineq_rhs(xleqy))); - ast rewrite1 = make_rewrite(LitA,top_pos,Acond,make(Equal,x,iter)); - ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); - return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); - } - if(get_term_type(y) == LitA){ - ast iter = z3_simplify(make(Plus,y,get_ineq_rhs(yleqx))); - ast rewrite2 = make_rewrite(LitA,top_pos,Acond,make(Equal,iter,y)); - ast rewrite1 = make_rewrite(LitB,top_pos,Bcond,make(Equal,x,iter)); - return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); - } - throw cannot_simplify(); - } - throw cannot_simplify(); - } - - ast round_ineq(const ast &ineq){ - if(sym(ineq) == normal) - return make_normal(round_ineq(arg(ineq,0)),arg(ineq,1)); - if(!is_ineq(ineq)) - throw cannot_simplify(); - ast res = simplify_ineq(ineq); - if(op(res) == Lt) - res = make(Leq,arg(res,0),make(Sub,arg(res,1),make_int("1"))); - return res; - } - - ast unmixed_eq2ineq(const ast &lhs, const ast &rhs, opr comp_op, const ast &equa, ast &cond){ - ast ineqs= chain_ineqs(comp_op,LitA,equa,lhs,rhs); // chain must be from lhs to rhs - cond = my_and(cond,chain_conditions(LitA,equa)); - ast Bconds = z3_simplify(chain_conditions(LitB,equa)); - if(is_true(Bconds) && op(ineqs) != And) - return my_implies(cond,ineqs); - if(op(ineqs) != And) - return my_and(Bconds,my_implies(cond,ineqs)); - throw iz3_exception("help!"); - } - - ast add_mixed_eq2ineq(const ast &lhs, const ast &rhs, const ast &equa, const ast &itp){ - if(is_true(equa)) - return itp; - std::vector args(3); - args[0] = itp; - args[1] = make_int("1"); - ast ineq = make(Leq,make_int(rational(0)),make_int(rational(0))); - args[2] = make_normal(ineq,cons_normal(fix_normal(lhs,rhs,equa),mk_true())); - return simplify_sum(args); - } - - - ast simplify_rotate_eq2leq(const ast &pl, const ast &neg_equality, const ast &pf){ - if(pl == arg(pf,1)){ - TRACE("duality", print_expr(tout, pl); print_expr(tout << "\n", neg_equality); print_expr(tout << "\n", pf); tout << "\n";); - ast cond = mk_true(); - ast equa = sep_cond(arg(pf,0),cond); - if(is_equivrel_chain(equa)){ - ast lhs,rhs; eq_from_ineq(arg(neg_equality,0),lhs,rhs); // get inequality we need to prove - if(!rewrites_from_to(equa,lhs,rhs)){ - lhs = arg(arg(neg_equality,0),0); // the equality proved is ambiguous, sadly - rhs = arg(arg(neg_equality,0),1); - } - LitType lhst = get_term_type(lhs), rhst = get_term_type(rhs); - if(lhst != LitMixed && rhst != LitMixed) - return unmixed_eq2ineq(lhs, rhs, op(arg(neg_equality,0)), equa, cond); - else { - ast left, left_term, middle, right_term, right; - left = get_left_movers(equa,lhs,middle,left_term); - middle = get_right_movers(middle,rhs,right,right_term); - ast itp = unmixed_eq2ineq(left_term, right_term, op(arg(neg_equality,0)), middle, cond); - // itp = my_implies(cond,itp); - itp = add_mixed_eq2ineq(lhs, left_term, left, itp); - itp = add_mixed_eq2ineq(right_term, rhs, right, itp); - return itp; - } - } - } - throw iz3_exception("help!"); - } - - void reverse_modpon(std::vector &args){ - std::vector sargs(1); sargs[0] = args[1]; - args[1] = simplify_symm(sargs); - if(is_equivrel_chain(args[2])) - args[1] = down_chain(args[1]); - std::swap(args[0],args[2]); - } - - ast simplify_rotate_modpon(const ast &pl, const ast &neg_equality, const ast &pf){ - std::vector args; args.resize(3); - args[0] = arg(pf,0); - args[1] = arg(pf,1); - args[2] = arg(pf,2); - if(pl == args[0]) - reverse_modpon(args); - if(pl == args[2]){ - ast cond = mk_true(); - ast chain = simplify_modpon_fwd(args, cond); - return my_implies(cond,chain); - } - throw cannot_simplify(); - } - - ast get_ineq_rhs(const ast &ineq2){ - opr o = op(ineq2); - if(o == Implies) - return get_ineq_rhs(arg(ineq2,1)); - else if(o == Leq || o == Lt) - return arg(ineq2,1); - throw cannot_simplify(); - } - - ast simplify_rotate_cong(const ast &pl, const ast &neg_equality, const ast &pf){ - if(pl == arg(pf,2)){ - if(op(arg(pf,0)) == True) - return mk_true(); - rational pos; - if(is_numeral(arg(pf,1),pos)){ - int ipos = pos.get_unsigned(); - ast cond = mk_true(); - ast equa = sep_cond(arg(pf,0),cond); -#if 0 - if(op(equa) == Equal){ - ast pe = mk_not(neg_equality); - ast lhs = subst_in_arg_pos(ipos,arg(equa,0),arg(pe,0)); - ast rhs = subst_in_arg_pos(ipos,arg(equa,1),arg(pe,1)); - ast res = make(Equal,lhs,rhs); - return my_implies(cond,res); - } -#endif - ast res = chain_pos_add(ipos,equa); - return my_implies(cond,res); - } - } - throw cannot_simplify(); - } - - ast simplify_symm(const std::vector &args){ - if(op(args[0]) == True) - return mk_true(); - ast cond = mk_true(); - ast equa = sep_cond(args[0],cond); - if(is_equivrel_chain(equa)) - return my_implies(cond,reverse_chain(equa)); - if(is_negation_chain(equa)) - return commute_negation_chain(equa); - throw cannot_simplify(); - } - - ast simplify_modpon_fwd(const std::vector &args, ast &cond){ - ast P = sep_cond(args[0],cond); - ast PeqQ = sep_cond(args[1],cond); - ast chain; - if(is_equivrel_chain(P)){ - try { - ast split[2]; - split_chain(PeqQ,split); - chain = reverse_chain(split[0]); - chain = concat_rewrite_chain(chain,P); - chain = concat_rewrite_chain(chain,split[1]); - } - catch(const cannot_split &){ - static int this_count = 0; - this_count++; - ast tail, pref = get_head_chain(PeqQ,tail,false); // pref is x=y, tail is x=y -> x'=y' - ast split[2]; split_chain(tail,split); // rewrites from x to x' and y to y' - ast head = chain_last(pref); - ast prem = make_rewrite(rewrite_side(head),top_pos,rewrite_cond(head),make(Iff,mk_true(),mk_not(rewrite_lhs(head)))); - ast back_chain = chain_cons(mk_true(),prem); - back_chain = concat_rewrite_chain(back_chain,chain_pos_add(0,reverse_chain(chain_rest(pref)))); - ast cond = contra_chain(back_chain,P); - if(is_rewrite_side(LitA,head)) - cond = mk_not(cond); - ast fwd_rewrite = make_rewrite(rewrite_side(head),top_pos,cond,rewrite_rhs(head)); - P = chain_cons(mk_true(),fwd_rewrite); - chain = reverse_chain(split[0]); - chain = concat_rewrite_chain(chain,P); - chain = concat_rewrite_chain(chain,split[1]); - } - } - else { // if not an equivalence, must be of form T <-> pred - chain = concat_rewrite_chain(P,PeqQ); - } - return chain; - } - - struct subterm_normals_failed: public iz3_exception { - subterm_normals_failed(): iz3_exception("subterm_normals_failed") {} - }; - - void get_subterm_normals(const ast &ineq1, const ast &ineq2, const ast &chain, ast &normals, - const ast &pos, hash_set &memo, ast &Aproves, ast &Bproves){ - opr o1 = op(ineq1); - opr o2 = op(ineq2); - if(o1 == Not || o1 == Leq || o1 == Lt || o1 == Geq || o1 == Gt || o1 == Plus || o1 == Times){ - int n = num_args(ineq1); - if(o2 != o1 || num_args(ineq2) != n) - throw iz3_exception("bad inequality rewriting"); - for(int i = 0; i < n; i++){ - ast new_pos = add_pos_to_end(pos,i); - get_subterm_normals(arg(ineq1,i), arg(ineq2,i), chain, normals, new_pos, memo, Aproves, Bproves); - } - } - else if(get_term_type(ineq2) == LitMixed){ - if(memo.find(ineq2) == memo.end()){ - memo.insert(ineq2); - ast sub_chain = extract_rewrites(chain,pos); - if(is_true(sub_chain)) - throw iz3_exception("bad inequality rewriting"); - ast new_normal = make_normal_step(ineq2,ineq1,reverse_chain(sub_chain)); - normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); - } - } - else if(!(ineq1 == ineq2)) - throw subterm_normals_failed(); - } - - ast rewrites_to_normals(const ast &ineq1, const ast &chain, ast &normals, ast &Aproves, ast &Bproves, ast &Aineqs){ - if(is_true(chain)) - return ineq1; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast new_ineq1 = rewrites_to_normals(ineq1, rest, normals, Aproves, Bproves, Aineqs); - ast p1 = rewrite_pos(last); - ast term1; - ast coeff = arith_rewrite_coeff(new_ineq1,p1,term1); - ast res = subst_in_pos(new_ineq1,rewrite_pos(last),rewrite_rhs(last)); - ast rpos; - pos_diff(p1,rewrite_pos(last),rpos); - ast term2 = subst_in_pos(term1,rpos,rewrite_rhs(last)); - if(get_term_type(term1) != LitMixed && get_term_type(term2) != LitMixed){ - if(is_rewrite_side(LitA,last)) - linear_comb(Aineqs,coeff,make(Leq,make_int(rational(0)),make(Sub,term2,term1))); - } - else { - ast pf = extract_rewrites(make(concat,mk_true(),last),p1); - ast new_normal = fix_normal(term1,term2,pf); - normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); - } - return res; - } - - ast arith_rewrite_coeff(const ast &ineq, ast &p1, ast &term){ - ast coeff = make_int(rational(1)); - if(p1 == top_pos){ - term = ineq; - return coeff; - } - int argpos = pos_arg(p1); - opr o = op(ineq); - switch(o){ - case Leq: - case Lt: - coeff = argpos ? make_int(rational(1)) : make_int(rational(-1)); - break; - case Geq: - case Gt: - coeff = argpos ? make_int(rational(-1)) : make_int(rational(1)); - break; - case Not: - coeff = make_int(rational(-1)); - case Plus: - break; - case Times: - coeff = arg(ineq,0); - break; - default: - p1 = top_pos; - term = ineq; - return coeff; - } - p1 = arg(p1,1); - ast res = arith_rewrite_coeff(arg(ineq,argpos),p1,term); - p1 = pos_add(argpos,p1); - return coeff == make_int(rational(1)) ? res : make(Times,coeff,res); - } - - ast rewrite_chain_to_normal_ineq(const ast &chain, ast &Aproves, ast &Bproves){ - ast tail, pref = get_head_chain(chain,tail,false); // pref is x=y, tail is x=y -> x'=y' - ast head = chain_last(pref); - ast ineq1 = rewrite_rhs(head); - ast ineq2 = apply_rewrite_chain(ineq1,tail); - ast nc = mk_true(); - hash_set memo; - ast itp = make(Leq,make_int(rational(0)),make_int(rational(0))); - ast Aproves_save = Aproves, Bproves_save = Bproves; try { - get_subterm_normals(ineq1,ineq2,tail,nc,top_pos,memo, Aproves, Bproves); - } - catch (const subterm_normals_failed &){ Aproves = Aproves_save; Bproves = Bproves_save; nc = mk_true(); - rewrites_to_normals(ineq1, tail, nc, Aproves, Bproves, itp); - } - if(is_rewrite_side(LitA,head)){ - linear_comb(itp,make_int("1"),ineq1); // make sure it is normal form - //itp = ineq1; - ast mc = z3_simplify(chain_side_proves(LitB,pref)); - Bproves = my_and(Bproves,mc); - } - else { - ast mc = z3_simplify(chain_side_proves(LitA,pref)); - Aproves = my_and(Aproves,mc); - } - if(is_true(nc)) - return itp; - return make_normal(itp,nc); - } - - /* Given a chain rewrite chain deriving not P and a rewrite chain deriving P, return an interpolant. */ - ast contra_chain(const ast &neg_chain, const ast &pos_chain){ - // equality is a special case. we use the derivation of x=y to rewrite not(x=y) to not(y=y) - if(is_equivrel_chain(pos_chain)){ - ast tail, pref = get_head_chain(neg_chain,tail); // pref is not(x=y), tail is not(x,y) -> not(x',y') - ast split[2]; split_chain(down_chain(tail),split); // rewrites from x to x' and y to y' - ast chain = split[0]; - chain = concat_rewrite_chain(chain,pos_chain); // rewrites from x to y' - chain = concat_rewrite_chain(chain,reverse_chain(split[1])); // rewrites from x to y - chain = concat_rewrite_chain(pref,chain_pos_add(0,chain_pos_add(0,chain))); // rewrites t -> not(y=y) - ast head = chain_last(pref); - if(is_rewrite_side(LitB,head)){ - ast condition = chain_conditions(LitB,chain); - return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),condition); - } - else { - ast condition = chain_conditions(LitA,chain); - return my_and(chain_conditions(LitB,chain),my_implies(condition,mk_not(chain_formulas(LitB,chain)))); - } - // ast chain = concat_rewrite_chain(neg_chain,chain_pos_add(0,chain_pos_add(0,pos_chain))); - // return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); - } - // otherwise, we reverse the derivation of t = P and use it to rewrite not(P) to not(t) - ast chain = concat_rewrite_chain(neg_chain,chain_pos_add(0,reverse_chain(pos_chain))); - return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); - } - - ast simplify_modpon(const std::vector &args){ - ast Aproves = mk_true(), Bproves = mk_true(); - ast chain = simplify_modpon_fwd(args,Bproves); - ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); - ast interp; - if(is_normal_ineq(Q2)){ // inequalities are special - ast nQ2 = rewrite_chain_to_normal_ineq(chain,Aproves,Bproves); - sum_cond_ineq(nQ2,make_int(rational(1)),Q2,Aproves,Bproves); - interp = normalize(nQ2); - } - else - interp = is_negation_chain(chain) ? contra_chain(chain,Q2) : contra_chain(Q2,chain); - return my_and(Aproves,my_implies(Bproves,interp)); - } - - - ast simplify_exmid(const std::vector &args){ - if(is_equivrel(args[0])){ - ast Aproves = mk_true(), Bproves = mk_true(); - ast chain = destruct_cond_ineq(args[1],Aproves,Bproves); - ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); - ast interp = contra_chain(Q2,chain); - return my_and(Aproves,my_implies(Bproves,interp)); - } - throw iz3_exception("bad exmid"); - } - - ast simplify_cong(const std::vector &args){ - ast Aproves = mk_true(), Bproves = mk_true(); - ast chain = destruct_cond_ineq(args[0],Aproves,Bproves); - rational pos; - if(is_numeral(args[1],pos)){ - int ipos = pos.get_unsigned(); - chain = chain_pos_add(ipos,chain); - ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); - ast interp = contra_chain(Q2,chain); - return my_and(Aproves,my_implies(Bproves,interp)); - } - throw iz3_exception("bad cong"); - } - - bool is_equivrel(const ast &p){ - opr o = op(p); - return o == Equal || o == Iff; - } - - struct rewrites_failed: public iz3_exception { - rewrites_failed(): iz3_exception("rewrites_failed") {} - }; - - /* Suppose p in Lang(B) and A |- p -> q and B |- q -> r. Return a z in Lang(B) such that - B |- p -> z and A |- z -> q. Collect any side conditions in "rules". */ - - ast commute_rewrites(const ast &p, const ast &q, const ast &r, ast &rules){ - if(q == r) - return p; - if(p == q) - return r; - else { - ast rew = make(Equal,q,r); - if(get_term_type(rew) == LitB){ - apply_common_rewrites(p,p,q,rules); // A rewrites must be over comon vocab - return r; - } - } - if(sym(p) != sym(q) || sym(q) != sym(r)) - throw rewrites_failed(); - int nargs = num_args(p); - if(nargs != num_args(q) || nargs != num_args(r)) - throw rewrites_failed(); - std::vector args; args.resize(nargs); - for(int i = 0; i < nargs; i++) - args[i] = commute_rewrites(arg(p,i),arg(q,i),arg(r,i),rules); - return clone(p,args); - } - - ast apply_common_rewrites(const ast &p, const ast &q, const ast &r, ast &rules){ - if(q == r) - return p; - ast rew = make(Equal,q,r); - if(term_common(rew)){ - if(p != q) - throw rewrites_failed(); - rules = my_and(rules,rew); - return r; - } - if(sym(p) != sym(q) || sym(q) != sym(r)) - return p; - int nargs = num_args(p); - if(nargs != num_args(q) || nargs != num_args(r)) - return p; - std::vector args; args.resize(nargs); - for(int i = 0; i < nargs; i++) - args[i] = apply_common_rewrites(arg(p,i),arg(q,i),arg(r,i),rules); - return clone(p,args); - } - - ast apply_all_rewrites(const ast &p, const ast &q, const ast &r){ - if(q == r) - return p; - if(p == q) - return r; - if(sym(p) != sym(q) || sym(q) != sym(r)) - throw rewrites_failed(); - int nargs = num_args(p); - if(nargs != num_args(q) || nargs != num_args(r)) - throw rewrites_failed(); - std::vector args; args.resize(nargs); - for(int i = 0; i < nargs; i++) - args[i] = apply_all_rewrites(arg(p,i),arg(q,i),arg(r,i)); - return clone(p,args); - } - - ast delta(const ast &x, const ast &y){ - if(op(x) != op(y) || (op(x) == Uninterpreted && sym(x) != sym(y)) || num_args(x) != num_args(y)) - return make(Equal,x,y); - ast res = mk_true(); - int nargs = num_args(x); - for(int i = 0; i < nargs; i++) - res = my_and(res,delta(arg(x,i),arg(y,i))); - return res; - } - - bool diff_rec(LitType t, const ast &p, const ast &q, ast &pd, ast &qd){ - if(p == q) - return false; - if(term_in_vocab(t,p) && term_in_vocab(t,q)){ - pd = p; - qd = q; - return true; - } - else { - if(sym(p) != sym(q)) return false; - int nargs = num_args(p); - if(num_args(q) != nargs) return false; - for(int i = 0; i < nargs; i++) - if(diff_rec(t,arg(p,i),arg(q,i),pd,qd)) - return true; - return false; - } - } - - void diff(LitType t, const ast &p, const ast &q, ast &pd, ast &qd){ - if(!diff_rec(t,p,q,pd,qd)) - throw cannot_simplify(); - } - - bool apply_diff_rec(LitType t, const ast &inp, const ast &p, const ast &q, ast &out){ - if(p == q) - return false; - if(term_in_vocab(t,p) && term_in_vocab(t,q)){ - if(inp != p) - throw cannot_simplify(); - out = q; - return true; - } - else { - int nargs = num_args(p); - if(sym(p) != sym(q)) throw cannot_simplify(); - if(num_args(q) != nargs) throw cannot_simplify(); - if(sym(p) != sym(inp)) throw cannot_simplify(); - if(num_args(inp) != nargs) throw cannot_simplify(); - for(int i = 0; i < nargs; i++) - if(apply_diff_rec(t,arg(inp,i),arg(p,i),arg(q,i),out)) - return true; - return false; - } - } - - ast apply_diff(LitType t, const ast &inp, const ast &p, const ast &q){ - ast out; - if(!apply_diff_rec(t,inp,p,q,out)) - throw cannot_simplify(); - return out; - } - - bool merge_A_rewrites(const ast &A1, const ast &A2, ast &merged) { - if(arg(A1,1) == arg(A2,0)){ - merged = make(op(A1),arg(A1,0),arg(A2,1)); - return true; - } - ast diff1l, diff1r, diff2l, diff2r,diffBl,diffBr; - diff(LitA,arg(A1,0),arg(A1,1),diff1l,diff1r); - diff(LitA,arg(A2,0),arg(A2,1),diff2l,diff2r); - diff(LitB,arg(A1,1),arg(A2,0),diffBl,diffBr); - if(!term_common(diff2l) && !term_common(diffBr)){ - ast A1r = apply_diff(LitB,arg(A2,1),arg(A2,0),arg(A1,1)); - merged = make(op(A1),arg(A1,0),A1r); - return true; - } - if(!term_common(diff1r) && !term_common(diffBl)){ - ast A2l = apply_diff(LitB,arg(A1,0),arg(A1,1),arg(A2,0)); - merged = make(op(A1),A2l,arg(A2,1)); - return true; - } - return false; - } - - void collect_A_rewrites(const ast &t, std::vector &res){ - if(is_true(t)) - return; - if(sym(t) == concat){ - res.push_back(arg(t,0)); - collect_A_rewrites(arg(t,0),res); - return; - } - res.push_back(t); - } - - ast concat_A_rewrites(const std::vector &rew){ - if(rew.size() == 0) - return mk_true(); - ast res = rew[0]; - for(unsigned i = 1; i < rew.size(); i++) - res = make(concat,res,rew[i]); - return res; - } - - ast merge_concat_rewrites(const ast &A1, const ast &A2){ - std::vector rew; - collect_A_rewrites(A1,rew); - int first = rew.size(), last = first; // range that might need merging - collect_A_rewrites(A2,rew); - while(first > 0 && first < (int)rew.size() && first <= last){ - ast merged; - if(merge_A_rewrites(rew[first-1],rew[first],merged)){ - rew[first] = merged; - first--; - rew.erase(rew.begin()+first); - last--; - if(first >= last) last = first+1; - } - else - first++; - } - return concat_A_rewrites(rew); - } - - ast sep_cond(const ast &t, ast &cond){ - if(op(t) == Implies){ - cond = my_and(cond,arg(t,0)); - return arg(t,1); - } - return t; - } - - - /* operations on term positions */ - - /** Finds the difference between two positions. If p1 < p2 (p1 is a - position below p2), returns -1 and sets diff to p2-p1 (the psath - from position p2 to position p1). If p2 < p1 (p2 is a position - below p1), returns 1 and sets diff to p1-p2 (the psath from - position p1 to position p2). If equal, return 0 and set diff to - top_pos. Else (if p1 and p2 are independent) returns 2 and - leaves diff unchanged. */ - - int pos_diff(const ast &p1, const ast &p2, ast &diff){ - if(p1 == top_pos && p2 != top_pos){ - diff = p2; - return 1; - } - if(p2 == top_pos && p1 != top_pos){ - diff = p1; - return -1; - } - if(p1 == top_pos && p2 == top_pos){ - diff = p1; - return 0; - } - if(arg(p1,0) == arg(p2,0)) // same argument position, recur - return pos_diff(arg(p1,1),arg(p2,1),diff); - return 2; - } - - /* return the position of pos in the argth argument */ - ast pos_add(int arg, const ast &pos){ - return make(add_pos,make_int(rational(arg)),pos); - } - - ast add_pos_to_end(const ast &pos, int i){ - if(pos == top_pos) - return pos_add(i,pos); - return make(add_pos,arg(pos,0),add_pos_to_end(arg(pos,1),i)); - } - - /* return the argument number of position, if not top */ - int pos_arg(const ast &pos){ - rational r; - if(is_numeral(arg(pos,0),r)) - return r.get_unsigned(); - throw iz3_exception("bad position!"); - } - - /* substitute y into position pos in x */ - ast subst_in_pos(const ast &x, const ast &pos, const ast &y){ - if(pos == top_pos) - return y; - int p = pos_arg(pos); - int nargs = num_args(x); - if(p >= 0 && p < nargs){ - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = i == p ? subst_in_pos(arg(x,i),arg(pos,1),y) : arg(x,i); - return clone(x,args); - } - throw iz3_exception("bad term position!"); - } - - ast diff_chain(LitType t, const ast &pos, const ast &x, const ast &y, const ast &prefix){ - int nargs = num_args(x); - if(x == y) return prefix; - if(sym(x) == sym(y) && nargs == num_args(y)){ - ast res = prefix; - for(int i = 0; i < nargs; i++) - res = diff_chain(t,pos_add(i,pos),arg(x,i),arg(y,i),res); - return res; - } - return chain_cons(prefix,make_rewrite(t,pos,mk_true(),make_equiv_rel(x,y))); - } - - /* operations on rewrites */ - ast make_rewrite(LitType t, const ast &pos, const ast &cond, const ast &equality){ -#if 0 - if(pos == top_pos && op(equality) == Iff && !is_true(arg(equality,0))) - throw iz3_exception("bad rewrite"); -#endif - if(!is_equivrel(equality)) - throw iz3_exception("bad rewrite"); - return make(t == LitA ? rewrite_A : rewrite_B, pos, cond, equality); - } - - ast rewrite_pos(const ast &rew){ - return arg(rew,0); - } - - ast rewrite_cond(const ast &rew){ - return arg(rew,1); - } - - ast rewrite_equ(const ast &rew){ - return arg(rew,2); - } - - ast rewrite_lhs(const ast &rew){ - return arg(arg(rew,2),0); - } - - ast rewrite_rhs(const ast &rew){ - return arg(arg(rew,2),1); - } - - /* operations on rewrite chains */ - - ast chain_cons(const ast &chain, const ast &elem){ - return make(concat,chain,elem); - } - - ast chain_rest(const ast &chain){ - return arg(chain,0); - } - - ast chain_last(const ast &chain){ - return arg(chain,1); - } - - ast rewrite_update_rhs(const ast &rew, const ast &pos, const ast &new_rhs, const ast &new_cond){ - ast foo = subst_in_pos(rewrite_rhs(rew),pos,new_rhs); - ast equality = arg(rew,2); - return make(sym(rew),rewrite_pos(rew),my_and(rewrite_cond(rew),new_cond),make(op(equality),arg(equality,0),foo)); - } - - ast rewrite_update_lhs(const ast &rew, const ast &pos, const ast &new_lhs, const ast &new_cond){ - ast foo = subst_in_pos(rewrite_lhs(rew),pos,new_lhs); - ast equality = arg(rew,2); - return make(sym(rew),rewrite_pos(rew),my_and(rewrite_cond(rew),new_cond),make(op(equality),foo,arg(equality,1))); - } - - bool is_common_rewrite(const ast &rew){ - return term_common(arg(rew,2)); - } - - bool is_right_mover(const ast &rew){ - return term_common(rewrite_lhs(rew)) && !term_common(rewrite_rhs(rew)); - } - - bool is_left_mover(const ast &rew){ - return term_common(rewrite_rhs(rew)) && !term_common(rewrite_lhs(rew)); - } - - bool same_side(const ast &rew1, const ast &rew2){ - return sym(rew1) == sym(rew2); - } - - bool is_rewrite_side(LitType t, const ast &rew){ - if(t == LitA) - return sym(rew) == rewrite_A; - return sym(rew) == rewrite_B; - } - - LitType rewrite_side(const ast &rew){ - return (sym(rew) == rewrite_A) ? LitA : LitB; - } - - ast rewrite_to_formula(const ast &rew){ - return my_implies(arg(rew,1),arg(rew,2)); - } - - // make rewrite rew condition on rewrite cond - ast rewrite_conditional(const ast &cond, const ast &rew){ - ast cf = rewrite_to_formula(cond); - return make(sym(rew),arg(rew,0),my_and(arg(rew,1),cf),arg(rew,2)); - } - - ast reverse_rewrite(const ast &rew){ - ast equ = arg(rew,2); - return make(sym(rew),arg(rew,0),arg(rew,1),make(op(equ),arg(equ,1),arg(equ,0))); - } - - ast rewrite_pos_add(int apos, const ast &rew){ - return make(sym(rew),pos_add(apos,arg(rew,0)),arg(rew,1),arg(rew,2)); - } - - ast rewrite_pos_set(const ast &pos, const ast &rew){ - return make(sym(rew),pos,arg(rew,1),arg(rew,2)); - } - - ast rewrite_up(const ast &rew){ - return make(sym(rew),arg(arg(rew,0),1),arg(rew,1),arg(rew,2)); - } - - /** Adds a rewrite to a chain of rewrites, keeping the chain in - normal form. An empty chain is represented by true.*/ - - ast add_rewrite_to_chain(const ast &chain, const ast &rewrite){ - if(is_true(chain)) - return chain_cons(chain,rewrite); - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(same_side(last,rewrite)){ - ast p1 = rewrite_pos(last); - ast p2 = rewrite_pos(rewrite); - ast diff; - switch(pos_diff(p1,p2,diff)){ - case 1: { - ast absorb = rewrite_update_rhs(last,diff,rewrite_rhs(rewrite),rewrite_cond(rewrite)); - return add_rewrite_to_chain(rest,absorb); - } - case 0: - case -1: { - ast absorb = rewrite_update_lhs(rewrite,diff,rewrite_lhs(last),rewrite_cond(last)); - return add_rewrite_to_chain(rest,absorb); - } - default: {// independent case - bool rm = is_right_mover(last); - bool lm = is_left_mover(rewrite); - if((lm && !rm) || (rm && !lm)) - return chain_swap(rest,last,rewrite); - } - } - } - else { - if(is_left_mover(rewrite)){ - if(is_common_rewrite(last)) - return add_rewrite_to_chain(chain_cons(rest,flip_rewrite(last)),rewrite); - if(!is_left_mover(last)) - return chain_swap(rest,last,rewrite); - } - if(is_right_mover(last)){ - if(is_common_rewrite(rewrite)) - return add_rewrite_to_chain(chain,flip_rewrite(rewrite)); - if(!is_right_mover(rewrite)) - return chain_swap(rest,last,rewrite); - } - } - return chain_cons(chain,rewrite); - } - - ast chain_swap(const ast &rest, const ast &last, const ast &rewrite){ - return chain_cons(add_rewrite_to_chain(rest,rewrite),last); - } - - ast flip_rewrite(const ast &rew){ - symb flip_sym = (sym(rew) == rewrite_A) ? rewrite_B : rewrite_A; - ast cf = rewrite_to_formula(rew); - return make(flip_sym,arg(rew,0),my_implies(arg(rew,1),cf),arg(rew,2)); - } - - /** concatenates two rewrite chains, keeping result in normal form. */ - - ast concat_rewrite_chain(const ast &chain1, const ast &chain2){ - if(is_true(chain2)) return chain1; - if(is_true(chain1)) return chain2; - ast foo = concat_rewrite_chain(chain1,chain_rest(chain2)); - return add_rewrite_to_chain(foo,chain_last(chain2)); - } - - /** reverse a chain of rewrites */ - - ast reverse_chain_rec(const ast &chain, const ast &prefix){ - if(is_true(chain)) - return prefix; - ast last = reverse_rewrite(chain_last(chain)); - ast rest = chain_rest(chain); - return reverse_chain_rec(rest,chain_cons(prefix,last)); - } - - ast reverse_chain(const ast &chain){ - return reverse_chain_rec(chain,mk_true()); - } - - bool is_equivrel_chain(const ast &chain){ - if(is_true(chain)) - return true; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(is_true(rest)) - return !is_true(rewrite_lhs(last)); - return is_equivrel_chain(rest); - } - - bool is_negation_chain(const ast &chain){ - if(is_true(chain)) - return false; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(is_true(rest)) - return op(rewrite_rhs(last)) == Not; - return is_negation_chain(rest); - } - - ast commute_negation_chain(const ast &chain){ - if(is_true(chain)) - return chain; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(is_true(rest)){ - ast old = rewrite_rhs(last); - if(!(op(old) == Not)) - throw iz3_exception("bad negative equality chain"); - ast equ = arg(old,0); - if(!is_equivrel(equ)) - throw iz3_exception("bad negative equality chain"); - last = rewrite_update_rhs(last,top_pos,make(Not,make(op(equ),arg(equ,1),arg(equ,0))),make(True)); - return chain_cons(rest,last); - } - ast pos = rewrite_pos(last); - if(pos == top_pos) - throw iz3_exception("bad negative equality chain"); - int idx = pos_arg(pos); - if(idx != 0) - throw iz3_exception("bad negative equality chain"); - pos = arg(pos,1); - if(pos == top_pos){ - ast lhs = rewrite_lhs(last); - ast rhs = rewrite_rhs(last); - if(op(lhs) != Equal || op(rhs) != Equal) - throw iz3_exception("bad negative equality chain"); - last = make_rewrite(rewrite_side(last),rewrite_pos(last),rewrite_cond(last), - make(Iff,make(Equal,arg(lhs,1),arg(lhs,0)),make(Equal,arg(rhs,1),arg(rhs,0)))); - } - else { - idx = pos_arg(pos); - if(idx == 0) - idx = 1; - else if(idx == 1) - idx = 0; - else - throw iz3_exception("bad negative equality chain"); - pos = pos_add(0,pos_add(idx,arg(pos,1))); - last = make_rewrite(rewrite_side(last),pos,rewrite_cond(last),rewrite_equ(last)); - } - return chain_cons(commute_negation_chain(rest),last); - } - - // split a rewrite chain into head and tail at last top-level rewrite - ast get_head_chain(const ast &chain, ast &tail, bool is_not = true){ - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast pos = rewrite_pos(last); - if(pos == top_pos || (is_not && arg(pos,1) == top_pos)){ - tail = mk_true(); - return chain; - } - if(is_true(rest)) - throw iz3_exception("bad rewrite chain"); - ast head = get_head_chain(rest,tail,is_not); - tail = chain_cons(tail,last); - return head; - } - - bool has_mixed_summands(const ast &e){ - if(op(e) == Plus){ - int nargs = num_args(e); - for(int i = 0; i < nargs; i++) - if(has_mixed_summands(arg(e,i))) - return true; - return false; - } - return get_term_type(e) == LitMixed; - } - - // split a rewrite chain into head and tail at last sum with no mixed sumands - ast get_right_movers(const ast &chain, const ast &rhs, ast &tail, ast &mid){ - if(is_true(chain) || !has_mixed_summands(rhs)){ - mid = rhs; - tail = mk_true(); - return chain; - } - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast mm = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); - ast res = get_right_movers(rest,mm,tail,mid); - tail = chain_cons(tail,last); - return res; - } - - // split a rewrite chain into head and tail at first sum with no mixed sumands - ast get_left_movers(const ast &chain, const ast &lhs, ast &tail, ast &mid){ - if(is_true(chain)){ - mid = lhs; - if(!has_mixed_summands(lhs)){ - tail = mk_true(); - return chain; - } - return ast(); - } - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast res = get_left_movers(rest,lhs,tail,mid); - if(res.null()){ - mid = subst_in_pos(mid,rewrite_pos(last),rewrite_rhs(last)); - if(get_term_type(mid) != LitMixed){ - tail = mk_true(); - return chain; - } - return ast(); - } - tail = chain_cons(tail,last); - return res; - } - - - struct cannot_split: public iz3_exception { - cannot_split(): iz3_exception("cannot_split") {} - }; - - /** Split a chain of rewrites two chains, operating on positions 0 and 1. - Fail if any rewrite in the chain operates on top position. */ - void split_chain_rec(const ast &chain, ast *res){ - if(is_true(chain)) - return; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - split_chain_rec(rest,res); - ast pos = rewrite_pos(last); - if(pos == top_pos){ - if(rewrite_lhs(last) == rewrite_rhs(last)) - return; // skip if it's a noop - throw cannot_split(); - } - int arg = pos_arg(pos); - if(arg<0 || arg > 1) - throw cannot_split(); - res[arg] = chain_cons(res[arg],rewrite_up(last)); - } - - void split_chain(const ast &chain, ast *res){ - res[0] = res[1] = mk_true(); - split_chain_rec(chain,res); - } - - ast extract_rewrites(const ast &chain, const ast &pos){ - if(is_true(chain)) - return chain; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast new_rest = extract_rewrites(rest,pos); - ast p1 = rewrite_pos(last); - ast diff; - switch(pos_diff(p1,pos,diff)){ - case -1: { - ast new_last = rewrite_pos_set(diff, last); - return chain_cons(new_rest,new_last); - } - case 1: - if(rewrite_lhs(last) != rewrite_rhs(last)) - throw iz3_exception("bad rewrite chain"); - break; - default:; - } - return new_rest; - } - - ast down_chain(const ast &chain){ - ast split[2]; - split_chain(chain,split); - return split[0]; - } - - ast chain_conditions(LitType t, const ast &chain){ - if(is_true(chain)) - return mk_true(); - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast cond = chain_conditions(t,rest); - if(is_rewrite_side(t,last)) - cond = my_and(cond,rewrite_cond(last)); - return cond; - } - - ast chain_formulas(LitType t, const ast &chain){ - if(is_true(chain)) - return mk_true(); - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast cond = chain_formulas(t,rest); - if(is_rewrite_side(t,last)) - cond = my_and(cond,rewrite_equ(last)); - return cond; - } - - - bool rewrites_from_to(const ast &chain, const ast &lhs, const ast &rhs){ - if(is_true(chain)) - return lhs == rhs; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast mid = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); - return rewrites_from_to(rest,lhs,mid); - } - - struct bad_ineq_inference: public iz3_exception { - bad_ineq_inference(): iz3_exception("bad_ineq_inference") {} - }; - - ast chain_ineqs(opr comp_op, LitType t, const ast &chain, const ast &lhs, const ast &rhs){ - if(is_true(chain)){ - if (lhs != rhs) { - TRACE("duality", print_expr(tout, lhs); tout << " "; print_expr(tout, rhs); tout << "\n";); - throw bad_ineq_inference(); - } - return make(Leq,make_int(rational(0)),make_int(rational(0))); - } - TRACE("duality", print_expr(tout, chain); print_expr(tout << "\n", lhs); tout << " "; print_expr(tout, rhs); tout << "\n";); - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast mid = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); - ast cond = chain_ineqs(comp_op,t,rest,lhs,mid); - if(is_rewrite_side(t,last)){ - ast diff; - if(comp_op == Leq) diff = make(Sub,rhs,mid); - else diff = make(Sub,mid,rhs); - ast foo = make(Leq,make_int("0"),z3_simplify(diff)); - if(is_true(cond)) - cond = foo; - else { - linear_comb(cond,make_int(rational(1)),foo); - cond = simplify_ineq(cond); - } - } - return cond; - } - - ast ineq_to_lhs(const ast &ineq){ - ast s = make(Leq,make_int(rational(0)),make_int(rational(0))); - linear_comb(s,make_int(rational(1)),ineq); - return simplify_ineq(s); - } - - void eq_from_ineq(const ast &ineq, ast &lhs, ast &rhs){ - // ast s = ineq_to_lhs(ineq); - // ast srhs = arg(s,1); - ast srhs = arg(ineq,0); - if(op(srhs) == Plus && num_args(srhs) == 2 && arg(ineq,1) == make_int(rational(0))){ - lhs = arg(srhs,0); - rhs = arg(srhs,1); - // if(op(lhs) == Times) - // std::swap(lhs,rhs); - if(op(rhs) == Times){ - rhs = arg(rhs,1); - // if(op(ineq) == Leq) - // std::swap(lhs,rhs); - return; - } - } - if(op(ineq) == Leq || op(ineq) == Geq){ - lhs = srhs; - rhs = arg(ineq,1); - return; - } - throw iz3_exception("bad ineq"); - } - - ast chain_pos_add(int arg, const ast &chain){ - if(is_true(chain)) - return mk_true(); - ast last = rewrite_pos_add(arg,chain_last(chain)); - ast rest = chain_pos_add(arg,chain_rest(chain)); - return chain_cons(rest,last); - } - - ast apply_rewrite_chain(const ast &t, const ast &chain){ - if(is_true(chain)) - return t; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast mid = apply_rewrite_chain(t,rest); - ast res = subst_in_pos(mid,rewrite_pos(last),rewrite_rhs(last)); - return res; - } - - ast drop_rewrites(LitType t, const ast &chain, ast &remainder){ - if(!is_true(chain)){ - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(is_rewrite_side(t,last)){ - ast res = drop_rewrites(t,rest,remainder); - remainder = chain_cons(remainder,last); - return res; - } - } - remainder = mk_true(); - return chain; - } - - // Normalization chains - - ast cons_normal(const ast &first, const ast &rest){ - return make(normal_chain,first,rest); - } - - ast normal_first(const ast &t){ - return arg(t,0); - } - - ast normal_rest(const ast &t){ - return arg(t,1); - } - - ast normal_lhs(const ast &t){ - return arg(arg(t,0),0); - } - - ast normal_rhs(const ast &t){ - return arg(arg(t,0),1); - } - - ast normal_proof(const ast &t){ - return arg(t,1); - } - - ast make_normal_step(const ast &lhs, const ast &rhs, const ast &proof){ - return make(normal_step,make_equiv(lhs,rhs),proof); - } - - ast make_normal(const ast &ineq, const ast &nrml){ - if(!is_ineq(ineq)) - throw iz3_exception("what?"); - return make(normal,ineq,nrml); - } - - ast fix_normal(const ast &lhs, const ast &rhs, const ast &proof){ - LitType lhst = get_term_type(lhs); - LitType rhst = get_term_type(rhs); - if(lhst == LitMixed && (rhst != LitMixed || ast_id(lhs) < ast_id(rhs))) - return make_normal_step(lhs,rhs,proof); - if(rhst == LitMixed && (lhst != LitMixed || ast_id(rhs) < ast_id(lhs))) - return make_normal_step(rhs,lhs,reverse_chain(proof)); - throw iz3_exception("help!"); - } - - ast chain_side_proves(LitType side, const ast &chain){ - LitType other_side = side == LitA ? LitB : LitA; - return my_and(chain_conditions(other_side,chain),my_implies(chain_conditions(side,chain),chain_formulas(side,chain))); - } - - // Merge two normalization chains - ast merge_normal_chains_rec(const ast &chain1, const ast &chain2, hash_map &trans, ast &Aproves, ast &Bproves){ - if(is_true(chain1)) - return chain2; - if(is_true(chain2)) - return chain1; - ast f1 = normal_first(chain1); - ast f2 = normal_first(chain2); - ast lhs1 = normal_lhs(f1); - ast lhs2 = normal_lhs(f2); - int id1 = ast_id(lhs1); - int id2 = ast_id(lhs2); - if(id1 < id2) - return cons_normal(f1,merge_normal_chains_rec(normal_rest(chain1),chain2,trans,Aproves,Bproves)); - if(id2 < id1) - return cons_normal(f2,merge_normal_chains_rec(chain1,normal_rest(chain2),trans,Aproves,Bproves)); - ast rhs1 = normal_rhs(f1); - ast rhs2 = normal_rhs(f2); - LitType t1 = get_term_type(rhs1); - LitType t2 = get_term_type(rhs2); - int tid1 = ast_id(rhs1); - int tid2 = ast_id(rhs2); - ast pf1 = normal_proof(f1); - ast pf2 = normal_proof(f2); - ast new_normal; - if(t1 == LitMixed && (t2 != LitMixed || tid2 > tid1)){ - ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); - new_normal = f2; - trans[rhs1] = make_normal_step(rhs1,rhs2,new_proof); - } - else if(t2 == LitMixed && (t1 != LitMixed || tid1 > tid2)) - return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); - else if(t1 == LitA && t2 == LitB){ - ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); - ast Bproof, Aproof = drop_rewrites(LitB,new_proof,Bproof); - ast mcA = chain_side_proves(LitB,Aproof); - Bproves = my_and(Bproves,mcA); - ast mcB = chain_side_proves(LitA,Bproof); - Aproves = my_and(Aproves,mcB); - ast rep = apply_rewrite_chain(rhs1,Aproof); - new_proof = concat_rewrite_chain(pf1,Aproof); - new_normal = make_normal_step(lhs1,rep,new_proof); - ast A_normal = make_normal_step(rhs1,rep,Aproof); - ast res = cons_normal(new_normal,merge_normal_chains_rec(normal_rest(chain1),normal_rest(chain2),trans,Aproves,Bproves)); - res = merge_normal_chains_rec(res,cons_normal(A_normal,make(True)),trans,Aproves,Bproves); - return res; - } - else if(t1 == LitB && t2 == LitA) - return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); - else if(t1 == LitA) { - ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); - ast mc = chain_side_proves(LitB,new_proof); - Bproves = my_and(Bproves,mc); - new_normal = f1; // choice is arbitrary - } - else { /* t1 = t2 = LitB */ - ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); - ast mc = chain_side_proves(LitA,new_proof); - Aproves = my_and(Aproves,mc); - new_normal = f1; // choice is arbitrary - } - return cons_normal(new_normal,merge_normal_chains_rec(normal_rest(chain1),normal_rest(chain2),trans,Aproves,Bproves)); - } - - ast trans_normal_chain(const ast &chain, hash_map &trans){ - if(is_true(chain)) - return chain; - ast f = normal_first(chain); - ast r = normal_rest(chain); - r = trans_normal_chain(r,trans); - ast rhs = normal_rhs(f); - hash_map::iterator it = trans.find(rhs); - ast new_normal; - if(it != trans.end() && get_term_type(normal_lhs(f)) == LitMixed){ - const ast &f2 = it->second; - ast pf = concat_rewrite_chain(normal_proof(f),normal_proof(f2)); - new_normal = make_normal_step(normal_lhs(f),normal_rhs(f2),pf); - } - else - new_normal = f; - if(get_term_type(normal_lhs(f)) == LitMixed) - trans[normal_lhs(f)] = new_normal; - return cons_normal(new_normal,r); - } - - ast merge_normal_chains(const ast &chain1, const ast &chain2, ast &Aproves, ast &Bproves){ - hash_map trans; - ast res = merge_normal_chains_rec(chain1,chain2,trans,Aproves,Bproves); - res = trans_normal_chain(res,trans); - return res; - } - - bool destruct_cond_ineq(ast t, ast &Aproves, ast &Bproves, ast&ineq){ - if(op(t) == And){ - Aproves = arg(t,0); - t = arg(t,1); - } - else - Aproves = mk_true(); - if(op(t) == Implies){ - Bproves = arg(t,0); - t = arg(t,1); - } - else - Bproves = mk_true(); - if(is_normal_ineq(t)){ - ineq = t; - return true; - } - return false; - } - - ast cons_cond_ineq(const ast &Aproves, const ast &Bproves, const ast &ineq){ - return my_and(Aproves,my_implies(Bproves,ineq)); - } - - ast normalize(const ast &ct){ - ast Aproves,Bproves,t; - if(!destruct_cond_ineq(ct,Aproves,Bproves,t)) - return ct; - if(sym(t) != normal) - return ct; - ast chain = arg(t,1); - hash_map map; - for(ast c = chain; !is_true(c); c = normal_rest(c)){ - ast first = normal_first(c); - ast lhs = normal_lhs(first); - ast rhs = normal_rhs(first); - map[lhs] = rhs; - } - ast res = subst(map,arg(t,0)); - return cons_cond_ineq(Aproves,Bproves,res); - } - - /** Make an assumption node. The given clause is assumed in the given frame. */ - node make_assumption(int frame, const std::vector &assumption) override { - if(!weak){ - if(pv->in_range(frame,rng)){ - std::vector itp_clause; - for(unsigned i = 0; i < assumption.size(); i++) - if(get_term_type(assumption[i]) != LitA) - itp_clause.push_back(assumption[i]); - ast res = my_or(itp_clause); - return res; - } - else { - return mk_true(); - } - } - else { - if(pv->in_range(frame,rng)){ - return mk_false(); - } - else { - std::vector itp_clause; - for(unsigned i = 0; i < assumption.size(); i++) - if(get_term_type(assumption[i]) != LitB) - itp_clause.push_back(assumption[i]); - ast res = my_or(itp_clause); - return mk_not(res); - } - } - } - - ast make_local_rewrite(LitType t, const ast &p){ - ast rew = is_equivrel(p) ? p : make(Iff,mk_true(),p); -#if 0 - if(op(rew) == Iff && !is_true(arg(rew,0))) - return diff_chain(t,top_pos,arg(rew,0),arg(rew,1), mk_true()); -#endif - return chain_cons(mk_true(),make_rewrite(t, top_pos, mk_true(), rew)); - } - - ast triv_interp(const symb &rule, const std::vector &premises, int mask_in){ - std::vector ps; ps.resize(premises.size()); - std::vector conjs; - int mask = 0; - for(unsigned i = 0; i < ps.size(); i++){ - ast p = premises[i]; - LitType t = get_term_type(p); - switch(t){ - case LitA: - case LitB: - ps[i] = make_local_rewrite(t,p); - break; - default: - ps[i] = get_placeholder(p); // can only prove consequent! - if(mask_in & (1 << i)) - mask |= (1 << conjs.size()); - conjs.push_back(p); - } - } - ast ref = make(rule,ps); - ast res = make_contra_node(ref,conjs,mask); - return res; - } - - ast triv_interp(const symb &rule, const ast &p0, const ast &p1, const ast &p2, int mask){ - std::vector ps; ps.resize(3); - ps[0] = p0; - ps[1] = p1; - ps[2] = p2; - return triv_interp(rule,ps,mask); - } - - /** Make a modus-ponens node. This takes derivations of |- x - and |- x = y and produces |- y */ - - node make_mp(const ast &p_eq_q, const ast &prem1, const ast &prem2) override { - - /* Interpolate the axiom p, p=q -> q */ - ast p = arg(p_eq_q,0); - ast q = arg(p_eq_q,1); - ast itp; - if(get_term_type(p_eq_q) == LitMixed){ - int mask = 1 << 2; - if(op(p) == Not && is_equivrel(arg(p,0))) - mask |= 1; // we may need to run this rule backward if first premise is negative equality - itp = triv_interp(modpon,p,p_eq_q,mk_not(q),mask); - } - else { - if(get_term_type(p) == LitA){ - if(get_term_type(q) == LitA){ - if(op(q) == Or) - itp = make_assumption(rng.hi,args(q)); - else - itp = mk_false(); - } - else { - if(get_term_type(p_eq_q) == LitA) - itp = q; - else - throw proof_error(); - } - } - else { - if(get_term_type(q) == LitA){ - if(get_term_type(make(Equal,p,q)) == LitA) - itp = mk_not(p); - else - throw proof_error(); - } - else - itp = mk_true(); - } - } - - /* Resolve it with the premises */ - std::vector conc; conc.push_back(q); conc.push_back(mk_not(p_eq_q)); - itp = make_resolution(p,conc,itp,prem1); - conc.pop_back(); - itp = make_resolution(p_eq_q,conc,itp,prem2); - return itp; - } - - ast capture_localization(ast e){ - // #define CAPTURE_LOCALIZATION -#ifdef CAPTURE_LOCALIZATION - for(int i = localization_vars.size() - 1; i >= 0; i--){ - LocVar &lv = localization_vars[i]; - if(occurs_in(lv.var,e)){ - symb q = (pv->in_range(lv.frame,rng)) ? sexists : sforall; - e = make(q,make(Equal,lv.var,lv.term),e); // use Equal because it is polymorphic - } - } -#endif - return e; - } - - /** Make an axiom node. The conclusion must be an instance of an axiom. */ - node make_axiom(const std::vector &conclusion, prover::range frng) override { - int nargs = conclusion.size(); - std::vector largs(nargs); - std::vector eqs; - std::vector pfs; - - for(int i = 0; i < nargs; i++){ - ast argpf; - ast lit = conclusion[i]; - largs[i] = localize_term(lit,frng,argpf); - frng = pv->range_glb(frng,pv->ast_scope(largs[i])); - if(largs[i] != lit){ - eqs.push_back(make_equiv(largs[i],lit)); - pfs.push_back(argpf); - } - } - - int frame = pv->range_max(frng); - ast itp = make_assumption(frame,largs); - - for(unsigned i = 0; i < eqs.size(); i++) - itp = make_mp(eqs[i],itp,pfs[i]); - return capture_localization(itp); - } - - node make_axiom(const std::vector &conclusion) override { - return make_axiom(conclusion,pv->range_full()); - } - - /** Make a Contra node. This rule takes a derivation of the form - Gamma |- False and produces |- \/~Gamma. */ - - node make_contra(node prem, const std::vector &conclusion) override { - return prem; - } - - /** Make hypothesis. Creates a node of the form P |- P. */ - - node make_hypothesis(const ast &P) override { - if(is_not(P)) - return make_hypothesis(arg(P,0)); - switch(get_term_type(P)){ - case LitA: - return mk_false(); - case LitB: - return mk_true(); - default: // mixed hypothesis - switch(op(P)){ - case Geq: - case Leq: - case Gt: - case Lt: { - ast zleqz = make(Leq,make_int("0"),make_int("0")); - ast fark1 = make(sum,zleqz,make_int("1"),get_placeholder(P)); - ast fark2 = make(sum,fark1,make_int("1"),get_placeholder(mk_not(P))); - ast res = make(And,make(contra,fark2,mk_false()), - make(contra,get_placeholder(mk_not(P)),P), - make(contra,get_placeholder(P),mk_not(P))); - return res; - } - default: { - ast em = make(exmid,P,get_placeholder(P),get_placeholder(mk_not(P))); - ast res = make(And,make(contra,em,mk_false()), - make(contra,get_placeholder(mk_not(P)),P), - make(contra,get_placeholder(P),mk_not(P))); - return res; - } - } - } - } - - /** Make a Reflexivity node. This rule produces |- x = x */ - - node make_reflexivity(ast con) override { - if(get_term_type(con) == LitA) - return mk_false(); - if(get_term_type(con) == LitB) - return mk_true(); - ast itp = make(And,make(contra,no_proof,mk_false()), - make(contra,mk_true(),mk_not(con))); - return itp; - } - - /** Make a Symmetry node. This takes a derivation of |- x = y and - produces | y = x. Ditto for ~(x=y) */ - - node make_symmetry(ast con, const ast &premcon, node prem) override { -#if 0 - ast x = arg(con,0); - ast y = arg(con,1); - ast p = make(op(con),y,x); -#endif - if(get_term_type(con) != LitMixed) - return prem; // symmetry shmymmetry... - ast em = make(exmid,con,make(symm,get_placeholder(premcon)),get_placeholder(mk_not(con))); - ast itp = make(And,make(contra,em,mk_false()), - make(contra,make(symm,get_placeholder(mk_not(con))),premcon), - make(contra,make(symm,get_placeholder(premcon)),mk_not(con))); - - std::vector conc; conc.push_back(con); - itp = make_resolution(premcon,conc,itp,prem); - return itp; - } - - ast make_equiv_rel(const ast &x, const ast &y){ - if(is_bool_type(get_type(x))) - return make(Iff,x,y); - return make(Equal,x,y); - } - - /** Make a transitivity node. This takes derivations of |- x = y - and |- y = z produces | x = z */ - - node make_transitivity(const ast &x, const ast &y, const ast &z, node prem1, node prem2) override { - - /* Interpolate the axiom x=y,y=z,-> x=z */ - ast p = make_equiv_rel(x,y); - ast q = make_equiv_rel(y,z); - ast r = make_equiv_rel(x,z); - ast equiv = make(Iff,p,r); - ast itp; - - itp = make_congruence(q,equiv,prem2); - itp = make_mp(equiv,prem1,itp); - - return itp; - - } - - /** Make a congruence node. This takes derivations of |- x_i = y_i - and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ - - node make_congruence(const ast &p, const ast &con, const ast &prem1) override { - ast x = arg(p,0), y = arg(p,1); - ast itp; - LitType con_t = get_term_type(con); - if(get_term_type(p) == LitA){ - if(con_t == LitA) - itp = mk_false(); - else if(con_t == LitB) - itp = p; - else - itp = make_mixed_congruence(x, y, p, con, prem1); - } - else { - if(con_t == LitA) - itp = mk_not(p); - else{ - if(con_t == LitB) - itp = mk_true(); - else - itp = make_mixed_congruence(x, y, p, con, prem1); - } - } - std::vector conc; conc.push_back(con); - itp = make_resolution(p,conc,itp,prem1); - return itp; - } - - int find_congruence_position(const ast &p, const ast &con){ - // find the argument position of x and y - const ast &x = arg(p,0); - const ast &y = arg(p,1); - int nargs = num_args(arg(con,0)); - for(int i = 0; i < nargs; i++) - if(x == arg(arg(con,0),i) && y == arg(arg(con,1),i)) - return i; - throw proof_error(); - } - - /** Make a congruence node. This takes derivations of |- x_i1 = y_i1, |- x_i2 = y_i2,... - and produces |- f(...x_i1...x_i2...) = f(...y_i1...y_i2...) */ - - node make_congruence(const std::vector &p, const ast &con, const std::vector &prems) override { - if(p.size() == 0) - throw proof_error(); - if(p.size() == 1) - return make_congruence(p[0],con,prems[0]); - ast thing = con; - ast res = mk_true(); - for(unsigned i = 0; i < p.size(); i++){ - int pos = find_congruence_position(p[i],thing); - ast next = subst_in_arg_pos(pos,arg(p[i],1),arg(thing,0)); - ast goal = make(op(thing),arg(thing,0),next); - ast equa = make_congruence(p[i],goal,prems[i]); - if(i == 0) - res = equa; - else { - ast trace = make(op(con),arg(con,0),arg(thing,0)); - ast equiv = make(Iff,trace,make(op(trace),arg(trace,0),next)); - ast foo = make_congruence(goal,equiv,equa); - res = make_mp(equiv,res,foo); - } - thing = make(op(thing),next,arg(thing,1)); - } - return res; - } - - /* Interpolate a mixed congruence axiom. */ - - virtual ast make_mixed_congruence(const ast &x, const ast &y, const ast &p, const ast &con, const ast &prem1){ - ast foo = p; - std::vector conjs; - LitType t = get_term_type(foo); - switch(t){ - case LitA: - case LitB: - foo = make_local_rewrite(t,foo); - break; - case LitMixed: - conjs.push_back(foo); - foo = get_placeholder(foo); - } - // find the argument position of x and y - int pos = -1; - int nargs = num_args(arg(con,0)); - for(int i = 0; i < nargs; i++) - if(x == arg(arg(con,0),i) && y == arg(arg(con,1),i)) - pos = i; - if(pos == -1) - throw proof_error(); - ast bar = make(cong,foo,make_int(rational(pos)),get_placeholder(mk_not(con))); - conjs.push_back(mk_not(con)); - return make_contra_node(bar,conjs); - } - - ast subst_in_arg_pos(int pos, ast term, ast app){ - std::vector args; - get_args(app,args); - args[pos] = term; - return clone(app,args); - } - - /** Make a farkas proof node. */ - - node make_farkas(ast con, const std::vector &prems, const std::vector &prem_cons, - const std::vector &coeffs) override { - - /* Compute the interpolant for the clause */ - - ast zero = make_int("0"); - std::vector conjs; - ast thing = make(Leq,zero,zero); - for(unsigned i = 0; i < prem_cons.size(); i++){ - const ast &lit = prem_cons[i]; - if(get_term_type(lit) == LitA) - // Farkas rule seems to assume strict integer inequalities are rounded - linear_comb(thing,coeffs[i],lit,true /*round_off*/); - } - thing = simplify_ineq(thing); - for(unsigned i = 0; i < prem_cons.size(); i++){ - const ast &lit = prem_cons[i]; - if(get_term_type(lit) == LitMixed){ - thing = make(sum,thing,coeffs[i],get_placeholder(lit)); - conjs.push_back(lit); - } - } - thing = make_contra_node(thing,conjs); - - /* Resolve it with the premises */ - std::vector conc; conc.resize(prem_cons.size()); - for(unsigned i = 0; i < prem_cons.size(); i++) - conc[prem_cons.size()-i-1] = prem_cons[i]; - for(unsigned i = 0; i < prem_cons.size(); i++){ - thing = make_resolution(prem_cons[i],conc,thing,prems[i]); - conc.pop_back(); - } - return thing; - } - - /** Set P to P + cQ, where P and Q are linear inequalities. Assumes P is 0 <= y or 0 < y. */ - - void linear_comb(ast &P, const ast &c, const ast &Q, bool round_off = false){ - ast Qrhs; - bool qstrict = false; - if(is_not(Q)){ - ast nQ = arg(Q,0); - switch(op(nQ)){ - case Gt: - Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); - break; - case Lt: - Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); - break; - case Geq: - Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); - qstrict = true; - break; - case Leq: - Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); - qstrict = true; - break; - default: - throw proof_error(); - } - } - else { - switch(op(Q)){ - case Leq: - Qrhs = make(Sub,arg(Q,1),arg(Q,0)); - break; - case Geq: - Qrhs = make(Sub,arg(Q,0),arg(Q,1)); - break; - case Lt: - Qrhs = make(Sub,arg(Q,1),arg(Q,0)); - qstrict = true; - break; - case Gt: - Qrhs = make(Sub,arg(Q,0),arg(Q,1)); - qstrict = true; - break; - default: - throw proof_error(); - } - } -#if 0 - bool pstrict = op(P) == Lt, strict = pstrict || qstrict; - if(pstrict && qstrict && round_off) - Qrhs = make(Sub,Qrhs,make_int(rational(1))); -#else - if (round_off && get_type(Qrhs) != int_type()) - round_off = false; - bool pstrict = op(P) == Lt; - if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ - Qrhs = make(Sub,Qrhs,make_int(rational(1))); - qstrict = false; - } - Qrhs = make(Times,c,Qrhs); - bool strict = pstrict || qstrict; -#endif - if(strict) - P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); - else - P = make(Leq,arg(P,0),make(Plus,arg(P,1),Qrhs)); - } - - /* Make an axiom instance of the form |- x<=y, y<= x -> x =y */ - node make_leq2eq(ast x, ast y, const ast &xleqy, const ast &yleqx) override { - ast con = make(Equal,x,y); - ast itp; - switch(get_term_type(con)){ - case LitA: - itp = mk_false(); - break; - case LitB: - itp = mk_true(); - break; - default: { // mixed equality - if(get_term_type(x) == LitMixed || get_term_type(y) == LitMixed){ - if(y == make_int(rational(0)) && op(x) == Plus && num_args(x) == 2){ - // std::cerr << "WARNING: untested case in leq2eq\n"; - } - else { - // std::cerr << "WARNING: mixed term in leq2eq\n"; - std::vector lits; - lits.push_back(con); - lits.push_back(make(Not,xleqy)); - lits.push_back(make(Not,yleqx)); - return make_axiom(lits); - } - } - std::vector conjs; conjs.resize(3); - conjs[0] = mk_not(con); - conjs[1] = xleqy; - conjs[2] = yleqx; - itp = make_contra_node(make(leq2eq, - get_placeholder(mk_not(con)), - get_placeholder(xleqy), - get_placeholder(yleqx)), - conjs,1); - } - } - return itp; - } - - /* Make an axiom instance of the form |- x = y -> x <= y */ - node make_eq2leq(ast x, ast y, const ast &xleqy) override { - ast itp; - switch(get_term_type(xleqy)){ - case LitA: - itp = mk_false(); - break; - case LitB: - itp = mk_true(); - break; - default: { // mixed equality - std::vector conjs; conjs.resize(2); - conjs[0] = make(Equal,x,y); - conjs[1] = mk_not(xleqy); - itp = make(eq2leq,get_placeholder(conjs[0]),get_placeholder(conjs[1])); - itp = make_contra_node(itp,conjs,2); - } - } - return itp; - } - - - /* Make an inference of the form t <= c |- t/d <= floor(c/d) where t - is an affine term divisble by d and c is an integer constant */ - node make_cut_rule(const ast &tleqc, const ast &d, const ast &con, const ast &prem) override { - ast itp = mk_false(); - switch(get_term_type(con)){ - case LitA: - itp = mk_false(); - break; - case LitB: - itp = mk_true(); - break; - default: { - std::vector conjs; conjs.resize(2); - conjs[0] = tleqc; - conjs[1] = mk_not(con); - itp = make(sum,get_placeholder(conjs[0]),d,get_placeholder(conjs[1])); - itp = make_contra_node(itp,conjs); - } - } - std::vector conc; conc.push_back(con); - itp = make_resolution(tleqc,conc,itp,prem); - return itp; - } - - - - // create a fresh variable for localization - ast fresh_localization_var(const ast &term, int frame){ - std::ostringstream s; - s << "%" << (localization_vars.size()); - ast var = make_var(s.str().c_str(),get_type(term)); - pv->sym_range(sym(var)) = pv->range_full(); // make this variable global - localization_vars.push_back(LocVar(var,term,frame)); - return var; - } - - struct LocVar { // localization vars - ast var; // a fresh variable - ast term; // term it represents - int frame; // frame in which it's defined - LocVar(ast v, ast t, int f){var=v;term=t;frame=f;} - }; - - std::vector localization_vars; // localization vars in order of creation - - struct locmaps { - hash_map localization_map; // maps terms to their localization vars - hash_map localization_pf_map; // maps terms to proofs of their localizations - }; - - hash_map localization_maps_per_range; - - /* "localize" a term e to a given frame range, creating new symbols to - represent non-local subterms. This returns the localized version e_l, - as well as a proof thet e_l = l. - */ - - ast make_refl(const ast &e){ - if(get_term_type(e) == LitA) - return mk_false(); - return mk_true(); // TODO: is this right? - } - - - ast make_equiv(const ast &x, const ast &y){ - if(get_type(x) == bool_type()) - return make(Iff,x,y); - else - return make(Equal,x,y); - } - - bool range_is_global(const prover::range &r){ - if(pv->range_contained(r,rng)) - return false; - if(!pv->ranges_intersect(r,rng)) - return false; - return true; - } - - ast localize_term(ast e, const prover::range &rng, ast &pf){ - - // we need to memoize this function separately for A, B and global - prover::range map_range = rng; - if(range_is_global(map_range)) - map_range = pv->range_full(); - locmaps &maps = localization_maps_per_range[map_range]; - hash_map &localization_map = maps.localization_map; - hash_map &localization_pf_map = maps.localization_pf_map; - - ast orig_e = e; - pf = make_refl(e); // proof that e = e - - // prover::range erng = - pv->ast_scope(e); -#if 0 - if(!(erng.lo > erng.hi) && pv->ranges_intersect(pv->ast_scope(e),rng)){ - return e; // this term occurs in range, so it's O.K. - } -#endif - - hash_map::iterator it = localization_map.find(e); - - if(it != localization_map.end() && is_bool_type(get_type(e)) - && !pv->ranges_intersect(pv->ast_scope(it->second),rng)) - it = localization_map.end(); // prevent quantifiers over booleans - - if(it != localization_map.end()){ - pf = localization_pf_map[e]; - e = it->second; - } - - else { - // if it is non-local, we must first localize the arguments to - // the range of its function symbol - - int nargs = num_args(e); - if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ - prover::range frng = rng; - opr o = op(e); - if(o == Uninterpreted){ - symb f = sym(e); - prover::range srng = pv->sym_range(f); - if(pv->ranges_intersect(srng,rng)) // localize to desired range if possible - frng = pv->range_glb(srng,rng); - else - frng = srng; // this term will be localized - } - else if(o == Plus || o == Times){ // don't want bound variables inside arith ops - // std::cout << "WARNING: non-local arithmetic\n"; - // frng = erng; // this term will be localized - } - else if(o == Select){ // treat the array term like a function symbol - prover::range srng = pv->ast_scope(arg(e,0)); - if(!(srng.lo > srng.hi) && pv->ranges_intersect(srng,rng)) // localize to desired range if possible - frng = pv->range_glb(srng,rng); - else - frng = srng; // this term will be localized - } - std::vector largs(nargs); - std::vector eqs; - std::vector pfs; - for(int i = 0; i < nargs; i++){ - ast argpf; - largs[i] = localize_term(arg(e,i),frng,argpf); - frng = pv->range_glb(frng,pv->ast_scope(largs[i])); - if(largs[i] != arg(e,i)){ - eqs.push_back(make_equiv(largs[i],arg(e,i))); - pfs.push_back(argpf); - } - } - - e = clone(e,largs); - if(pfs.size()) - pf = make_congruence(eqs,make_equiv(e,orig_e),pfs); - // assert(is_local(e)); - } - - localization_pf_map[orig_e] = pf; - localization_map[orig_e] = e; - } - - if(pv->ranges_intersect(pv->ast_scope(e),rng)) - return e; // this term occurs in range, so it's O.K. - - if(is_array_type(get_type(e))) - std::cerr << "WARNING: array quantifier\n"; - - // choose a frame for the constraint that is close to range - int frame = pv->range_near(pv->ast_scope(e),rng); - - ast new_var = fresh_localization_var(e,frame); - localization_map[orig_e] = new_var; - std::vector foo; foo.push_back(make_equiv(new_var,e)); - ast bar = make_assumption(frame,foo); - pf = make_transitivity(new_var,e,orig_e,bar,pf); - localization_pf_map[orig_e] = pf; - - // HACK: try to bias this term in the future - if(!pv->range_is_full(rng)){ - prover::range rf = pv->range_full(); - locmaps &fmaps = localization_maps_per_range[rf]; - hash_map &flocalization_map = fmaps.localization_map; - hash_map &flocalization_pf_map = fmaps.localization_pf_map; - // if(flocalization_map.find(orig_e) == flocalization_map.end()) - { - flocalization_map[orig_e] = new_var; - flocalization_pf_map[orig_e] = pf; - } - } - - - return new_var; - } - - ast delete_quant(hash_map &memo, const ast &v, const ast &e){ - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - opr o = op(e); - switch(o){ - case Or: - case And: - case Implies: { - unsigned nargs = num_args(e); - std::vector args; args.resize(nargs); - for(unsigned i = 0; i < nargs; i++) - args[i] = delete_quant(memo, v, arg(e,i)); - res = make(o,args); - break; - } - case Uninterpreted: { - symb s = sym(e); - ast w = arg(arg(e,0),0); - if(s == sforall || s == sexists){ - res = delete_quant(memo,v,arg(e,1)); - if(w != v) - res = make(s,w,res); - break; - } - } - default: - res = e; - } - } - return res; - } - - ast insert_quants(hash_map &memo, const ast &e){ - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - opr o = op(e); - switch(o){ - case Or: - case And: - case Implies: { - unsigned nargs = num_args(e); - std::vector args; args.resize(nargs); - for(unsigned i = 0; i < nargs; i++) - args[i] = insert_quants(memo, arg(e,i)); - res = make(o,args); - break; - } - case Uninterpreted: { - symb s = sym(e); - if(s == sforall || s == sexists){ - opr q = (s == sforall) ? Forall : Exists; - ast v = arg(arg(e,0),0); - hash_map dmemo; - ast body = delete_quant(dmemo,v,arg(e,1)); - body = insert_quants(memo,body); - res = apply_quant(q,v,body); - break; - } - } - default: - res = e; - } - } - return res; - } - - ast add_quants(ast e){ -#ifdef CAPTURE_LOCALIZATION - if(!localization_vars.empty()){ - hash_map memo; - e = insert_quants(memo,e); - } -#else - for(int i = localization_vars.size() - 1; i >= 0; i--){ - LocVar &lv = localization_vars[i]; - opr quantifier = (pv->in_range(lv.frame,rng)) ? Exists : Forall; - e = apply_quant(quantifier,lv.var,e); - } -#endif - return e; - } - - node make_resolution(ast pivot, node premise1, node premise2) { - std::vector lits; - return make_resolution(pivot,lits,premise1,premise2); - } - - /* Return an interpolant from a proof of false */ - ast interpolate(const node &pf) override { - // proof of false must be a formula, with quantified symbols -#ifndef BOGUS_QUANTS - return close_universally(add_quants(z3_simplify(pf))); -#else - return close_universally(z3_simplify(pf)); -#endif - } - - ast resolve_with_quantifier(const ast &pivot1, const ast &conj1, - const ast &pivot2, const ast &conj2){ - if(is_not(arg(pivot1,1))) - return resolve_with_quantifier(pivot2,conj2,pivot1,conj1); - ast eqpf; - ast P = arg(pivot1,1); - ast Ploc = localize_term(P, rng, eqpf); - ast pPloc = make_hypothesis(Ploc); - ast pP = make_mp(make(Iff,Ploc,P),pPloc,eqpf); - ast rP = make_resolution(P,conj1,pP); - ast nP = mk_not(P); - ast nPloc = mk_not(Ploc); - ast neqpf = make_congruence(make(Iff,Ploc,P),make(Iff,nPloc,nP),eqpf); - ast npPloc = make_hypothesis(nPloc); - ast npP = make_mp(make(Iff,nPloc,nP),npPloc,neqpf); - ast nrP = make_resolution(nP,conj2,npP); - ast res = make_resolution(Ploc,rP,nrP); - return capture_localization(res); - } - - ast get_contra_coeff(const ast &f){ - ast c = arg(f,0); - // if(!is_not(arg(f,1))) - // c = make(Uminus,c); - return c; - } - - ast my_or(const ast &a, const ast &b){ - return mk_or(a,b); - } - - ast my_and(const ast &a, const ast &b){ - return mk_and(a,b); - } - - ast my_implies(const ast &a, const ast &b){ - return mk_implies(a,b); - } - - ast my_or(const std::vector &a){ - return mk_or(a); - } - - ast my_and(const std::vector &a){ - return mk_and(a); - } - - ast get_lit_atom(const ast &l){ - if(op(l) == Not) - return arg(l,0); - return l; - } - - bool is_placeholder(const ast &e){ - if(op(e) == Uninterpreted){ - std::string name = string_of_symbol(sym(e)); - if(name.size() > 2 && name[0] == '@' && name[1] == 'p') - return true; - } - return false; - } - -public: - iz3proof_itp_impl(prover *p, const prover::range &r, bool w) - : iz3proof_itp(*p) - { - pv = p; - rng = r; - weak = false ; //w; - type boolintbooldom[3] = {bool_type(),int_type(),bool_type()}; - type booldom[1] = {bool_type()}; - type boolbooldom[2] = {bool_type(),bool_type()}; - type boolboolbooldom[3] = {bool_type(),bool_type(),bool_type()}; - type intbooldom[2] = {int_type(),bool_type()}; - contra = function("@contra",2,boolbooldom,bool_type()); - m().inc_ref(contra); - sum = function("@sum",3,boolintbooldom,bool_type()); - m().inc_ref(sum); - rotate_sum = function("@rotsum",2,boolbooldom,bool_type()); - m().inc_ref(rotate_sum); - leq2eq = function("@leq2eq",3,boolboolbooldom,bool_type()); - m().inc_ref(leq2eq); - eq2leq = function("@eq2leq",2,boolbooldom,bool_type()); - m().inc_ref(eq2leq); - cong = function("@cong",3,boolintbooldom,bool_type()); - m().inc_ref(cong); - exmid = function("@exmid",3,boolboolbooldom,bool_type()); - m().inc_ref(exmid); - symm = function("@symm",1,booldom,bool_type()); - m().inc_ref(symm); - epsilon = make_var("@eps",int_type()); - modpon = function("@mp",3,boolboolbooldom,bool_type()); - m().inc_ref(modpon); - no_proof = make_var("@nop",bool_type()); - concat = function("@concat",2,boolbooldom,bool_type()); - m().inc_ref(concat); - top_pos = make_var("@top_pos",bool_type()); - add_pos = function("@add_pos",2,intbooldom,bool_type()); - m().inc_ref(add_pos); - rewrite_A = function("@rewrite_A",3,boolboolbooldom,bool_type()); - m().inc_ref(rewrite_A); - rewrite_B = function("@rewrite_B",3,boolboolbooldom,bool_type()); - m().inc_ref(rewrite_B); - normal_step = function("@normal_step",2,boolbooldom,bool_type()); - m().inc_ref(normal_step); - normal_chain = function("@normal_chain",2,boolbooldom,bool_type()); - m().inc_ref(normal_chain); - normal = function("@normal",2,boolbooldom,bool_type()); - m().inc_ref(normal); - sforall = function("@sforall",2,boolbooldom,bool_type()); - m().inc_ref(sforall); - sexists = function("@sexists",2,boolbooldom,bool_type()); - m().inc_ref(sexists); - } - - ~iz3proof_itp_impl() override { - m().dec_ref(contra); - m().dec_ref(sum); - m().dec_ref(rotate_sum); - m().dec_ref(leq2eq); - m().dec_ref(eq2leq); - m().dec_ref(cong); - m().dec_ref(exmid); - m().dec_ref(symm); - m().dec_ref(modpon); - m().dec_ref(concat); - m().dec_ref(add_pos); - m().dec_ref(rewrite_A); - m().dec_ref(rewrite_B); - m().dec_ref(normal_step); - m().dec_ref(normal_chain); - m().dec_ref(normal); - m().dec_ref(sforall); - m().dec_ref(sexists); - } -}; - -iz3proof_itp *iz3proof_itp::create(prover *p, const prover::range &r, bool w){ - return new iz3proof_itp_impl(p,r,w); -} - diff --git a/src/interp/iz3proof_itp.h b/src/interp/iz3proof_itp.h deleted file mode 100644 index c9a36e9b1..000000000 --- a/src/interp/iz3proof_itp.h +++ /dev/null @@ -1,143 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3proof.h - - Abstract: - - This class defines a simple interpolating proof system. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3PROOF_ITP_H -#define IZ3PROOF_ITP_H - -#include - -#include "interp/iz3base.h" -#include "interp/iz3secondary.h" - -// #define CHECK_PROOFS - -/** This class defines a simple proof system. - - As opposed to iz3proof, this class directly computes interpolants, - so the proof representation is just the interpolant itself. - -*/ - -class iz3proof_itp : public iz3mgr { - public: - - /** Enumeration of proof rules. */ - enum rule {Resolution,Assumption,Hypothesis,Theory,Axiom,Contra,Lemma,Reflexivity,Symmetry,Transitivity,Congruence,EqContra}; - - /** Interface to prover. */ - typedef iz3base prover; - - /** Ast type. */ - typedef prover::ast ast; - - /** The type of proof nodes (just interpolants). */ - typedef ast node; - - /** Object thrown in case of a proof error. */ - struct proof_error: public iz3_exception { - proof_error(): iz3_exception("proof_error") {} - }; - - - /** Make a resolution node with given pivot literal and premises. - The conclusion of premise1 should contain the negation of the - pivot literal, while the conclusion of premise2 should containe the - pivot literal. - */ - virtual node make_resolution(ast pivot, const std::vector &conc, node premise1, node premise2) = 0; - - /** Make an assumption node. The given clause is assumed in the given frame. */ - virtual node make_assumption(int frame, const std::vector &assumption) = 0; - - /** Make a hypothesis node. If phi is the hypothesis, this is - effectively phi |- phi. */ - virtual node make_hypothesis(const ast &hypothesis) = 0; - - /** Make an axiom node. The conclusion must be an instance of an axiom. */ - virtual node make_axiom(const std::vector &conclusion) = 0; - - /** Make an axiom node. The conclusion must be an instance of an axiom. Localize axiom instance to range*/ - virtual node make_axiom(const std::vector &conclusion, prover::range) = 0; - - /** Make a Contra node. This rule takes a derivation of the form - Gamma |- False and produces |- \/~Gamma. */ - - virtual node make_contra(node prem, const std::vector &conclusion) = 0; - - /** Make a Reflexivity node. This rule produces |- x = x */ - - virtual node make_reflexivity(ast con) = 0; - - /** Make a Symmetry node. This takes a derivation of |- x = y and - produces | y = x */ - - virtual node make_symmetry(ast con, const ast &premcon, node prem) = 0; - - /** Make a transitivity node. This takes derivations of |- x = y - and |- y = z produces | x = z */ - - virtual node make_transitivity(const ast &x, const ast &y, const ast &z, node prem1, node prem2) = 0; - - /** Make a congruence node. This takes a derivation of |- x_i = y_i - and produces |- f(...x_i,...) = f(...,y_i,...) */ - - virtual node make_congruence(const ast &xi_eq_yi, const ast &con, const ast &prem1) = 0; - - /** Make a congruence node. This takes derivations of |- x_i1 = y_i1, |- x_i2 = y_i2,... - and produces |- f(...x_i1...x_i2...) = f(...y_i1...y_i2...) */ - - virtual node make_congruence(const std::vector &xi_eq_yi, const ast &con, const std::vector &prems) = 0; - - /** Make a modus-ponens node. This takes derivations of |- x - and |- x = y and produces |- y */ - - virtual node make_mp(const ast &x_eq_y, const ast &prem1, const ast &prem2) = 0; - - /** Make a farkas proof node. */ - - virtual node make_farkas(ast con, const std::vector &prems, const std::vector &prem_cons, const std::vector &coeffs) = 0; - - /* Make an axiom instance of the form |- x<=y, y<= x -> x =y */ - virtual node make_leq2eq(ast x, ast y, const ast &xleqy, const ast &yleqx) = 0; - - /* Make an axiom instance of the form |- x = y -> x <= y */ - virtual node make_eq2leq(ast x, ast y, const ast &xeqy) = 0; - - /* Make an inference of the form t <= c |- t/d <= floor(c/d) where t - is an affine term divisble by d and c is an integer constant */ - virtual node make_cut_rule(const ast &tleqc, const ast &d, const ast &con, const ast &prem) = 0; - - /* Return an interpolant from a proof of false */ - virtual ast interpolate(const node &pf) = 0; - - /** Create proof object to construct an interpolant. */ - static iz3proof_itp *create(prover *p, const prover::range &r, bool _weak); - - protected: - iz3proof_itp(iz3mgr &m) - : iz3mgr(m) - { - } - - public: - virtual ~iz3proof_itp(){ - } -}; - -#endif diff --git a/src/interp/iz3scopes.cpp b/src/interp/iz3scopes.cpp deleted file mode 100755 index e3a28abdd..000000000 --- a/src/interp/iz3scopes.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3scopes.cpp - - Abstract: - - Calculations with scopes, for both sequence and tree interpolation. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#include - -#include - -#include "interp/iz3scopes.h" - - -/** computes the least common ancestor of two nodes in the tree, or SHRT_MAX if none */ -int scopes::tree_lca(int n1, int n2){ - if(!tree_mode()) - return std::max(n1,n2); - if(n1 == SHRT_MIN) return n2; - if(n2 == SHRT_MIN) return n1; - if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; - while(n1 != n2){ - if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; - assert(n1 >= 0 && n2 >= 0 && n1 < (int)parents.size() && n2 < (int)parents.size()); - if(n1 < n2) n1 = parents[n1]; - else n2 = parents[n2]; - } - return n1; -} - -/** computes the greatest common descendant two nodes in the tree, or SHRT_MIN if none */ -int scopes::tree_gcd(int n1, int n2){ - if(!tree_mode()) - return std::min(n1,n2); - int foo = tree_lca(n1,n2); - if(foo == n1) return n2; - if(foo == n2) return n1; - return SHRT_MIN; -} - -#ifndef FULL_TREE - -/** test whether a tree node is contained in a range */ -bool scopes::in_range(int n, const range &rng){ - return tree_lca(rng.lo,n) == n && tree_gcd(rng.hi,n) == n; -} - -/** test whether two ranges of tree nodes intersect */ -bool scopes::ranges_intersect(const range &rng1, const range &rng2){ - return tree_lca(rng1.lo,rng2.hi) == rng2.hi && tree_lca(rng1.hi,rng2.lo) == rng1.hi; -} - - -bool scopes::range_contained(const range &rng1, const range &rng2){ - return tree_lca(rng2.lo,rng1.lo) == rng1.lo - && tree_lca(rng1.hi,rng2.hi) == rng2.hi; -} - -scopes::range scopes::range_lub(const range &rng1, const range &rng2){ - range res; - res.lo = tree_gcd(rng1.lo,rng2.lo); - res.hi = tree_lca(rng1.hi,rng2.hi); - return res; -} - -scopes::range scopes::range_glb(const range &rng1, const range &rng2){ - range res; - res.lo = tree_lca(rng1.lo,rng2.lo); - res.hi = tree_gcd(rng1.hi,rng2.hi); - return res; -} - -#else - - -namespace std { - template <> - class hash { - public: - size_t operator()(const scopes::range_lo &p) const { - return p.lo + (size_t)p.next; - } - }; -} - -template <> inline -size_t stdext::hash_value(const scopes::range_lo& p) -{ - std::hash h; - return h(p); -} - -namespace std { - template <> - class less { - public: - bool operator()(const scopes::range_lo &x, const scopes::range_lo &y) const { - return x.lo < y.lo || x.lo == y.lo && (size_t)x.next < (size_t)y.next; - } - }; -} - - -struct range_op { - scopes::range_lo *x, *y; - int hi; - range_op(scopes::range_lo *_x, scopes::range_lo *_y, int _hi){ - x = _x; y = _y; hi = _hi; - } -}; - -namespace std { - template <> - class hash { - public: - size_t operator()(const range_op &p) const { - return (size_t) p.x + (size_t)p.y + p.hi; - } - }; -} - -template <> inline -size_t stdext::hash_value(const range_op& p) -{ - std::hash h; - return h(p); -} - -namespace std { - template <> - class less { - public: - bool operator()(const range_op &x, const range_op &y) const { - return (size_t)x.x < (size_t)y.x || x.x == y.x && - ((size_t)x.y < (size_t)y.y || x.y == y.y && x.hi < y.hi); - } - }; -} - -struct range_tables { - hash_map unique; - hash_map lub; - hash_map glb; -}; - - -scopes::range_lo *scopes::find_range_lo(int lo, range_lo *next){ - range_lo foo(lo,next); - std::pair baz(foo,(range_lo *)0); - std::pair::iterator,bool> bar = rt->unique.insert(baz); - if(bar.second) - bar.first->second = new range_lo(lo,next); - return bar.first->second; - //std::pair::iterator,bool> bar = rt->unique.insert(foo); - // const range_lo *baz = &*(bar.first); - // return (range_lo *)baz; // coerce const -} - -scopes::range_lo *scopes::range_lub_lo(range_lo *rng1, range_lo *rng2){ - if(!rng1) return rng2; - if(!rng2) return rng1; - if(rng1->lo > rng2->lo) - std::swap(rng1,rng2); - std::pair foo(range_op(rng1,rng2,0),(range_lo *)0); - std::pair::iterator,bool> bar = rt->lub.insert(foo); - range_lo *&res = bar.first->second; - if(!bar.second) return res; - if(!(rng1->next && rng1->next->lo <= rng2->lo)){ - for(int lo = rng1->lo; lo <= rng2->lo; lo = parents[lo]) - if(lo == rng2->lo) - {rng2 = rng2->next; break;} - } - range_lo *baz = range_lub_lo(rng1->next,rng2); - res = find_range_lo(rng1->lo,baz); - return res; -} - - -scopes::range_lo *scopes::range_glb_lo(range_lo *rng1, range_lo *rng2, int hi){ - if(!rng1) return rng1; - if(!rng2) return rng2; - if(rng1->lo > rng2->lo) - std::swap(rng1,rng2); - std::pair cand(range_op(rng1,rng2,hi),(range_lo *)0); - std::pair::iterator,bool> bar = rt->glb.insert(cand); - range_lo *&res = bar.first->second; - if(!bar.second) return res; - range_lo *foo; - if(!(rng1->next && rng1->next->lo <= rng2->lo)){ - int lim = hi; - if(rng1->next) lim = std::min(lim,rng1->next->lo); - int a = rng1->lo, b = rng2->lo; - while(a != b && b <= lim){ - a = parents[a]; - if(a > b)std::swap(a,b); - } - if(a == b && b <= lim){ - foo = range_glb_lo(rng1->next,rng2->next,hi); - foo = find_range_lo(b,foo); - } - else - foo = range_glb_lo(rng2,rng1->next,hi); - } - else foo = range_glb_lo(rng1->next,rng2,hi); - res = foo; - return res; -} - -/** computes the lub (smallest containing subtree) of two ranges */ -scopes::range scopes::range_lub(const range &rng1, const range &rng2){ - int hi = tree_lca(rng1.hi,rng2.hi); - if(hi == SHRT_MAX) return range_full(); - range_lo *lo = range_lub_lo(rng1.lo,rng2.lo); - return range(hi,lo); -} - -/** computes the glb (intersection) of two ranges */ -scopes::range scopes::range_glb(const range &rng1, const range &rng2){ - if(rng1.hi == SHRT_MAX) return rng2; - if(rng2.hi == SHRT_MAX) return rng1; - int hi = tree_gcd(rng1.hi,rng2.hi); - range_lo *lo = hi == SHRT_MIN ? 0 : range_glb_lo(rng1.lo,rng2.lo,hi); - if(!lo) hi = SHRT_MIN; - return range(hi,lo); -} - -/** is this range empty? */ -bool scopes::range_is_empty(const range &rng){ - return rng.hi == SHRT_MIN; -} - -/** return an empty range */ -scopes::range scopes::range_empty(){ - return range(SHRT_MIN,0); -} - -/** return a full range */ -scopes::range scopes::range_full(){ - return range(SHRT_MAX,0); -} - -/** return the maximal element of a range */ -int scopes::range_max(const range &rng){ - return rng.hi; -} - -/** return a minimal (not necessarily unique) element of a range */ -int scopes::range_min(const range &rng){ - if(rng.hi == SHRT_MAX) return SHRT_MIN; - return rng.lo ? rng.lo->lo : SHRT_MAX; -} - - -/** return range consisting of downward closure of a point */ -scopes::range scopes::range_downward(int _hi){ - std::vector descendants(parents.size()); - for(int i = descendants.size() - 1; i >= 0 ; i--) - descendants[i] = i == _hi || parents[i] < parents.size() && descendants[parents[i]]; - for(unsigned i = 0; i < descendants.size() - 1; i++) - if(parents[i] < parents.size()) - descendants[parents[i]] = false; - range_lo *foo = 0; - for(int i = descendants.size() - 1; i >= 0; --i) - if(descendants[i]) foo = find_range_lo(i,foo); - return range(_hi,foo); -} - -/** add an element to a range */ -void scopes::range_add(int i, range &n){ - range foo = range(i, find_range_lo(i,0)); - n = range_lub(foo,n); -} - -/** Choose an element of rng1 that is near to rng2 */ -int scopes::range_near(const range &rng1, const range &rng2){ - - int frame; - int thing = tree_lca(rng1.hi,rng2.hi); - if(thing != rng1.hi) return rng1.hi; - range line = range(rng1.hi,find_range_lo(rng2.hi,(range_lo *)0)); - line = range_glb(line,rng1); - return range_min(line); -} - - -/** test whether a tree node is contained in a range */ -bool scopes::in_range(int n, const range &rng){ - range r = range_empty(); - range_add(n,r); - r = range_glb(rng,r); - return !range_is_empty(r); -} - -/** test whether two ranges of tree nodes intersect */ -bool scopes::ranges_intersect(const range &rng1, const range &rng2){ - range r = range_glb(rng1,rng2); - return !range_is_empty(r); -} - - -bool scopes::range_contained(const range &rng1, const range &rng2){ - range r = range_glb(rng1,rng2); - return r.hi == rng1.hi && r.lo == rng1.lo; -} - - -#endif - - diff --git a/src/interp/iz3scopes.h b/src/interp/iz3scopes.h deleted file mode 100755 index ece30dc25..000000000 --- a/src/interp/iz3scopes.h +++ /dev/null @@ -1,222 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3scopes.h - - Abstract: - - Calculations with scopes, for both sequence and tree interpolation. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifndef IZ3SOPES_H -#define IZ3SOPES_H - -#include -#include -#include "interp/iz3hash.h" - -class scopes { - - public: - /** Construct from parents vector. */ - scopes(const std::vector &_parents){ - parents = _parents; - } - - scopes(){ - } - - void initialize(const std::vector &_parents){ - parents = _parents; - } - - /** The parents vector defining the tree structure */ - std::vector parents; - - // #define FULL_TREE -#ifndef FULL_TREE - struct range { - range(){ - lo = SHRT_MAX; - hi = SHRT_MIN; - } - short lo, hi; - }; - - /** computes the lub (smallest containing subtree) of two ranges */ - range range_lub(const range &rng1, const range &rng2); - - /** computes the glb (intersection) of two ranges */ - range range_glb(const range &rng1, const range &rng2); - - /** is this range empty? */ - bool range_is_empty(const range &rng){ - return rng.hi < rng.lo; - } - - /** is this range full? */ - bool range_is_full(const range &rng){ - return rng.lo == SHRT_MIN && rng.hi == SHRT_MAX; - } - - /** return an empty range */ - range range_empty(){ - range res; - res.lo = SHRT_MAX; - res.hi = SHRT_MIN; - return res; - } - - /** return an empty range */ - range range_full(){ - range res; - res.lo = SHRT_MIN; - res.hi = SHRT_MAX; - return res; - } - - /** return the maximal element of a range */ - int range_max(const range &rng){ - return rng.hi; - } - - /** return a minimal (not necessarily unique) element of a range */ - int range_min(const range &rng){ - return rng.lo; - } - - /** return range consisting of downward closure of a point */ - range range_downward(int _hi){ - range foo; - foo.lo = SHRT_MIN; - foo.hi = _hi; - return foo; - } - - void range_add(int i, range &n){ -#if 0 - if(i < n.lo) n.lo = i; - if(i > n.hi) n.hi = i; -#else - range rng; rng.lo = i; rng.hi = i; - n = range_lub(rng,n); -#endif - } - - /** Choose an element of rng1 that is near to rng2 */ - int range_near(const range &rng1, const range &rng2){ - int frame; - int thing = tree_lca(rng1.lo,rng2.hi); - if(thing == rng1.lo) frame = rng1.lo; - else frame = tree_gcd(thing,rng1.hi); - return frame; - } -#else - - struct range_lo { - int lo; - range_lo *next; - range_lo(int _lo, range_lo *_next){ - lo = _lo; - next = _next; - } - }; - - struct range { - int hi; - range_lo *lo; - range(int _hi, range_lo *_lo){ - hi = _hi; - lo = _lo; - } - range(){ - hi = SHRT_MIN; - lo = 0; - } - }; - - range_tables *rt; - - /** computes the lub (smallest containing subtree) of two ranges */ - range range_lub(const range &rng1, const range &rng2); - - /** computes the glb (intersection) of two ranges */ - range range_glb(const range &rng1, const range &rng2); - - /** is this range empty? */ - bool range_is_empty(const range &rng); - - /** return an empty range */ - range range_empty(); - - /** return a full range */ - range range_full(); - - /** return the maximal element of a range */ - int range_max(const range &rng); - - /** return a minimal (not necessarily unique) element of a range */ - int range_min(const range &rng); - - /** return range consisting of downward closure of a point */ - range range_downward(int _hi); - - /** add an element to a range */ - void range_add(int i, range &n); - - /** Choose an element of rng1 that is near to rng2 */ - int range_near(const range &rng1, const range &rng2); - - range_lo *find_range_lo(int lo, range_lo *next); - range_lo *range_lub_lo(range_lo *rng1, range_lo *rng2); - range_lo *range_glb_lo(range_lo *rng1, range_lo *rng2, int lim); - -#endif - - /** test whether a tree node is contained in a range */ - bool in_range(int n, const range &rng); - - /** test whether two ranges of tree nodes intersect */ - bool ranges_intersect(const range &rng1, const range &rng2); - - /** test whether range rng1 contained in range rng2 */ - bool range_contained(const range &rng1, const range &rng2); - - private: - int tree_lca(int n1, int n2); - int tree_gcd(int n1, int n2); - bool tree_mode(){return parents.size() != 0;} - - - -}; - -// let us hash on ranges - -#ifndef FULL_TREE -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const scopes::range &p) const { - return (size_t)p.lo + (size_t)p.hi; - } - }; -} - -inline bool operator==(const scopes::range &x, const scopes::range &y){ - return x.lo == y.lo && x.hi == y.hi; -} -#endif - -#endif diff --git a/src/interp/iz3secondary.h b/src/interp/iz3secondary.h deleted file mode 100755 index a5a949b54..000000000 --- a/src/interp/iz3secondary.h +++ /dev/null @@ -1,40 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3secondary - - Abstract: - - Interface for secondary provers. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifndef IZ3SECONDARY_H -#define IZ3SECONDARY_H - -/** Interface class for secondary provers. */ - -#include "interp/iz3base.h" -#include - -class iz3secondary : public iz3mgr { - public: - virtual int interpolate(const std::vector &frames, std::vector &interpolants) = 0; - virtual ~iz3secondary(){} - - protected: - iz3secondary(const iz3mgr &mgr) : iz3mgr(mgr) {} -}; - - - -#endif diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp deleted file mode 100644 index 44bac0643..000000000 --- a/src/interp/iz3translate.cpp +++ /dev/null @@ -1,2200 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3translate.cpp - - Abstract: - - Translate a Z3 proof to in interpolated proof. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3translate.h" -#include "interp/iz3proof.h" -#include "interp/iz3profiling.h" -#include "interp/iz3interp.h" -#include "interp/iz3proof_itp.h" -#include "ast/ast_pp.h" - -#include -#include -#include -#include -#include -#include -#include - -//using std::vector; -using namespace stl_ext; - - - -/* This translator goes directly from Z3 proofs to interpolated - proofs without an intermediate representation. No secondary - prover is used. -*/ - - -#define throw_unsupported(_e_) { TRACE("iz3", tout << expr_ref((expr*)_e_.raw(), *_e_.mgr()) << "\n";); throw unsupported(_e_); } - -class iz3translation_full : public iz3translation { -public: - - - typedef iz3proof_itp Iproof; - - Iproof *iproof; - - /* Here we have lots of hash tables for memoizing various methods and - other such global data structures. - */ - - typedef hash_map AstToInt; - AstToInt locality; // memoizes locality of Z3 proof terms - - typedef std::pair EquivEntry; - typedef hash_map EquivTab; - EquivTab equivs; // maps non-local terms to equivalent local terms, with proof - - typedef hash_set AstHashSet; - AstHashSet equivs_visited; // proofs already checked for equivalences - - typedef std::pair, hash_map > AstToIpf; - AstToIpf translation; // Z3 proof nodes to Iproof nodes - - int frames; // number of frames - - typedef std::set AstSet; - typedef hash_map AstToAstSet; - AstToAstSet hyp_map; // map proof terms to hypothesis set - - struct LocVar { // localization vars - ast var; // a fresh variable - ast term; // term it represents - int frame; // frame in which it's defined - LocVar(ast v, ast t, int f){var=v;term=t;frame=f;} - }; - - std::vector localization_vars; // localization vars in order of creation - typedef hash_map AstToAst; - AstToAst localization_map; // maps terms to their localization vars - - typedef hash_map AstToBool; - AstToBool occurs_in_memo; // memo of occurs_in function - - AstHashSet cont_eq_memo; // memo of cont_eq function - - AstToAst subst_memo; // memo of subst function - - symb commute; - -public: - - -#define from_ast(x) (x) - - // #define NEW_LOCALITY - -#ifdef NEW_LOCALITY - range rng; // the range of frames in the "A" part of the interpolant -#endif - - /* To handle skolemization, we have to scan the proof for skolem - symbols and assign each to a frame. THe assignment is heuristic. - */ - - int scan_skolems_rec(hash_map &memo, const ast &proof, int frame){ - std::pair foo(proof,INT_MAX); - std::pair bar = memo.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - pfrule dk = pr(proof); - if(dk == PR_ASSERTED){ - ast ass = conc(proof); - res = frame_of_assertion(ass); - } - else if(dk == PR_SKOLEMIZE){ - ast quanted = arg(conc(proof),0); - if(op(quanted) == Not) - quanted = arg(quanted,0); - // range r = ast_range(quanted); - // if(range_is_empty(r)) - range r = ast_scope(quanted); - if(range_is_empty(r)) - throw iz3_exception("can't skolemize"); - if(frame == INT_MAX || !in_range(frame,r)) - frame = range_max(r); // this is desperation -- may fail - if(frame >= frames) frame = frames - 1; - add_frame_range(frame,arg(conc(proof),1)); - r = ast_scope(arg(conc(proof),1)); - } - else if(dk==PR_MODUS_PONENS_OEQ){ - frame = scan_skolems_rec(memo,prem(proof,0),frame); - scan_skolems_rec(memo,prem(proof,1),frame); - } - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - int bar = scan_skolems_rec(memo,prem(proof,i),frame); - if(res == INT_MAX || res == bar) res = bar; - else if(bar != INT_MAX) res = -1; - } - } - return res; - } - - void scan_skolems(const ast &proof) { - hash_map memo; - scan_skolems_rec(memo,proof, INT_MAX); - } - - // determine locality of a proof term - // return frame of derivation if local, or -1 if not - // result INT_MAX means the proof term is a tautology - // memoized in hash_map "locality" - - int get_locality_rec(ast proof){ - std::pair foo(proof,INT_MAX); - std::pair bar = locality.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - pfrule dk = pr(proof); - if(dk == PR_ASSERTED){ - ast ass = conc(proof); - res = frame_of_assertion(ass); -#ifdef NEW_LOCALITY - if(in_range(res,rng)) - res = range_max(rng); - else - res = frames-1; -#endif - } - else if(dk == PR_QUANT_INST){ - std::vector lits; - ast con = conc(proof); - get_Z3_lits(con, lits); - iproof->make_axiom(lits); - } -#ifdef LOCALIZATION_KLUDGE - else if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST - && get_locality_rec(prem(proof,1)) == INT_MAX){ - std::vector lits; - ast con = conc(proof); - get_Z3_lits(con, lits); - iproof->make_axiom(lits); - } -#endif - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - int bar = get_locality_rec(arg); - if(res == INT_MAX || res == bar) res = bar; - else if(bar != INT_MAX) res = -1; - } - } - return res; - } - - - int get_locality(ast proof){ - // if(lia_z3_axioms_only) return -1; - int res = get_locality_rec(proof); - if(res != -1){ - ast con = conc(proof); - range rng = ast_scope(con); - - // hack: if a clause contains "true", it reduces to "true", - // which means we won't compute the range correctly. we handle - // this case by computing the ranges of the literals separately - - if(is_true(con)){ - std::vector lits; - get_Z3_lits(conc(proof),lits); - for(unsigned i = 0; i < lits.size(); i++) - rng = range_glb(rng,ast_scope(lits[i])); - } - - if(!range_is_empty(rng)){ - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ - ast hyp = *it; - rng = range_glb(rng,ast_scope(hyp)); - } - } - - // if(!range_is_empty(rng)){ - // if (num_free_variables(con) > 0) - // rng = range_empty(); - // } - - if(res == INT_MAX){ - if(range_is_empty(rng)) - res = -1; - else res = range_max(rng); - } - else { - if(!in_range(res,rng)) - res = -1; - } - } - return res; - } - - - AstSet &get_hyps(ast proof){ - std::pair foo(proof,AstSet()); - std::pair bar = hyp_map.insert(foo); - AstSet &res = bar.first->second; - if(!bar.second) return res; - pfrule dk = pr(proof); - if(dk == PR_HYPOTHESIS){ - ast con = conc(proof); - res.insert(con); - } - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - AstSet &arg_hyps = get_hyps(arg); - res.insert(arg_hyps.begin(),arg_hyps.end()); - } - if(dk == PR_LEMMA){ - ast con = conc(proof); - res.erase(mk_not(con)); - if(is_or(con)){ - int clause_size = num_args(con); - for(int i = 0; i < clause_size; i++){ - ast neglit = mk_not(arg(con,i)); - res.erase(neglit); - } - } - } - } -#if 0 - AstSet::iterator it = res.begin(), en = res.end(); - if(it != en){ - AstSet::iterator old = it; - ++it; - for(; it != en; ++it, ++old) - if(!(*old < *it)) - std::cout << "foo!"; - } -#endif - return res; - } - - // Find all the judgements of the form p <-> q, where - // p is local and q is non-local, recording them in "equivs" - // the map equivs_visited is used to record the already visited proof terms - - void find_equivs(ast proof){ - if(equivs_visited.find(proof) != equivs_visited.end()) - return; - equivs_visited.insert(proof); - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++) // do all the sub_terms - find_equivs(prem(proof,i)); - ast con = conc(proof); // get the conclusion - if(is_iff(con)){ - ast iff = con; - for(int i = 0; i < 2; i++) - if(!is_local(arg(iff,i)) && is_local(arg(iff,1-i))){ - std::pair > foo(arg(iff,i),std::pair(arg(iff,1-i),proof)); - equivs.insert(foo); - } - } - } - - // get the lits of a Z3 clause - void get_Z3_lits(ast t, std::vector &lits){ - opr dk = op(t); - if(dk == False) - return; // false = empty clause - if(dk == Or){ - unsigned nargs = num_args(t); - lits.resize(nargs); - for(unsigned i = 0; i < nargs; i++) // do all the sub_terms - lits[i] = arg(t,i); - } - else { - lits.push_back(t); - } - } - - // resolve two clauses represented as vectors of lits. replace first clause - void resolve(ast pivot, std::vector &cls1, std::vector &cls2){ - ast neg_pivot = mk_not(pivot); - for(unsigned i = 0; i < cls1.size(); i++){ - if(cls1[i] == pivot){ - cls1[i] = cls1.back(); - cls1.pop_back(); - bool found_pivot2 = false; - for(unsigned j = 0; j < cls2.size(); j++){ - if(cls2[j] == neg_pivot) - found_pivot2 = true; - else - cls1.push_back(cls2[j]); - } - (void)found_pivot2; - assert(found_pivot2); - return; - } - } - assert(0 && "resolve failed"); - } - - // get lits resulting from unit resolution up to and including "position" - // TODO: this is quadratic -- fix it - void do_unit_resolution(ast proof, int position, std::vector &lits){ - ast orig_clause = conc(prem(proof,0)); - get_Z3_lits(orig_clause,lits); - for(int i = 1; i <= position; i++){ - std::vector unit(1); - unit[0] = conc(prem(proof,i)); - resolve(mk_not(unit[0]),lits,unit); - } - } - -#if 0 - // clear the localization variables - void clear_localization(){ - localization_vars.clear(); - localization_map.clear(); - } - - // create a fresh variable for localization - ast fresh_localization_var(ast term, int frame){ - std::ostringstream s; - s << "%" << (localization_vars.size()); - ast var = make_var(s.str().c_str(),get_type(term)); - sym_range(sym(var)) = range_full(); // make this variable global - localization_vars.push_back(LocVar(var,term,frame)); - return var; - } - - - // "localize" a term to a given frame range by - // creating new symbols to represent non-local subterms - - ast localize_term(ast e, const range &rng){ - if(ranges_intersect(ast_scope(e),rng)) - return e; // this term occurs in range, so it's O.K. - AstToAst::iterator it = localization_map.find(e); - if(it != localization_map.end()) - return it->second; - - // if is is non-local, we must first localize the arguments to - // the range of its function symbol - - int nargs = num_args(e); - if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ - range frng = rng; - if(op(e) == Uninterpreted){ - symb f = sym(e); - range srng = sym_range(f); - if(ranges_intersect(srng,rng)) // localize to desired range if possible - frng = range_glb(srng,rng); - } - std::vector largs(nargs); - for(int i = 0; i < nargs; i++){ - largs[i] = localize_term(arg(e,i),frng); - frng = range_glb(frng,ast_scope(largs[i])); - } - e = clone(e,largs); - assert(is_local(e)); - } - - - if(ranges_intersect(ast_scope(e),rng)) - return e; // this term occurs in range, so it's O.K. - - // choose a frame for the constraint that is close to range - int frame = range_near(ast_scope(e),rng); - - ast new_var = fresh_localization_var(e,frame); - localization_map[e] = new_var; - ast cnst = make(Equal,new_var,e); - // antes.push_back(std::pair(cnst,frame)); - return new_var; - } - - // some patterm matching functions - - // match logical or with nargs arguments - // assumes AIG form - bool match_or(ast e, ast *args, int nargs){ - if(op(e) != Or) return false; - int n = num_args(e); - if(n != nargs) return false; - for(int i = 0; i < nargs; i++) - args[i] = arg(e,i); - return true; - } - - // match operator f with exactly nargs arguments - bool match_op(ast e, opr f, ast *args, int nargs){ - if(op(e) != f) return false; - int n = num_args(e); - if(n != nargs) return false; - for(int i = 0; i < nargs; i++) - args[i] = arg(e,i); - return true; - } - - // see if the given formula can be interpreted as - // an axiom instance (e.g., an array axiom instance). - // if so, add it to "antes" in an appropriate frame. - // this may require "localization" - - void get_axiom_instance(ast e){ - - // "store" axiom - // (or (= w q) (= (select (store a1 w y) q) (select a1 q))) - // std::cout << "ax: "; show(e); - ast lits[2],eq_ops_l[2],eq_ops_r[2],sel_ops[2], sto_ops[3], sel_ops2[2] ; - if(match_or(e,lits,2)) - if(match_op(lits[0],Equal,eq_ops_l,2)) - if(match_op(lits[1],Equal,eq_ops_r,2)) - for(int i = 0; i < 2; i++){ // try the second equality both ways - if(match_op(eq_ops_r[0],Select,sel_ops,2)) - if(match_op(sel_ops[0],Store,sto_ops,3)) - if(match_op(eq_ops_r[1],Select,sel_ops2,2)) - for(int j = 0; j < 2; j++){ // try the first equality both ways - if(eq_ops_l[0] == sto_ops[1] - && eq_ops_l[1] == sel_ops[1] - && eq_ops_l[1] == sel_ops2[1] - && sto_ops[0] == sel_ops2[0]) - if(is_local(sel_ops[0])) // store term must be local - { - ast sto = sel_ops[0]; - ast addr = localize_term(eq_ops_l[1],ast_scope(sto)); - ast res = make(Or, - make(Equal,eq_ops_l[0],addr), - make(Equal, - make(Select,sto,addr), - make(Select,sel_ops2[0],addr))); - // int frame = range_min(ast_scope(res)); TODO - // antes.push_back(std::pair(res,frame)); - return; - } - std::swap(eq_ops_l[0],eq_ops_l[1]); - } - std::swap(eq_ops_r[0],eq_ops_r[1]); - } - } - - // a quantifier instantation looks like (~ forall x. P) \/ P[z/x] - // we need to find a time frame for P, then localize P[z/x] in this frame - - void get_quantifier_instance(ast e){ - ast disjs[2]; - if(match_or(e,disjs,2)){ - if(is_local(disjs[0])){ - ast res = localize_term(disjs[1], ast_scope(disjs[0])); - // int frame = range_min(ast_scope(res)); TODO - // antes.push_back(std::pair(res,frame)); - return; - } - } - } - - ast get_judgement(ast proof){ - ast con = from_ast(conc(proof)); - AstSet &hyps = get_hyps(proof); - std::vector hyps_vec; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - hyps_vec.push_back(*it); - if(hyps_vec.size() == 0) return con; - con = make(Or,mk_not(make(And,hyps_vec)),con); - return con; - } - - // does variable occur in expression? - int occurs_in1(ast var, ast e){ - std::pair foo(e,false); - std::pair bar = occurs_in_memo.insert(foo); - bool &res = bar.first->second; - if(bar.second){ - if(e == var) res = true; - int nargs = num_args(e); - for(int i = 0; i < nargs; i++) - res |= occurs_in1(var,arg(e,i)); - } - return res; - } - - int occurs_in(ast var, ast e){ - occurs_in_memo.clear(); - return occurs_in1(var,e); - } - - // find a controlling equality for a given variable v in a term - // a controlling equality is of the form v = t, which, being - // false would force the formula to have the specifid truth value - // returns t, or null if no such - - ast cont_eq(bool truth, ast v, ast e){ - if(is_not(e)) return cont_eq(!truth,v,arg(e,0)); - if(cont_eq_memo.find(e) != cont_eq_memo.end()) - return ast(); - cont_eq_memo.insert(e); - if(!truth && op(e) == Equal){ - if(arg(e,0) == v) return(arg(e,1)); - if(arg(e,1) == v) return(arg(e,0)); - } - if((!truth && op(e) == And) || (truth && op(e) == Or)){ - int nargs = num_args(e); - for(int i = 0; i < nargs; i++){ - ast res = cont_eq(truth, v, arg(e,i)); - if(!res.null()) return res; - } - } - return ast(); - } - - // substitute a term t for unbound occurrences of variable v in e - - ast subst(ast var, ast t, ast e){ - if(e == var) return t; - std::pair foo(e,ast()); - std::pair bar = subst_memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - int nargs = num_args(e); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = subst(var,t,arg(e,i)); - opr f = op(e); - if(f == Equal && args[0] == args[1]) res = mk_true(); - else res = clone(e,args); - } - return res; - } - - // apply a quantifier to a formula, with some optimizations - // 1) bound variable does not occur -> no quantifier - // 2) bound variable must be equal to some term -> substitute - - ast apply_quant(opr quantifier, ast var, ast e){ - if(!occurs_in(var,e))return e; - cont_eq_memo.clear(); - ast cterm = cont_eq(quantifier == Forall, var, e); - if(!cterm.null()){ - subst_memo.clear(); - return subst(var,cterm,e); - } - std::vector bvs; bvs.push_back(var); - return make_quant(quantifier,bvs,e); - } - - // add quantifiers over the localization vars - // to an interpolant for frames lo-hi - - ast add_quants(ast e, int lo, int hi){ - for(int i = localization_vars.size() - 1; i >= 0; i--){ - LocVar &lv = localization_vars[i]; - opr quantifier = (lv.frame >= lo && lv.frame <= hi) ? Exists : Forall; - e = apply_quant(quantifier,lv.var,e); - } - return e; - } - - int get_lits_locality(std::vector &lits){ - range rng = range_full(); - for(std::vector::iterator it = lits.begin(), en = lits.end(); it != en; ++it){ - ast lit = *it; - rng = range_glb(rng,ast_scope(lit)); - } - if(range_is_empty(rng)) return -1; - int hi = range_max(rng); - if(hi >= frames) return frames - 1; - return hi; - } -#endif - - int num_lits(ast ast){ - opr dk = op(ast); - if(dk == False) - return 0; - if(dk == Or){ - unsigned nargs = num_args(ast); - int n = 0; - for(unsigned i = 0; i < nargs; i++) // do all the sub_terms - n += num_lits(arg(ast,i)); - return n; - } - else - return 1; - } - - void symbols_out_of_scope_rec(hash_set &memo, hash_set &symb_memo, int frame, const ast &t){ - if(memo.find(t) != memo.end()) - return; - memo.insert(t); - if(op(t) == Uninterpreted){ - symb s = sym(t); - range r = sym_range(s); - if(!in_range(frame,r) && symb_memo.find(s) == symb_memo.end()){ - std::cout << string_of_symbol(s) << "\n"; - symb_memo.insert(s); - } - } - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - symbols_out_of_scope_rec(memo,symb_memo,frame,arg(t,i)); - } - - void symbols_out_of_scope(int frame, const ast &t){ - hash_set memo; - hash_set symb_memo; - symbols_out_of_scope_rec(memo,symb_memo,frame,t); - } - - void conc_symbols_out_of_scope(int frame, const ast &t){ - symbols_out_of_scope(frame,conc(t)); - } - - std::vector lit_trace; - hash_set marked_proofs; - - bool proof_has_lit(const ast &proof, const ast &lit){ - AstSet &hyps = get_hyps(proof); - if(hyps.find(mk_not(lit)) != hyps.end()) - return true; - std::vector lits; - ast con = conc(proof); - get_Z3_lits(con, lits); - for(unsigned i = 0; i < lits.size(); i++) - if(lits[i] == lit) - return true; - return false; - } - - - void trace_lit_rec(const ast &lit, const ast &proof, AstHashSet &memo){ - if(memo.find(proof) == memo.end()){ - memo.insert(proof); - AstSet &hyps = get_hyps(proof); - std::vector lits; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - lits.push_back(mk_not(*it)); - ast con = conc(proof); - get_Z3_lits(con, lits); - for(unsigned i = 0; i < lits.size(); i++){ - if(lits[i] == lit){ - print_expr(std::cout,proof); - std::cout << "\n"; - marked_proofs.insert(proof); - pfrule dk = pr(proof); - if(dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA){ - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - trace_lit_rec(lit,arg,memo); - } - } - else - lit_trace.push_back(proof); - } - } - } - } - - ast traced_lit; - - int trace_lit(const ast &lit, const ast &proof){ - marked_proofs.clear(); - lit_trace.clear(); - traced_lit = lit; - AstHashSet memo; - trace_lit_rec(lit,proof,memo); - return lit_trace.size(); - } - - bool is_literal_or_lit_iff(const ast &lit){ - if(my_is_literal(lit)) return true; - if(op(lit) == Iff){ - return my_is_literal(arg(lit,0)) && my_is_literal(arg(lit,1)); - } - return false; - } - - bool my_is_literal(const ast &lit){ - ast abslit = is_not(lit) ? arg(lit,0) : lit; - int f = op(abslit); - return !(f == And || f == Or || f == Iff); - } - - hash_map asts_by_id; - - void print_lit(const ast &lit){ - ast abslit = is_not(lit) ? arg(lit,0) : lit; - if(!is_literal_or_lit_iff(lit)){ - if(is_not(lit)) std::cout << "~"; - int id = ast_id(abslit); - asts_by_id[id] = abslit; - std::cout << "[" << id << "]"; - } - else - print_expr(std::cout,lit); - } - - void expand(int id){ - if(asts_by_id.find(id) == asts_by_id.end()) - std::cout << "undefined\n"; - else { - ast lit = asts_by_id[id]; - std::string s = string_of_symbol(sym(lit)); - std::cout << "(" << s; - unsigned nargs = num_args(lit); - for(unsigned i = 0; i < nargs; i++){ - std::cout << " "; - print_lit(arg(lit,i)); - } - std::cout << ")\n";; - } - } - - void show_lit(const ast &lit){ - print_lit(lit); - std::cout << "\n"; - } - - void print_z3_lit(const ast &a){ - print_lit(from_ast(a)); - } - - void show_z3_lit(const ast &a){ - print_z3_lit(a); - std::cout << "\n"; - } - - - void show_con(const ast &proof, bool brief){ - if(!traced_lit.null() && proof_has_lit(proof,traced_lit)) - std::cout << "(*) "; - ast con = conc(proof); - AstSet &hyps = get_hyps(proof); - int count = 0; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ - if(brief && ++count > 5){ - std::cout << "... "; - break; - } - print_lit(*it); - std::cout << " "; - } - std::cout << "|- "; - std::vector lits; - get_Z3_lits(con,lits); - for(unsigned i = 0; i < lits.size(); i++){ - print_lit(lits[i]); - std::cout << " "; - } - range r = ast_scope(con); - std::cout << " {" << r.lo << "," << r.hi << "}"; - std::cout << "\n"; - } - - void show_step(const ast &proof){ - std::cout << "\n"; - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - std::cout << "(" << i << ") "; - ast arg = prem(proof,i); - show_con(arg,true); - } - std::cout << "|------ "; - std::cout << string_of_symbol(sym(proof)) << "\n"; - show_con(proof,false); - } - - void show_marked( const ast &proof){ - std::cout << "\n"; - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - if(!traced_lit.null() && proof_has_lit(arg,traced_lit)){ - std::cout << "(" << i << ") "; - show_con(arg,true); - } - } - } - - std::vector pfhist; - int pfhist_pos; - - void pfgoto(const ast &proof){ - if(pfhist.size() == 0) - pfhist_pos = 0; - else pfhist_pos++; - pfhist.resize(pfhist_pos); - pfhist.push_back(proof); - show_step(proof); - } - - - void pfback(){ - if(pfhist_pos > 0){ - pfhist_pos--; - show_step(pfhist[pfhist_pos]); - } - } - - void pffwd(){ - if(pfhist_pos < ((int)pfhist.size()) - 1){ - pfhist_pos++; - show_step(pfhist[pfhist_pos]); - } - } - - void pfprem(int i){ - if(pfhist.size() > 0){ - ast proof = pfhist[pfhist_pos]; - unsigned nprems = num_prems(proof); - if(i >= 0 && i < (int)nprems) - pfgoto(prem(proof,i)); - } - } - - - - // translate a unit resolution sequence - Iproof::node translate_ur(ast proof){ - ast prem0 = prem(proof,0); - Iproof::node itp = translate_main(prem0,true); - std::vector clause; - ast conc0 = conc(prem0); - int nprems = num_prems(proof); - if(nprems == 2 && conc0 == mk_not(conc(prem(proof,1)))) - clause.push_back(conc0); - else - get_Z3_lits(conc0,clause); - for(int position = 1; position < nprems; position++){ - ast ante = prem(proof,position); - ast pnode = conc(ante); - ast pnode_abs = !is_not(pnode) ? pnode : mk_not(pnode); - Iproof::node neg = itp; - Iproof::node pos = translate_main(ante, false); - if(is_not(pnode)){ - pnode = mk_not(pnode); - std::swap(neg,pos); - } - std::vector unit(1); - unit[0] = conc(ante); - resolve(mk_not(conc(ante)),clause,unit); - itp = iproof->make_resolution(pnode,clause,neg,pos); - } - return itp; - } - - // get an inequality in the form 0 <= t where t is a linear term - ast rhs_normalize_inequality(const ast &ineq){ - ast zero = make_int("0"); - ast thing = make(Leq,zero,zero); - linear_comb(thing,make_int("1"),ineq); - thing = simplify_ineq(thing); - return thing; - } - - bool check_farkas(const std::vector &prems, const ast &con){ - ast zero = make_int("0"); - ast thing = make(Leq,zero,zero); - for(unsigned i = 0; i < prems.size(); i++) - linear_comb(thing,make_int(rational(1)),prems[i]); - linear_comb(thing,make_int(rational(-1)),con); - thing = simplify_ineq(thing); - return arg(thing,1) == make_int(rational(0)); - } - - // get an inequality in the form t <= c or t < c, there t is affine and c constant - ast normalize_inequality(const ast &ineq){ - ast zero = make_int("0"); - ast thing = make(Leq,zero,zero); - linear_comb(thing,make_int("1"),ineq); - thing = simplify_ineq(thing); - ast lhs = arg(thing,0); - ast rhs = arg(thing,1); - opr o = op(rhs); - if(o != Numeral){ - if(op(rhs) == Plus){ - int nargs = num_args(rhs); - ast const_term = zero; - int i = 0; - if(nargs > 0 && op(arg(rhs,0)) == Numeral){ - const_term = arg(rhs,0); - i++; - } - if(i < nargs){ - std::vector non_const; - for(; i < nargs; i++) - non_const.push_back(arg(rhs,i)); - lhs = make(Sub,lhs,make(Plus,non_const)); - } - rhs = const_term; - } - else { - lhs = make(Sub,lhs,make(Plus,rhs)); - rhs = zero; - } - lhs = z3_simplify(lhs); - rhs = z3_simplify(rhs); - thing = make(op(thing),lhs,rhs); - } - return thing; - } - - void get_linear_coefficients(const ast &t, std::vector &coeffs){ - if(op(t) == Plus){ - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - coeffs.push_back(get_coeff(arg(t,i))); - } - else - coeffs.push_back(get_coeff(t)); - } - - /* given an affine term t, get the GCD of the coefficients in t. */ - ast gcd_of_coefficients(const ast &t){ - std::vector coeffs; - get_linear_coefficients(t,coeffs); - if(coeffs.size() == 0) - return make_int("1"); // arbitrary - rational d = abs(coeffs[0]); - for(unsigned i = 1; i < coeffs.size(); i++){ - d = gcd(d,coeffs[i]); - } - return make_int(d); - } - - ast get_bounded_variable(const ast &ineq, bool &lb){ - ast nineq = normalize_inequality(ineq); - ast lhs = arg(nineq,0); - lhs.raw(); - switch(op(lhs)){ - case Uninterpreted: - lb = false; - return lhs; - case Times: - if(arg(lhs,0) == make_int(rational(1))) - lb = false; - else if(arg(lhs,0) == make_int(rational(-1))) - lb = true; - else - throw_unsupported(lhs); - return arg(lhs,1); - default: - throw_unsupported(lhs); - } - } - - rational get_term_coefficient(const ast &t1, const ast &v){ - ast t = arg(normalize_inequality(t1),0); - if(op(t) == Plus){ - int nargs = num_args(t); - for(int i = 0; i < nargs; i++){ - if(get_linear_var(arg(t,i)) == v) - return get_coeff(arg(t,i)); - } - } - else - if(get_linear_var(t) == v) - return get_coeff(t); - return rational(0); - } - - - Iproof::node GCDtoDivRule(const ast &proof, bool pol, std::vector &coeffs, std::vector &prems, ast &cut_con){ - // gather the summands of the desired polarity - std::vector my_prems; - std::vector my_coeffs; - std::vector my_prem_cons; - for(unsigned i = pol ? 0 : 1; i < coeffs.size(); i+= 2){ - rational &c = coeffs[i]; - if(c.is_pos()){ - my_prems.push_back(prems[i]); - my_coeffs.push_back(make_int(c)); - my_prem_cons.push_back(conc(prem(proof,i))); - } - else if(c.is_neg()){ - int j = (i % 2 == 0) ? i + 1 : i - 1; - my_prems.push_back(prems[j]); - my_coeffs.push_back(make_int(-coeffs[j])); - my_prem_cons.push_back(conc(prem(proof,j))); - } - } - ast my_con = sum_inequalities(my_coeffs,my_prem_cons); - - // handle generalized GCD test. sadly, we dont' get the coefficients... - if(coeffs[0].is_zero()){ - bool lb; - int xtra_prem = 0; - ast bv = get_bounded_variable(conc(prem(proof,0)),lb); - rational bv_coeff = get_term_coefficient(my_con,bv); - if(bv_coeff.is_pos() != lb) - xtra_prem = 1; - if(bv_coeff.is_neg()) - bv_coeff = -bv_coeff; - - my_prems.push_back(prems[xtra_prem]); - my_coeffs.push_back(make_int(bv_coeff)); - my_prem_cons.push_back(conc(prem(proof,xtra_prem))); - my_con = sum_inequalities(my_coeffs,my_prem_cons); - } - - my_con = normalize_inequality(my_con); - Iproof::node hyp = iproof->make_hypothesis(mk_not(my_con)); - my_prems.push_back(hyp); - my_coeffs.push_back(make_int("1")); - my_prem_cons.push_back(mk_not(my_con)); - Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_prem_cons,my_coeffs); - - ast t = arg(my_con,0); - ast c = arg(my_con,1); - ast d = gcd_of_coefficients(t); - t = z3_simplify(mk_idiv(t,d)); - c = z3_simplify(mk_idiv(c,d)); - cut_con = make(op(my_con),t,c); - return iproof->make_cut_rule(my_con,d,cut_con,res); - } - - - rational get_first_coefficient(const ast &t, ast &v){ - if(op(t) == Plus){ - unsigned best_id = UINT_MAX; - rational best_coeff(0); - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - if(op(arg(t,i)) != Numeral){ - ast lv = get_linear_var(arg(t,i)); - unsigned id = ast_id(lv); - if(id < best_id) { - v = lv; - best_id = id; - best_coeff = get_coeff(arg(t,i)); - } - } - return best_coeff; - } - else - if(op(t) != Numeral){ - v = get_linear_var(t); - return(get_coeff(t)); - } - return rational(0); - } - - ast divide_inequalities(const ast &x, const ast&y){ - ast xvar, yvar; - rational xcoeff = get_first_coefficient(arg(x,0),xvar); - rational ycoeff = get_first_coefficient(arg(y,0),yvar); - if(xcoeff == rational(0) || ycoeff == rational(0) || xvar != yvar) - throw_unsupported(x); // can be caused by non-linear arithmetic - rational ratio = xcoeff/ycoeff; - if(denominator(ratio) != rational(1)) - throw_unsupported(y); // can this ever happen? - return make_int(ratio); // better be integer! - } - - ast AssignBounds2Farkas(const ast &proof, const ast &con){ - std::vector farkas_coeffs; - get_assign_bounds_coeffs(proof,farkas_coeffs); - int nargs = num_args(con); - if(nargs != (int)(farkas_coeffs.size())) - throw_unsupported(proof); // should never happen -#if 0 - if(farkas_coeffs[0] != make_int(rational(1))) - farkas_coeffs[0] = make_int(rational(1)); -#else - std::vector lits, lit_coeffs; - for(int i = 1; i < nargs; i++){ - lits.push_back(mk_not(arg(con,i))); - lit_coeffs.push_back(farkas_coeffs[i]); - } - ast sum = normalize_inequality(sum_inequalities(lit_coeffs,lits)); - ast conseq = normalize_inequality(arg(con,0)); - ast d = divide_inequalities(sum,conseq); -#if 0 - if(d != farkas_coeffs[0]) - std::cout << "wow!\n"; -#endif - farkas_coeffs[0] = d; -#endif - std::vector my_coeffs; - std::vector my_cons; - for(int i = 1; i < nargs; i++){ - my_cons.push_back(mk_not(arg(con,i))); - my_coeffs.push_back(farkas_coeffs[i]); - } - ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons,true /* round_off */)); - my_cons.push_back(mk_not(farkas_con)); - my_coeffs.push_back(make_int("1")); - std::vector my_hyps; - my_hyps.reserve(nargs); - for(int i = 0; i < nargs; i++) - my_hyps.push_back(iproof->make_hypothesis(my_cons[i])); - ast res = iproof->make_farkas(mk_false(),my_hyps,my_cons,my_coeffs); - res = iproof->make_cut_rule(farkas_con,farkas_coeffs[0],arg(con,0),res); - return res; - } - - ast AssignBoundsRule2Farkas(const ast &proof, const ast &con, std::vector prems){ - std::vector farkas_coeffs; - get_assign_bounds_rule_coeffs(proof,farkas_coeffs); - int nargs = num_prems(proof)+1; - if(nargs != (int)(farkas_coeffs.size())) - throw iz3_exception("bad assign-bounds theory lemma"); -#if 0 - if(farkas_coeffs[0] != make_int(rational(1))) - farkas_coeffs[0] = make_int(rational(1)); -#else - std::vector lits, lit_coeffs; - for(int i = 1; i < nargs; i++){ - lits.push_back(conc(prem(proof,i-1))); - lit_coeffs.push_back(farkas_coeffs[i]); - } - ast sum = normalize_inequality(sum_inequalities(lit_coeffs,lits)); - ast conseq = normalize_inequality(con); - ast d = divide_inequalities(sum,conseq); -#if 0 - if(d != farkas_coeffs[0]) - std::cout << "wow!\n"; -#endif - farkas_coeffs[0] = d; -#endif - std::vector my_coeffs; - std::vector my_cons; - for(int i = 1; i < nargs; i++){ - my_cons.push_back(conc(prem(proof,i-1))); - my_coeffs.push_back(farkas_coeffs[i]); - } - ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons,true /* round_off */)); - std::vector my_hyps; - for(int i = 1; i < nargs; i++) - my_hyps.push_back(prems[i-1]); - my_cons.push_back(mk_not(farkas_con)); - my_coeffs.push_back(make_int("1")); - my_hyps.push_back(iproof->make_hypothesis(mk_not(farkas_con))); - ast res = iproof->make_farkas(mk_false(),my_hyps,my_cons,my_coeffs); - res = iproof->make_cut_rule(farkas_con,farkas_coeffs[0],conc(proof),res); - return res; - } - - ast GomoryCutRule2Farkas(const ast &proof, const ast &con, std::vector prems){ - std::vector my_prems = prems; - std::vector my_coeffs; - std::vector my_prem_cons; - get_gomory_cut_coeffs(proof,my_coeffs); - int nargs = num_prems(proof); - if(nargs != (int)(my_coeffs.size())) - throw "bad gomory-cut theory lemma"; - my_prem_cons.reserve(nargs); - for(int i = 0; i < nargs; i++) - my_prem_cons.push_back(conc(prem(proof,i))); - ast my_con = normalize_inequality(sum_inequalities(my_coeffs,my_prem_cons)); - Iproof::node hyp = iproof->make_hypothesis(mk_not(my_con)); - my_prems.push_back(hyp); - my_coeffs.push_back(make_int("1")); - my_prem_cons.push_back(mk_not(my_con)); - Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_prem_cons,my_coeffs); - ast t = arg(my_con,0); - ast c = arg(my_con,1); - ast d = gcd_of_coefficients(t); - /* - t = z3_simplify(mk_idiv(t,d)); - c = z3_simplify(mk_idiv(c,d)); - ast cut_con = make(op(my_con),t,c); - */ - ast cut_con = con; - return iproof->make_cut_rule(my_con,d,cut_con,res); - } - - Iproof::node RewriteClause(Iproof::node clause, const ast &rew){ - if(pr(rew) == PR_MONOTONICITY){ - int nequivs = num_prems(rew); - for(int i = 0; i < nequivs; i++){ - Iproof::node equiv_pf = translate_main(prem(rew,i),false); - ast equiv = conc(prem(rew,i)); - clause = iproof->make_mp(equiv,clause,equiv_pf); - } - return clause; - } - if(pr(rew) == PR_TRANSITIVITY){ - clause = RewriteClause(clause,prem(rew,0)); - clause = RewriteClause(clause,prem(rew,1)); - return clause; - } - if(pr(rew) == PR_REWRITE){ - return clause; // just hope the rewrite does nothing! - } - throw_unsupported(rew); - } - - - // Following code is for elimination of "commutativity" axiom - - Iproof::node make_commuted_modus_ponens(const ast &proof, const std::vector &args){ - ast pf = arg(args[1],0); - ast comm_equiv = arg(args[1],1); // equivalence relation with possible commutations - ast P = conc(prem(proof,0)); - ast Q = conc(proof); - Iproof::node P_pf = args[0]; - ast P_comm = arg(comm_equiv,0); - ast Q_comm = arg(comm_equiv,1); - if(P != P_comm) - P_pf = iproof->make_symmetry(P_comm,P,P_pf); - Iproof::node res = iproof->make_mp(comm_equiv,P_pf,pf); - if(Q != Q_comm) - res = iproof->make_symmetry(Q,Q_comm,res); - return res; - } - - Iproof::node make_commuted_monotonicity(const ast &proof, const std::vector &args){ - ast pf = arg(args[0],0); - ast comm_equiv = arg(args[0],1); // equivalence relation with possible commutations - ast con = make(Iff,make(Not,arg(comm_equiv,0)),make(Not,arg(comm_equiv,1))); - std::vector eqs; eqs.push_back(comm_equiv); - std::vector pfs; pfs.push_back(pf); - ast res = iproof->make_congruence(eqs,con,pfs); - res = make(commute,res,con); - return res; - } - - Iproof::node make_commuted_symmetry(const ast &proof, const std::vector &args){ - ast pf = arg(args[0],0); - ast comm_equiv = arg(args[0],1); // equivalence relation with possible commutations - ast con = make(Iff,arg(comm_equiv,1),arg(comm_equiv,0)); - ast res = iproof->make_symmetry(con,comm_equiv,pf); - res = make(commute,res,con); - return res; - } - - void unpack_commuted(const ast &proof, const ast &cm, ast &pf, ast &comm_equiv){ - if(sym(cm) == commute){ - pf = arg(cm,0); - comm_equiv = arg(cm,1); - } - else { - pf = cm; - comm_equiv = conc(proof); - } - } - - Iproof::node make_commuted_transitivity(const ast &proof, const std::vector &args){ - ast pf[2], comm_equiv[2]; - for(int i = 0; i < 2; i++) - unpack_commuted(prem(proof,i),args[i],pf[i],comm_equiv[i]); - if(!(arg(comm_equiv[0],1) == arg(comm_equiv[1],0))){ - ast tw = twist(prem(proof,1)); - ast np = translate_main(tw,false); - unpack_commuted(tw,np,pf[1],comm_equiv[1]); - } - ast con = make(Iff,arg(comm_equiv[0],0),arg(comm_equiv[1],1)); - ast res = iproof->make_transitivity(arg(comm_equiv[0],0),arg(comm_equiv[0],1),arg(comm_equiv[1],1),pf[0],pf[1]); - res = make(commute,res,con); - return res; - } - - ast commute_equality(const ast &eq){ - return make(Equal,arg(eq,1),arg(eq,0)); - } - - ast commute_equality_iff(const ast &con){ - if(op(con) != Iff || op(arg(con,0)) != Equal) - throw_unsupported(con); - return make(Iff,commute_equality(arg(con,0)),commute_equality(arg(con,1))); - } - - // convert a proof of a=b <-> c=d into a proof of b=a <-> d=c - // TODO: memoize this? - ast twist(const ast &proof){ - pfrule dk = pr(proof); - ast con = commute_equality_iff(conc(proof)); - int n = num_prems(proof); - std::vector prs(n); - if(dk == PR_MONOTONICITY){ - for(int i = 0; i < n; i++) - prs[i] = prem(proof,i); - } - else - for(int i = 0; i < n; i++) - prs[i] = twist(prem(proof,i)); - switch(dk){ - case PR_MONOTONICITY: - case PR_SYMMETRY: - case PR_TRANSITIVITY: - case PR_COMMUTATIVITY: - prs.push_back(con); - return clone(proof,prs); - default: - throw_unsupported(proof); - } - } - - struct TermLt { - iz3mgr &m; - bool operator()(const ast &x, const ast &y){ - unsigned xid = m.ast_id(x); - unsigned yid = m.ast_id(y); - return xid < yid; - } - TermLt(iz3mgr &_m) : m(_m) {} - }; - - void SortTerms(std::vector &terms){ - TermLt foo(*this); - std::sort(terms.begin(),terms.end(),foo); - } - - ast SortSum(const ast &t){ - if(!(op(t) == Plus)) - return t; - int nargs = num_args(t); - if(nargs < 2) return t; - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = arg(t,i); - SortTerms(args); - return make(Plus,args); - } - - void get_sum_as_vector(const ast &t, std::vector &coeffs, std::vector &vars){ - if(!(op(t) == Plus)){ - coeffs.push_back(get_coeff(t)); - vars.push_back(get_linear_var(t)); - } - else { - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - get_sum_as_vector(arg(t,i),coeffs,vars); - } - } - - ast replace_summands_with_fresh_vars(const ast &t, hash_map &map){ - if(op(t) == Plus){ - int nargs = num_args(t); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = replace_summands_with_fresh_vars(arg(t,i),map); - return make(Plus,args); - } - if(op(t) == Times) - return make(Times,arg(t,0),replace_summands_with_fresh_vars(arg(t,1),map)); - if(map.find(t) == map.end()) - map[t] = mk_fresh_constant("@s",get_type(t)); - return map[t]; - } - - rational lcd(const std::vector &rats){ - rational res = rational(1); - for(unsigned i = 0; i < rats.size(); i++){ - res = lcm(res,denominator(rats[i])); - } - return res; - } - - Iproof::node reconstruct_farkas_with_dual(const std::vector &prems, const std::vector &pfs, const ast &con){ - int nprems = prems.size(); - std::vector npcons(nprems); - hash_map pain_map; // not needed - for(int i = 0; i < nprems; i++){ - npcons[i] = painfully_normalize_ineq(conc(prems[i]),pain_map); - if(op(npcons[i]) == Lt){ - ast constval = z3_simplify(make(Sub,arg(npcons[i],1),make_int(rational(1)))); - npcons[i] = make(Leq,arg(npcons[i],0),constval); - } - } - ast ncon = painfully_normalize_ineq(mk_not(con),pain_map); - npcons.push_back(ncon); - - hash_map dual_map; - std::vector cvec, vars_seen; - m().enable_int_real_coercions(true); - ast rhs = make_real(rational(0)); - for(unsigned i = 0; i < npcons.size(); i++){ - ast c= mk_fresh_constant("@c",real_type()); - cvec.push_back(c); - ast lhs = arg(npcons[i],0); - std::vector coeffs; - std::vector vars; - get_sum_as_vector(lhs,coeffs,vars); - for(unsigned j = 0; j < coeffs.size(); j++){ - rational coeff = coeffs[j]; - ast var = vars[j]; - if(dual_map.find(var) == dual_map.end()){ - dual_map[var] = make_real(rational(0)); - vars_seen.push_back(var); - } - ast foo = make(Plus,dual_map[var],make(Times,make_real(coeff),c)); - dual_map[var] = foo; - } - rhs = make(Plus,rhs,make(Times,c,arg(npcons[i],1))); - } - std::vector cnstrs; - for(unsigned i = 0; i < vars_seen.size(); i++) - cnstrs.push_back(make(Equal,dual_map[vars_seen[i]],make_real(rational(0)))); - cnstrs.push_back(make(Leq,rhs,make_real(rational(0)))); - for(unsigned i = 0; i < cvec.size() - 1; i++) - cnstrs.push_back(make(Geq,cvec[i],make_real(rational(0)))); - cnstrs.push_back(make(Equal,cvec.back(),make_real(rational(1)))); - ast new_proof; - - // greedily reduce the core - for(unsigned i = 0; i < cvec.size() - 1; i++){ - std::vector dummy; - cnstrs.push_back(make(Equal,cvec[i],make_real(rational(0)))); - if(!is_sat(cnstrs,new_proof,dummy)) - cnstrs.pop_back(); - } - - std::vector vals = cvec; - if(!is_sat(cnstrs,new_proof,vals)) - throw iz3_exception("Proof error!"); - std::vector rat_farkas_coeffs; - for(unsigned i = 0; i < cvec.size(); i++){ - ast bar = vals[i]; - rational r; - if(is_numeral(bar,r)) - rat_farkas_coeffs.push_back(r); - else - throw iz3_exception("Proof error!"); - } - rational the_lcd = lcd(rat_farkas_coeffs); - std::vector farkas_coeffs; - std::vector my_prems; - std::vector my_pcons; - for(unsigned i = 0; i < prems.size(); i++){ - ast fc = make_int(rat_farkas_coeffs[i] * the_lcd); - if(!(fc == make_int(rational(0)))){ - farkas_coeffs.push_back(fc); - my_prems.push_back(pfs[i]); - my_pcons.push_back(conc(prems[i])); - } - } - farkas_coeffs.push_back(make_int(the_lcd)); - my_prems.push_back(iproof->make_hypothesis(mk_not(con))); - my_pcons.push_back(mk_not(con)); - - Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); - return res; - } - - ast painfully_normalize_ineq(const ast &ineq, hash_map &map){ - ast res = normalize_inequality(ineq); - ast lhs = arg(res,0); - lhs = replace_summands_with_fresh_vars(lhs,map); - res = make(op(res),SortSum(lhs),arg(res,1)); - return res; - } - - Iproof::node painfully_reconstruct_farkas(const std::vector &prems, const std::vector &pfs, const ast &con){ - int nprems = prems.size(); - std::vector pcons(nprems),npcons(nprems); - hash_map pcon_to_pf, npcon_to_pcon, pain_map; - for(int i = 0; i < nprems; i++){ - pcons[i] = conc(prems[i]); - npcons[i] = painfully_normalize_ineq(pcons[i],pain_map); - pcon_to_pf[npcons[i]] = pfs[i]; - npcon_to_pcon[npcons[i]] = pcons[i]; - } - // ast leq = make(Leq,arg(con,0),arg(con,1)); - ast ncon = painfully_normalize_ineq(mk_not(con),pain_map); - pcons.push_back(mk_not(con)); - npcons.push_back(ncon); - // ast assumps = make(And,pcons); - ast new_proof; - std::vector dummy; - if(is_sat(npcons,new_proof,dummy)) - throw iz3_exception("Proof error!"); - pfrule dk = pr(new_proof); - int nnp = num_prems(new_proof); - std::vector my_prems; - std::vector farkas_coeffs, my_pcons; - - if(dk == PR_TH_LEMMA - && get_theory_lemma_theory(new_proof) == ArithTheory - && get_theory_lemma_kind(new_proof) == FarkasKind) - get_farkas_coeffs(new_proof,farkas_coeffs); - else if(dk == PR_UNIT_RESOLUTION && nnp == 2){ - for(int i = 0; i < nprems; i++) - farkas_coeffs.push_back(make_int(rational(1))); - } - else - return reconstruct_farkas_with_dual(prems,pfs,con); - - for(int i = 0; i < nnp; i++){ - ast p = conc(prem(new_proof,i)); - p = really_normalize_ineq(p); - if(pcon_to_pf.find(p) != pcon_to_pf.end()){ - my_prems.push_back(pcon_to_pf[p]); - my_pcons.push_back(npcon_to_pcon[p]); - } - else if(p == ncon){ - my_prems.push_back(iproof->make_hypothesis(mk_not(con))); - my_pcons.push_back(mk_not(con)); - } - else - return reconstruct_farkas_with_dual(prems,pfs,con); - } - Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); - return res; - } - - - - ast really_normalize_ineq(const ast &ineq){ - ast res = normalize_inequality(ineq); - res = make(op(res),SortSum(arg(res,0)),arg(res,1)); - return res; - } - - Iproof::node reconstruct_farkas(const std::vector &prems, const std::vector &pfs, const ast &con){ - int nprems = prems.size(); - std::vector pcons(nprems),npcons(nprems); - hash_map pcon_to_pf, npcon_to_pcon; - for(int i = 0; i < nprems; i++){ - pcons[i] = conc(prems[i]); - npcons[i] = really_normalize_ineq(pcons[i]); - pcon_to_pf[npcons[i]] = pfs[i]; - npcon_to_pcon[npcons[i]] = pcons[i]; - } - // ast leq = make(Leq,arg(con,0),arg(con,1)); - ast ncon = really_normalize_ineq(mk_not(con)); - pcons.push_back(mk_not(con)); - npcons.push_back(ncon); - // ast assumps = make(And,pcons); - ast new_proof; - std::vector dummy; - if(is_sat(npcons,new_proof,dummy)) - throw iz3_exception("Proof error!"); - pfrule dk = pr(new_proof); - int nnp = num_prems(new_proof); - std::vector my_prems; - std::vector farkas_coeffs, my_pcons; - - if(dk == PR_TH_LEMMA - && get_theory_lemma_theory(new_proof) == ArithTheory - && get_theory_lemma_kind(new_proof) == FarkasKind) - get_farkas_coeffs(new_proof,farkas_coeffs); - else if(dk == PR_UNIT_RESOLUTION && nnp == 2){ - for(int i = 0; i < nprems; i++) - farkas_coeffs.push_back(make_int(rational(1))); - } - else - return painfully_reconstruct_farkas(prems,pfs,con); - - for(int i = 0; i < nnp; i++){ - ast p = conc(prem(new_proof,i)); - p = really_normalize_ineq(p); - if(pcon_to_pf.find(p) != pcon_to_pf.end()){ - my_prems.push_back(pcon_to_pf[p]); - my_pcons.push_back(npcon_to_pcon[p]); - } - else if(p == ncon){ - my_prems.push_back(iproof->make_hypothesis(mk_not(con))); - my_pcons.push_back(mk_not(con)); - } - else - return painfully_reconstruct_farkas(prems,pfs,con); - } - Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); - return res; - } - - bool is_eq_propagate(const ast &proof){ - return pr(proof) == PR_TH_LEMMA && get_theory_lemma_theory(proof) == ArithTheory && get_theory_lemma_kind(proof) == EqPropagateKind; - } - - ast EqPropagate(const ast &con, const std::vector &prems, const std::vector &args){ - Iproof::node fps[2]; - ast ineq_con[2]; - for(int i = 0; i < 2; i++){ - opr o = i == 0 ? Leq : Geq; - ineq_con[i] = make(o, arg(con,0), arg(con,1)); - fps[i] = reconstruct_farkas(prems,args,ineq_con[i]); - } - ast res = iproof->make_leq2eq(arg(con,0), arg(con,1), ineq_con[0], ineq_con[1]); - std::vector dummy_clause; - for(int i = 0; i < 2; i++) - res = iproof->make_resolution(ineq_con[i],dummy_clause,res,fps[i]); - return res; - } - - ast ArithMysteryRule(const ast &con, const std::vector &prems, const std::vector &args){ - // Hope for the best! - Iproof::node guess = reconstruct_farkas(prems,args,con); - return guess; - } - - struct CannotCombineEqPropagate {}; - - void CombineEqPropagateRec(const ast &proof, std::vector &prems, std::vector &args, ast &eqprem){ - if(pr(proof) == PR_TRANSITIVITY && is_eq_propagate(prem(proof,1))){ - CombineEqPropagateRec(prem(proof,0), prems, args, eqprem); - ast dummy; - CombineEqPropagateRec(prem(proof,1), prems, args, dummy); - return; - } - if(is_eq_propagate(proof)){ - int nprems = num_prems(proof); - for(int i = 0; i < nprems; i++){ - prems.push_back(prem(proof,i)); - ast ppf = translate_main(prem(proof,i),false); - args.push_back(ppf); - } - return; - } - eqprem = proof; - } - - ast CombineEqPropagate(const ast &proof){ - std::vector prems, args; - ast eq1; - CombineEqPropagateRec(proof, prems, args, eq1); - ast eq2con = conc(proof); - if(!eq1.null()) - eq2con = make(Equal,arg(conc(eq1),1),arg(conc(proof),1)); - ast eq2 = EqPropagate(eq2con,prems,args); - if(!eq1.null()){ - Iproof::node foo = translate_main(eq1,false); - eq2 = iproof->make_transitivity(arg(conc(eq1),0), arg(conc(eq1),1), arg(conc(proof),1), foo, eq2); - } - return eq2; - } - - bool get_store_array(const ast &t, ast &res){ - if(op(t) == Store){ - res = t; - return true; - } - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - if(get_store_array(arg(t,i),res)) - return true; - return false; - } - - // translate a Z3 proof term into interpolating proof system - - Iproof::node translate_main(ast proof, bool expect_clause = true){ - AstToIpf &tr = translation; - hash_map &trc = expect_clause ? tr.first : tr.second; - std::pair foo(proof,Iproof::node()); - std::pair::iterator, bool> bar = trc.insert(foo); - Iproof::node &res = bar.first->second; - if(!bar.second) return res; - - // Try the locality rule first - - int frame = get_locality(proof); - if(frame != -1){ - ast e = from_ast(conc(proof)); - if(frame >= frames) frame = frames - 1; - std::vector foo; - if(expect_clause) - get_Z3_lits(conc(proof),foo); - else - foo.push_back(e); - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - foo.push_back(mk_not(*it)); - res = iproof->make_assumption(frame,foo); - return res; - } - - // If the proof is not local, break it down by proof rule - - pfrule dk = pr(proof); - unsigned nprems = num_prems(proof); - if(dk == PR_UNIT_RESOLUTION){ - res = translate_ur(proof); - } - else if(dk == PR_LEMMA){ - ast contra = prem(proof,0); // this is a proof of false from some hyps - res = translate_main(contra); - if(!expect_clause){ - std::vector foo; // the negations of the hyps form a clause - foo.push_back(from_ast(conc(proof))); - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - foo.push_back(mk_not(*it)); - res = iproof->make_contra(res,foo); - } - } - else { - std::vector lits; - ast con = conc(proof); - if(expect_clause) - get_Z3_lits(con, lits); - else - lits.push_back(from_ast(con)); - - // pattern match some idioms - if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST){ - if(get_locality_rec(prem(proof,1)) == INT_MAX) { - res = iproof->make_axiom(lits); - return res; - } - } - if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or && op(conc(prem(proof,0))) == Or){ - Iproof::node clause = translate_main(prem(proof,0),true); - res = RewriteClause(clause,prem(proof,1)); - return res; - } - -#if 0 - if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or) - std::cout << "foo!\n"; -#endif - - // no idea why this shows up - if(dk == PR_MODUS_PONENS_OEQ){ - if(conc(prem(proof,0)) == con){ - res = translate_main(prem(proof,0),expect_clause); - return res; - } - if(expect_clause && op(con) == Or){ // skolemization does this - Iproof::node clause = translate_main(prem(proof,0),true); - res = RewriteClause(clause,prem(proof,1)); - return res; - } - } - -#if 0 - if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,1)) == PR_COMMUTATIVITY){ - Iproof::node clause = translate_main(prem(proof,0),true); - res = make(commute,clause,conc(prem(proof,0))); // HACK -- we depend on Iproof::node being same as ast. - return res; - } - - if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,0)) == PR_COMMUTATIVITY){ - Iproof::node clause = translate_main(prem(proof,1),true); - res = make(commute,clause,conc(prem(proof,1))); // HACK -- we depend on Iproof::node being same as ast. - return res; - } -#endif - - if(dk == PR_TRANSITIVITY && is_eq_propagate(prem(proof,1))){ - try { - res = CombineEqPropagate(proof); - return res; - } - catch(const CannotCombineEqPropagate &){ - } - } - - /* this is the symmetry rule for ~=, that is, takes x ~= y and yields y ~= x. - the proof idiom uses commutativity, monotonicity and mp, but we replace it here - with symmtrey and resolution, that is, we prove y = x |- x = y, then resolve - with the proof of ~(x=y) to get ~y=x. */ - if(dk == PR_MODUS_PONENS && pr(prem(proof,1)) == PR_MONOTONICITY && pr(prem(prem(proof,1),0)) == PR_COMMUTATIVITY && num_prems(prem(proof,1)) == 1){ - Iproof::node ante = translate_main(prem(proof,0),false); - ast eq0 = arg(conc(prem(prem(proof,1),0)),0); - ast eq1 = arg(conc(prem(prem(proof,1),0)),1); - Iproof::node eq1hy = iproof->make_hypothesis(eq1); - Iproof::node eq0pf = iproof->make_symmetry(eq0,eq1,eq1hy); - std::vector clause; // just a dummy - res = iproof->make_resolution(eq0,clause,ante,eq0pf); - return res; - } - - /* This idiom takes ~P and Q=P, yielding ~Q. It uses a "rewrite" - (Q=false) = ~Q. We eliminate the rewrite by using symmetry, - congruence and modus ponens. */ - - if(dk == PR_MODUS_PONENS && pr(prem(proof,1)) == PR_REWRITE && pr(prem(proof,0)) == PR_TRANSITIVITY && pr(prem(prem(proof,0),1)) == PR_IFF_FALSE){ - if(op(con) == Not && arg(con,0) == arg(conc(prem(proof,0)),0)){ - Iproof::node ante1 = translate_main(prem(prem(proof,0),0),false); - Iproof::node ante2 = translate_main(prem(prem(prem(proof,0),1),0),false); - ast ante1_con = conc(prem(prem(proof,0),0)); - ast eq0 = arg(ante1_con,0); - ast eq1 = arg(ante1_con,1); - ast symm_con = make(Iff,eq1,eq0); - Iproof::node ante1s = iproof->make_symmetry(symm_con,ante1_con,ante1); - ast cong_con = make(Iff,make(Not,eq1),make(Not,eq0)); - Iproof::node ante1sc = iproof->make_congruence(symm_con,cong_con,ante1s); - res = iproof->make_mp(cong_con,ante2,ante1sc); - return res; - } - } - - - // translate all the premises - std::vector args(nprems); - for(unsigned i = 0; i < nprems; i++) - args[i] = translate_main(prem(proof,i),false); - - for(unsigned i = 0; i < nprems; i++) - if(sym(args[i]) == commute - && !(dk == PR_TRANSITIVITY || dk == PR_MODUS_PONENS || dk == PR_SYMMETRY || (dk == PR_MONOTONICITY && op(arg(con,0)) == Not))) - throw_unsupported(proof); - - switch(dk){ - case PR_TRANSITIVITY: { - if(sym(args[0]) == commute || sym(args[1]) == commute) - res = make_commuted_transitivity(proof,args); - else { - // assume the premises are x = y, y = z - ast x = arg(conc(prem(proof,0)),0); - ast y = arg(conc(prem(proof,0)),1); - ast z = arg(conc(prem(proof,1)),1); - res = iproof->make_transitivity(x,y,z,args[0],args[1]); - } - break; - } - case PR_TRANSITIVITY_STAR: { - // assume the premises are x = y, y = z, z = u, u = v, .. - - ast x = arg(conc(prem(proof,0)),0); - ast y = arg(conc(prem(proof,0)),1); - ast z = arg(conc(prem(proof,1)),1); - res = iproof->make_transitivity(x,y,z,args[0],args[1]); - - for (unsigned i = 2; i < nprems; ++i) { - y = z; - z = arg(conc(prem(proof,i)),1); - res = iproof->make_transitivity(x,y,z,res,args[i]); - } - break; - } - case PR_QUANT_INTRO: - case PR_MONOTONICITY: - { - std::vector eqs; eqs.resize(args.size()); - for(unsigned i = 0; i < args.size(); i++) - eqs[i] = conc(prem(proof,i)); - if(op(arg(con,0)) == Not && sym(args[0]) == commute) - res = make_commuted_monotonicity(proof,args); - else - res = iproof->make_congruence(eqs,con,args); - break; - } - case PR_REFLEXIVITY: { - res = iproof->make_reflexivity(con); - break; - } - case PR_SYMMETRY: { - if(sym(args[0]) == commute) - res = make_commuted_symmetry(proof,args); - else - res = iproof->make_symmetry(con,conc(prem(proof,0)),args[0]); - break; - } - case PR_MODUS_PONENS: { - if(sym(args[1]) == commute) - res = make_commuted_modus_ponens(proof,args); - else - res = iproof->make_mp(conc(prem(proof,1)),args[0],args[1]); - break; - } - case PR_TH_LEMMA: { - switch(get_theory_lemma_theory(proof)){ - case ArithTheory: - switch(get_theory_lemma_kind(proof)){ - case FarkasKind: { - std::vector farkas_coeffs, prem_cons; - get_farkas_coeffs(proof,farkas_coeffs); - if(nprems == 0) {// axiom, not rule - int nargs = num_args(con); - if(farkas_coeffs.size() != (unsigned)nargs){ - pfgoto(proof); - throw_unsupported(proof); - } - for(int i = 0; i < nargs; i++){ - ast lit = mk_not(arg(con,i)); - prem_cons.push_back(lit); - args.push_back(iproof->make_hypothesis(lit)); - } - } - else { // rule version (proves false) - prem_cons.resize(nprems); - for(unsigned i = 0; i < nprems; i++) - prem_cons[i] = conc(prem(proof,i)); - } - res = iproof->make_farkas(con,args,prem_cons,farkas_coeffs); - break; - } - case Leq2EqKind: { - // conc should be (or x = y (not (leq x y)) (not(leq y z)) ) - ast xeqy = arg(conc(proof),0); - ast x = arg(xeqy,0); - ast y = arg(xeqy,1); - res = iproof->make_leq2eq(x,y,arg(arg(conc(proof),1),0),arg(arg(conc(proof),2),0)); - break; - } - case Eq2LeqKind: { - // conc should be (or (not (= x y)) (leq x y)) - ast xeqy = arg(arg(conc(proof),0),0); - ast xleqy = arg(conc(proof),1); - ast x = arg(xeqy,0); - ast y = arg(xeqy,1); - res = iproof->make_eq2leq(x,y,xleqy); - break; - } - case GCDTestKind: { - std::vector farkas_coeffs; - get_broken_gcd_test_coeffs(proof,farkas_coeffs); - if(farkas_coeffs.size() != nprems){ - pfgoto(proof); - throw_unsupported(proof); - } - std::vector my_prems; my_prems.resize(2); - std::vector my_prem_cons; my_prem_cons.resize(2); - std::vector my_farkas_coeffs; my_farkas_coeffs.resize(2); - my_prems[0] = GCDtoDivRule(proof, true, farkas_coeffs, args, my_prem_cons[0]); - my_prems[1] = GCDtoDivRule(proof, false, farkas_coeffs, args, my_prem_cons[1]); - ast con = mk_false(); - my_farkas_coeffs[0] = my_farkas_coeffs[1] = make_int("1"); - res = iproof->make_farkas(con,my_prems,my_prem_cons,my_farkas_coeffs); - break; - } - case AssignBoundsKind: { - if(args.size() > 0) - res = AssignBoundsRule2Farkas(proof, conc(proof), args); - else - res = AssignBounds2Farkas(proof,conc(proof)); - break; - } - case GomoryCutKind: { - if(args.size() > 0) - res = GomoryCutRule2Farkas(proof, conc(proof), args); - else - throw_unsupported(proof); - break; - } - case EqPropagateKind: { - std::vector prems(nprems); - for(unsigned i = 0; i < nprems; i++) - prems[i] = prem(proof,i); - res = EqPropagate(con,prems,args); - break; - } - case ArithMysteryKind: { - // Z3 hasn't told us what kind of lemma this is -- maybe we can guess - std::vector prems(nprems); - for(unsigned i = 0; i < nprems; i++) - prems[i] = prem(proof,i); - res = ArithMysteryRule(con,prems,args); - break; - } - default: - throw_unsupported(proof); - } - break; - case ArrayTheory: {// nothing fancy for this - ast store_array; - if(get_store_array(con,store_array)) - res = iproof->make_axiom(lits,ast_scope(store_array)); - else - res = iproof->make_axiom(lits); // for array extensionality axiom - break; - } - default: - throw_unsupported(proof); - } - break; - } - case PR_HYPOTHESIS: { - res = iproof->make_hypothesis(conc(proof)); - break; - } - case PR_QUANT_INST: { - res = iproof->make_axiom(lits); - break; - } - case PR_DEF_AXIOM: { // this should only happen for formulas resulting from quantifier instantiation - res = iproof->make_axiom(lits); - break; - } - case PR_IFF_TRUE: { // turns p into p <-> true, noop for us - res = args[0]; - break; - } - case PR_IFF_FALSE: { // turns ~p into p <-> false, noop for us - if(is_local(con)) - res = args[0]; - else - throw_unsupported(proof); - break; - } - case PR_COMMUTATIVITY: { - ast comm_equiv = make(op(con),arg(con,0),arg(con,0)); - ast pf = iproof->make_reflexivity(comm_equiv); - res = make(commute,pf,comm_equiv); - break; - } - case PR_NOT_OR_ELIM: - case PR_AND_ELIM: { - std::vector rule_ax, res_conc; - ast piv = conc(prem(proof,0)); - rule_ax.push_back(make(Not,piv)); - rule_ax.push_back(con); - ast pf = iproof->make_axiom(rule_ax); - res_conc.push_back(con); - res = iproof->make_resolution(piv,res_conc,pf,args[0]); - break; - } - default: - IF_VERBOSE(0, verbose_stream() << "Unsupported proof rule: " << expr_ref((expr*)proof.raw(), *proof.mgr()) << "\n";); - // pfgoto(proof); - // SASSERT(0 && "translate_main: unsupported proof rule"); - throw_unsupported(proof); - } - } - - return res; - } - - void clear_translation(){ - translation.first.clear(); - translation.second.clear(); - } - - // We actually compute the interpolant here and then produce a proof consisting of just a lemma - - iz3proof::node translate(ast proof, iz3proof &dst) override { - std::vector itps; - scan_skolems(proof); - for(int i = 0; i < frames -1; i++){ -#ifdef NEW_LOCALITY - rng = range_downward(i); - locality.clear(); -#endif - iproof = iz3proof_itp::create(this,range_downward(i),weak_mode()); - try { - Iproof::node ipf = translate_main(proof); - ast itp = iproof->interpolate(ipf); - itps.push_back(itp); - delete iproof; - clear_translation(); - } - catch (const iz3proof_itp::proof_error &) { - delete iproof; - clear_translation(); - throw iz3proof::proof_error(); - } - catch (const unsupported &exc) { - delete iproof; - clear_translation(); - throw exc; - } - } - // Very simple proof -- lemma of the empty clause with computed interpolation - iz3proof::node Ipf = dst.make_lemma(std::vector(),itps); // builds result in dst - return Ipf; - } - - iz3translation_full(iz3mgr &mgr, - iz3secondary *_secondary, - const std::vector > &cnsts, - const std::vector &parents, - const std::vector &theory) - : iz3translation(mgr, cnsts, parents, theory) - { - frames = cnsts.size(); - traced_lit = ast(); - type boolbooldom[2] = {bool_type(),bool_type()}; - commute = function("@commute",2,boolbooldom,bool_type()); - m().inc_ref(commute); - } - - ~iz3translation_full() override { - m().dec_ref(commute); - } -}; - - - - -#ifdef IZ3_TRANSLATE_FULL - -iz3translation *iz3translation::create(iz3mgr &mgr, - iz3secondary *secondary, - const std::vector > &cnsts, - const std::vector &parents, - const std::vector &theory){ - return new iz3translation_full(mgr,secondary,cnsts,parents,theory); -} - - -#if 1 - -// This is just to make sure certain methods are compiled, so we can call then from the debugger. - -void iz3translation_full_trace_lit(iz3translation_full *p, iz3mgr::ast lit, iz3mgr::ast proof){ - p->trace_lit(lit, proof); -} - -void iz3translation_full_show_step(iz3translation_full *p, iz3mgr::ast proof){ - p->show_step(proof); -} - -void iz3translation_full_show_marked(iz3translation_full *p, iz3mgr::ast proof){ - p->show_marked(proof); -} - -void iz3translation_full_show_lit(iz3translation_full *p, iz3mgr::ast lit){ - p->show_lit(lit); -} - -void iz3translation_full_show_z3_lit(iz3translation_full *p, iz3mgr::ast a){ - p->show_z3_lit(a); -} - -void iz3translation_full_pfgoto(iz3translation_full *p, iz3mgr::ast proof){ - p->pfgoto(proof); -} - - -void iz3translation_full_pfback(iz3translation_full *p ){ - p->pfback(); -} - -void iz3translation_full_pffwd(iz3translation_full *p ){ - p->pffwd(); -} - -void iz3translation_full_pfprem(iz3translation_full *p, int i){ - p->pfprem(i); -} - -void iz3translation_full_expand(iz3translation_full *p, int i){ - p->expand(i); -} - -void iz3translation_full_symbols_out_of_scope(iz3translation_full *p, int i, const iz3mgr::ast &t){ - p->symbols_out_of_scope(i,t); -} - -void iz3translation_full_conc_symbols_out_of_scope(iz3translation_full *p, int i, const iz3mgr::ast &t){ - p->conc_symbols_out_of_scope(i,t); -} - -struct stdio_fixer { - stdio_fixer(){ - std::cout.rdbuf()->pubsetbuf(nullptr,0); - } - -} my_stdio_fixer; - -#endif - -#endif - - diff --git a/src/interp/iz3translate.h b/src/interp/iz3translate.h deleted file mode 100755 index d80c3b3fe..000000000 --- a/src/interp/iz3translate.h +++ /dev/null @@ -1,63 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3translate.h - - Abstract: - - Interface for proof translations from Z3 proofs to interpolatable - proofs. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifndef IZ3TRANSLATION_H -#define IZ3TRANSLATION_H - -#include "interp/iz3proof.h" -#include "interp/iz3secondary.h" - -// This is a interface class for translation from Z3 proof terms to -// an interpolatable proof - -class iz3translation : public iz3base { - public: - virtual iz3proof::node translate(ast, iz3proof &) = 0; - virtual ast quantify(ast e, const range &rng){return e;} - virtual ~iz3translation(){} - - /** This is thrown when the proof cannot be translated. */ - struct unsupported: public iz3_exception { - raw_ast* m_ast; - unsupported(ast const& a): iz3_exception("unsupported"), m_ast(a.raw()) { } - }; - - static iz3translation *create(iz3mgr &mgr, - iz3secondary *secondary, - const std::vector > &frames, - const std::vector &parents, - const std::vector &theory); - - protected: - iz3translation(iz3mgr &mgr, - const std::vector > &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3base(mgr,_cnsts,_parents,_theory) {} -}; - -// To use a secondary prover, define IZ3_TRANSLATE_DIRECT instead of this -#define IZ3_TRANSLATE_FULL - -#endif - - - diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp deleted file mode 100644 index 8c2016149..000000000 --- a/src/interp/iz3translate_direct.cpp +++ /dev/null @@ -1,1717 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3translate_direct.cpp - - Abstract: - - Translate a Z3 proof into the interpolating proof calculus. - Translation is direct, without transformations on the target proof - representation. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#pragma warning(disable:4390) -#endif - -#include "interp/iz3translate.h" -#include "interp/iz3proof.h" -#include "interp/iz3profiling.h" -#include "interp/iz3interp.h" - -#include -#include -#include -#include -#include -#include -#include - -//using std::vector; -using namespace stl_ext; - -/* This can introduce an address dependency if the range type of hash_map has - a destructor. Since the code in this file is not used and only here for - historical comparisons, we allow this non-determinism. -*/ -namespace stl_ext { - template - class hash { - public: - size_t operator()(const T *p) const { - return (size_t) p; - } - }; -} - -static int lemma_count = 0; -#if 0 -static int nll_lemma_count = 0; -#endif -#define SHOW_LEMMA_COUNT -1 - -// One half of a resolution. We need this to distinguish -// between resolving as a clause and as a unit clause. -// if pivot == conclusion(proof) it is unit. - -struct Z3_resolvent { - iz3base::ast proof; - bool is_unit; - iz3base::ast pivot; - Z3_resolvent(const iz3base::ast &_proof, bool _is_unit, const iz3base::ast &_pivot){ - proof = _proof; - is_unit = _is_unit; - pivot = _pivot; - } -}; - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Z3_resolvent &p) const { - return (p.proof.hash() + p.pivot.hash()); - } - }; -} - - -bool operator==(const Z3_resolvent &x, const Z3_resolvent &y) { - return x.proof == y.proof && x.pivot == y.pivot; -} - - - -typedef std::vector ResolventAppSet; - -struct non_local_lits { - ResolventAppSet proofs; // the proof nodes being raised - non_local_lits(ResolventAppSet &_proofs){ - proofs.swap(_proofs); - } -}; - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const non_local_lits &p) const { - size_t h = 0; - for(ResolventAppSet::const_iterator it = p.proofs.begin(), en = p.proofs.end(); it != en; ++it) - h += (size_t)*it; - return h; - } - }; -} - - -bool operator==(const non_local_lits &x, const non_local_lits &y) { - ResolventAppSet::const_iterator itx = x.proofs.begin(); - ResolventAppSet::const_iterator ity = y.proofs.begin(); - while(true){ - if(ity == y.proofs.end()) return itx == x.proofs.end(); - if(itx == x.proofs.end()) return ity == y.proofs.end(); - if(*itx != *ity) return false; - ++itx; ++ity; - } -} - - -/* This translator goes directly from Z3 proofs to interpolatable - proofs without an intermediate representation as an iz3proof. */ - -class iz3translation_direct : public iz3translation { -public: - - typedef ast Zproof; // type of non-interpolating proofs - typedef iz3proof Iproof; // type of interpolating proofs - - /* Here we have lots of hash tables for memoizing various methods and - other such global data structures. - */ - - typedef hash_map AstToInt; - AstToInt locality; // memoizes locality of Z3 proof terms - - typedef std::pair EquivEntry; - typedef hash_map EquivTab; - EquivTab equivs; // maps non-local terms to equivalent local terms, with proof - - typedef hash_set AstHashSet; - AstHashSet equivs_visited; // proofs already checked for equivalences - - - typedef std::pair, hash_map > AstToIpf; - AstToIpf translation; // Zproof nodes to Iproof nodes - - AstHashSet antes_added; // Z3 proof terms whose antecedents have been added to the list - std::vector > antes; // list of antecedent/frame pairs - std::vector local_antes; // list of local antecedents - - Iproof *iproof; // the interpolating proof we are constructing - - int frames; // number of frames - - typedef std::set AstSet; - typedef hash_map AstToAstSet; - AstToAstSet hyp_map; // map proof terms to hypothesis set - - struct LocVar { // localization vars - ast var; // a fresh variable - ast term; // term it represents - int frame; // frame in which it's defined - LocVar(ast v, ast t, int f){var=v;term=t;frame=f;} - }; - - std::vector localization_vars; // localization vars in order of creation - typedef hash_map AstToAst; - AstToAst localization_map; // maps terms to their localization vars - - typedef hash_map AstToBool; - - - - iz3secondary *secondary; // the secondary prover - - // Unique table for sets of non-local resolutions - hash_map non_local_lits_unique; - - // Unique table for resolvents - hash_map Z3_resolvent_unique; - - // Translation memo for case of non-local resolutions - hash_map non_local_translation; - -public: - - -#define from_ast(x) (x) - - // determine locality of a proof term - // return frame of derivation if local, or -1 if not - // result INT_MAX means the proof term is a tautology - // memoized in hash_map "locality" - - int get_locality_rec(ast proof){ - std::pair foo(proof,INT_MAX); - std::pair bar = locality.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - if(pr(proof) == PR_ASSERTED){ - ast ass = conc(proof); - res = frame_of_assertion(ass); - } - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - int bar = get_locality_rec(arg); - if(res == INT_MAX || res == bar) res = bar; - else if(bar != INT_MAX) res = -1; - } - } - return res; - } - - - int get_locality(ast proof){ - // if(lia_z3_axioms_only) return -1; - int res = get_locality_rec(proof); - if(res != -1){ - ast con = conc(proof); - range rng = ast_scope(con); - - // hack: if a clause contains "true", it reduces to "true", - // which means we won't compute the range correctly. we handle - // this case by computing the ranges of the literals separately - - if(is_true(con)){ - std::vector lits; - get_Z3_lits(conc(proof),lits); - for(unsigned i = 0; i < lits.size(); i++) - rng = range_glb(rng,ast_scope(lits[i])); - } - - if(!range_is_empty(rng)){ - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ - ast hyp = *it; - rng = range_glb(rng,ast_scope(hyp)); - } - } - - if(res == INT_MAX){ - if(range_is_empty(rng)) - res = -1; - else res = range_max(rng); - } - else { - if(!in_range(res,rng)) - res = -1; - } - } - return res; - } - - AstSet &get_hyps(ast proof){ - std::pair foo(proof,AstSet()); - std::pair bar = hyp_map.insert(foo); - AstSet &res = bar.first->second; - if(!bar.second) return res; - pfrule dk = pr(proof); - if(dk == PR_HYPOTHESIS){ - ast con = conc(proof); - res.insert(con); - } - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - AstSet &arg_hyps = get_hyps(arg); - res.insert(arg_hyps.begin(),arg_hyps.end()); - } - if(dk == PR_LEMMA){ - ast con = conc(proof); - res.erase(mk_not(con)); - if(is_or(con)){ - int clause_size = num_args(con); - for(int i = 0; i < clause_size; i++){ - ast neglit = mk_not(arg(con,i)); - res.erase(neglit); - } - } - } - } -#if 0 - AstSet::iterator it = res.begin(), en = res.end(); - if(it != en){ - AstSet::iterator old = it; - ++it; - for(; it != en; ++it, ++old) - if(!(*old < *it)) - std::cout << "foo!"; - } -#endif - return res; - } - - - // Find all the judgements of the form p <-> q, where - // p is local and q is non-local, recording them in "equivs" - // the map equivs_visited is used to record the already visited proof terms - - void find_equivs(ast proof){ - if(equivs_visited.find(proof) != equivs_visited.end()) - return; - equivs_visited.insert(proof); - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++) // do all the sub_terms - find_equivs(prem(proof,i)); - ast con = conc(proof); // get the conclusion - if(is_iff(con)){ - ast iff = con; - for(int i = 0; i < 2; i++) - if(!is_local(arg(iff,i)) && is_local(arg(iff,1-i))){ - std::pair > foo(arg(iff,i),std::pair(arg(iff,1-i),proof)); - equivs.insert(foo); - } - } - } - - // get the lits of a Z3 clause as secondary prover terms - void get_Z3_lits(ast t, std::vector &lits){ - opr dk = op(t); - if(dk == False) - return; // false = empty clause - if(dk == Or){ - unsigned nargs = num_args(t); - lits.resize(nargs); - for(unsigned i = 0; i < nargs; i++) // do all the sub_terms - lits[i] = arg(t,i); - } - else { - lits.push_back(t); - } - } - - // resolve two clauses represented as vectors of lits. replace first clause - void resolve(ast pivot, std::vector &cls1, std::vector &cls2){ - ast neg_pivot = mk_not(pivot); - for(unsigned i = 0; i < cls1.size(); i++){ - if(cls1[i] == pivot){ - cls1[i] = cls1.back(); - cls1.pop_back(); - bool found_pivot2 = false; - for(unsigned j = 0; j < cls2.size(); j++){ - if(cls2[j] == neg_pivot) - found_pivot2 = true; - else - cls1.push_back(cls2[j]); - } - (void)found_pivot2; - assert(found_pivot2); - return; - } - } - assert(0 && "resolve failed"); - } - - // get lits resulting from unit resolution up to and including "position" - // TODO: this is quadratic -- fix it - void do_unit_resolution(ast proof, int position, std::vector &lits){ - ast orig_clause = conc(prem(proof,0)); - get_Z3_lits(orig_clause,lits); - for(int i = 1; i <= position; i++){ - std::vector unit(1); - unit[0] = conc(prem(proof,i)); - resolve(mk_not(unit[0]),lits,unit); - } - } - - - // clear the localization variables - void clear_localization(){ - localization_vars.clear(); - localization_map.clear(); - } - - // create a fresh variable for localization - ast fresh_localization_var(ast term, int frame){ - std::ostringstream s; - s << "%" << (localization_vars.size()); - ast var = make_var(s.str().c_str(),get_type(term)); - sym_range(sym(var)) = range_full(); // make this variable global - localization_vars.push_back(LocVar(var,term,frame)); - return var; - } - - - // "localize" a term to a given frame range by - // creating new symbols to represent non-local subterms - - ast localize_term(ast e, const range &rng){ - if(ranges_intersect(ast_scope(e),rng)) - return e; // this term occurs in range, so it's O.K. - AstToAst::iterator it = localization_map.find(e); - if(it != localization_map.end()) - return it->second; - - // if is is non-local, we must first localize the arguments to - // the range of its function symbol - - int nargs = num_args(e); - if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ - range frng = rng; - if(op(e) == Uninterpreted){ - symb f = sym(e); - range srng = sym_range(f); - if(ranges_intersect(srng,rng)) // localize to desired range if possible - frng = range_glb(srng,rng); - } - std::vector largs(nargs); - for(int i = 0; i < nargs; i++){ - largs[i] = localize_term(arg(e,i),frng); - frng = range_glb(frng,ast_scope(largs[i])); - } - e = clone(e,largs); - assert(is_local(e)); - } - - - if(ranges_intersect(ast_scope(e),rng)) - return e; // this term occurs in range, so it's O.K. - - // choose a frame for the constraint that is close to range - int frame = range_near(ast_scope(e),rng); - - ast new_var = fresh_localization_var(e,frame); - localization_map[e] = new_var; - ast cnst = make(Equal,new_var,e); - antes.push_back(std::pair(cnst,frame)); - return new_var; - } - - // some patterm matching functions - - // match logical or with nargs arguments - // assumes AIG form - bool match_or(ast e, ast *args, int nargs){ - if(op(e) != Or) return false; - int n = num_args(e); - if(n != nargs) return false; - for(int i = 0; i < nargs; i++) - args[i] = arg(e,i); - return true; - } - - // match operator f with exactly nargs arguments - bool match_op(ast e, opr f, ast *args, int nargs){ - if(op(e) != f) return false; - int n = num_args(e); - if(n != nargs) return false; - for(int i = 0; i < nargs; i++) - args[i] = arg(e,i); - return true; - } - - // see if the given formula can be interpreted as - // an axiom instance (e.g., an array axiom instance). - // if so, add it to "antes" in an appropriate frame. - // this may require "localization" - - void get_axiom_instance(ast e){ - - // "store" axiom - // (or (= w q) (= (select (store a1 w y) q) (select a1 q))) - // std::cout << "ax: "; show(e); - ast lits[2],eq_ops_l[2],eq_ops_r[2],sel_ops[2], sto_ops[3], sel_ops2[2] ; - if(match_or(e,lits,2)) - if(match_op(lits[0],Equal,eq_ops_l,2)) - if(match_op(lits[1],Equal,eq_ops_r,2)) - for(int i = 0; i < 2; i++){ // try the second equality both ways - if(match_op(eq_ops_r[0],Select,sel_ops,2)) - if(match_op(sel_ops[0],Store,sto_ops,3)) - if(match_op(eq_ops_r[1],Select,sel_ops2,2)) - for(int j = 0; j < 2; j++){ // try the first equality both ways - if(eq_ops_l[0] == sto_ops[1] - && eq_ops_l[1] == sel_ops[1] - && eq_ops_l[1] == sel_ops2[1] - && sto_ops[0] == sel_ops2[0]) - if(is_local(sel_ops[0])) // store term must be local - { - ast sto = sel_ops[0]; - ast addr = localize_term(eq_ops_l[1],ast_scope(sto)); - ast res = make(Or, - make(Equal,eq_ops_l[0],addr), - make(Equal, - make(Select,sto,addr), - make(Select,sel_ops2[0],addr))); - int frame = range_min(ast_scope(res)); - antes.push_back(std::pair(res,frame)); - return; - } - std::swap(eq_ops_l[0],eq_ops_l[1]); - } - std::swap(eq_ops_r[0],eq_ops_r[1]); - } - } - - // a quantifier instantation looks like (~ forall x. P) \/ P[z/x] - // we need to find a time frame for P, then localize P[z/x] in this frame - - void get_quantifier_instance(ast e){ - ast disjs[2]; - if(match_or(e,disjs,2)){ - if(is_local(disjs[0])){ - ast res = localize_term(disjs[1], ast_scope(disjs[0])); - int frame = range_min(ast_scope(res)); - antes.push_back(std::pair(res,frame)); - return; - } - } - } - - ast get_judgement(ast proof){ - ast con = from_ast(conc(proof)); - AstSet &hyps = get_hyps(proof); - std::vector hyps_vec; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - hyps_vec.push_back(*it); - if(hyps_vec.size() == 0) return con; - con = make(Or,mk_not(make(And,hyps_vec)),con); - return con; - } - - // add the premises of a proof term to the "antes" list - - void add_antes(ast proof){ - if(antes_added.find(proof) != antes_added.end()) return; - antes_added.insert(proof); - int frame = get_locality(proof); - if(frame != -1) - if(1){ - ast e = get_judgement(proof); - if(frame >= frames) frame = frames-1; // can happen if there are no symbols - antes.push_back(std::pair(e,frame)); - return; - } - pfrule dk = pr(proof); - if(dk == PR_ASSERTED){ - ast ass = conc(proof); - frame = frame_of_assertion(ass); - if(frame >= frames) frame = frames-1; // can happen if a theory fact - antes.push_back(std::pair(ass,frame)); - return; - } - if(dk == PR_TH_LEMMA && num_prems(proof) == 0){ - get_axiom_instance(conc(proof)); - } - if(dk == PR_QUANT_INST && num_prems(proof) == 0){ - get_quantifier_instance(conc(proof)); - } - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - add_antes(arg); - } - } - - - // add quantifiers over the localization vars - // to an interpolant for frames lo-hi - - ast add_quants(ast e, int lo, int hi){ - for(int i = localization_vars.size() - 1; i >= 0; i--){ - LocVar &lv = localization_vars[i]; - opr quantifier = (lv.frame >= lo && lv.frame <= hi) ? Exists : Forall; - e = apply_quant(quantifier,lv.var,e); - } - return e; - } - - int get_lits_locality(std::vector &lits){ - range rng = range_full(); - for(std::vector::iterator it = lits.begin(), en = lits.end(); it != en; ++it){ - ast lit = *it; - rng = range_glb(rng,ast_scope(lit)); - } - if(range_is_empty(rng)) return -1; - int hi = range_max(rng); - if(hi >= frames) return frames - 1; - return hi; - } - - - struct invalid_lemma: public iz3_exception { - invalid_lemma(): iz3_exception("invalid_lemma") {} - }; - - - - - // prove a lemma (clause) using current antes list - // return proof of the lemma - // use the secondary prover - - int prove_lemma(std::vector &lits){ - - - // first try localization - if(antes.size() == 0){ - int local_frame = get_lits_locality(lits); - if(local_frame != -1) - return iproof->make_assumption(local_frame,lits); // no proof needed for purely local fact - } - - // group the assumptions by frame - std::vector preds(frames); - for(unsigned i = 0; i < preds.size(); i++) - preds[i] = mk_true(); - for(unsigned i = 0; i < antes.size(); i++){ - int frame = antes[i].second; - preds[frame] = mk_and(preds[frame],antes[i].first); // conjoin it to frame - } - - for(unsigned i = 0; i < lits.size(); i++){ - int frame; - if(!weak_mode()){ - frame = range_max(ast_scope(lits[i])); - if(frame >= frames) frame = frames-1; // could happen if contains no symbols - } - else { - frame = range_min(ast_scope(lits[i])); - if(frame < 0){ - frame = range_max(ast_scope(lits[i])); // could happen if contains no symbols - if(frame >= frames) frame = frames-1; - } - } - preds[frame] = mk_and(preds[frame],mk_not(lits[i])); - } - - - std::vector itps; // holds interpolants - - -#if 1 - ++lemma_count; - // std::cout << "lemma: " << lemma_count << std::endl; - if(lemma_count == SHOW_LEMMA_COUNT){ - for(unsigned i = 0; i < lits.size(); i++) - show_lit(lits[i]); - std::cerr << "lemma written to file lemma.smt:\n"; - iz3base foo(*this,preds,std::vector(),std::vector()); - foo.print("lemma.smt"); - throw invalid_lemma(); - } -#endif - -#if 0 - std::cout << "\nLemma:\n"; - for(unsigned i = 0; i < lits.size(); i++) - show_lit(lits[i]); -#endif - - // interpolate using secondary prover - profiling::timer_start("secondary prover"); - int sat = secondary->interpolate(preds,itps); - profiling::timer_stop("secondary prover"); - - std::cout << "lemma done" << std::endl; - - // if sat, lemma isn't valid, something is wrong - if(sat){ -#if 1 - std::cerr << "invalid lemma written to file invalid_lemma.smt:\n"; - iz3base foo(*this,preds,std::vector(),std::vector()); - foo.print("invalid_lemma.smt"); -#endif - throw iz3_incompleteness(); - } - assert(sat == 0); // if sat, lemma doesn't hold! - - // quantifiy the localization vars - for(unsigned i = 0; i < itps.size(); i++) - itps[i] = add_quants(itps[i],0,i); - - // Make a lemma, storing interpolants - Iproof::node res = iproof->make_lemma(lits,itps); - -#if 0 - std::cout << "Lemma interps\n"; - for(unsigned i = 0; i < itps.size(); i++) - show(itps[i]); -#endif - - // Reset state for the next lemma - antes.clear(); - antes_added.clear(); - clear_localization(); // use a fresh localization for each lemma - - return res; - } - - // sanity check: make sure that any non-local lit is really resolved - // with something in the non_local_lits set - - void check_non_local(ast lit, non_local_lits *nll){ - if(nll) - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - ast con = (*it)->pivot; - if(con == mk_not(lit)) return; - } - assert(0 && "bug in non-local resolution handling"); - } - - - void get_local_conclusion_lits(ast proof, bool expect_clause, AstSet &lits){ - std::vector reslits; - if(expect_clause) - get_Z3_lits(conc(proof),reslits); - else reslits.push_back(conc(proof)); - for(unsigned i = 0; i < reslits.size(); i++) - if(is_local(reslits[i])) - lits.insert(reslits[i]); - AstSet &pfhyps = get_hyps(proof); - for(AstSet::iterator hit = pfhyps.begin(), hen = pfhyps.end(); hit != hen; ++hit) - if(is_local(*hit)) - lits.insert(mk_not(*hit)); - } - - - void collect_resolvent_lits(Z3_resolvent *res, const AstSet &hyps, std::vector &lits){ - if(!res->is_unit){ - std::vector reslits; - get_Z3_lits(conc(res->proof),reslits); - for(unsigned i = 0; i < reslits.size(); i++) - if(reslits[i] != res->pivot) - lits.push_back(reslits[i]); - } - AstSet &pfhyps = get_hyps(res->proof); - for(AstSet::iterator hit = pfhyps.begin(), hen = pfhyps.end(); hit != hen; ++hit) - if(hyps.find(*hit) == hyps.end()) - lits.push_back(mk_not(*hit)); - } - - void filter_resolvent_lits(non_local_lits *nll, std::vector &lits){ - std::vector orig_lits; orig_lits.swap(lits); - std::set pivs; - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - pivs.insert(res->pivot); - pivs.insert(mk_not(res->pivot)); - } - for(unsigned i = 0; i < orig_lits.size(); i++) - if(pivs.find(orig_lits[i]) == pivs.end()) - lits.push_back(orig_lits[i]); - } - - void collect_all_resolvent_lits(non_local_lits *nll, std::vector &lits){ - if(nll){ - std::vector orig_lits; orig_lits.swap(lits); - std::set pivs; - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - pivs.insert(res->pivot); - pivs.insert(mk_not(res->pivot)); - } - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - { - std::vector reslits; - if(!res->is_unit) get_Z3_lits(conc(res->proof),reslits); - else reslits.push_back(conc(res->proof)); - for(unsigned i = 0; i < reslits.size(); i++) -#if 0 - if(reslits[i] != res->pivot && pivs.find(reslits[i]) == pivs.end()) -#endif - if(is_local(reslits[i])) - lits.push_back(reslits[i]); - } - } - for(unsigned i = 0; i < orig_lits.size(); i++) - if(pivs.find(orig_lits[i]) == pivs.end()) - lits.push_back(orig_lits[i]); - } - } - - void collect_proof_clause(ast proof, bool expect_clause, std::vector &lits){ - if(expect_clause) - get_Z3_lits(conc(proof),lits); - else - lits.push_back(from_ast(conc(proof))); - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator hit = hyps.begin(), hen = hyps.end(); hit != hen; ++hit) - lits.push_back(mk_not(*hit)); - } - - - // turn a bunch of literals into a lemma, replacing - // non-local lits with their local equivalents - // adds the accumulated antecedents (antes) as - // proof obligations of the lemma - - Iproof::node fix_lemma(std::vector &con_lits, AstSet &hyps, non_local_lits *nll){ - std::vector lits(con_lits); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - lits.push_back(mk_not(*it)); - if(nll){ - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - collect_resolvent_lits(res,hyps,lits); - add_antes(res->proof); - } - filter_resolvent_lits(nll,lits); - } - for(unsigned int i = 0; i < lits.size(); i++){ - EquivTab::iterator it = equivs.find(lits[i]); - if(it != equivs.end()){ - lits[i] = it->second.first; // replace with local equivalent - add_antes(it->second.second); // collect the premises that prove this - } - else { - if(!is_local(lits[i])){ - check_non_local(lits[i],nll); - lits[i] = mk_false(); - } - } - } - // TODO: should check here that derivation is local? - Iproof::node res = prove_lemma(lits); - return res; - } - - int num_lits(ast ast){ - opr dk = op(ast); - if(dk == False) - return 0; - if(dk == Or){ - unsigned nargs = num_args(ast); - int n = 0; - for(unsigned i = 0; i < nargs; i++) // do all the sub_terms - n += num_lits(arg(ast,i)); - return n; - } - else - return 1; - } - - struct non_lit_local_ante: public iz3_exception { - non_lit_local_ante(): iz3_exception("non_lit_local_ante") {} - }; - - bool local_antes_simple; - - bool add_local_antes(ast proof, AstSet &hyps, bool expect_clause = false){ - if(antes_added.find(proof) != antes_added.end()) return true; - antes_added.insert(proof); - ast con = from_ast(conc(proof)); - pfrule dk = pr(proof); - if(is_local(con) || equivs.find(con) != equivs.end()){ - if(!expect_clause || num_lits(conc(proof)) == 1){ - AstSet &this_hyps = get_hyps(proof); - if(std::includes(hyps.begin(),hyps.end(),this_hyps.begin(),this_hyps.end())){ - // if(hyps.find(con) == hyps.end()) -#if 0 - if(/* lemma_count == SHOW_LEMMA_COUNT - 1 && */ !is_literal_or_lit_iff(conc(proof))){ - std::cout << "\nnon-lit local ante\n"; - show_step(proof); - show(conc(proof)); - throw non_lit_local_ante(); - } -#endif - local_antes.push_back(proof); - return true; - } - else - ; //std::cout << "bar!\n"; - } - } - if(dk == PR_ASSERTED - //|| dk == PR_HYPOTHESIS - //|| dk == PR_TH_LEMMA - || dk == PR_QUANT_INST - //|| dk == PR_UNIT_RESOLUTION - //|| dk == PR_LEMMA - ) - return false; - if(dk == PR_HYPOTHESIS && hyps.find(con) != hyps.end()) - ; //std::cout << "blif!\n"; - if(dk == PR_HYPOTHESIS - || dk == PR_LEMMA) - ; //std::cout << "foo!\n"; - if(dk == PR_TH_LEMMA && num_prems(proof) == 0){ - // Check if this is an axiom instance - get_axiom_instance(conc(proof)); - } - - // #define SIMPLE_PROOFS -#ifdef SIMPLE_PROOFS - if(!(dk == PR_TRANSITIVITY - || dk == PR_MONOTONICITY)) - local_antes_simple = false; -#endif - - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - try { - if(!add_local_antes(arg, hyps, dk == PR_UNIT_RESOLUTION && i == 0)) - return false; - } - catch (non_lit_local_ante) { - std::cout << "\n"; - show_step(proof); - show(conc(proof)); - throw non_lit_local_ante(); - } - } - return true; - } - - std::vector lit_trace; - hash_set marked_proofs; - - bool proof_has_lit(const ast &proof, const ast &lit){ - AstSet &hyps = get_hyps(proof); - if(hyps.find(mk_not(lit)) != hyps.end()) - return true; - std::vector lits; - ast con = conc(proof); - get_Z3_lits(con, lits); - for(unsigned i = 0; i < lits.size(); i++) - if(lits[i] == lit) - return true; - return false; - } - - - void trace_lit_rec(const ast &lit, const ast &proof, AstHashSet &memo){ - if(memo.find(proof) == memo.end()){ - memo.insert(proof); - AstSet &hyps = get_hyps(proof); - std::vector lits; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - lits.push_back(mk_not(*it)); - ast con = conc(proof); - get_Z3_lits(con, lits); - for(unsigned i = 0; i < lits.size(); i++){ - if(lits[i] == lit){ - print_expr(std::cout,proof); - std::cout << "\n"; - marked_proofs.insert(proof); - pfrule dk = pr(proof); - if(dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA){ - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - trace_lit_rec(lit,arg,memo); - } - } - else - lit_trace.push_back(proof); - } - } - } - } - - ast traced_lit; - - int trace_lit(const ast &lit, const ast &proof){ - marked_proofs.clear(); - lit_trace.clear(); - traced_lit = lit; - AstHashSet memo; - trace_lit_rec(lit,proof,memo); - return lit_trace.size(); - } - - bool is_literal_or_lit_iff(const ast &lit){ - if(my_is_literal(lit)) return true; - if(op(lit) == Iff){ - return my_is_literal(arg(lit,0)) && my_is_literal(arg(lit,1)); - } - return false; - } - - bool my_is_literal(const ast &lit){ - ast abslit = is_not(lit) ? arg(lit,0) : lit; - int f = op(abslit); - return !(f == And || f == Or || f == Iff); - } - - void print_lit(const ast &lit){ - ast abslit = is_not(lit) ? arg(lit,0) : lit; - if(!is_literal_or_lit_iff(lit)){ - if(is_not(lit)) std::cout << "~"; - std::cout << "["; - print_expr(std::cout,abslit); - std::cout << "]"; - } - else - print_expr(std::cout,lit); - } - - void show_lit(const ast &lit){ - print_lit(lit); - std::cout << "\n"; - } - - void print_z3_lit(const ast &a){ - print_lit(from_ast(a)); - } - - void show_z3_lit(const ast &a){ - print_z3_lit(a); - std::cout << "\n"; - } - - - void show_con(const ast &proof, bool brief){ - if(!traced_lit.null() && proof_has_lit(proof,traced_lit)) - std::cout << "(*) "; - ast con = conc(proof); - AstSet &hyps = get_hyps(proof); - int count = 0; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ - if(brief && ++count > 5){ - std::cout << "... "; - break; - } - print_lit(*it); - std::cout << " "; - } - std::cout << "|- "; - std::vector lits; - get_Z3_lits(con,lits); - for(unsigned i = 0; i < lits.size(); i++){ - print_lit(lits[i]); - std::cout << " "; - } - std::cout << "\n"; - } - - void show_step(const ast &proof){ - std::cout << "\n"; - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - std::cout << "(" << i << ") "; - ast arg = prem(proof,i); - show_con(arg,true); - } - std::cout << "|------ "; - std::cout << string_of_symbol(sym(proof)) << "\n"; - show_con(proof,false); - } - - void show_marked( const ast &proof){ - std::cout << "\n"; - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - if(!traced_lit.null() && proof_has_lit(arg,traced_lit)){ - std::cout << "(" << i << ") "; - show_con(arg,true); - } - } - } - - std::vector pfhist; - int pfhist_pos; - - void pfgoto(const ast &proof){ - if(pfhist.size() == 0) - pfhist_pos = 0; - else pfhist_pos++; - pfhist.resize(pfhist_pos); - pfhist.push_back(proof); - show_step(proof); - } - - void show_nll(non_local_lits *nll){ - if(!nll)return; - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - show_step(res->proof); - std::cout << "Pivot: "; - show(res->pivot); - std::cout << std::endl; - } - } - - void pfback(){ - if(pfhist_pos > 0){ - pfhist_pos--; - show_step(pfhist[pfhist_pos]); - } - } - - void pffwd(){ - if(pfhist_pos < ((int)pfhist.size()) - 1){ - pfhist_pos++; - show_step(pfhist[pfhist_pos]); - } - } - - void pfprem(int i){ - if(pfhist.size() > 0){ - ast proof = pfhist[pfhist_pos]; - unsigned nprems = num_prems(proof); - if(i >= 0 && i < (int)nprems) - pfgoto(prem(proof,i)); - } - } - - int extract_th_lemma_common(std::vector &lits, non_local_lits *nll, bool lemma_nll = true){ - std::vector la = local_antes; - local_antes.clear(); // clear antecedents for next lemma - antes_added.clear(); - // std::vector lits; - AstSet hyps; // no hyps - for(unsigned i = 0; i < la.size(); i++) - lits.push_back(mk_not(from_ast(conc(la[i])))); - // lits.push_back(from_ast(conc(proof))); - Iproof::node res =fix_lemma(lits,hyps, lemma_nll ? nll : nullptr); - for(unsigned i = 0; i < la.size(); i++){ - Iproof::node q = translate_main(la[i],nll,false); - ast pnode = from_ast(conc(la[i])); - assert(is_local(pnode) || equivs.find(pnode) != equivs.end()); - Iproof::node neg = res; - Iproof::node pos = q; - if(is_not(pnode)){ - pnode = mk_not(pnode); - std::swap(neg,pos); - } - try { - res = iproof->make_resolution(pnode,neg,pos); - } - catch (const iz3proof::proof_error){ - std::cout << "\nresolution error in theory lemma\n"; - std::cout << "lits:\n"; - for(unsigned j = 0; j < lits.size(); j++) - show_lit(lits[j]); - std::cout << "\nstep:\n"; - show_step(la[i]); - throw invalid_lemma(); - } - } - return res; - } - - Iproof::node extract_simple_proof(const ast &proof, hash_set &leaves){ - if(leaves.find(proof) != leaves.end()) - return iproof->make_hypothesis(conc(proof)); - ast con = from_ast(conc(proof)); - pfrule dk = pr(proof); - unsigned nprems = num_prems(proof); - std::vector args(nprems); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - args[i] = extract_simple_proof(arg,leaves); - } - - switch(dk){ - case PR_TRANSITIVITY: - return iproof->make_transitivity(con,args[0],args[1]); - case PR_MONOTONICITY: - return iproof->make_congruence(con,args); - case PR_REFLEXIVITY: - return iproof->make_reflexivity(con); - case PR_SYMMETRY: - return iproof->make_symmetry(con,args[0]); - } - assert(0 && "extract_simple_proof: unknown op"); - return 0; - } - - int extract_th_lemma_simple(const ast &proof, std::vector &lits){ - std::vector la = local_antes; - local_antes.clear(); // clear antecedents for next lemma - antes_added.clear(); - - hash_set leaves; - for(unsigned i = 0; i < la.size(); i++) - leaves.insert(la[i]); - - Iproof::node ipf = extract_simple_proof(proof,leaves); - ast con = from_ast(conc(proof)); - Iproof::node hyp = iproof->make_hypothesis(mk_not(con)); - ipf = iproof->make_eqcontra(ipf,hyp); - - // std::vector lits; - AstSet hyps; // no hyps - for(unsigned i = 0; i < la.size(); i++) - lits.push_back(mk_not(from_ast(conc(la[i])))); - // lits.push_back(from_ast(conc(proof))); - - Iproof::node res = iproof->make_contra(ipf,lits); - - for(unsigned i = 0; i < la.size(); i++){ - Iproof::node q = translate_main(la[i],nullptr,false); - ast pnode = from_ast(conc(la[i])); - assert(is_local(pnode) || equivs.find(pnode) != equivs.end()); - Iproof::node neg = res; - Iproof::node pos = q; - if(is_not(pnode)){ - pnode = mk_not(pnode); - std::swap(neg,pos); - } - try { - res = iproof->make_resolution(pnode,neg,pos); - } - catch (const iz3proof::proof_error){ - std::cout << "\nresolution error in theory lemma\n"; - std::cout << "lits:\n"; - for(unsigned j = 0; j < lits.size(); j++) - show_lit(lits[j]); - std::cout << "\nstep:\n"; - show_step(la[i]); - throw invalid_lemma(); - } - } - return res; - } - - // #define NEW_EXTRACT_TH_LEMMA - - void get_local_hyps(const ast &proof, std::set &res){ - std::set hyps = get_hyps(proof); - for(std::set::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ - ast hyp = *it; - if(is_local(hyp)) - res.insert(hyp); - } - } - - int extract_th_lemma(ast proof, std::vector &lits, non_local_lits *nll){ - pfrule dk = pr(proof); - unsigned nprems = num_prems(proof); -#ifdef NEW_EXTRACT_TH_LEMMA - if(nprems == 0 && !nll) -#else - if(nprems == 0) -#endif - return 0; - if(nprems == 0 && dk == PR_TH_LEMMA) - // Check if this is an axiom instance - get_axiom_instance(conc(proof)); - - local_antes_simple = true; - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - if(!add_local_antes(arg,get_hyps(proof))){ - local_antes.clear(); // clear antecedents for next lemma - antes_added.clear(); - antes.clear(); - return 0; - } - } -#ifdef NEW_EXTRACT_TH_LEMMA - bool lemma_nll = nprems > 1; - if(nll && !lemma_nll){ - lemma_nll = false; - // std::cout << "lemma count = " << nll_lemma_count << "\n"; - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - ast arg = res->proof; - std::set loc_hyps; get_local_hyps(arg,loc_hyps); - if(!add_local_antes(arg,loc_hyps)){ - local_antes.clear(); // clear antecedents for next lemma - antes_added.clear(); - antes.clear(); - return 0; - } - } - collect_all_resolvent_lits(nll,lits); - } - int my_count = nll_lemma_count++; - int res; - try { - res = extract_th_lemma_common(lits,nll,lemma_nll); - } -#if 1 - catch (const invalid_lemma &) { - std::cout << "\n\nlemma: " << my_count; - std::cout << "\n\nproof node: \n"; - show_step(proof); - std::cout << "\n\nnon-local: \n"; - show_nll(nll); - pfgoto(nll->proofs[0]->proof); - show(conc(pfhist.back())); - pfprem(1); - show(conc(pfhist.back())); - pfprem(0); - show(conc(pfhist.back())); - pfprem(0); - show(conc(pfhist.back())); - pfprem(0); - show(conc(pfhist.back())); - std::cout << "\n\nliterals: \n"; - for(int i = 0; i < lits.size(); i++) - show_lit(lits[i]); - throw invalid_lemma(); - } -#endif - - return res; -#else -#ifdef SIMPLE_PROOFS - if(local_antes_simple && !nll) - return extract_th_lemma_simple(proof, lits); -#endif - return extract_th_lemma_common(lits,nll); -#endif - } - - int extract_th_lemma_ur(ast proof, int position, std::vector &lits, non_local_lits *nll){ - for(int i = 0; i <= position; i++){ - ast arg = prem(proof,i); - if(!add_local_antes(arg,get_hyps(proof),i==0)){ - local_antes.clear(); // clear antecedents for next lemma - antes_added.clear(); - antes.clear(); - return 0; - } - } - return extract_th_lemma_common(lits,nll); - } - - // see if any of the pushed resolvents are resolutions - // push the current proof into the latest such - int push_into_resolvent(ast proof, std::vector &lits, non_local_lits *nll, bool expect_clause){ - if(!nll) return 0; - if(num_args(proof) > 1) return 0; - ResolventAppSet resos = nll->proofs; - int pos = resos.size()-1; - for( ResolventAppSet::reverse_iterator it = resos.rbegin(), en = resos.rend(); it != en; ++it, --pos){ - Z3_resolvent *reso = *it; - ast ante = reso->proof; - ast pivot = reso->pivot; - bool is_unit = reso->is_unit; - pfrule dk = pr(ante); - bool pushable = dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA; - if(!pushable && num_args(ante) > 1){ -#if 0 - if (!is_local(conc(ante))) - std::cout << "non-local "; - std::cout << "pushable!\n"; -#endif - pushable = true; - } - if(pushable){ - // remove the resolvent from list and add new clause as resolvent - resos.erase((++it).base()); - for(; pos < (int)resos.size(); pos++){ - Z3_resolvent *r = resos[pos]; - resos[pos] = find_resolvent(r->proof,r->is_unit,mk_not(pivot)); - } - resos.push_back(find_resolvent(proof,!expect_clause,mk_not(pivot))); - non_local_lits *new_nll = find_nll(resos); - try { - int res = translate_main(ante,new_nll,!is_unit); - return res; - } - catch (const invalid_lemma &) { - std::cout << "\n\npushing: \n"; - std::cout << "nproof node: \n"; - show_step(proof); - std::cout << "\n\nold non-local: \n"; - show_nll(nll); - std::cout << "\n\nnew non-local: \n"; - show_nll(new_nll); - throw invalid_lemma(); - } - } - } - return 0; // no pushed resolvents are resolution steps - } - - non_local_lits *find_nll(ResolventAppSet &proofs){ - if(proofs.empty()) - return (non_local_lits *)nullptr; - std::pair foo(non_local_lits(proofs),(non_local_lits *)nullptr); - std::pair::iterator,bool> bar = - non_local_lits_unique.insert(foo); - non_local_lits *&res = bar.first->second; - if(bar.second) - res = new non_local_lits(bar.first->first); - return res; - } - - Z3_resolvent *find_resolvent(ast proof, bool unit, ast pivot){ - std::pair foo(Z3_resolvent(proof,unit,pivot),(Z3_resolvent *)nullptr); - std::pair::iterator,bool> bar = - Z3_resolvent_unique.insert(foo); - Z3_resolvent *&res = bar.first->second; - if(bar.second) - res = new Z3_resolvent(bar.first->first); - return res; - } - - // translate a unit resolution at position pos of given app - int translate_ur(ast proof, int position, non_local_lits *nll){ - ast ante = prem(proof,position); - if(position <= 0) - return translate_main(ante, nll); - ast pnode = conc(ante); - ast pnode_abs = !is_not(pnode) ? pnode : mk_not(pnode); - if(is_local(pnode) || equivs.find(pnode) != equivs.end()){ - Iproof::node neg = translate_ur(proof,position-1,nll); - Iproof::node pos = translate_main(ante, nll, false); - if(is_not(pnode)){ - pnode = mk_not(pnode); - std::swap(neg,pos); - } - try { - return iproof->make_resolution(pnode,neg,pos); - } - catch (const iz3proof::proof_error){ - std::cout << "resolution error in unit_resolution, position" << position << "\n"; - show_step(proof); - throw invalid_lemma(); - } - } - else { - // non-local pivot we have no local equivalent for - if(true){ - // try pushing the non-local resolution up - pfrule dk = pr(ante); - non_local_lits *old_nll = nll; - if(dk == PR_HYPOTHESIS) - ; //std::cout << "non-local hyp!\n"; // resolving with a hyp is a no-op - else { - ResolventAppSet new_proofs; - if(nll) new_proofs = nll->proofs; - Z3_resolvent *reso = find_resolvent(ante,true,pnode); - new_proofs.push_back(reso); - nll = find_nll(new_proofs); - } - try { - return translate_ur(proof,position-1,nll); - } - catch (const invalid_lemma &) { - if(old_nll != nll){ - std::cout << "\n\nadded_nll: \n"; - std::cout << "nproof node: \n"; - show_step(proof); - std::cout << "\n\new non-local step: \n"; - show_step(nll->proofs.back()->proof); - } - throw invalid_lemma(); - } - - } - else { - // just make a lemma - std::vector lits; - do_unit_resolution(proof,position,lits); - int res; - if(!(res = extract_th_lemma_ur(proof,position,lits,nll))){ - for(int i = 0; i <= position; i++){ - z3pf p = prem(proof,i); - add_antes(p); - } - res = fix_lemma(lits,get_hyps(proof),nll); - } - return res; - } - } - } - - non_local_lits *update_nll(ast proof, bool expect_clause, non_local_lits *nll){ - std::vector lits; - collect_proof_clause(proof,expect_clause,lits); - AstSet litset; - litset.insert(lits.begin(),lits.end()); - ResolventAppSet to_keep; - for(int i = nll->proofs.size()-1; i >= 0; --i){ - ast traced_lit = (nll->proofs[i])->pivot; - ast traced_lit_neg = mk_not(traced_lit); - if(litset.find(traced_lit) != litset.end() || litset.find(traced_lit_neg) != litset.end()){ - to_keep.push_back(nll->proofs[i]); - std::vector reslits; - AstSet dummy; - collect_resolvent_lits(nll->proofs[i],dummy,reslits); - litset.insert(reslits.begin(),reslits.end()); - } - } - if(to_keep.size() == nll->proofs.size()) return nll; - ResolventAppSet new_proofs; - for(int i = to_keep.size() - 1; i >= 0; --i) - new_proofs.push_back(to_keep[i]); - return find_nll(new_proofs); - } - - // translate a Z3 proof term into a secondary prover proof term - - Iproof::node translate_main(ast proof, non_local_lits *nll, bool expect_clause = true){ - non_local_lits *old_nll = nll; - if(nll) nll = update_nll(proof,expect_clause,nll); - AstToIpf &tr = nll ? non_local_translation[nll] : translation; - hash_map &trc = expect_clause ? tr.first : tr.second; - std::pair foo(proof,INT_MAX); - std::pair bar = trc.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - - - try { - int frame = get_locality(proof); - if(frame != -1){ - ast e = from_ast(conc(proof)); - if(frame >= frames) frame = frames - 1; - std::vector foo; - if(expect_clause) - get_Z3_lits(conc(proof),foo); - else - foo.push_back(e); - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - foo.push_back(mk_not(*it)); - res = iproof->make_assumption(frame,foo); - return res; - } - - pfrule dk = pr(proof); - unsigned nprems = num_prems(proof); - if(dk == PR_UNIT_RESOLUTION){ - res = translate_ur(proof, nprems - 1, nll); - } - else if(dk == PR_LEMMA){ - ast contra = prem(proof,0); // this is a proof of false from some hyps - res = translate_main(contra, nll); - if(!expect_clause){ - std::vector foo; // the negations of the hyps form a clause - foo.push_back(from_ast(conc(proof))); - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - foo.push_back(mk_not(*it)); - res = iproof->make_contra(res,foo); - } - } - else { - std::vector lits; - ast con = conc(proof); - if(expect_clause) - get_Z3_lits(con, lits); - else - lits.push_back(from_ast(con)); -#ifdef NEW_EXTRACT_TH_LEMMA - if(!(res = push_into_resolvent(proof,lits,nll,expect_clause))){ - if(!(res = extract_th_lemma(proof,lits,nll))){ -#else - if(!(res = extract_th_lemma(proof,lits,nll))){ - if(!(res = push_into_resolvent(proof,lits,nll,expect_clause))){ -#endif - // std::cout << "extract theory lemma failed\n"; - add_antes(proof); - res = fix_lemma(lits,get_hyps(proof),nll); - } - } - } -#ifdef CHECK_PROOFS - - if(0){ - AstSet zpf_con_lits, ipf_con_lits; - get_local_conclusion_lits(proof, expect_clause, zpf_con_lits); - if(nll){ - for(unsigned i = 0; i < nll->proofs.size(); i++) - get_local_conclusion_lits(nll->proofs[i]->proof,!nll->proofs[i]->is_unit,zpf_con_lits); - } - std::vector ipf_con; - iproof->get_conclusion(res,ipf_con); - for(unsigned i = 0; i < ipf_con.size(); i++) - ipf_con_lits.insert(ipf_con[i]); - if(!(ipf_con_lits == zpf_con_lits)){ - std::cout << "proof error:\n"; - std::cout << "expected lits:\n"; - for(AstSet::iterator hit = zpf_con_lits.begin(), hen = zpf_con_lits.end(); hit != hen; ++hit) - show_lit(*hit); - std::cout << "got lits:\n"; - for(AstSet::iterator hit = ipf_con_lits.begin(), hen = ipf_con_lits.end(); hit != hen; ++hit) - show_lit(*hit); - std::cout << "\nproof step:"; - show_step(proof); - std::cout << "\n"; - throw invalid_lemma(); - } - } -#endif - - return res; - } - - catch (const invalid_lemma &) { - if(old_nll != nll){ - std::cout << "\n\nupdated nll: \n"; - std::cout << "nproof node: \n"; - show_step(proof); - std::cout << "\n\new non-local: \n"; - show_nll(nll); - } - throw invalid_lemma(); - } - - } - - // Proof translation is in two stages: - // 1) Translate ast proof term to Zproof - // 2) Translate Zproof to Iproof - - Iproof::node translate(ast proof, Iproof &dst) override { - iproof = &dst; - Iproof::node Ipf = translate_main(proof,nullptr); // builds result in dst - return Ipf; - } - - iz3translation_direct(iz3mgr &mgr, - iz3secondary *_secondary, - const std::vector > &cnsts, - const std::vector &parents, - const std::vector &theory) - : iz3translation(mgr, cnsts, parents, theory) - { - secondary = _secondary; - frames = cnsts.size(); - traced_lit = ast(); - } - - ~iz3translation_direct() override { - for(hash_map::iterator - it = non_local_lits_unique.begin(), - en = non_local_lits_unique.end(); - it != en; - ++it) - delete it->second; - - for(hash_map::iterator - it = Z3_resolvent_unique.begin(), - en = Z3_resolvent_unique.end(); - it != en; - ++it) - delete it->second; - } - }; - - - - -#ifdef IZ3_TRANSLATE_DIRECT - - iz3translation *iz3translation::create(iz3mgr &mgr, - iz3secondary *secondary, - const std::vector > &cnsts, - const std::vector &parents, - const std::vector &theory){ - return new iz3translation_direct(mgr,secondary,cnsts,parents,theory); - } - - -#if 1 - - void iz3translation_direct_trace_lit(iz3translation_direct *p, iz3mgr::ast lit, iz3mgr::ast proof){ - p->trace_lit(lit, proof); - } - - void iz3translation_direct_show_step(iz3translation_direct *p, iz3mgr::ast proof){ - p->show_step(proof); - } - - void iz3translation_direct_show_marked(iz3translation_direct *p, iz3mgr::ast proof){ - p->show_marked(proof); - } - - void iz3translation_direct_show_lit(iz3translation_direct *p, iz3mgr::ast lit){ - p->show_lit(lit); - } - - void iz3translation_direct_show_z3_lit(iz3translation_direct *p, iz3mgr::ast a){ - p->show_z3_lit(a); - } - - void iz3translation_direct_pfgoto(iz3translation_direct *p, iz3mgr::ast proof){ - p->pfgoto(proof); - } - - void iz3translation_direct_show_nll(iz3translation_direct *p, non_local_lits *nll){ - p->show_nll(nll); - } - - void iz3translation_direct_pfback(iz3translation_direct *p ){ - p->pfback(); - } - - void iz3translation_direct_pffwd(iz3translation_direct *p ){ - p->pffwd(); - } - - void iz3translation_direct_pfprem(iz3translation_direct *p, int i){ - p->pfprem(i); - } - - - struct stdio_fixer { - stdio_fixer(){ - std::cout.rdbuf()->pubsetbuf(0,0); - } - - } my_stdio_fixer; - -#endif - -#endif - - diff --git a/src/math/subpaving/subpaving.cpp b/src/math/subpaving/subpaving.cpp index 16a9a9a9e..c43b74f0d 100644 --- a/src/math/subpaving/subpaving.cpp +++ b/src/math/subpaving/subpaving.cpp @@ -121,7 +121,7 @@ namespace subpaving { int2mpf(c, m_c); return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } - catch (f2n::exception) { + catch (const f2n::exception &) { throw subpaving::exception(); } } @@ -135,7 +135,7 @@ namespace subpaving { m.set(m_c, k); return reinterpret_cast(m_ctx.mk_ineq(x, m_c, lower, open)); } - catch (f2n::exception) { + catch (const f2n::exception &) { throw subpaving::exception(); } } @@ -178,7 +178,7 @@ namespace subpaving { int2hwf(c, m_c); return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } - catch (f2n::exception) { + catch (const f2n::exception &) { throw subpaving::exception(); } } @@ -192,7 +192,7 @@ namespace subpaving { m.set(m_c, k); return reinterpret_cast(m_ctx.mk_ineq(x, m_c, lower, open)); } - catch (f2n::exception) { + catch (const f2n::exception &) { throw subpaving::exception(); } } @@ -236,7 +236,7 @@ namespace subpaving { int2fpoint(c, m_c); return this->m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } - catch (typename context_fpoint::numeral_manager::exception) { + catch (const typename context_fpoint::numeral_manager::exception &) { throw subpaving::exception(); } } @@ -251,7 +251,7 @@ namespace subpaving { m.set(m_c, m_qm, k); return reinterpret_cast(this->m_ctx.mk_ineq(x, m_c, lower, open)); } - catch (typename context_fpoint::numeral_manager::exception) { + catch (const typename context_fpoint::numeral_manager::exception &) { throw subpaving::exception(); } } diff --git a/src/math/subpaving/subpaving_t_def.h b/src/math/subpaving/subpaving_t_def.h index bb129fee7..cf93fbfad 100644 --- a/src/math/subpaving/subpaving_t_def.h +++ b/src/math/subpaving/subpaving_t_def.h @@ -1310,7 +1310,7 @@ bool context_t::relevant_new_bound(var x, numeral const & k, bool lower, bool TRACE("subpaving_relevant_bound", tout << "new bound is relevant\n";); return true; } - catch (typename C::exception) { + catch (const typename C::exception &) { // arithmetic module failed. set_arith_failed(); return false; @@ -1722,7 +1722,7 @@ void context_t::propagate(node * n, bound * b) { } } } - catch (typename C::exception) { + catch (const typename C::exception &) { // arithmetic module failed, ignore constraint set_arith_failed(); } diff --git a/src/math/subpaving/tactic/subpaving_tactic.cpp b/src/math/subpaving/tactic/subpaving_tactic.cpp index 1f5f87eef..935fd5e19 100644 --- a/src/math/subpaving/tactic/subpaving_tactic.cpp +++ b/src/math/subpaving/tactic/subpaving_tactic.cpp @@ -241,19 +241,13 @@ public: m_stats.reset(); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { m_imp->process(*in); m_imp->collect_statistics(m_stats); result.reset(); result.push_back(in.get()); - mc = nullptr; - pc = nullptr; - core = nullptr; } catch (z3_exception & ex) { // convert all Z3 exceptions into tactic exceptions diff --git a/src/model/model.cpp b/src/model/model.cpp index 486b1e92e..e6c7b74a0 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -42,10 +42,8 @@ model::~model() { void model::copy_const_interps(model const & source) { - decl2expr::iterator it1 = source.m_interp.begin(); - decl2expr::iterator end1 = source.m_interp.end(); - for (; it1 != end1; ++it1) { - register_decl(it1->m_key, it1->m_value); + for (auto const& kv : source.m_interp) { + register_decl(kv.m_key, kv.m_value); } } diff --git a/src/model/model_pp.cpp b/src/model/model_pp.cpp index 2f9b114bb..08d63803b 100644 --- a/src/model/model_pp.cpp +++ b/src/model/model_pp.cpp @@ -31,10 +31,8 @@ static void display_uninterp_sorts(std::ostream & out, model_core const & md) { sort * s = md.get_uninterpreted_sort(i); out << "(define-sort " << mk_pp(s, m); ptr_vector const & univ = md.get_universe(s); - ptr_vector::const_iterator it = univ.begin(); - ptr_vector::const_iterator end = univ.end(); - for (; it != end; ++it) { - out << " " << mk_ismt2_pp(*it, m); + for (expr* e : univ) { + out << " " << mk_ismt2_pp(e, m); } out << ")\n"; } diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 54c07da28..cc14de5ce 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -188,7 +188,7 @@ namespace datalog { if (m_trail.get_num_scopes() == 0) { throw default_exception("there are no backtracking points to pop to"); } - if (m_engine.get() && get_engine() != DUALITY_ENGINE) { + if (m_engine.get()) { throw default_exception("pop operation is only supported by duality engine"); } m_trail.pop_scope(1); @@ -601,11 +601,6 @@ namespace datalog { m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); break; - case DUALITY_ENGINE: - m_rule_properties.collect(r); - m_rule_properties.check_existential_tail(); - m_rule_properties.check_for_negated_predicates(); - break; case CLP_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); @@ -828,9 +823,6 @@ namespace datalog { else if (e == symbol("clp")) { m_engine_type = CLP_ENGINE; } - else if (e == symbol("duality")) { - m_engine_type = DUALITY_ENGINE; - } else if (e == symbol("ddnf")) { m_engine_type = DDNF_ENGINE; } @@ -875,11 +867,6 @@ namespace datalog { case DDNF_ENGINE: flush_add_rules(); break; - case DUALITY_ENGINE: - // this lets us use duality with SAS 2013 abstraction - if(quantify_arrays()) - flush_add_rules(); - break; default: UNREACHABLE(); } diff --git a/src/muz/base/dl_engine_base.h b/src/muz/base/dl_engine_base.h index 576ed7f6b..b9ac6e7b5 100644 --- a/src/muz/base/dl_engine_base.h +++ b/src/muz/base/dl_engine_base.h @@ -32,7 +32,6 @@ namespace datalog { QBMC_ENGINE, TAB_ENGINE, CLP_ENGINE, - DUALITY_ENGINE, DDNF_ENGINE, LAST_ENGINE }; diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index 68e0aca6b..dd6728486 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -41,7 +41,7 @@ Revision History: #include "ast/rewriter/expr_replacer.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/expr_safe_replace.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/scoped_proof.h" #include "ast/datatype_decl_plugin.h" #include "ast/ast_util.h" @@ -326,8 +326,8 @@ namespace datalog { rules.set_output_predicate(qpred); if (m_ctx.get_model_converter()) { - filter_model_converter* mc = alloc(filter_model_converter, m); - mc->insert(qpred); + generic_model_converter* mc = alloc(generic_model_converter, m, "dl_rule"); + mc->hide(qpred); m_ctx.add_model_converter(mc); } diff --git a/src/muz/base/dl_util.cpp b/src/muz/base/dl_util.cpp index 87cfac04c..6c52d0537 100644 --- a/src/muz/base/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -389,24 +389,32 @@ namespace datalog { public: skip_model_converter() {} - model_converter * translate(ast_translation & translator) override { + model_converter * translate(ast_translation & translator) override { return alloc(skip_model_converter); } + void operator()(model_ref&) override {} + + void display(std::ostream & out) override { } + + void get_units(obj_map& units) override {} + }; model_converter* mk_skip_model_converter() { return alloc(skip_model_converter); } class skip_proof_converter : public proof_converter { - void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) override { + + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { SASSERT(num_source == 1); - result = source[0]; + return proof_ref(source[0], m); } proof_converter * translate(ast_translation & translator) override { return alloc(skip_proof_converter); } + void display(std::ostream & out) override { out << "(skip-proof-converter)\n"; } }; proof_converter* mk_skip_proof_converter() { return alloc(skip_proof_converter); } @@ -517,10 +525,9 @@ namespace datalog { } void collect_and_transform(const unsigned_vector & src, const unsigned_vector & translation, - unsigned_vector & res) { - unsigned n = src.size(); - for(unsigned i=0; i; - -#ifdef WIN32 -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "duality/duality.h" -#include "duality/duality_profiling.h" - -// using namespace Duality; - -namespace Duality { - - enum DualityStatus {StatusModel, StatusRefutation, StatusUnknown, StatusNull}; - - class duality_data { - public: - context ctx; - RPFP::LogicSolver *ls; - RPFP *rpfp; - - DualityStatus status; - std::vector clauses; - std::vector > clause_labels; - hash_map map; // edges to clauses - Solver *old_rs; - Solver::Counterexample cex; - - duality_data(ast_manager &_m) : ctx(_m,config(params_ref())) { - ls = nullptr; - rpfp = nullptr; - status = StatusNull; - old_rs = nullptr; - } - ~duality_data(){ - if(old_rs) - dealloc(old_rs); - if(rpfp) - dealloc(rpfp); - if(ls) - dealloc(ls); - } - }; - - - dl_interface::dl_interface(datalog::context& dl_ctx) : - engine_base(dl_ctx.get_manager(), "duality"), - m_ctx(dl_ctx) - - { - _d = nullptr; - // dl_ctx.get_manager().toggle_proof_mode(PGM_FINE); - } - - - dl_interface::~dl_interface() { - if(_d) - dealloc(_d); - } - - - // - // Check if the new rules are weaker so that we can - // re-use existing context. - // -#if 0 - void dl_interface::check_reset() { - // TODO - datalog::rule_ref_vector const& new_rules = m_ctx.get_rules().get_rules(); - datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules(); - bool is_subsumed = !old_rules.empty(); - for (unsigned i = 0; is_subsumed && i < new_rules.size(); ++i) { - is_subsumed = false; - for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) { - if (m_ctx.check_subsumes(*old_rules[j], *new_rules[i])) { - is_subsumed = true; - } - } - if (!is_subsumed) { - TRACE("pdr", new_rules[i]->display(m_ctx, tout << "Fresh rule ");); - m_context->reset(); - } - } - m_old_rules.reset(); - m_old_rules.add_rules(new_rules.size(), new_rules.c_ptr()); - } -#endif - - - lbool dl_interface::query(::expr * query) { - - // we restore the initial state in the datalog context - m_ctx.ensure_opened(); - - // if there is old data, get the cex and dispose (later) - duality_data *old_data = _d; - Solver *old_rs = nullptr; - if(old_data){ - old_rs = old_data->old_rs; - old_rs->GetCounterexample().swap(old_data->cex); - } - - scoped_proof generate_proofs_please(m_ctx.get_manager()); - - // make a new problem and solver - _d = alloc(duality_data,m_ctx.get_manager()); - _d->ctx.set("mbqi",m_ctx.get_params().duality_mbqi()); - _d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx); - _d->rpfp = alloc(RPFP,_d->ls); - - - - expr_ref_vector rules(m_ctx.get_manager()); - svector< ::symbol> names; - unsigned_vector bounds; - // m_ctx.get_rules_as_formulas(rules, names); - - - // If using SAS 2013 abstractiion, we need to perform some transforms - expr_ref query_ref(m_ctx.get_manager()); - if(m_ctx.quantify_arrays()){ - datalog::rule_manager& rm = m_ctx.get_rule_manager(); - rm.mk_query(query, m_ctx.get_rules()); - apply_default_transformation(m_ctx); - datalog::rule_set &rs = m_ctx.get_rules(); - if(m_ctx.get_rules().get_output_predicates().empty()) - query_ref = m_ctx.get_manager().mk_false(); - else { - func_decl_ref query_pred(m_ctx.get_manager()); - query_pred = m_ctx.get_rules().get_output_predicate(); - ptr_vector sorts; - unsigned nargs = query_pred.get()->get_arity(); - expr_ref_vector vars(m_ctx.get_manager()); - for(unsigned i = 0; i < nargs; i++){ - ::sort *s = query_pred.get()->get_domain(i); - vars.push_back(m_ctx.get_manager().mk_var(nargs-1-i,s)); - } - query_ref = m_ctx.get_manager().mk_app(query_pred.get(),nargs,vars.c_ptr()); - query = query_ref.get(); - } - unsigned nrules = rs.get_num_rules(); - for(unsigned i = 0; i < nrules; i++){ - expr_ref f(m_ctx.get_manager()); - rm.to_formula(*rs.get_rule(i), f); - rules.push_back(f); - } - } - else - m_ctx.get_raw_rule_formulas(rules, names, bounds); - - // get all the rules as clauses - std::vector &clauses = _d->clauses; - clauses.clear(); - for (unsigned i = 0; i < rules.size(); ++i) { - expr e(_d->ctx,rules[i].get()); - clauses.push_back(e); - } - - std::vector b_sorts; - std::vector b_names; - used_vars uv; - uv.process(query); - unsigned nuv = uv.get_max_found_var_idx_plus_1(); - for(int i = nuv-1; i >= 0; i--){ // var indices are backward - ::sort * s = uv.get(i); - if(!s) - s = _d->ctx.m().mk_bool_sort(); // missing var, whatever - b_sorts.push_back(sort(_d->ctx,s)); - b_names.push_back(symbol(_d->ctx,::symbol(i))); // names? - } - -#if 0 - // turn the query into a clause - expr q(_d->ctx,m_ctx.bind_variables(query,false)); - - std::vector b_sorts; - std::vector b_names; - if (q.is_quantifier() && !q.is_quantifier_forall()) { - int bound = q.get_quantifier_num_bound(); - for(int j = 0; j < bound; j++){ - b_sorts.push_back(q.get_quantifier_bound_sort(j)); - b_names.push_back(q.get_quantifier_bound_name(j)); - } - q = q.arg(0); - } -#else - expr q(_d->ctx,query); -#endif - - expr qc = implies(q,_d->ctx.bool_val(false)); - qc = _d->ctx.make_quant(Forall,b_sorts,b_names,qc); - clauses.push_back(qc); - bounds.push_back(UINT_MAX); - - // get the background axioms - unsigned num_asserts = m_ctx.get_num_assertions(); - for (unsigned i = 0; i < num_asserts; ++i) { - expr e(_d->ctx,m_ctx.get_assertion(i)); - _d->rpfp->AssertAxiom(e); - } - - // make sure each predicate is the head of at least one clause - func_decl_set heads; - for(unsigned i = 0; i < clauses.size(); i++){ - expr cl = clauses[i]; - - while(true){ - if(cl.is_app()){ - decl_kind k = cl.decl().get_decl_kind(); - if(k == Implies) - cl = cl.arg(1); - else { - heads.insert(cl.decl()); - break; - } - } - else if(cl.is_quantifier()) - cl = cl.body(); - else break; - } - } - ast_ref_vector const &pinned = m_ctx.get_pinned(); - for(unsigned i = 0; i < pinned.size(); i++){ - ::ast *fa = pinned[i]; - if(is_func_decl(fa)){ - ::func_decl *fd = to_func_decl(fa); - if (m_ctx.is_predicate(fd)) { - func_decl f(_d->ctx, fd); - if (!heads.contains(fd)) { - int arity = f.arity(); - std::vector args; - args.reserve(arity); - for (int j = 0; j < arity; j++) - args.push_back(_d->ctx.fresh_func_decl("X", f.domain(j))()); - expr c = implies(_d->ctx.bool_val(false), f(args)); - c = _d->ctx.make_quant(Forall, args, c); - clauses.push_back(c); - bounds.push_back(UINT_MAX); - } - } - } - } - unsigned rb = m_ctx.get_params().duality_recursion_bound(); - std::vector std_bounds; - for(unsigned i = 0; i < bounds.size(); i++){ - unsigned b = bounds[i]; - if (b == UINT_MAX) b = rb; - std_bounds.push_back(b); - } - - // creates 1-1 map between clauses and rpfp edges - _d->rpfp->FromClauses(clauses,&std_bounds); - - // populate the edge-to-clause map - for(unsigned i = 0; i < _d->rpfp->edges.size(); ++i) - _d->map[_d->rpfp->edges[i]] = i; - - // create a solver object - - Solver *rs = Solver::Create("duality", _d->rpfp); - - if(old_rs) - rs->LearnFrom(old_rs); // new solver gets hints from old solver - - // set its options - IF_VERBOSE(1, rs->SetOption("report","1");); - rs->SetOption("full_expand",m_ctx.get_params().duality_full_expand() ? "1" : "0"); - rs->SetOption("no_conj",m_ctx.get_params().duality_no_conj() ? "1" : "0"); - rs->SetOption("feasible_edges",m_ctx.get_params().duality_feasible_edges() ? "1" : "0"); - rs->SetOption("use_underapprox",m_ctx.get_params().duality_use_underapprox() ? "1" : "0"); - rs->SetOption("stratified_inlining",m_ctx.get_params().duality_stratified_inlining() ? "1" : "0"); - rs->SetOption("batch_expand",m_ctx.get_params().duality_batch_expand() ? "1" : "0"); - rs->SetOption("conjecture_file",m_ctx.get_params().duality_conjecture_file()); - rs->SetOption("enable_restarts",m_ctx.get_params().duality_enable_restarts() ? "1" : "0"); -#if 0 - if(rb != UINT_MAX){ - std::ostringstream os; os << rb; - rs->SetOption("recursion_bound", os.str()); - } -#endif - - // Solve! - bool ans; - try { - ans = rs->Solve(); - } - catch (Duality::solver::cancel_exception &exn){ - throw default_exception(Z3_CANCELED_MSG); - } - catch (Duality::Solver::Incompleteness &exn){ - throw default_exception("incompleteness"); - } - - // profile! - - if(m_ctx.get_params().duality_profile()) - print_profile(std::cout); - - // save the result and counterexample if there is one - _d->status = ans ? StatusModel : StatusRefutation; - _d->cex.swap(rs->GetCounterexample()); // take ownership of cex - _d->old_rs = rs; // save this for later hints - - if(old_data){ - dealloc(old_data); // this deallocates the old solver if there is one - } - - // dealloc(rs); this is now owned by data - - // true means the RPFP problem is SAT, so the query is UNSAT - // but we return undef if the UNSAT result is bounded - if(ans){ - if(rs->IsResultRecursionBounded()){ -#if 0 - m_ctx.set_status(datalog::BOUNDED); - return l_undef; -#else - return l_false; -#endif - } - return l_false; - } - return l_true; - } - - expr_ref dl_interface::get_cover_delta(int level, ::func_decl* pred_orig) { - SASSERT(false); - return expr_ref(m_ctx.get_manager()); - } - - void dl_interface::add_cover(int level, ::func_decl* pred, ::expr* property) { - SASSERT(false); - } - - unsigned dl_interface::get_num_levels(::func_decl* pred) { - SASSERT(false); - return 0; - } - - void dl_interface::collect_statistics(::statistics& st) const { - } - - void dl_interface::reset_statistics() { - } - - static hash_set *local_func_decls; - - static void print_proof(dl_interface *d, std::ostream& out, RPFP *tree, RPFP::Node *root) { - context &ctx = d->dd()->ctx; - RPFP::Node &node = *root; - RPFP::Edge &edge = *node.Outgoing; - - // first, prove the children (that are actually used) - - for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!tree->Empty(edge.Children[i])){ - print_proof(d,out,tree,edge.Children[i]); - } - } - - // print the label and the proved fact - - out << "(step s!" << node.number; - out << " (" << node.Name.name(); - for(unsigned i = 0; i < edge.F.IndParams.size(); i++) - out << " " << tree->Eval(&edge,edge.F.IndParams[i]); - out << ")\n"; - - // print the rule number - - out << " rule!" << node.Outgoing->map->number; - - // print the substitution - - out << " (subst\n"; - RPFP::Edge *orig_edge = edge.map; - int orig_clause = d->dd()->map[orig_edge]; - expr &t = d->dd()->clauses[orig_clause]; - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - hash_map subst; - for(int j = 0; j < bound; j++){ - sort the_sort = t.get_quantifier_bound_sort(j); - symbol name = t.get_quantifier_bound_name(j); - expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - out << " (= " << skolem << " " << tree->Eval(&edge,skolem) << ")\n"; - expr local_skolem = tree->Localize(&edge,skolem); - (*local_func_decls).insert(local_skolem.decl()); - } - } - out << " )\n"; - - out << " (labels"; - std::vector labels; - tree->GetLabels(&edge,labels); - for(unsigned j = 0; j < labels.size(); j++){ - out << " " << labels[j]; - } - - out << " )\n"; - - // reference the proofs of all the children, in syntactic order - // "true" means the child is not needed - - out << " (ref "; - for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!tree->Empty(edge.Children[i])) - out << " s!" << edge.Children[i]->number; - else - out << " true"; - } - out << " )"; - out << ")\n"; - } - - - void dl_interface::display_certificate(std::ostream& out) const { - ((dl_interface *)this)->display_certificate_non_const(out); - } - - void dl_interface::display_certificate_non_const(std::ostream& out) { - if(_d->status == StatusModel){ - ast_manager &m = m_ctx.get_manager(); - model_ref md = get_model(); - out << "(fixedpoint \n"; - model_smt2_pp(out, m, *md.get(), 0); - out << ")\n"; - } - else if(_d->status == StatusRefutation){ - out << "(derivation\n"; - // negation of the query is the last clause -- prove it - hash_set locals; - local_func_decls = &locals; - print_proof(this,out,_d->cex.get_tree(),_d->cex.get_root()); - out << ")\n"; - out << "(model \n\""; - ::model mod(m_ctx.get_manager()); - model orig_model = _d->cex.get_tree()->dualModel; - for(unsigned i = 0; i < orig_model.num_consts(); i++){ - func_decl cnst = orig_model.get_const_decl(i); - if (locals.find(cnst) == locals.end()) { - expr thing = orig_model.get_const_interp(cnst); - mod.register_decl(to_func_decl(cnst.raw()), to_expr(thing.raw())); - } - } - for(unsigned i = 0; i < orig_model.num_funcs(); i++){ - func_decl cnst = orig_model.get_func_decl(i); - if (locals.find(cnst) == locals.end()) { - func_interp thing = orig_model.get_func_interp(cnst); - ::func_interp *thing_raw = thing; - mod.register_decl(to_func_decl(cnst.raw()), thing_raw->copy()); - } - } - model_v2_pp(out,mod); - out << "\")\n"; - } - } - - expr_ref dl_interface::get_answer() { - SASSERT(false); - return expr_ref(m_ctx.get_manager()); - } - - void dl_interface::cancel() { -#if 0 - if(_d && _d->ls) - _d->ls->cancel(); -#else - // HACK: duality can't cancel at all times, we just exit here - std::cout << "(error \"duality canceled\")\nunknown\n"; - abort(); -#endif - } - - void dl_interface::cleanup() { - } - - void dl_interface::updt_params() { - } - - model_ref dl_interface::get_model() { - ast_manager &m = m_ctx.get_manager(); - model_ref md(alloc(::model, m)); - std::vector &nodes = _d->rpfp->nodes; - expr_ref_vector conjs(m); - for (unsigned i = 0; i < nodes.size(); ++i) { - RPFP::Node *node = nodes[i]; - func_decl &pred = node->Name; - expr_ref prop(m); - prop = to_expr(node->Annotation.Formula); - std::vector ¶ms = node->Annotation.IndParams; - expr_ref q(m); - expr_ref_vector sig_vars(m); - for (unsigned j = 0; j < params.size(); ++j) - sig_vars.push_back(params[params.size()-j-1]); // TODO: why backwards? - expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q); - if (params.empty()) { - md->register_decl(pred, q); - } - else { - ::func_interp* fi = alloc(::func_interp, m, params.size()); - fi->set_else(q); - md->register_decl(pred, fi); - } - } - return md; - } - - static proof_ref extract_proof(dl_interface *d, RPFP *tree, RPFP::Node *root) { - context &ctx = d->dd()->ctx; - ast_manager &mgr = ctx.m(); - RPFP::Node &node = *root; - RPFP::Edge &edge = *node.Outgoing; - RPFP::Edge *orig_edge = edge.map; - - // first, prove the children (that are actually used) - - proof_ref_vector prems(mgr); - ::vector substs; - int orig_clause = d->dd()->map[orig_edge]; - expr &t = d->dd()->clauses[orig_clause]; - prems.push_back(mgr.mk_asserted(ctx.uncook(t))); - - substs.push_back(expr_ref_vector(mgr)); - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - hash_map subst; - for(int j = 0; j < bound; j++){ - sort the_sort = t.get_quantifier_bound_sort(j); - symbol name = t.get_quantifier_bound_name(j); - expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - expr val = tree->Eval(&edge,skolem); - expr_ref thing(ctx.uncook(val),mgr); - substs[0].push_back(thing); - expr local_skolem = tree->Localize(&edge,skolem); - (*local_func_decls).insert(local_skolem.decl()); - } - } - - svector > pos; - for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!tree->Empty(edge.Children[i])){ - pos.push_back(std::pair(i+1,0)); - proof_ref prem = extract_proof(d,tree,edge.Children[i]); - prems.push_back(prem); - substs.push_back(expr_ref_vector(mgr)); - } - } - - func_decl f = node.Name; - std::vector args; - for(unsigned i = 0; i < edge.F.IndParams.size(); i++) - args.push_back(tree->Eval(&edge,edge.F.IndParams[i])); - expr conc = f(args); - - - ::vector< ::proof *> pprems; - for(unsigned i = 0; i < prems.size(); i++) - pprems.push_back(prems[i].get()); - - proof_ref res(mgr.mk_hyper_resolve(pprems.size(),&pprems[0], ctx.uncook(conc), pos, substs),mgr); - return res; - - } - - proof_ref dl_interface::get_proof() { - if(_d->status == StatusRefutation){ - hash_set locals; - local_func_decls = &locals; - return extract_proof(this,_d->cex.get_tree(),_d->cex.get_root()); - } - else - return proof_ref(m_ctx.get_manager()); - } -} diff --git a/src/muz/duality/duality_dl_interface.h b/src/muz/duality/duality_dl_interface.h deleted file mode 100644 index 8178618ae..000000000 --- a/src/muz/duality/duality_dl_interface.h +++ /dev/null @@ -1,80 +0,0 @@ -/*++ - Copyright (c) 2013 Microsoft Corporation - - Module Name: - - duality_dl_interface.h - - Abstract: - - SMT2 interface for Duality - - Author: - - Krystof Hoder (t-khoder) 2011-9-22. - Modified by Ken McMIllan (kenmcmil) 2013-4-18. - - Revision History: - - --*/ - -#ifndef DUALITY_DL_INTERFACE_H_ -#define DUALITY_DL_INTERFACE_H_ - -#include "util/lbool.h" -#include "muz/base/dl_rule.h" -#include "muz/base/dl_rule_set.h" -#include "muz/base/dl_engine_base.h" -#include "util/statistics.h" - -namespace datalog { - class context; -} - -namespace Duality { - - class duality_data; - - class dl_interface : public datalog::engine_base { - duality_data *_d; - datalog::context &m_ctx; - - public: - dl_interface(datalog::context& ctx); - ~dl_interface() override; - - lbool query(expr* query) override; - - void cancel() override; - - void cleanup() override; - - void display_certificate(std::ostream& out) const override; - - void collect_statistics(statistics& st) const override; - - void reset_statistics() override; - - expr_ref get_answer() override; - - unsigned get_num_levels(func_decl* pred) override; - - expr_ref get_cover_delta(int level, func_decl* pred) override; - - void add_cover(int level, func_decl* pred, expr* property) override; - - void updt_params() override; - - model_ref get_model() override; - - proof_ref get_proof() override; - - duality_data *dd(){return _d;} - - private: - void display_certificate_non_const(std::ostream& out); - }; -} - - -#endif diff --git a/src/muz/fp/CMakeLists.txt b/src/muz/fp/CMakeLists.txt index 0c5f5e915..41262813a 100644 --- a/src/muz/fp/CMakeLists.txt +++ b/src/muz/fp/CMakeLists.txt @@ -8,7 +8,6 @@ z3_add_component(fp bmc clp ddnf - duality_intf muz pdr rel diff --git a/src/muz/fp/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp index dddca492b..a23d654b0 100644 --- a/src/muz/fp/datalog_parser.cpp +++ b/src/muz/fp/datalog_parser.cpp @@ -108,7 +108,9 @@ public: #endif } ~line_reader() { - fclose(m_file); + if (m_file != nullptr){ + fclose(m_file); + } } bool operator()() { return m_ok; } diff --git a/src/muz/fp/dl_register_engine.cpp b/src/muz/fp/dl_register_engine.cpp index 267bfc390..a2270d774 100644 --- a/src/muz/fp/dl_register_engine.cpp +++ b/src/muz/fp/dl_register_engine.cpp @@ -23,7 +23,6 @@ Revision History: #include "muz/rel/rel_context.h" #include "muz/pdr/pdr_dl_interface.h" #include "muz/ddnf/ddnf.h" -#include "muz/duality/duality_dl_interface.h" #include "muz/spacer/spacer_dl_interface.h" namespace datalog { @@ -45,8 +44,6 @@ namespace datalog { return alloc(tab, *m_ctx); case CLP_ENGINE: return alloc(clp, *m_ctx); - case DUALITY_ENGINE: - return alloc(Duality::dl_interface, *m_ctx); case DDNF_ENGINE: return alloc(ddnf, *m_ctx); case LAST_ENGINE: diff --git a/src/muz/fp/horn_tactic.cpp b/src/muz/fp/horn_tactic.cpp index 88fb4e03d..4843a2623 100644 --- a/src/muz/fp/horn_tactic.cpp +++ b/src/muz/fp/horn_tactic.cpp @@ -25,7 +25,7 @@ Revision History: #include "ast/rewriter/expr_replacer.h" #include "muz/base/dl_rule_transformer.h" #include "muz/transforms/dl_mk_slice.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "muz/transforms/dl_transforms.h" #include "muz/base/fixedpoint_params.hpp" #include "ast/ast_util.h" @@ -177,12 +177,8 @@ class horn_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("horn", *g); bool produce_proofs = g->proofs_enabled(); @@ -229,18 +225,22 @@ class horn_tactic : public tactic { } queries.reset(); queries.push_back(q); - filter_model_converter* mc1 = alloc(filter_model_converter, m); - mc1->insert(to_app(q)->get_decl()); - mc = mc1; + generic_model_converter* mc1 = alloc(generic_model_converter, m, "horn"); + mc1->hide(q); + g->add(mc1); } SASSERT(queries.size() == 1); q = queries[0].get(); + proof_converter_ref pc = g->pc(); + model_converter_ref mc; if (m_is_simplify) { simplify(q, g, result, mc, pc); } else { verify(q, g, result, mc, pc); } + g->set(pc.get()); + g->set(mc.get()); } void verify(expr* q, @@ -383,12 +383,9 @@ public: m_imp->collect_param_descrs(r); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } void collect_statistics(statistics & st) const override { diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp index 29d01d1f3..77b79ba04 100644 --- a/src/muz/pdr/pdr_context.cpp +++ b/src/muz/pdr/pdr_context.cpp @@ -199,11 +199,8 @@ namespace pdr { void pred_transformer::simplify_formulas(tactic& tac, expr_ref_vector& v) { goal_ref g(alloc(goal, m, false, false, false)); for (expr* e : v) g->assert_expr(e); - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); goal_ref_buffer result; - tac(g, result, mc, pc, core); + tac(g, result); SASSERT(result.size() == 1); goal* r = result[0]; v.reset(); @@ -387,7 +384,7 @@ namespace pdr { md->register_decl(m_head, fi); } model_converter_ref mc = ctx.get_model_converter(); - apply(mc, md, 0); + apply(mc, md); if (p_orig->get_arity() == 0) { result = md->get_const_interp(p_orig); } diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp index cb2d3529e..6695788c2 100644 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ b/src/muz/pdr/pdr_farkas_learner.cpp @@ -520,12 +520,9 @@ namespace pdr { g->assert_expr(lemmas[i].get()); } expr_ref tmp(m); - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); goal_ref_buffer result; tactic_ref simplifier = mk_arith_bounds_tactic(m); - (*simplifier)(g, result, mc, pc, core); + (*simplifier)(g, result); lemmas.reset(); SASSERT(result.size() == 1); goal* r = result[0]; diff --git a/src/muz/pdr/pdr_manager.cpp b/src/muz/pdr/pdr_manager.cpp index 077d27427..da15bf094 100644 --- a/src/muz/pdr/pdr_manager.cpp +++ b/src/muz/pdr/pdr_manager.cpp @@ -107,7 +107,7 @@ namespace pdr { } } TRACE("pdr", model_smt2_pp(tout, m, *md, 0);); - apply(const_cast(m_mc), md, 0); + apply(const_cast(m_mc), md); } expr_ref inductive_property::to_expr() const { diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index ae67d3793..7995f030c 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -528,7 +528,7 @@ expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) md->register_decl(m_head, fi); } model_converter_ref mc = ctx.get_model_converter(); - apply(mc, md, 0); + apply(mc, md); if (p_orig->get_arity() == 0) { result = md->get_const_interp(p_orig); } else { @@ -1367,9 +1367,6 @@ void pred_transformer::frames::simplify_formulas () // normalize level unsigned level = i < m_size ? i : infty_level (); - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); goal_ref_buffer result; // simplify lemmas of the current level @@ -1395,7 +1392,7 @@ void pred_transformer::frames::simplify_formulas () } // more than one lemma at current level. simplify. - (*simplifier)(g, result, mc, pc, core); + (*simplifier)(g, result); SASSERT(result.size () == 1); goal *r = result[0]; @@ -2063,8 +2060,8 @@ bool context::validate() expr_ref_vector refs(m); expr_ref tmp(m); model_ref model; - vector rs; model_converter_ref mc; + vector rs; get_level_property(m_inductive_lvl, refs, rs); inductive_property ex(m, mc, rs); ex.to_model(model); diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_itp_solver.h index 8dbac022e..466e0a2f1 100644 --- a/src/muz/spacer/spacer_itp_solver.h +++ b/src/muz/spacer/spacer_itp_solver.h @@ -104,19 +104,13 @@ public: /* solver interface */ - solver* translate(ast_manager &m, params_ref const &p) override - {return m_solver.translate(m, p);} - void updt_params(params_ref const &p) override - {m_solver.updt_params(p);} - void collect_param_descrs(param_descrs &r) override - {m_solver.collect_param_descrs(r);} - void set_produce_models(bool f) override - {m_solver.set_produce_models(f);} - void assert_expr(expr *t) override - {m_solver.assert_expr(t);} - - void assert_expr(expr *t, expr *a) override - {NOT_IMPLEMENTED_YET();} + solver* translate(ast_manager &m, params_ref const &p) override { return m_solver.translate(m, p);} + void updt_params(params_ref const &p) override { m_solver.updt_params(p);} + void collect_param_descrs(param_descrs &r) override { m_solver.collect_param_descrs(r);} + void set_produce_models(bool f) override { m_solver.set_produce_models(f);} + void assert_expr_core(expr *t) override { m_solver.assert_expr(t);} + void assert_expr_core2(expr *t, expr *a) override { NOT_IMPLEMENTED_YET();} + expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } void push() override; void pop(unsigned n) override; @@ -141,8 +135,9 @@ public: void collect_statistics(statistics &st) const override ; virtual void reset_statistics(); + void get_unsat_core(ptr_vector &r) override; - void get_model(model_ref &m) override {m_solver.get_model(m);} + void get_model_core(model_ref &m) override {m_solver.get_model(m);} proof *get_proof() override {return m_solver.get_proof();} std::string reason_unknown() const override {return m_solver.reason_unknown();} diff --git a/src/muz/spacer/spacer_legacy_frames.cpp b/src/muz/spacer/spacer_legacy_frames.cpp index 679736add..49157a085 100644 --- a/src/muz/spacer/spacer_legacy_frames.cpp +++ b/src/muz/spacer/spacer_legacy_frames.cpp @@ -46,11 +46,8 @@ void pred_transformer::legacy_frames::simplify_formulas(tactic& tac, ast_manager &m = m_pt.get_ast_manager(); goal_ref g(alloc(goal, m, false, false, false)); for (unsigned j = 0; j < v.size(); ++j) { g->assert_expr(v[j].get()); } - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); goal_ref_buffer result; - tac(g, result, mc, pc, core); + tac(g, result); SASSERT(result.size() == 1); goal* r = result[0]; v.reset(); diff --git a/src/muz/spacer/spacer_manager.cpp b/src/muz/spacer/spacer_manager.cpp index 4ad3e0d7f..ba4ca0da7 100644 --- a/src/muz/spacer/spacer_manager.cpp +++ b/src/muz/spacer/spacer_manager.cpp @@ -113,7 +113,7 @@ void inductive_property::to_model(model_ref& md) const } } TRACE("spacer", model_smt2_pp(tout, m, *md, 0);); - apply(const_cast(m_mc), md, 0); + apply(const_cast(m_mc), md); } expr_ref inductive_property::to_expr() const diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 625ac4b7b..8b8da8a69 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -929,12 +929,9 @@ void simplify_bounds_old(expr_ref_vector& cube) { } expr_ref tmp(m); - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); goal_ref_buffer result; tactic_ref simplifier = mk_arith_bounds_tactic(m); - (*simplifier)(g, result, mc, pc, core); + (*simplifier)(g, result); SASSERT(result.size() == 1); goal* r = result[0]; @@ -955,15 +952,12 @@ void simplify_bounds_new (expr_ref_vector &cube) { g->assert_expr(cube.get(i)); } - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref dep(m); goal_ref_buffer goals; tactic_ref prop_values = mk_propagate_values_tactic(m); tactic_ref prop_bounds = mk_propagate_ineqs_tactic(m); tactic_ref t = and_then(prop_values.get(), prop_bounds.get()); - (*t)(g, goals, mc, pc, dep); + (*t)(g, goals); SASSERT(goals.size() == 1); g = goals[0]; diff --git a/src/muz/spacer/spacer_virtual_solver.cpp b/src/muz/spacer/spacer_virtual_solver.cpp index 562fc1ee0..903f82d23 100644 --- a/src/muz/spacer/spacer_virtual_solver.cpp +++ b/src/muz/spacer/spacer_virtual_solver.cpp @@ -50,7 +50,7 @@ virtual_solver::virtual_solver(virtual_solver_factory &factory, // -- change m_context, but will add m_pred to // -- the private field solver_na2as::m_assumptions if (m_virtual) - { solver_na2as::assert_expr(m.mk_true(), m_pred); } + { solver_na2as::assert_expr_core2(m.mk_true(), m_pred); } } virtual_solver::~virtual_solver() @@ -210,7 +210,7 @@ void virtual_solver::get_unsat_core(ptr_vector &r) } } -void virtual_solver::assert_expr(expr *e) +void virtual_solver::assert_expr_core(expr *e) { SASSERT(!m_pushed || get_scope_level() > 0); if (m.is_true(e)) { return; } @@ -266,16 +266,10 @@ solver* virtual_solver::translate(ast_manager& m, params_ref const& p) UNREACHABLE(); return nullptr; } -void virtual_solver::updt_params(params_ref const &p) -{ m_factory.updt_params(p); } -void virtual_solver::collect_param_descrs(param_descrs &r) -{ m_factory.collect_param_descrs(r); } -void virtual_solver::set_produce_models(bool f) -{ m_factory.set_produce_models(f); } -bool virtual_solver::get_produce_models() -{return m_factory.get_produce_models(); } -smt_params &virtual_solver::fparams() -{return m_factory.fparams();} +void virtual_solver::updt_params(params_ref const &p) { m_factory.updt_params(p); } +void virtual_solver::collect_param_descrs(param_descrs &r) { m_factory.collect_param_descrs(r); } +void virtual_solver::set_produce_models(bool f) { m_factory.set_produce_models(f); } +smt_params &virtual_solver::fparams() {return m_factory.fparams();} void virtual_solver::to_smt2_benchmark(std::ostream &out, smt::kernel &context, diff --git a/src/muz/spacer/spacer_virtual_solver.h b/src/muz/spacer/spacer_virtual_solver.h index 38e038d9e..9dc20c241 100644 --- a/src/muz/spacer/spacer_virtual_solver.h +++ b/src/muz/spacer/spacer_virtual_solver.h @@ -77,10 +77,11 @@ public: return solver_na2as::get_assumption(idx); } + void get_unsat_core(ptr_vector &r) override; - void assert_expr(expr *e) override; + void assert_expr_core(expr *e) override; void collect_statistics(statistics &st) const override {} - void get_model(model_ref &m) override {m_context.get_model(m);} + void get_model_core(model_ref &m) override {m_context.get_model(m);} proof* get_proof() override; std::string reason_unknown() const override {return m_context.last_failure_as_string();} @@ -89,11 +90,10 @@ public: ast_manager& get_manager() const override {return m;} void get_labels(svector &r) override; void set_produce_models(bool f) override; - virtual bool get_produce_models(); - virtual smt_params &fparams(); - virtual void reset(); - void set_progress_callback(progress_callback *callback) override - {UNREACHABLE();} + smt_params &fparams(); + void reset(); + expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } + void set_progress_callback(progress_callback *callback) override {UNREACHABLE();} solver *translate(ast_manager &m, params_ref const &p) override; diff --git a/src/muz/tab/tab_context.cpp b/src/muz/tab/tab_context.cpp index da5a96e2c..39b7af634 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -1602,7 +1602,7 @@ namespace datalog { pc.invert(); prs.push_back(m.mk_asserted(root)); - pc(m, 1, prs.c_ptr(), pr); + pr = pc(m, 1, prs.c_ptr()); return pr; } diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index a9461c1e3..8a21bc37c 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -22,7 +22,7 @@ Revision History: #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/rewriter/expr_safe_replace.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" #include "muz/base/fixedpoint_params.hpp" #include "ast/scoped_proof.h" @@ -63,6 +63,10 @@ namespace datalog { return alloc(bit_blast_model_converter, m); } + void get_units(obj_map& units) override {} + + void display(std::ostream& out) override { out << "(bit-blast-model-converter)\n"; } + void operator()(model_ref & model) override { for (unsigned i = 0; i < m_new_funcs.size(); ++i) { func_decl* p = m_new_funcs[i].get(); @@ -297,12 +301,12 @@ namespace datalog { } if (m_context.get_model_converter()) { - filter_model_converter* fmc = alloc(filter_model_converter, m); + generic_model_converter* fmc = alloc(generic_model_converter, m, "dl_mk_bit_blast"); 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]); + fmc->hide(new_funcs[i]); bvmc->insert(old_funcs[i], new_funcs[i]); } m_context.add_model_converter(concat(bvmc, fmc)); diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index 53722a22c..e37444c5e 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -21,6 +21,7 @@ Author: #include "muz/dataflow/dataflow.h" #include "muz/dataflow/reachability.h" #include "ast/ast_pp.h" +#include "tactic/generic_model_converter.h" #include "ast/ast_util.h" #include "tactic/extension_model_converter.h" @@ -101,14 +102,14 @@ namespace datalog { // set to false each unreached predicate if (res && m_context.get_model_converter()) { - extension_model_converter* mc0 = alloc(extension_model_converter, m); + generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl_coi"); for (auto const& kv : engine) { if (!kv.m_value.is_reachable()) { - mc0->insert(kv.m_key, m.mk_false()); + mc0->add(kv.m_key, m.mk_false()); } } for (func_decl* f : unreachable) { - mc0->insert(f, m.mk_false()); + mc0->add(f, m.mk_false()); } m_context.add_model_converter(mc0); } @@ -137,7 +138,7 @@ namespace datalog { res = nullptr; } if (res && m_context.get_model_converter()) { - extension_model_converter* mc0 = alloc(extension_model_converter, m); + generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl_coi"); for (func_decl* f : pruned_preds) { const rule_vector& rules = source.get_predicate_rules(f); expr_ref_vector fmls(m); @@ -152,7 +153,9 @@ namespace datalog { } fmls.push_back(mk_and(conj)); } - mc0->insert(f, mk_or(fmls)); + expr_ref fml(m); + fml = m.mk_or(fmls.size(), fmls.c_ptr()); + mc0->add(f, fml); } m_context.add_model_converter(mc0); } diff --git a/src/muz/transforms/dl_mk_karr_invariants.cpp b/src/muz/transforms/dl_mk_karr_invariants.cpp index a17aecd15..fd14538c5 100644 --- a/src/muz/transforms/dl_mk_karr_invariants.cpp +++ b/src/muz/transforms/dl_mk_karr_invariants.cpp @@ -120,6 +120,8 @@ namespace datalog { } } + void get_units(obj_map& units) override {} + void operator()(model_ref & mr) override { for (unsigned i = 0; i < m_funcs.size(); ++i) { func_decl* p = m_funcs[i].get(); @@ -150,6 +152,10 @@ namespace datalog { return mc; } + void display(std::ostream& out) override { + out << "(add-invariant-model-converter)\n"; + } + private: void mk_body(matrix const& M, expr_ref& body) { expr_ref_vector conj(m); diff --git a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp index 744efdb93..e9a4f2f54 100644 --- a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp +++ b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp @@ -53,6 +53,10 @@ namespace datalog { return alloc(qa_model_converter, m); } + void display(std::ostream& out) override { display_add(out, m); } + + void get_units(obj_map& units) override { units.reset(); } + 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); @@ -81,7 +85,11 @@ namespace datalog { SASSERT(body); } else { - body = m.mk_false(); + expr_ref_vector args(m); + for (unsigned i = 0; i < p->get_arity(); ++i) { + args.push_back(m.mk_var(i, p->get_domain(i))); + } + body = m.mk_app(p, args.size(), args.c_ptr()); } // Create quantifier wrapper around body. diff --git a/src/muz/transforms/dl_mk_scale.cpp b/src/muz/transforms/dl_mk_scale.cpp index 49a8ff423..0144948bd 100644 --- a/src/muz/transforms/dl_mk_scale.cpp +++ b/src/muz/transforms/dl_mk_scale.cpp @@ -38,13 +38,13 @@ namespace datalog { m_new2old.insert(new_f, old_f); } + void get_units(obj_map& units) override { units.reset(); } + void operator()(model_ref& md) override { model_ref old_model = alloc(model, m); - obj_map::iterator it = m_new2old.begin(); - obj_map::iterator end = m_new2old.end(); - for (; it != end; ++it) { - func_decl* old_p = it->m_value; - func_decl* new_p = it->m_key; + for (auto const& kv : m_new2old) { + func_decl* old_p = kv.m_value; + func_decl* new_p = kv.m_key; func_interp* old_fi = alloc(func_interp, m, old_p->get_arity()); if (new_p->get_arity() == 0) { @@ -99,6 +99,9 @@ namespace datalog { UNREACHABLE(); return nullptr; } + + void display(std::ostream& out) override { out << "(scale-model-converter)\n"; } + }; diff --git a/src/muz/transforms/dl_mk_slice.cpp b/src/muz/transforms/dl_mk_slice.cpp index c6d08493c..8f3d5c220 100644 --- a/src/muz/transforms/dl_mk_slice.cpp +++ b/src/muz/transforms/dl_mk_slice.cpp @@ -271,11 +271,12 @@ namespace datalog { m_renaming.insert(orig_rule, unsigned_vector(sz, renaming)); } - void operator()(ast_manager& m, unsigned num_source, proof * const * source, proof_ref & result) override { + proof_ref operator()(ast_manager& m, unsigned num_source, proof * const * source) override { SASSERT(num_source == 1); - result = source[0]; + proof_ref result(source[0], m); init_form2rule(); translate_proof(result); + return result; } proof_converter * translate(ast_translation & translator) override { @@ -283,6 +284,8 @@ namespace datalog { // this would require implementing translation for the dl_context. return nullptr; } + + void display(std::ostream& out) override { out << "(slice-proof-converter)\n"; } }; class mk_slice::slice_model_converter : public model_converter { @@ -305,6 +308,8 @@ namespace datalog { m_sliceable.insert(f, bv); } + void get_units(obj_map& units) override {} + void operator()(model_ref & md) override { if (m_slice2old.empty()) { return; @@ -396,6 +401,8 @@ namespace datalog { return nullptr; } + void display(std::ostream& out) override { out << "(slice-model-converter)\n"; } + }; mk_slice::mk_slice(context & ctx): diff --git a/src/muz/transforms/dl_mk_subsumption_checker.cpp b/src/muz/transforms/dl_mk_subsumption_checker.cpp index 1280b3e4b..56883460a 100644 --- a/src/muz/transforms/dl_mk_subsumption_checker.cpp +++ b/src/muz/transforms/dl_mk_subsumption_checker.cpp @@ -25,7 +25,7 @@ Revision History: #include "ast/rewriter/rewriter_def.h" #include "muz/transforms/dl_mk_subsumption_checker.h" #include "muz/base/fixedpoint_params.hpp" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" namespace datalog { @@ -241,9 +241,9 @@ namespace datalog { } tgt.inherit_predicates(orig); if (!m_total_relations.empty() && m_context.get_model_converter()) { - extension_model_converter* mc0 = alloc(extension_model_converter, m); + generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl-subsumption"); for (func_decl* p : m_total_relations) { - mc0->insert(p, m.mk_true()); + mc0->add(p, m.mk_true()); } m_context.add_model_converter(mc0); } diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index ac16d9d68..2278e53dd 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -1463,6 +1463,7 @@ namespace nlsat { void project(var x, unsigned num, literal const * ls, scoped_literal_vector & result) { + m_result = &result; svector lits; TRACE("nlsat", tout << "project x" << x << "\n"; m_solver.display(tout);); @@ -1643,6 +1644,7 @@ namespace nlsat { ++num_lub; } } + TRACE("nlsat_explain", tout << ps << "\n";); if (num_lub == 0) { project_plus_infinity(x, ps); @@ -1668,7 +1670,7 @@ namespace nlsat { unsigned d = degree(p, x); lc = m_pm.coeff(p, x, d); if (!is_const(lc)) { - unsigned s = sign(p); + int s = sign(p); SASSERT(s != 0); atom::kind k = (s > 0)?(atom::GT):(atom::LT); add_simple_assumption(k, lc); @@ -1683,7 +1685,8 @@ namespace nlsat { unsigned d = degree(p, x); lc = m_pm.coeff(p, x, d); if (!is_const(lc)) { - unsigned s = sign(p); + int s = sign(p); + TRACE("nlsat_explain", tout << "degree: " << d << " " << lc << " sign: " << s << "\n";); SASSERT(s != 0); atom::kind k; if (s > 0) { diff --git a/src/nlsat/tactic/nlsat_tactic.cpp b/src/nlsat/tactic/nlsat_tactic.cpp index 92c6e0ffb..1392df3f6 100644 --- a/src/nlsat/tactic/nlsat_tactic.cpp +++ b/src/nlsat/tactic/nlsat_tactic.cpp @@ -129,12 +129,8 @@ class nlsat_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("nlsat", *g); if (g->is_decided()) { @@ -166,9 +162,11 @@ class nlsat_tactic : public tactic { if (!contains_unsupported(b2a, x2t)) { // If mk_model is false it means that the model produced by nlsat // assigns noninteger values to integer variables + model_converter_ref mc; if (mk_model(*g.get(), b2a, x2t, mc)) { // result goal is trivially SAT g->reset(); + g->add(mc.get()); } } } @@ -177,8 +175,8 @@ class nlsat_tactic : public tactic { if (g->unsat_core_enabled()) { vector assumptions; m_solver.get_core(assumptions); - for (unsigned i = 0; i < assumptions.size(); ++i) { - expr_dependency* d = static_cast(assumptions[i]); + for (nlsat::assumption a : assumptions) { + expr_dependency* d = static_cast(a); lcore = m.mk_join(lcore, d); } } @@ -232,15 +230,12 @@ public: algebraic_numbers::manager::collect_param_descrs(r); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { imp local_imp(in->m(), m_params); scoped_set_imp setter(*this, local_imp); - local_imp(in, result, mc, pc, core); + local_imp(in, result); } catch (z3_error & ex) { throw ex; diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index 6d91e70fc..d49044d8d 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -52,17 +52,18 @@ Notes: --*/ -#include "solver/solver.h" -#include "opt/maxsmt.h" -#include "opt/maxres.h" #include "ast/ast_pp.h" +#include "ast/pb_decl_plugin.h" +#include "ast/ast_util.h" +#include "model/model_smt2_pp.h" +#include "solver/solver.h" #include "solver/mus.h" #include "sat/sat_solver/inc_sat_solver.h" -#include "opt/opt_context.h" -#include "ast/pb_decl_plugin.h" -#include "opt/opt_params.hpp" -#include "ast/ast_util.h" #include "smt/smt_solver.h" +#include "opt/opt_context.h" +#include "opt/opt_params.hpp" +#include "opt/maxsmt.h" +#include "opt/maxres.h" using namespace opt; @@ -282,11 +283,13 @@ public: m_last_index = 0; bool first = index > 0; SASSERT(index < asms.size() || asms.empty()); + IF_VERBOSE(10, verbose_stream() << "start hill climb " << index << " asms: " << asms.size() << "\n";); while (index < asms.size() && is_sat == l_true) { while (!first && asms.size() > 20*(index - m_last_index) && index < asms.size()) { index = next_index(asms, index); } first = false; + IF_VERBOSE(3, verbose_stream() << "hill climb " << index << "\n";); // IF_VERBOSE(3, verbose_stream() << "weight: " << get_weight(asms[0].get()) << " " << get_weight(asms[index-1].get()) << " num soft: " << index << "\n";); m_last_index = index; is_sat = check_sat(index, asms.c_ptr()); @@ -323,6 +326,7 @@ public: m_found_feasible_optimum = true; } + virtual lbool operator()() { m_defs.reset(); switch(m_st) { @@ -363,6 +367,7 @@ public: m_lower = m_upper; return l_true; } + split_core(core); cores.push_back(core); if (core.size() >= m_max_core_size) { break; @@ -489,7 +494,7 @@ public: expr_ref fml(m); remove_core(core); SASSERT(!core.empty()); - rational w = split_core(core); + rational w = core_weight(core); TRACE("opt", display_vec(tout << "minimized core: ", core);); IF_VERBOSE(10, display_vec(verbose_stream() << "core: ", core);); max_resolve(core, w); @@ -555,19 +560,24 @@ public: return m_asm2weight.find(e); } - rational split_core(exprs const& core) { + rational core_weight(exprs const& core) { if (core.empty()) return rational(0); // find the minimal weight: rational w = get_weight(core[0]); for (unsigned i = 1; i < core.size(); ++i) { w = std::min(w, get_weight(core[i])); } + return w; + } + + rational split_core(exprs const& core) { + rational w = core_weight(core); // add fresh soft clauses for weights that are above w. - for (unsigned i = 0; i < core.size(); ++i) { - rational w2 = get_weight(core[i]); + for (expr* e : core) { + rational w2 = get_weight(e); if (w2 > w) { rational w3 = w2 - w; - new_assumption(core[i], w3); + new_assumption(e, w3); } } return w; @@ -726,11 +736,13 @@ public: m_model = mdl; m_c.model_updated(mdl); + TRACE("opt", model_smt2_pp(tout << "updated model\n", m, *m_model, 0);); + for (unsigned i = 0; i < m_soft.size(); ++i) { m_assignment[i] = is_true(m_soft[i]); } - DEBUG_CODE(verify_assignment();); + // DEBUG_CODE(verify_assignment();); m_upper = upper; trace(); @@ -769,6 +781,9 @@ public: bool is_true(expr_ref_vector const& es) { unsigned i = 0; for (; i < es.size() && is_true(es[i]); ++i) { } + CTRACE("opt", i < es.size(), tout << mk_pp(es[i], m) << "\n"; + model_smt2_pp(tout, m, *m_model, 0); + ); return i == es.size(); } @@ -865,6 +880,7 @@ public: if (is_sat == l_false) { IF_VERBOSE(0, verbose_stream() << "assignment is infeasible\n";); } + IF_VERBOSE(1, verbose_stream() << "verified\n";); } }; diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index 91e843ca0..afecf7d2b 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -105,7 +105,7 @@ namespace opt { app* maxsmt_solver_base::mk_fresh_bool(char const* name) { app* result = m.mk_fresh_const(name, m.mk_bool_sort()); - m_c.fm().insert(result->get_decl()); + m_c.fm().hide(result); return result; } @@ -266,7 +266,7 @@ namespace opt { } } - IF_VERBOSE(1, verbose_stream() << "is-sat: " << is_sat << "\n"; + IF_VERBOSE(5, verbose_stream() << "is-sat: " << is_sat << "\n"; if (is_sat == l_true) { verbose_stream() << "Satisfying soft constraints\n"; display_answer(verbose_stream()); @@ -353,12 +353,26 @@ namespace opt { m_upper += w; } + struct cmp_first { + bool operator()(std::pair const& x, std::pair const& y) const { + return x.first < y.first; + } + }; + void maxsmt::display_answer(std::ostream& out) const { - for (unsigned i = 0; i < m_soft_constraints.size(); ++i) { - expr* e = m_soft_constraints[i]; + vector> sorted_weights; + unsigned n = m_weights.size(); + for (unsigned i = 0; i < n; ++i) { + sorted_weights.push_back(std::make_pair(i, m_weights[i])); + } + std::sort(sorted_weights.begin(), sorted_weights.end(), cmp_first()); + sorted_weights.reverse(); + for (unsigned i = 0; i < n; ++i) { + unsigned idx = sorted_weights[i].first; + expr* e = m_soft_constraints[idx]; bool is_not = m.is_not(e, e); - out << m_weights[i] << ": " << mk_pp(e, m) - << ((is_not != get_assignment(i))?" |-> true ":" |-> false ") + out << m_weights[idx] << ": " << mk_pp(e, m) + << ((is_not != get_assignment(idx))?" |-> true ":" |-> false ") << "\n"; } diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h index ebe226db0..9e52481f2 100644 --- a/src/opt/maxsmt.h +++ b/src/opt/maxsmt.h @@ -22,7 +22,6 @@ Notes: #include "ast/ast.h" #include "util/params.h" #include "solver/solver.h" -#include "tactic/filter_model_converter.h" #include "util/statistics.h" #include "smt/smt_context.h" #include "smt/smt_theory.h" diff --git a/src/opt/opt_cmds.cpp b/src/opt/opt_cmds.cpp index 4ff8fab71..d8e301e08 100644 --- a/src/opt/opt_cmds.cpp +++ b/src/opt/opt_cmds.cpp @@ -166,7 +166,9 @@ public: } virtual void execute(cmd_context & ctx) { - get_opt(ctx, m_opt).display_assignment(ctx.regular_stream()); + if (!ctx.ignore_check()) { + get_opt(ctx, m_opt).display_assignment(ctx.regular_stream()); + } } }; diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 69177e290..2850dd59c 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -17,32 +17,33 @@ Notes: --*/ -#include "opt/opt_context.h" -#include "ast/ast_pp.h" -#include "opt/opt_solver.h" -#include "opt/opt_params.hpp" +#include "util/gparams.h" #include "ast/for_each_expr.h" +#include "ast/ast_pp.h" +#include "ast/bv_decl_plugin.h" +#include "ast/pb_decl_plugin.h" +#include "ast/ast_smt_pp.h" +#include "ast/ast_pp_util.h" +#include "model/model_smt2_pp.h" #include "tactic/goal.h" #include "tactic/tactic.h" #include "tactic/arith/lia2card_tactic.h" -#include "tactic/arith/elim01_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/tactical.h" -#include "model/model_smt2_pp.h" #include "tactic/arith/card2bv_tactic.h" #include "tactic/arith/eq2bv_tactic.h" #include "tactic/bv/dt2bv_tactic.h" +#include "tactic/generic_model_converter.h" #include "sat/sat_solver/inc_sat_solver.h" -#include "ast/bv_decl_plugin.h" -#include "ast/pb_decl_plugin.h" -#include "ast/ast_smt_pp.h" -#include "tactic/filter_model_converter.h" -#include "ast/ast_pp_util.h" #include "qe/qsat.h" +#include "opt/opt_context.h" +#include "opt/opt_solver.h" +#include "opt/opt_params.hpp" + namespace opt { @@ -126,7 +127,8 @@ namespace opt { m_box_index(UINT_MAX), m_optsmt(m), m_scoped_state(m), - m_fm(m), + m_fm(alloc(generic_model_converter, m, "opt")), + m_model_fixed(), m_objective_refs(m), m_enable_sat(false), m_is_clausal(false), @@ -146,9 +148,8 @@ namespace opt { } void context::reset_maxsmts() { - map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); - for (; it != end; ++it) { - dealloc(it->m_value); + for (auto& kv : m_maxsmts) { + dealloc(kv.m_value); } m_maxsmts.reset(); } @@ -275,32 +276,31 @@ namespace opt { #endif solver& s = get_solver(); s.assert_expr(m_hard_constraints); - display_benchmark(); - IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n";); - lbool is_sat = s.check_sat(0,nullptr); - TRACE("opt", tout << "initial search result: " << is_sat << "\n"; - s.display(tout);); + + opt_params optp(m_params); + symbol pri = optp.priority(); + + IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n"); + lbool is_sat = s.check_sat(0,0); + TRACE("opt", s.display(tout << "initial search result: " << is_sat << "\n");); if (is_sat != l_false) { s.get_model(m_model); s.get_labels(m_labels); model_updated(m_model.get()); } if (is_sat != l_true) { - TRACE("opt", tout << m_hard_constraints << "\n";); + TRACE("opt", tout << m_hard_constraints << "\n";); return is_sat; } - IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n";); + IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n"); TRACE("opt", model_smt2_pp(tout, m, *m_model, 0);); m_optsmt.setup(*m_opt_solver.get()); update_lower(); - - opt_params optp(m_params); - symbol pri = optp.priority(); - if (0 == m_objectives.size()) { - // no op - } - else if (1 == m_objectives.size()) { + switch (m_objectives.size()) { + case 0: + break; + case 1: if (m_pareto1) { is_sat = l_false; m_pareto1 = false; @@ -309,16 +309,22 @@ namespace opt { m_pareto1 = (pri == symbol("pareto")); is_sat = execute(m_objectives[0], true, false); } + break; + default: { + opt_params optp(m_params); + symbol pri = optp.priority(); + if (pri == symbol("pareto")) { + is_sat = execute_pareto(); + } + else if (pri == symbol("box")) { + is_sat = execute_box(); + } + else { + is_sat = execute_lex(); + } } - else if (pri == symbol("pareto")) { - is_sat = execute_pareto(); - } - else if (pri == symbol("box")) { - is_sat = execute_box(); - } - else { - is_sat = execute_lex(); } + if (is_sat == l_true) validate_model(); return adjust_unknown(is_sat); } @@ -334,17 +340,17 @@ namespace opt { } void context::fix_model(model_ref& mdl) { - if (mdl) { - if (m_model_converter) { - (*m_model_converter)(mdl, 0); - } - m_fm(mdl, 0); + if (mdl && !m_model_fixed.contains(mdl.get())) { + (*m_fm)(mdl); + apply(m_model_converter, mdl); + m_model_fixed.push_back(mdl.get()); } } - void context::get_model(model_ref& mdl) { + void context::get_model_core(model_ref& mdl) { mdl = m_model; fix_model(mdl); + TRACE("opt", model_smt2_pp(tout, m, *mdl.get(), 0);); } void context::get_box_model(model_ref& mdl, unsigned index) { @@ -421,7 +427,10 @@ namespace opt { objective const& o = m_objectives[i]; bool is_last = i + 1 == sz; r = execute(o, i + 1 < sz, sc && !is_last && o.m_type != O_MAXSMT); - if (r == l_true && !get_lower_as_num(i).is_finite()) { + if (r == l_true && o.m_type == O_MINIMIZE && !get_lower_as_num(i).is_finite()) { + return r; + } + if (r == l_true && o.m_type == O_MAXIMIZE && !get_upper_as_num(i).is_finite()) { return r; } if (r == l_true && i + 1 < sz) { @@ -493,7 +502,7 @@ namespace opt { case O_MINIMIZE: is_ge = !is_ge; case O_MAXIMIZE: - if (mdl->eval(obj.m_term, val) && is_numeral(val, k)) { + if (mdl->eval(obj.m_term, val, true) && is_numeral(val, k)) { if (is_ge) { result = mk_ge(obj.m_term, val); } @@ -513,9 +522,12 @@ namespace opt { for (unsigned i = 0; i < sz; ++i) { terms.push_back(obj.m_terms[i]); coeffs.push_back(obj.m_weights[i]); - if (mdl->eval(obj.m_terms[i], val) && m.is_true(val)) { + if (mdl->eval(obj.m_terms[i], val, true) && m.is_true(val)) { k += obj.m_weights[i]; } + else { + TRACE("opt", tout << val << "\n";); + } } if (is_ge) { result = pb.mk_ge(sz, coeffs.c_ptr(), terms.c_ptr(), k); @@ -546,9 +558,11 @@ namespace opt { } void context::yield() { + SASSERT (m_pareto); m_pareto->get_model(m_model, m_labels); update_bound(true); update_bound(false); + TRACE("opt", model_smt2_pp(tout, m, *m_model.get(), 0);); } lbool context::execute_pareto() { @@ -565,6 +579,7 @@ namespace opt { return is_sat; } + std::string context::reason_unknown() const { if (m.canceled()) { return Z3_CANCELED_MSG; @@ -594,7 +609,7 @@ namespace opt { void context::init_solver() { setup_arith_solver(); - m_opt_solver = alloc(opt_solver, m, m_params, m_fm); + m_opt_solver = alloc(opt_solver, m, m_params, *m_fm); m_opt_solver->set_logic(m_logic); m_solver = m_opt_solver.get(); m_opt_solver->ensure_pb(); @@ -746,8 +761,8 @@ namespace opt { } goal_ref g(alloc(goal, m, true, false)); - for (unsigned i = 0; i < fmls.size(); ++i) { - g->assert_expr(fmls[i].get()); + for (expr* fml : fmls) { + g->assert_expr(fml); } tactic_ref tac0 = and_then(mk_simplify_tactic(m, m_params), @@ -759,23 +774,21 @@ namespace opt { tactic_ref tac1, tac2, tac3, tac4; if (optp.elim_01()) { tac1 = mk_dt2bv_tactic(m); - tac2 = mk_elim01_tactic(m); - tac3 = mk_lia2card_tactic(m); - tac4 = mk_eq2bv_tactic(m); + tac2 = mk_lia2card_tactic(m); + tac3 = mk_eq2bv_tactic(m); params_ref lia_p; lia_p.set_bool("compile_equality", optp.pb_compile_equality()); - tac3->updt_params(lia_p); - set_simplify(and_then(tac0.get(), tac1.get(), tac2.get(), tac3.get(), tac4.get(), mk_simplify_tactic(m))); + tac2->updt_params(lia_p); + set_simplify(and_then(tac0.get(), tac1.get(), tac2.get(), tac3.get(), mk_simplify_tactic(m))); } else { set_simplify(tac0.get()); } - proof_converter_ref pc; - expr_dependency_ref core(m); goal_ref_buffer result; - (*m_simplify)(g, result, m_model_converter, pc, core); + (*m_simplify)(g, result); SASSERT(result.size() == 1); goal* r = result[0]; + m_model_converter = r->mc(); fmls.reset(); expr_ref tmp(m); for (unsigned i = 0; i < r->size(); ++i) { @@ -819,13 +832,13 @@ namespace opt { offset -= weight; } if (m.is_true(arg)) { - IF_VERBOSE(1, verbose_stream() << weight << ": " << mk_pp(m_objectives[index].m_terms[i].get(), m) << " |-> true\n";); + IF_VERBOSE(5, verbose_stream() << weight << ": " << mk_pp(m_objectives[index].m_terms[i].get(), m) << " |-> true\n";); } else if (weight.is_zero()) { // skip } else if (m.is_false(arg)) { - IF_VERBOSE(1, verbose_stream() << weight << ": " << mk_pp(m_objectives[index].m_terms[i].get(), m) << " |-> false\n";); + IF_VERBOSE(5, verbose_stream() << weight << ": " << mk_pp(m_objectives[index].m_terms[i].get(), m) << " |-> false\n";); offset += weight; } else { @@ -951,8 +964,7 @@ namespace opt { TRACE("opt", tout << fmls << "\n";); m_hard_constraints.reset(); expr_ref orig_term(m); - for (unsigned i = 0; i < fmls.size(); ++i) { - expr* fml = fmls[i]; + for (expr * fml : fmls) { app_ref tr(m); expr_ref_vector terms(m); vector weights; @@ -1013,6 +1025,7 @@ namespace opt { } } + void context::model_updated(model* md) { opt_params optp(m_params); symbol prefix = optp.solution_prefix(); @@ -1040,7 +1053,7 @@ namespace opt { model_ref mdl = md->copy(); fix_model(mdl); - if (!mdl->eval(term, val)) { + if (!mdl->eval(term, val, true)) { TRACE("opt", tout << "Term does not evaluate " << term << "\n";); return false; } @@ -1060,12 +1073,10 @@ namespace opt { } void context::purify(app_ref& term) { - filter_model_converter_ref fm; + generic_model_converter_ref fm; if (m_arith.is_add(term)) { expr_ref_vector args(m); - unsigned sz = term->get_num_args(); - for (unsigned i = 0; i < sz; ++i) { - expr* arg = term->get_arg(i); + for (expr* arg : *term) { if (is_mul_const(arg)) { args.push_back(arg); } @@ -1097,13 +1108,13 @@ namespace opt { (m_arith.is_mul(e, e2, e1) && m_arith.is_numeral(e1) && is_uninterp_const(e2)); } - app* context::purify(filter_model_converter_ref& fm, expr* term) { + app* context::purify(generic_model_converter_ref& fm, expr* term) { std::ostringstream out; out << mk_pp(term, m); app* q = m.mk_fresh_const(out.str().c_str(), m.get_sort(term)); - if (!fm) fm = alloc(filter_model_converter, m); + if (!fm) fm = alloc(generic_model_converter, m, "opt"); m_hard_constraints.push_back(m.mk_eq(q, term)); - fm->insert(q->get_decl()); + fm->hide(q); return q; } @@ -1114,7 +1125,7 @@ namespace opt { - filter "obj" from generated model. */ void context::mk_atomic(expr_ref_vector& terms) { - filter_model_converter_ref fm; + generic_model_converter_ref fm; for (unsigned i = 0; i < terms.size(); ++i) { expr_ref p(terms[i].get(), m); app_ref q(m); @@ -1151,8 +1162,7 @@ namespace opt { } void context::internalize() { - for (unsigned i = 0; i < m_objectives.size(); ++i) { - objective & obj = m_objectives[i]; + for (objective & obj : m_objectives) { switch(obj.m_type) { case O_MINIMIZE: { app_ref tmp(m); @@ -1185,7 +1195,7 @@ namespace opt { rational r; switch(obj.m_type) { case O_MINIMIZE: { - bool evaluated = m_model->eval(obj.m_term, val); + bool evaluated = m_model->eval(obj.m_term, val, true); TRACE("opt", tout << obj.m_term << " " << val << " " << evaluated << " " << is_numeral(val, r) << "\n";); if (evaluated && is_numeral(val, r)) { inf_eps val = inf_eps(obj.m_adjust_value(r)); @@ -1200,7 +1210,7 @@ namespace opt { break; } case O_MAXIMIZE: { - bool evaluated = m_model->eval(obj.m_term, val); + bool evaluated = m_model->eval(obj.m_term, val, true); TRACE("opt", tout << obj.m_term << " " << val << "\n";); if (evaluated && is_numeral(val, r)) { inf_eps val = inf_eps(obj.m_adjust_value(r)); @@ -1217,7 +1227,7 @@ namespace opt { case O_MAXSMT: { bool ok = true; for (unsigned j = 0; ok && j < obj.m_terms.size(); ++j) { - bool evaluated = m_model->eval(obj.m_terms[j], val); + bool evaluated = m_model->eval(obj.m_terms[j], val, true); TRACE("opt", tout << mk_pp(obj.m_terms[j], m) << " " << val << "\n";); if (evaluated) { if (!m.is_true(val)) { @@ -1232,11 +1242,11 @@ namespace opt { maxsmt& ms = *m_maxsmts.find(obj.m_id); if (is_lower) { ms.update_upper(r); - TRACE("opt", tout << r << " " << ms.get_upper() << "\n";); + TRACE("opt", tout << "update upper from " << r << " to " << ms.get_upper() << "\n";); } else { ms.update_lower(r); - TRACE("opt", tout << r << " " << ms.get_lower() << "\n";); + TRACE("opt", tout << "update lower from " << r << " to " << ms.get_lower() << "\n";); } } break; @@ -1246,6 +1256,9 @@ namespace opt { } void context::display_benchmark() { + display(verbose_stream()); + return; + if (opt_params(m_params).dump_benchmarks() && sat_enabled() && m_objectives.size() == 1 && @@ -1255,6 +1268,8 @@ namespace opt { unsigned sz = o.m_terms.size(); inc_sat_display(verbose_stream(), get_solver(), sz, o.m_terms.c_ptr(), o.m_weights.c_ptr()); } + + } void context::display(std::ostream& out) { @@ -1388,9 +1403,10 @@ namespace opt { } void context::clear_state() { - set_pareto(nullptr); + m_pareto = nullptr; m_box_index = UINT_MAX; m_model.reset(); + m_model_fixed.reset(); } void context::set_pareto(pareto_base* p) { @@ -1405,9 +1421,8 @@ namespace opt { if (m_simplify) { m_simplify->collect_statistics(stats); } - map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); - for (; it != end; ++it) { - it->m_value->collect_statistics(stats); + for (auto const& kv : m_maxsmts) { + kv.m_value->collect_statistics(stats); } get_memory_statistics(stats); get_rlimit_statistics(m.limit(), stats); @@ -1425,10 +1440,12 @@ namespace opt { if (m_solver) { m_solver->updt_params(m_params); } + if (m_sat_solver) { + m_sat_solver->updt_params(m_params); + } m_optsmt.updt_params(m_params); - map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); - for (; it != end; ++it) { - it->m_value->updt_params(m_params); + for (auto & kv : m_maxsmts) { + kv.m_value->updt_params(m_params); } opt_params _p(p); m_enable_sat = _p.enable_sat(); @@ -1438,21 +1455,21 @@ namespace opt { } std::string context::to_string() const { - return to_string(m_scoped_state.m_hard, m_scoped_state.m_objectives); + return to_string(false, m_scoped_state.m_hard, m_scoped_state.m_objectives); } std::string context::to_string_internal() const { - return to_string(m_hard_constraints, m_objectives); + return to_string(true, m_hard_constraints, m_objectives); } - std::string context::to_string(expr_ref_vector const& hard, vector const& objectives) const { + std::string context::to_string(bool is_internal, expr_ref_vector const& hard, vector const& objectives) const { smt2_pp_environment_dbg env(m); ast_pp_util visitor(m); std::ostringstream out; visitor.collect(hard); + model_converter_ref mc = concat(m_model_converter.get(), m_fm.get()); - for (unsigned i = 0; i < objectives.size(); ++i) { - objective const& obj = objectives[i]; + for (objective const& obj : objectives) { switch(obj.m_type) { case O_MAXIMIZE: case O_MINIMIZE: @@ -1467,10 +1484,16 @@ namespace opt { } } + if (is_internal && mc) { + mc->collect(visitor); + } + + param_descrs descrs; + collect_param_descrs(descrs); + m_params.display_smt2(out, "opt", descrs); visitor.display_decls(out); visitor.display_asserts(out, hard, m_pp_neat); - for (unsigned i = 0; i < objectives.size(); ++i) { - objective const& obj = objectives[i]; + for (objective const& obj : objectives) { switch(obj.m_type) { case O_MAXIMIZE: out << "(maximize "; @@ -1505,15 +1528,34 @@ namespace opt { break; } } - - param_descrs descrs; - collect_param_descrs(descrs); - m_params.display_smt2(out, "opt", descrs); - + if (is_internal && mc) { + mc->display(out); + } + out << "(check-sat)\n"; return out.str(); } + void context::validate_model() { + return; + if (!gparams::get_ref().get_bool("model_validate", false)) return; + expr_ref_vector fmls(m); + get_hard_constraints(fmls); + expr_ref tmp(m); + model_ref mdl; + get_model(mdl); + for (expr * f : fmls) { + if (!mdl->eval(f, tmp) || !m.is_true(tmp)) { + //IF_VERBOSE(0, m_fm->display(verbose_stream() << "fm\n")); + IF_VERBOSE(0, m_model_converter->display(verbose_stream() << "mc\n")); + IF_VERBOSE(0, verbose_stream() << "Failed to validate " << mk_pp(f, m) << "\n" << tmp << "\n"); + IF_VERBOSE(0, model_smt2_pp(verbose_stream(), m, *mdl, 0)); + IF_VERBOSE(11, verbose_stream() << to_string_internal() << "\n"); + exit(0); + } + } + } + void context::validate_maxsat(symbol const& id) { maxsmt& ms = *m_maxsmts.find(id); TRACE("opt", tout << "Validate: " << id << "\n";); @@ -1592,8 +1634,8 @@ namespace opt { if (!m_arith.is_real(m_objectives[0].m_term)) { return false; } - for (unsigned i = 0; i < m_hard_constraints.size(); ++i) { - if (has_quantifiers(m_hard_constraints[i].get())) { + for (expr* fml : m_hard_constraints) { + if (has_quantifiers(fml)) { return true; } } diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index e97fabbe4..29b327855 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -19,16 +19,17 @@ Notes: #define OPT_CONTEXT_H_ #include "ast/ast.h" +#include "ast/arith_decl_plugin.h" +#include "ast/bv_decl_plugin.h" +#include "tactic/model_converter.h" +#include "tactic/tactic.h" +#include "qe/qsat.h" #include "opt/opt_solver.h" #include "opt/opt_pareto.h" #include "opt/optsmt.h" #include "opt/maxsmt.h" -#include "tactic/model_converter.h" -#include "tactic/tactic.h" -#include "ast/arith_decl_plugin.h" -#include "ast/bv_decl_plugin.h" #include "cmd_context/cmd_context.h" -#include "qe/qsat.h" + namespace opt { @@ -45,7 +46,7 @@ namespace opt { class maxsat_context { public: - virtual filter_model_converter& fm() = 0; // converter that removes fresh names introduced by simplification. + virtual generic_model_converter& fm() = 0; // converter that removes fresh names introduced by simplification. virtual bool sat_enabled() const = 0; // is using th SAT solver core enabled? virtual solver& get_solver() = 0; // retrieve solver object (SAT or SMT solver) virtual ast_manager& get_manager() const = 0; @@ -145,7 +146,7 @@ namespace opt { ref m_opt_solver; ref m_solver; ref m_sat_solver; - scoped_ptr m_pareto; + scoped_ptr m_pareto; bool m_pareto1; scoped_ptr m_qmax; sref_vector m_box_models; @@ -155,9 +156,10 @@ namespace opt { map_t m_maxsmts; scoped_state m_scoped_state; vector m_objectives; - model_ref m_model; + model_ref m_model; model_converter_ref m_model_converter; - filter_model_converter m_fm; + generic_model_converter_ref m_fm; + sref_vector m_model_fixed; unsigned m_model_counter; obj_map m_objective_fns; obj_map m_objective_orig; @@ -187,7 +189,7 @@ namespace opt { void set_hard_constraints(ptr_vector & hard) override; lbool optimize() override; void set_model(model_ref& _m) override { m_model = _m; } - void get_model(model_ref& _m) override; + void get_model_core(model_ref& _m) override; void get_box_model(model_ref& _m, unsigned index) override; void fix_model(model_ref& _m) override; void collect_statistics(statistics& stats) const override; @@ -200,6 +202,7 @@ namespace opt { void display_assignment(std::ostream& out) override; bool is_pareto() override { return m_pareto.get() != nullptr; } void set_logic(symbol const& s) override { m_logic = s; } + void set_clausal(bool f) { m_is_clausal = f; } void display(std::ostream& out); @@ -221,8 +224,8 @@ namespace opt { expr_ref mk_ge(unsigned i, model_ref& model) override; expr_ref mk_le(unsigned i, model_ref& model) override; + generic_model_converter& fm() override { return *m_fm; } smt::context& smt_context() override { return m_opt_solver->get_context(); } - filter_model_converter& fm() override { return m_fm; } bool sat_enabled() const override { return nullptr != m_sat_solver.get(); } solver& get_solver() override; ast_manager& get_manager() const override { return this->m; } @@ -259,7 +262,7 @@ namespace opt { vector& weights, rational& offset, bool& neg, symbol& id, expr_ref& orig_term, unsigned& index); void purify(app_ref& term); - app* purify(filter_model_converter_ref& fm, expr* e); + app* purify(generic_model_converter_ref& fm, expr* e); bool is_mul_const(expr* e); expr* mk_maximize(unsigned index, app* t); expr* mk_minimize(unsigned index, app* t); @@ -296,12 +299,13 @@ namespace opt { void display_objective(std::ostream& out, objective const& obj) const; void display_bounds(std::ostream& out, bounds_t const& b) const; - std::string to_string(expr_ref_vector const& hard, vector const& objectives) const; + std::string to_string(bool is_internal, expr_ref_vector const& hard, vector const& objectives) const; std::string to_string_internal() const; void validate_lex(); void validate_maxsat(symbol const& id); + void validate_model(); void display_benchmark(); diff --git a/src/opt/opt_params.pyg b/src/opt/opt_params.pyg index 1d6d7ee6a..f72bafbd8 100644 --- a/src/opt/opt_params.pyg +++ b/src/opt/opt_params.pyg @@ -3,7 +3,7 @@ def_module_params('opt', export=True, params=(('optsmt_engine', SYMBOL, 'basic', "select optimization engine: 'basic', 'farkas', 'symba'"), ('maxsat_engine', SYMBOL, 'maxres', "select engine for maxsat: 'core_maxsat', 'wmax', 'maxres', 'pd-maxres'"), - ('priority', SYMBOL, 'lex', "select how to priortize objectives: 'lex' (lexicographic), 'pareto', or 'box'"), + ('priority', SYMBOL, 'lex', "select how to priortize objectives: 'lex' (lexicographic), 'pareto', 'box'"), ('dump_benchmarks', BOOL, False, 'dump benchmarks for profiling'), ('solution_prefix', SYMBOL, '', "path prefix to dump intermediary, but non-optimal, solutions"), ('timeout', UINT, UINT_MAX, 'timeout (in milliseconds) (UINT_MAX and 0 mean no timeout)'), diff --git a/src/opt/opt_pareto.cpp b/src/opt/opt_pareto.cpp index c316e697e..56eed72ac 100644 --- a/src/opt/opt_pareto.cpp +++ b/src/opt/opt_pareto.cpp @@ -20,6 +20,7 @@ Notes: #include "opt/opt_pareto.h" #include "ast/ast_pp.h" +#include "ast/ast_util.h" #include "model/model_smt2_pp.h" namespace opt { @@ -66,8 +67,8 @@ namespace opt { fmls.push_back(cb.mk_ge(i, m_model)); gt.push_back(cb.mk_gt(i, m_model)); } - fmls.push_back(m.mk_or(gt.size(), gt.c_ptr())); - fml = m.mk_and(fmls.size(), fmls.c_ptr()); + fmls.push_back(mk_or(gt)); + fml = mk_and(fmls); IF_VERBOSE(10, verbose_stream() << "dominates: " << fml << "\n";); TRACE("opt", tout << fml << "\n"; model_smt2_pp(tout, m, *m_model, 0);); m_solver->assert_expr(fml); @@ -80,7 +81,7 @@ namespace opt { for (unsigned i = 0; i < sz; ++i) { le.push_back(cb.mk_le(i, m_model)); } - fml = m.mk_not(m.mk_and(le.size(), le.c_ptr())); + fml = m.mk_not(mk_and(le)); IF_VERBOSE(10, verbose_stream() << "not dominated by: " << fml << "\n";); TRACE("opt", tout << fml << "\n";); m_solver->assert_expr(fml); diff --git a/src/opt/opt_parse.cpp b/src/opt/opt_parse.cpp index 2cba7561f..fec97d675 100644 --- a/src/opt/opt_parse.cpp +++ b/src/opt/opt_parse.cpp @@ -36,40 +36,35 @@ public: void next() { m_val = m_stream.get(); } bool eof() const { return ch() == EOF; } unsigned line() const { return m_line; } - void skip_whitespace(); - void skip_space(); - void skip_line(); + void skip_whitespace() { + while ((ch() >= 9 && ch() <= 13) || ch() == 32) { + if (ch() == 10) ++m_line; + next(); + } + } + void skip_space() { + while (ch() != 10 && ((ch() >= 9 && ch() <= 13) || ch() == 32)) + next(); + } + void skip_line() { + while(true) { + if (eof()) { + return; + } + if (ch() == '\n') { + ++m_line; + next(); + return; + } + next(); + } + } bool parse_token(char const* token); int parse_int(); unsigned parse_unsigned(); }; -void opt_stream_buffer::skip_whitespace() { - while ((ch() >= 9 && ch() <= 13) || ch() == 32) { - if (ch() == 10) ++m_line; - next(); - } -} - -void opt_stream_buffer::skip_space() { - while (ch() != 10 && ((ch() >= 9 && ch() <= 13) || ch() == 32)) { - next(); - } -} -void opt_stream_buffer::skip_line() { - while(true) { - if (eof()) { - return; - } - if (ch() == '\n') { - ++m_line; - next(); - return; - } - next(); - } -} bool opt_stream_buffer::parse_token(char const* token) { skip_whitespace(); @@ -314,4 +309,542 @@ void parse_opb(opt::context& opt, std::istream& is, unsigned_vector& h) { opb.parse(); } +/** + * \brief Parser for a modest subset of the CPLEX LP format. + * Reference: http://eaton.math.rpi.edu/cplex90html/reffileformatscplex/reffileformatscplex5.html + */ + +struct asymbol { + bool m_is_num; + symbol m_sym; + rational m_num; + unsigned m_line; + asymbol(symbol const& s, unsigned l): m_is_num(false), m_sym(s), m_line(l) {} + asymbol(rational const& r, unsigned l): m_is_num(true), m_num(r), m_line(l) {} +}; + +class lp_tokenizer { + vector m_tokens; + unsigned m_pos; + svector m_buffer; +public: + lp_tokenizer(opt_stream_buffer& in): + m_pos(0) + { + parse_all(in); + } + + symbol const& peek(unsigned i) { + if (i + m_pos >= m_tokens.size()) { + return symbol::null; + } + return m_tokens[i + m_pos].m_sym; + } + + bool peek_num(unsigned i) { + if (i + m_pos >= m_tokens.size()) { + return false; + } + return m_tokens[i + m_pos].m_is_num; + } + + rational const& get_num(unsigned i) { + return m_tokens[i + m_pos].m_num; + } + + void next(unsigned delta = 1) { + m_pos += delta; + } + + bool eof() const { + return m_pos == m_tokens.size(); + } + + unsigned line() const { + if (m_pos < m_tokens.size()) return m_tokens[m_pos].m_line; + return 0; + } + +private: + + bool is_separator(char c) { + return c == '\n' || c == '\\' || c == '*' || c == '+'; + } + + char lower(char c) { + if ('A' <= c && c <= 'Z') + return c - ('A' - 'a'); + return c; + } + + void parse_all(opt_stream_buffer& in) { + while (!in.eof()) { + in.skip_whitespace(); + char c = in.ch(); + if (c == '\\') { + in.skip_line(); + continue; + } + bool neg = false; + if (c == '-') { + in.next(); + c = in.ch(); + m_buffer.reset(); + m_buffer.push_back('-'); + if (is_num(c)) { + neg = true; + } + else { + while (!is_ws(c) && !in.eof()) { + m_buffer.push_back(c); + in.next(); + c = in.ch(); + } + m_buffer.push_back(0); + m_tokens.push_back(asymbol(symbol(m_buffer.c_ptr()), in.line())); + continue; + } + } + + if (is_num(c)) { + rational n(0); + unsigned div = 1; + while (is_num(c) && !in.eof()) { + n = n*rational(10) + rational(c - '0'); + in.next(); + c = in.ch(); + } + if (c == '.') { + in.next(); + while (is_num(c) && !in.eof()) { + n = n*rational(10) + rational(c - '0'); + in.next(); + div *= 10; + c = in.ch(); + } + } + if (div > 1) n = n / rational(div); + if (neg) n.neg(); + m_tokens.push_back(asymbol(n, in.line())); + continue; + } + m_buffer.reset(); + if (is_alpha(c)) { + while (is_sym(c) && !in.eof()) { + m_buffer.push_back(lower(c)); + in.next(); + c = in.ch(); + } + } + else { + while (!is_ws(c) && !in.eof()) { + m_buffer.push_back(c); + in.next(); + c = in.ch(); + } + } + m_buffer.push_back(0); + m_tokens.push_back(asymbol(symbol(m_buffer.c_ptr()), in.line())); + } + } + + bool is_alpha(char c) const { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + } + + bool is_num(char c) const { + return '0' <= c && c <= '9'; + } + + bool is_ws(char c) const { + return c == ' ' || c == '\n' || c == '\t'; + } + + bool is_sym(char c) const { + return + is_alpha(c) || + is_num(c) || + c == '!' || + c == '"' || + c == '#' || + c == '$' || + c == '%' || + c == '&' || + c == '{' || + c == '}' || + c == ',' || + c == '_' || + c == '.' || + c == ';' || + c == '?' || + c == '@' || + c == '`' || + c == '\'' || + c == '(' || + c == ')' || + c == '~'; + } + +}; + +class lp_parse { + typedef vector > lin_term; + + struct objective { + bool m_is_max; + symbol m_name; + lin_term m_expr; + }; + + enum rel_op { + le, ge, eq + }; + + struct constraint { + symbol m_name; + symbol m_bvar; + rational m_bval; + lin_term m_expr; + rel_op m_rel; + rational m_bound; + constraint(symbol const& name, symbol const& v, rational const& val, lin_term& terms, rel_op r, rational const& bound): + m_name(name), m_bvar(v), m_bval(val), m_expr(terms), m_rel(r), m_bound(bound) {} + }; + + struct bound { + optional m_lo, m_hi; + bool m_int; + bound() : m_int(false) {} + }; + + opt::context& opt; + unsigned_vector& m_h; + lp_tokenizer tok; + objective m_objective; + vector m_constraints; + map m_bounds; + +public: + + lp_parse(opt::context& opt, opt_stream_buffer& in, unsigned_vector& h) : + opt(opt), m_h(h), tok(in) {} + + void parse() { + parse_objective(); + if (!try_subject_to()) { + error("subject to section expected"); + return; + } + + while (!is_section()) { + parse_constraint(); + } + + while (true) { + if (is_bounds()) { + tok.next(); + while (!is_section()) { + parse_bound(); + } + } + else if (is_binary()) { + tok.next(); + while (!is_section()) { + parse_binary(); + } + } + else if (is_general()) { + tok.next(); + while (!is_section()) { + parse_general(); + } + } + else { + break; + } + } + post_process(); + } + +private: + + void error(char const* msg) { + std::ostringstream ous; + ous << tok.line() << ": " << msg << " got: " << peek(0) << "\n"; + throw default_exception(ous.str()); + } + + symbol const& peek(unsigned i) { return tok.peek(i); } + + bool try_accept(char const * token) { + if (peek(0) == token) { + tok.next(); + return true; + } + return false; + } + + void parse_objective() { + m_objective.m_is_max = minmax(); + if (peek(1) == ":") { + m_objective.m_name = peek(0); + tok.next(2); + } + parse_expr(m_objective.m_expr); + } + + bool minmax() { + if (try_accept("minimize")) + return false; + if (try_accept("min")) + return false; + if (try_accept("maximize")) + return true; + if (try_accept("max")) + return true; + error("expected min or max objective"); + return false; + } + + void parse_constraint() { + symbol name; + if (peek(1) == ":") { + name = peek(0); + tok.next(2); + } + rational val(0); + symbol var; + parse_indicator(var, val); + lin_term terms; + parse_expr(terms); + rel_op op = parse_relation(); + rational rhs = tok.get_num(0); + tok.next(); + m_constraints.push_back(constraint(name, var, val, terms, op, rhs)); + } + + void parse_expr(lin_term& terms) { + bool pos = true; + if (peek(0) == "-") { + pos = false; + tok.next(); + } + while (peek(0) == "+") { + tok.next(); + } + terms.push_back(parse_term()); + if (!pos) terms.back().first = -terms.back().first; + while (peek(0) == "+" || peek(0) == "-") { + bool pos = peek(0) == "+"; + tok.next(); + terms.push_back(parse_term()); + if (!pos) terms.back().first = -terms.back().first; + } + } + + std::pair parse_term() { + std::pair r(rational::one(), peek(0)); + if (tok.peek_num(0)) { + r.first = tok.get_num(0); + r.second = peek(1); + tok.next(2); + } + else { + tok.next(1); + } + return r; + } + + rel_op parse_relation() { + if (try_accept("<=")) return le; + if (try_accept("=<")) return le; + if (try_accept(">=")) return ge; + if (try_accept("=>")) return ge; + if (try_accept("=")) return eq; + error("expected relation"); + return eq; + } + + bool peek_le(unsigned pos) { + return peek(pos) == "<=" || peek(pos) == "=<"; + } + + bool peek_minus_infty(unsigned pos) { + return peek(pos) == "-" && (peek(pos+1) == "inf" || peek(pos+1) == "infinity"); + } + + bool peek_plus_infty(unsigned pos) { + return peek(pos) == "+" && (peek(pos+1) == "inf" || peek(pos+1) == "infinity"); + } + + void parse_indicator(symbol& var, rational& val) { + if (peek(1) == "=" && tok.peek_num(2) && peek(3) == "->") { + var = peek(0); + val = tok.get_num(2); + tok.next(4); + } + } + + bool try_subject_to() { + if (try_accept("subject") && try_accept("to")) return true; + if (try_accept("such") && try_accept("that")) return true; + if (try_accept("st")) return true; + if (try_accept("s.t.")) return true; + return false; + } + + bool is_section() { return is_general() || is_binary() || is_bounds() || is_end();} + bool is_bounds() { return peek(0) == "bounds"; } + bool is_general() { return peek(0) == "general" || peek(0) == "gen" || peek(0) == "generals"; } + bool is_binary() { return peek(0) == "binary" || peek(0) == "binaries" || peek(0) == "bin"; } + bool is_end() { return peek(0) == "end" || tok.eof(); } + + // lo <= x + // x <= hi + // lo <= x <= hi + // + void parse_bound() { + symbol v; + if (peek_le(1) && tok.peek_num(0)) { + rational lhs = tok.get_num(0); + v = peek(2); + update_lower(lhs, v); + tok.next(3); + parse_upper(v); + } + else if (peek_minus_infty(0) && peek_le(2)) { + v = peek(3); + tok.next(4); + parse_upper(v); + } + else if (peek_plus_infty(2) && peek_le(1)) { + tok.next(4); + } + else if (peek_le(1) && tok.peek_num(2)) { + v = peek(0); + tok.next(2); + rational rhs = tok.get_num(0); + update_upper(v, rhs); + tok.next(1); + } + else { + error("bound expected"); + } + } + + void parse_upper(symbol const& v) { + if (peek_le(0) && tok.peek_num(1)) { + rational rhs = tok.get_num(1); + update_upper(v, rhs); + tok.next(2); + } + else if (peek_le(0) && peek_plus_infty(1)) { + tok.next(3); + } + + } + + void update_lower(rational const& r, symbol const& v) { + bound b; + m_bounds.find(v, b); + b.m_lo = r; + m_bounds.insert(v, b); + } + + void update_upper(symbol const& v, rational const& r) { + bound b; + if (!m_bounds.find(v, b)) { + // set the lower bound to default 0 + b.m_lo = rational::zero(); + } + b.m_hi = r; + m_bounds.insert(v, b); + } + + void parse_binary() { + symbol const& v = peek(0); + update_lower(rational::zero(), v); + update_upper(v, rational::one()); + m_bounds[v].m_int = true; + tok.next(); + } + + void parse_general() { + symbol const& v = peek(0); + bound b; + m_bounds.find(v, b); + b.m_int = true; + m_bounds.insert(v, b); + tok.next(); + } + + void post_process() { + ast_manager& m = opt.get_manager(); + arith_util a(m); + for (constraint const& c : m_constraints) { + expr_ref fml(m); + expr_ref term = process_terms(c.m_expr); + bool is_int = a.is_int(term) && c.m_bound.is_int(); + switch (c.m_rel) { + case le: fml = a.mk_le(term, a.mk_numeral(c.m_bound, is_int)); break; + case ge: fml = a.mk_ge(term, a.mk_numeral(c.m_bound, is_int)); break; + case eq: fml = m.mk_eq(term, a.mk_numeral(c.m_bound, is_int)); break; + } + if (c.m_bvar != symbol::null) { + term = mk_var(c.m_bvar); + bool is_int = c.m_bval.is_int() && a.is_int(term); + term = m.mk_eq(mk_var(c.m_bvar), a.mk_numeral(c.m_bval, is_int)); + fml = m.mk_implies(term, fml); + } + opt.add_hard_constraint(fml); + } + for (auto const& kv : m_bounds) { + bound const& b = kv.m_value; + expr_ref term = mk_var(kv.m_key); + if (b.m_lo) { + bool is_int = b.m_lo->is_int() && a.is_int(term); + opt.add_hard_constraint(a.mk_le(a.mk_numeral(*b.m_lo, is_int), term)); + } + if (b.m_hi) { + bool is_int = b.m_hi->is_int() && a.is_int(term); + opt.add_hard_constraint(a.mk_le(term, a.mk_numeral(*b.m_hi, is_int))); + } + } + expr_ref term = process_terms(m_objective.m_expr); + m_h.push_back(opt.add_objective(to_app(term), m_objective.m_is_max)); + } + + expr_ref process_terms(lin_term const& terms) { + ast_manager& m = opt.get_manager(); + arith_util a(m); + expr_ref_vector result(m); + for (auto const& kv : terms) { + expr_ref term = mk_var(kv.second); + if (!kv.first.is_one()) { + bool is_int = kv.first.is_int() && a.is_int(term); + term = a.mk_mul(a.mk_numeral(kv.first, is_int), term); + } + result.push_back(term); + } + return expr_ref(a.mk_add(result.size(), result.c_ptr()), m); + } + + expr_ref mk_var(symbol const& v) { + ast_manager& m = opt.get_manager(); + arith_util a(m); + bound b; + if (!m_bounds.find(v, b)) { + b.m_lo = rational::zero(); + m_bounds.insert(v, b); + } + return expr_ref(m.mk_const(v, b.m_int ? a.mk_int() : a.mk_real()), m); + } + +}; + +void parse_lp(opt::context& opt, std::istream& is, unsigned_vector& h) { + opt_stream_buffer _is(is); + lp_parse lp(opt, _is, h); + lp.parse(); +} diff --git a/src/opt/opt_parse.h b/src/opt/opt_parse.h index b058efcac..6cf92bd9c 100644 --- a/src/opt/opt_parse.h +++ b/src/opt/opt_parse.h @@ -23,6 +23,8 @@ void parse_wcnf(opt::context& opt, std::istream& is, unsigned_vector& h); void parse_opb(opt::context& opt, std::istream& is, unsigned_vector& h); +void parse_lp(opt::context& opt, std::istream& is, unsigned_vector& h); + #endif /* OPT_PARSE_H_ */ diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp index c5363816a..1a6587585 100644 --- a/src/opt/opt_solver.cpp +++ b/src/opt/opt_solver.cpp @@ -37,7 +37,7 @@ Notes: namespace opt { opt_solver::opt_solver(ast_manager & mgr, params_ref const & p, - filter_model_converter& fm): + generic_model_converter& fm): solver_na2as(mgr), m_params(p), m_context(mgr, m_params), @@ -80,7 +80,7 @@ namespace opt { m_context.collect_statistics(st); } - void opt_solver::assert_expr(expr * t) { + void opt_solver::assert_expr_core(expr * t) { if (has_quantifiers(t)) { m_params.m_relevancy_lvl = 2; } @@ -301,7 +301,7 @@ namespace opt { } } - void opt_solver::get_model(model_ref & m) { + void opt_solver::get_model_core(model_ref & m) { m_context.get_model(m); if (!m) m = m_model; else m_model = m; } diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h index ac9884770..7ffb531be 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -30,7 +30,7 @@ Notes: #include "smt/params/smt_params.h" #include "smt/smt_types.h" #include "smt/theory_opt.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" namespace opt { @@ -70,7 +70,7 @@ namespace opt { smt_params m_params; smt::kernel m_context; ast_manager& m; - filter_model_converter& m_fm; + generic_model_converter& m_fm; progress_callback * m_callback; symbol m_logic; model_ref m_model; @@ -85,19 +85,19 @@ namespace opt { bool m_first; bool m_was_unknown; public: - opt_solver(ast_manager & m, params_ref const & p, filter_model_converter& fm); + opt_solver(ast_manager & m, params_ref const & p, generic_model_converter& fm); ~opt_solver() override; solver* translate(ast_manager& m, params_ref const& p) override; void updt_params(params_ref const& p) override; void collect_param_descrs(param_descrs & r) override; void collect_statistics(statistics & st) const override; - void assert_expr(expr * t) override; + void assert_expr_core(expr * t) override; void push_core() override; void pop_core(unsigned n) override; lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override; void get_unsat_core(ptr_vector & r) override; - void get_model(model_ref & _m) override; + void get_model_core(model_ref & _m) override; proof * get_proof() override; std::string reason_unknown() const override; void set_reason_unknown(char const* msg) override; @@ -108,6 +108,8 @@ namespace opt { ast_manager& get_manager() const override { return m; } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override; lbool preferred_sat(expr_ref_vector const& asms, vector& cores) override; + expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } + void set_logic(symbol const& logic); smt::theory_var add_objective(app* term); @@ -116,7 +118,7 @@ namespace opt { void maximize_objectives(expr_ref_vector& blockers); inf_eps const & saved_objective_value(unsigned obj_index); inf_eps current_objective_value(unsigned obj_index); - model* get_model(unsigned obj_index) { return m_models[obj_index]; } + model* get_model_idx(unsigned obj_index) { return m_models[obj_index]; } bool objective_is_model_valid(unsigned obj_index) const { return m_valid_objectives[obj_index]; } diff --git a/src/opt/optsmt.cpp b/src/opt/optsmt.cpp index 66174a28b..702702ef4 100644 --- a/src/opt/optsmt.cpp +++ b/src/opt/optsmt.cpp @@ -46,7 +46,7 @@ namespace opt { for (unsigned i = 0; i < src.size(); ++i) { if (src[i] >= dst[i]) { dst[i] = src[i]; - m_models.set(i, m_s->get_model(i)); + m_models.set(i, m_s->get_model_idx(i)); m_s->get_labels(m_labels); m_lower_fmls[i] = fmls[i].get(); if (dst[i].is_pos() && !dst[i].is_finite()) { // review: likely done already. diff --git a/src/opt/sortmax.cpp b/src/opt/sortmax.cpp index 1bbafc90b..9c45e42a2 100644 --- a/src/opt/sortmax.cpp +++ b/src/opt/sortmax.cpp @@ -24,18 +24,18 @@ Notes: #include "smt/smt_context.h" #include "opt/opt_context.h" #include "util/sorting_network.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" namespace opt { class sortmax : public maxsmt_solver_base { public: - typedef expr* literal; - typedef ptr_vector literal_vector; + typedef expr* pliteral; + typedef ptr_vector pliteral_vector; psort_nw m_sort; expr_ref_vector m_trail; func_decl_ref_vector m_fresh; - ref m_filter; + ref m_filter; sortmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): maxsmt_solver_base(c, ws, soft), m_sort(*this), m_trail(m), m_fresh(m) {} @@ -50,7 +50,7 @@ namespace opt { if (is_sat != l_true) { return is_sat; } - m_filter = alloc(filter_model_converter, m); + m_filter = alloc(generic_model_converter, m, "sortmax"); rational offset = m_lower; m_upper = offset; expr_ref_vector in(m); @@ -126,27 +126,27 @@ namespace opt { } // definitions used for sorting network - literal mk_false() { return m.mk_false(); } - literal mk_true() { return m.mk_true(); } - literal mk_max(literal a, literal b) { return trail(m.mk_or(a, b)); } - literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } - literal mk_not(literal a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } + pliteral mk_false() { return m.mk_false(); } + pliteral mk_true() { return m.mk_true(); } + pliteral mk_max(pliteral a, pliteral b) { return trail(m.mk_or(a, b)); } + pliteral mk_min(pliteral a, pliteral b) { return trail(m.mk_and(a, b)); } + pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } - std::ostream& pp(std::ostream& out, literal lit) { return out << mk_pp(lit, m); } + std::ostream& pp(std::ostream& out, pliteral lit) { return out << mk_pp(lit, m); } - literal trail(literal l) { + pliteral trail(pliteral l) { m_trail.push_back(l); return l; } - literal fresh() { - expr_ref fr(m.mk_fresh_const("sn", m.mk_bool_sort()), m); + pliteral fresh(char const* n) { + expr_ref fr(m.mk_fresh_const(n, m.mk_bool_sort()), m); func_decl* f = to_app(fr)->get_decl(); m_fresh.push_back(f); - m_filter->insert(f); + m_filter->hide(f); return trail(fr); } - void mk_clause(unsigned n, literal const* lits) { + void mk_clause(unsigned n, pliteral const* lits) { s().assert_expr(mk_or(m, n, lits)); } diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index e72b9ad79..9276b60cd 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -96,6 +96,8 @@ namespace smt2 { symbol m_check_sat; symbol m_define_fun; symbol m_define_const; + symbol m_model_add; + symbol m_model_del; symbol m_declare_fun; symbol m_declare_const; symbol m_define_sort; @@ -2192,9 +2194,9 @@ namespace smt2 { next(); } - void parse_define_fun() { + void parse_define(bool is_fun) { SASSERT(curr_is_identifier()); - SASSERT(curr_id() == m_define_fun); + SASSERT(curr_id() == (is_fun ? m_define_fun : m_model_add)); SASSERT(m_num_bindings == 0); next(); check_identifier("invalid function/constant definition, symbol expected"); @@ -2208,7 +2210,10 @@ namespace smt2 { parse_expr(); if (m().get_sort(expr_stack().back()) != sort_stack().back()) throw parser_exception("invalid function/constant definition, sort mismatch"); - m_ctx.insert(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back()); + if (is_fun) + m_ctx.insert(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back()); + else + m_ctx.model_add(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back()); check_rparen("invalid function/constant definition, ')' expected"); // restore stacks & env symbol_stack().shrink(sym_spos); @@ -2221,6 +2226,24 @@ namespace smt2 { next(); } + void parse_define_fun() { + parse_define(true); + } + + void parse_model_add() { + parse_define(false); + } + + void parse_model_del() { + next(); + symbol id = curr_id(); + func_decl * f = m_ctx.find_func_decl(id); + m_ctx.model_del(f); + next(); + check_rparen_next("invalid model-del, ')' expected"); + m_ctx.print_success(); + } + void parse_define_fun_rec() { // ( define-fun-rec hfun_defi ) SASSERT(curr_is_identifier()); @@ -2578,15 +2601,11 @@ namespace smt2 { } check_rparen("invalid get-value command, ')' expected"); - if (!m_ctx.is_model_available() || m_ctx.get_check_sat_result() == nullptr) - throw cmd_exception("model is not available"); model_ref md; - if (index == 0) { - m_ctx.get_check_sat_result()->get_model(md); - } - else { + if (!m_ctx.is_model_available(md) || m_ctx.get_check_sat_result() == 0) + throw cmd_exception("model is not available"); + if (index != 0) { m_ctx.get_opt()->get_box_model(md, index); - } m_ctx.regular_stream() << "("; expr ** expr_it = expr_stack().c_ptr() + spos; @@ -2923,6 +2942,14 @@ namespace smt2 { parse_define_funs_rec(); return; } + if (s == m_model_add) { + parse_model_add(); + return; + } + if (s == m_model_del) { + parse_model_del(); + return; + } parse_ext_cmd(line, pos); } @@ -2954,6 +2981,8 @@ namespace smt2 { m_check_sat("check-sat"), m_define_fun("define-fun"), m_define_const("define-const"), + m_model_add("model-add"), + m_model_del("model-del"), m_declare_fun("declare-fun"), m_declare_const("declare-const"), m_define_sort("define-sort"), diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp index c71e942a1..d8d6705fe 100644 --- a/src/qe/nlqsat.cpp +++ b/src/qe/nlqsat.cpp @@ -658,11 +658,9 @@ namespace qe { fml = m.mk_iff(is_true, fml); goal_ref g = alloc(goal, m); g->assert_expr(fml); - proof_converter_ref pc; expr_dependency_ref core(m); - model_converter_ref mc; goal_ref_buffer result; - (*m_nftactic)(g, result, mc, pc, core); + (*m_nftactic)(g, result); SASSERT(result.size() == 1); TRACE("qe", result[0]->display(tout);); g2s(*result[0], m_params, m_solver, m_a2b, m_t2x); @@ -812,16 +810,12 @@ namespace qe { void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) override { + /* out */ goal_ref_buffer & result) override { tactic_report report("nlqsat-tactic", *in); ptr_vector fmls; expr_ref fml(m); - mc = nullptr; pc = nullptr; core = nullptr; in->get_formulas(fmls); fml = mk_and(m, fmls.size(), fmls.c_ptr()); if (m_mode == elim_t) { @@ -852,7 +846,9 @@ namespace qe { in->inc_depth(); result.push_back(in.get()); if (in->models_enabled()) { + model_converter_ref mc; VERIFY(mk_model(mc)); + in->add(mc.get()); } break; case l_undef: diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index e96626479..6ecdfd835 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -89,7 +89,7 @@ namespace eq { var * v = vars[i]; expr * t = definitions[i]; if (t == nullptr || has_quantifiers(t) || occurs_var(v->get_idx(), t)) - definitions[i] = 0; + definitions[i] = nullptr; else found = true; // found at least one candidate } @@ -106,7 +106,7 @@ namespace eq { unsigned vidx, num; for (unsigned i = 0; i < definitions.size(); i++) { - if (definitions[i] == 0) + if (definitions[i] == nullptr) continue; var * v = vars[i]; SASSERT(v->get_idx() == i); @@ -116,7 +116,7 @@ namespace eq { start: frame & fr = todo.back(); expr * t = fr.first; - if (t->get_ref_count() > 1 && done.is_marked(t)) { + if (done.is_marked(t)) { todo.pop_back(); continue; } @@ -126,11 +126,11 @@ namespace eq { if (fr.second == 0) { CTRACE("der_bug", vidx >= definitions.size(), tout << "vidx: " << vidx << "\n";); // Remark: The size of definitions may be smaller than the number of variables occurring in the quantified formula. - if (definitions.get(vidx, 0) != 0) { + if (definitions.get(vidx, nullptr) != nullptr) { if (visiting.is_marked(t)) { // cycle detected: remove t visiting.reset_mark(t); - definitions[vidx] = 0; + definitions[vidx] = nullptr; } else { visiting.mark(t); @@ -142,17 +142,14 @@ namespace eq { } else { SASSERT(fr.second == 1); - if (definitions.get(vidx, 0) != 0) { - visiting.reset_mark(t); - order.push_back(vidx); - } - else { - // var was removed from the list of candidate vars to elim cycle - // do nothing + visiting.reset_mark(t); + if (!done.is_marked(t)) { + if (definitions.get(vidx, nullptr) != nullptr) + order.push_back(vidx); + done.mark(t); } } - if (t->get_ref_count() > 1) - done.mark(t); + done.mark(t); todo.pop_back(); break; case AST_QUANTIFIER: @@ -164,13 +161,11 @@ namespace eq { while (fr.second < num) { expr * arg = to_app(t)->get_arg(fr.second); fr.second++; - if (arg->get_ref_count() > 1 && done.is_marked(arg)) - continue; + if (done.is_marked(arg)) continue; todo.push_back(frame(arg, 0)); goto start; } - if (t->get_ref_count() > 1) - done.mark(t); + done.mark(t); todo.pop_back(); break; default: @@ -574,12 +569,15 @@ namespace eq { checkpoint(); ptr_vector vs; expr_ref_vector ts(m); + expr_ref t(m); if (is_var_def(is_exists, args[i], vs, ts)) { for (unsigned j = 0; j < vs.size(); ++j) { var* v = vs[j]; - expr* t = ts[j].get(); + t = ts.get(j); + m_rewriter(t); + if (t != ts.get(j)) m_new_exprs.push_back(t); unsigned idx = v->get_idx(); - if (m_map.get(idx, 0) == 0) { + if (m_map.get(idx, nullptr) == nullptr) { m_map.reserve(idx + 1, 0); m_inx2var.reserve(idx + 1, 0); m_map[idx] = t; @@ -2461,9 +2459,7 @@ public: fmls[index] = fml; return; } - TRACE("qe_lite", for (unsigned i = 0; i < fmls.size(); ++i) { - tout << mk_pp(fmls[i].get(), m) << "\n"; - }); + TRACE("qe_lite", tout << fmls << "\n";); is_variable_test is_var(index_set, index_of_bound); m_der.set_is_variable_proc(is_var); m_fm.set_is_variable_proc(is_var); @@ -2555,12 +2551,8 @@ class qe_lite_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("qe-lite", *g); proof_ref new_pr(m); expr_ref new_f(m); @@ -2626,11 +2618,8 @@ public: } void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + goal_ref_buffer & result) override { + (*m_imp)(in, result); } diff --git a/src/qe/qe_lite.h b/src/qe/qe_lite.h index 4fc5572a2..63ad8bedd 100644 --- a/src/qe/qe_lite.h +++ b/src/qe/qe_lite.h @@ -57,7 +57,6 @@ public: void operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml); void operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& conjs); - /** \brief full rewriting based light-weight quantifier elimination round. */ diff --git a/src/qe/qe_sat_tactic.cpp b/src/qe/qe_sat_tactic.cpp index 4c56b74a7..5fbe10d35 100644 --- a/src/qe/qe_sat_tactic.cpp +++ b/src/qe/qe_sat_tactic.cpp @@ -231,12 +231,7 @@ namespace qe { reset(); } - void operator()(goal_ref const& goal, - goal_ref_buffer& result, - model_converter_ref& mc, - proof_converter_ref & pc, - expr_dependency_ref& core) override - { + void operator()(goal_ref const& goal, goal_ref_buffer& result) override { try { checkpoint(); reset(); @@ -259,7 +254,7 @@ namespace qe { else { goal->reset(); // equi-satisfiable. What to do with model? - mc = model2model_converter(&*model); + goal->add(model2model_converter(&*model)); } result.push_back(goal.get()); } @@ -269,16 +264,16 @@ namespace qe { } void collect_statistics(statistics & st) const override { - for (unsigned i = 0; i < m_solvers.size(); ++i) { - m_solvers[i]->collect_statistics(st); + for (auto const * s : m_solvers) { + s->collect_statistics(st); } m_solver.collect_statistics(st); m_ctx_rewriter.collect_statistics(st); } void reset_statistics() override { - for (unsigned i = 0; i < m_solvers.size(); ++i) { - m_solvers[i]->reset_statistics(); + for (auto * s : m_solvers) { + s->reset_statistics(); } m_solver.reset_statistics(); m_ctx_rewriter.reset_statistics(); diff --git a/src/qe/qe_tactic.cpp b/src/qe/qe_tactic.cpp index fab1f3382..a4e04485b 100644 --- a/src/qe/qe_tactic.cpp +++ b/src/qe/qe_tactic.cpp @@ -17,7 +17,6 @@ Revision History: --*/ #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" #include "util/cooperate.h" #include "qe/qe.h" @@ -51,12 +50,8 @@ class qe_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("qe", *g); m_fparams.m_model = g->models_enabled(); proof_ref new_pr(m); @@ -121,12 +116,9 @@ public: m_imp->collect_param_descrs(r); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); m_st.reset(); m_imp->collect_statistics(m_st); diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index f463a62e5..64cde4b1d 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -44,11 +44,11 @@ namespace qe { m(m), m_asms(m), m_trail(m), - m_fmc(alloc(filter_model_converter, m)) + m_fmc(alloc(generic_model_converter, m, "qsat")) { } - filter_model_converter* pred_abs::fmc() { + generic_model_converter* pred_abs::fmc() { return m_fmc.get(); } @@ -282,7 +282,7 @@ namespace qe { app_ref pred_abs::fresh_bool(char const* name) { app_ref r(m.mk_fresh_const(name, m.mk_bool_sort()), m); - m_fmc->insert(r->get_decl()); + m_fmc->hide(r); return r; } @@ -750,9 +750,7 @@ namespace qe { } void filter_vars(app_ref_vector const& vars) { - for (unsigned i = 0; i < vars.size(); ++i) { - m_pred_abs.fmc()->insert(vars[i]->get_decl()); - } + for (app* v : vars) m_pred_abs.fmc()->hide(v); } void initialize_levels() { @@ -1216,18 +1214,12 @@ namespace qe { void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) override { + /* out */ goal_ref_buffer & result) override { tactic_report report("qsat-tactic", *in); ptr_vector fmls; expr_ref_vector defs(m); expr_ref fml(m); - mc = nullptr; pc = nullptr; core = nullptr; in->get_formulas(fmls); - - fml = mk_and(m, fmls.size(), fmls.c_ptr()); // for now: @@ -1277,8 +1269,10 @@ namespace qe { in->inc_depth(); result.push_back(in.get()); if (in->models_enabled()) { - mc = model2model_converter(m_model_save.get()); + model_converter_ref mc; + mc = model2model_converter(m_model.get()); mc = concat(m_pred_abs.fmc(), mc.get()); + in->add(mc.get()); } break; case l_undef: diff --git a/src/qe/qsat.h b/src/qe/qsat.h index 85a8181d6..dca7cc00b 100644 --- a/src/qe/qsat.h +++ b/src/qe/qsat.h @@ -22,7 +22,7 @@ Revision History: #define QE_QSAT_H__ #include "tactic/tactic.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "qe/qe_mbp.h" namespace qe { @@ -72,7 +72,7 @@ namespace qe { obj_map m_asm2pred; // maintain map from assumptions to predicates obj_map m_pred2asm; // predicates |-> assumptions expr_ref_vector m_trail; - filter_model_converter_ref m_fmc; + generic_model_converter_ref m_fmc; ptr_vector todo; obj_map m_elevel; obj_map m_flevel; @@ -91,7 +91,7 @@ namespace qe { public: pred_abs(ast_manager& m); - filter_model_converter* fmc(); + generic_model_converter* fmc(); void reset(); max_level compute_level(app* e); void push(); diff --git a/src/sat/CMakeLists.txt b/src/sat/CMakeLists.txt index d88a73708..320a674a2 100644 --- a/src/sat/CMakeLists.txt +++ b/src/sat/CMakeLists.txt @@ -1,22 +1,30 @@ z3_add_component(sat SOURCES + ba_solver.cpp dimacs.cpp sat_asymm_branch.cpp + sat_bdd.cpp + sat_big.cpp sat_clause.cpp sat_clause_set.cpp sat_clause_use_list.cpp sat_cleaner.cpp sat_config.cpp + sat_drat.cpp sat_elim_eqs.cpp + sat_elim_vars.cpp sat_iff3_finder.cpp sat_integrity_checker.cpp + sat_local_search.cpp + sat_lookahead.cpp sat_model_converter.cpp sat_mus.cpp - sat_par.cpp + sat_parallel.cpp sat_probing.cpp sat_scc.cpp sat_simplifier.cpp sat_solver.cpp + sat_unit_walk.cpp sat_watched.cpp COMPONENT_DEPENDENCIES util diff --git a/src/sat/ba_solver.cpp b/src/sat/ba_solver.cpp new file mode 100644 index 000000000..4fbf7570d --- /dev/null +++ b/src/sat/ba_solver.cpp @@ -0,0 +1,4205 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + ba_solver.cpp + +Abstract: + + Extension for cardinality and xr reasoning. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-01-30 + +Revision History: + +--*/ + +#include +#include "sat/ba_solver.h" +#include "sat/sat_types.h" +#include "util/mpz.h" +#include "sat/sat_simplifier_params.hpp" + + +namespace sat { + + static unsigned _bad_id = 11111111; // 2759; // +#define BADLOG(_cmd_) if (p.id() == _bad_id) { _cmd_; } + + ba_solver::card& ba_solver::constraint::to_card() { + SASSERT(is_card()); + return static_cast(*this); + } + + ba_solver::card const& ba_solver::constraint::to_card() const{ + SASSERT(is_card()); + return static_cast(*this); + } + + ba_solver::pb& ba_solver::constraint::to_pb() { + SASSERT(is_pb()); + return static_cast(*this); + } + + ba_solver::pb const& ba_solver::constraint::to_pb() const{ + SASSERT(is_pb()); + return static_cast(*this); + } + + ba_solver::xr& ba_solver::constraint::to_xr() { + SASSERT(is_xr()); + return static_cast(*this); + } + + ba_solver::xr const& ba_solver::constraint::to_xr() const{ + SASSERT(is_xr()); + return static_cast(*this); + } + + std::ostream& operator<<(std::ostream& out, ba_solver::constraint const& cnstr) { + if (cnstr.lit() != null_literal) out << cnstr.lit() << " == "; + switch (cnstr.tag()) { + case ba_solver::card_t: { + ba_solver::card const& c = cnstr.to_card(); + for (literal l : c) { + out << l << " "; + } + out << " >= " << c.k(); + break; + } + case ba_solver::pb_t: { + ba_solver::pb const& p = cnstr.to_pb(); + for (ba_solver::wliteral wl : p) { + if (wl.first != 1) out << wl.first << " * "; + out << wl.second << " "; + } + out << " >= " << p.k(); + break; + } + case ba_solver::xr_t: { + ba_solver::xr const& x = cnstr.to_xr(); + for (unsigned i = 0; i < x.size(); ++i) { + out << x[i] << " "; + if (i + 1 < x.size()) out << "x "; + } + break; + } + default: + UNREACHABLE(); + } + return out; + } + + + // ----------------------- + // pb_base + + bool ba_solver::pb_base::well_formed() const { + uint_set vars; + if (lit() != null_literal) vars.insert(lit().var()); + for (unsigned i = 0; i < size(); ++i) { + bool_var v = get_lit(i).var(); + if (vars.contains(v)) return false; + if (get_coeff(i) > k()) return false; + vars.insert(v); + } + return true; + } + + // ---------------------- + // card + + ba_solver::card::card(unsigned id, literal lit, literal_vector const& lits, unsigned k): + pb_base(card_t, id, lit, lits.size(), get_obj_size(lits.size()), k) { + for (unsigned i = 0; i < size(); ++i) { + m_lits[i] = lits[i]; + } + } + + void ba_solver::card::negate() { + m_lit.neg(); + for (unsigned i = 0; i < m_size; ++i) { + m_lits[i].neg(); + } + m_k = m_size - m_k + 1; + SASSERT(m_size >= m_k && m_k > 0); + } + + bool ba_solver::card::is_watching(literal l) const { + unsigned sz = std::min(k() + 1, size()); + for (unsigned i = 0; i < sz; ++i) { + if ((*this)[i] == l) return true; + } + return false; + } + + // ----------------------------------- + // pb + + ba_solver::pb::pb(unsigned id, literal lit, svector const& wlits, unsigned k): + pb_base(pb_t, id, lit, wlits.size(), get_obj_size(wlits.size()), k), + m_slack(0), + m_num_watch(0), + m_max_sum(0) { + for (unsigned i = 0; i < size(); ++i) { + m_wlits[i] = wlits[i]; + } + update_max_sum(); + } + + void ba_solver::pb::update_max_sum() { + m_max_sum = 0; + for (unsigned i = 0; i < size(); ++i) { + m_wlits[i].first = std::min(k(), m_wlits[i].first); + if (m_max_sum + m_wlits[i].first < m_max_sum) { + throw default_exception("addition of pb coefficients overflows"); + } + m_max_sum += m_wlits[i].first; + } + } + + void ba_solver::pb::negate() { + m_lit.neg(); + unsigned w = 0; + for (unsigned i = 0; i < m_size; ++i) { + m_wlits[i].second.neg(); + w += m_wlits[i].first; + } + m_k = w - m_k + 1; + SASSERT(w >= m_k && m_k > 0); + } + + bool ba_solver::pb::is_watching(literal l) const { + for (unsigned i = 0; i < m_num_watch; ++i) { + if ((*this)[i].second == l) return true; + } + return false; + } + + + bool ba_solver::pb::is_cardinality() const { + if (size() == 0) return false; + unsigned w = (*this)[0].first; + for (wliteral wl : *this) if (w != wl.first) return false; + return true; + } + + + // ----------------------------------- + // xr + + ba_solver::xr::xr(unsigned id, literal_vector const& lits): + constraint(xr_t, id, null_literal, lits.size(), get_obj_size(lits.size())) { + for (unsigned i = 0; i < size(); ++i) { + m_lits[i] = lits[i]; + } + } + + bool ba_solver::xr::is_watching(literal l) const { + return + l == (*this)[0] || l == (*this)[1] || + ~l == (*this)[0] || ~l == (*this)[1]; + } + + bool ba_solver::xr::well_formed() const { + uint_set vars; + if (lit() != null_literal) vars.insert(lit().var()); + for (literal l : *this) { + bool_var v = l.var(); + if (vars.contains(v)) return false; + vars.insert(v); + } + return true; + } + + // ---------------------------- + // card + + bool ba_solver::init_watch(card& c) { + literal root = c.lit(); + if (root != null_literal && value(root) == l_false) { + clear_watch(c); + c.negate(); + root.neg(); + } + if (root != null_literal) { + if (!is_watched(root, c)) watch_literal(root, c); + if (!c.is_pure() && !is_watched(~root, c)) watch_literal(~root, c); + } + TRACE("ba", display(tout << "init watch: ", c, true);); + SASSERT(root == null_literal || value(root) == l_true); + unsigned j = 0, sz = c.size(), bound = c.k(); + // put the non-false literals into the head. + + if (bound == sz) { + for (literal l : c) assign(c, l); + return false; + } + + for (unsigned i = 0; i < sz; ++i) { + if (value(c[i]) != l_false) { + if (j != i) { + if (c.is_watched() && j <= bound && i > bound) { + unwatch_literal(c[j], c); + watch_literal(c[i], c); + } + c.swap(i, j); + } + ++j; + } + } + DEBUG_CODE( + bool is_false = false; + for (literal l : c) { + SASSERT(!is_false || value(l) == l_false); + is_false = value(l) == l_false; + }); + + // j is the number of non-false, sz - j the number of false. + + if (j < bound) { + if (c.is_watched()) clear_watch(c); + SASSERT(0 < bound && bound < sz); + literal alit = c[j]; + + // + // we need the assignment level of the asserting literal to be maximal. + // such that conflict resolution can use the asserting literal as a starting + // point. + // + + for (unsigned i = bound; i < sz; ++i) { + if (lvl(alit) < lvl(c[i])) { + c.swap(i, j); + alit = c[j]; + } + } + set_conflict(c, alit); + return false; + } + else if (j == bound) { + for (unsigned i = 0; i < bound; ++i) { + assign(c, c[i]); + } + return false; + } + else { + if (c.is_watched()) return true; + clear_watch(c); + for (unsigned i = 0; i <= bound; ++i) { + watch_literal(c[i], c); + } + c.set_watch(); + return true; + } + } + + void ba_solver::clear_watch(card& c) { + if (c.is_clear()) return; + c.clear_watch(); + unsigned sz = std::min(c.k() + 1, c.size()); + for (unsigned i = 0; i < sz; ++i) { + unwatch_literal(c[i], c); + } + } + + // ----------------------- + // constraint + + void ba_solver::set_conflict(constraint& c, literal lit) { + m_stats.m_num_conflicts++; + TRACE("ba", display(tout, c, true); ); + if (!validate_conflict(c)) { + display(std::cout, c, true); + UNREACHABLE(); + } + SASSERT(validate_conflict(c)); + if (c.is_xr() && value(lit) == l_true) lit.neg(); + SASSERT(value(lit) == l_false); + set_conflict(justification::mk_ext_justification(c.index()), ~lit); + SASSERT(inconsistent()); + } + + void ba_solver::assign(constraint& c, literal lit) { + if (inconsistent()) return; + switch (value(lit)) { + case l_true: + break; + case l_false: + set_conflict(c, lit); + break; + default: + m_stats.m_num_propagations++; + m_num_propagations_since_pop++; + //TRACE("ba", tout << "#prop: " << m_stats.m_num_propagations << " - " << c.lit() << " => " << lit << "\n";); + SASSERT(validate_unit_propagation(c, lit)); + if (get_config().m_drat) { + svector ps; + literal_vector lits; + get_antecedents(lit, c, lits); + lits.push_back(lit); + ps.push_back(drat::premise(drat::s_ext(), c.lit())); // null_literal case. + drat_add(lits, ps); + } + assign(lit, justification::mk_ext_justification(c.index())); + break; + } + } + + // ------------------- + // pb_base + + void ba_solver::simplify(pb_base& p) { + SASSERT(s().at_base_lvl()); + if (p.lit() != null_literal && value(p.lit()) == l_false) { + TRACE("ba", tout << "pb: flip sign " << p << "\n";); + IF_VERBOSE(0, verbose_stream() << "sign is flipped " << p << "\n";); + return; + } + bool nullify = p.lit() != null_literal && value(p.lit()) == l_true; + if (nullify) { + IF_VERBOSE(100, display(verbose_stream() << "nullify tracking literal\n", p, true);); + SASSERT(lvl(p.lit()) == 0); + nullify_tracking_literal(p); + init_watch(p); + } + + SASSERT(p.lit() == null_literal || value(p.lit()) != l_false); + + unsigned true_val = 0, slack = 0, num_false = 0; + for (unsigned i = 0; i < p.size(); ++i) { + literal l = p.get_lit(i); + if (s().was_eliminated(l.var())) { + SASSERT(p.learned()); + remove_constraint(p, "contains eliminated"); + return; + } + switch (value(l)) { + case l_true: true_val += p.get_coeff(i); break; + case l_false: ++num_false; break; + default: slack += p.get_coeff(i); break; + } + } + if (p.k() == 1 && p.lit() == null_literal) { + literal_vector lits(p.literals()); + s().mk_clause(lits.size(), lits.c_ptr(), p.learned()); + IF_VERBOSE(100, display(verbose_stream() << "add clause: " << lits << "\n", p, true);); + remove_constraint(p, "implies clause"); + } + else if (true_val == 0 && num_false == 0) { + if (p.lit() == null_literal || value(p.lit()) == l_true) { + init_watch(p); + } + } + else if (true_val >= p.k()) { + if (p.lit() != null_literal) { + IF_VERBOSE(100, display(verbose_stream() << "assign true literal ", p, true);); + s().assign(p.lit(), justification()); + } + remove_constraint(p, "is true"); + } + else if (slack + true_val < p.k()) { + if (p.lit() != null_literal) { + IF_VERBOSE(100, display(verbose_stream() << "assign false literal ", p, true);); + s().assign(~p.lit(), justification()); + } + else { + IF_VERBOSE(0, verbose_stream() << "unsat during simplification\n";); + s().set_conflict(justification()); + } + remove_constraint(p, "is false"); + } + else if (slack + true_val == p.k()) { + literal_vector lits(p.literals()); + assert_unconstrained(p.lit(), lits); + remove_constraint(p, "is tight"); + } + else { + + unsigned sz = p.size(); + clear_watch(p); + unsigned j = 0; + for (unsigned i = 0; i < sz; ++i) { + literal l = p.get_lit(i); + if (value(l) == l_undef) { + if (i != j) p.swap(i, j); + ++j; + } + } + sz = j; + // _bad_id = p.id(); + BADLOG(display(verbose_stream() << "simplify ", p, true)); + + unsigned k = p.k() - true_val; + + if (k == 1 && p.lit() == null_literal) { + literal_vector lits(sz, p.literals().c_ptr()); + s().mk_clause(sz, lits.c_ptr(), p.learned()); + remove_constraint(p, "is clause"); + return; + } + p.set_size(sz); + p.set_k(k); + if (p.lit() == null_literal || value(p.lit()) == l_true) { + init_watch(p); + } + else { + SASSERT(value(p.lit()) == l_undef); + } + BADLOG(display(verbose_stream() << "simplified ", p, true); verbose_stream() << "\n"); + // display(verbose_stream(), c, true); + _bad_id = 11111111; + SASSERT(p.well_formed()); + m_simplify_change = true; + } + } + + /* + \brief split PB constraint into two because root is reused in arguments. + + x <=> a*x + B*y >= k + + x => a*x + By >= k + ~x => a*x + By < k + + k*~x + a*x + By >= k + (B+a-k + 1)*x + a*~x + B*~y >= B + a - k + 1 + + (k - a) * ~x + By >= k - a + k' * x + B'y >= k' + + */ + + void ba_solver::split_root(pb_base& p) { + SASSERT(p.lit() != null_literal); + SASSERT(!p.learned()); + m_weights.resize(2*s().num_vars(), 0); + unsigned k = p.k(); + unsigned w, w1, w2; + literal root = p.lit(); + m_weights[(~root).index()] = k; + for (unsigned i = 0; i < p.size(); ++i) { + m_weights[p.get_lit(i).index()] += p.get_coeff(i); + } + literal_vector lits(p.literals()); + lits.push_back(~root); + + for (literal l : lits) { + w1 = m_weights[l.index()]; + w2 = m_weights[(~l).index()]; + if (w1 >= w2) { + if (w2 >= k) { + for (literal l2 : lits) m_weights[l2.index()] = 0; + // constraint is true + return; + } + k -= w2; + m_weights[(~l).index()] = 0; + m_weights[l.index()] = w1 - w2; + } + } + SASSERT(k > 0); + + // ~root * (k - a) + p >= k - a + + m_wlits.reset(); + for (literal l : lits) { + w = m_weights[l.index()]; + if (w != 0) { + m_wlits.push_back(wliteral(w, l)); + } + m_weights[l.index()] = 0; + } + + add_pb_ge(null_literal, m_wlits, k, false); + } + + + // ------------------- + // pb + + + // watch a prefix of literals, such that the slack of these is >= k + bool ba_solver::init_watch(pb& p) { + clear_watch(p); + if (p.lit() != null_literal && value(p.lit()) == l_false) { + p.negate(); + } + + VERIFY(p.lit() == null_literal || value(p.lit()) == l_true); + unsigned sz = p.size(), bound = p.k(); + + // put the non-false literals into the head. + unsigned slack = 0, slack1 = 0, num_watch = 0, j = 0; + for (unsigned i = 0; i < sz; ++i) { + if (value(p[i].second) != l_false) { + if (j != i) { + p.swap(i, j); + } + if (slack <= bound) { + slack += p[j].first; + ++num_watch; + } + else { + slack1 += p[j].first; + } + ++j; + } + } + BADLOG(verbose_stream() << "watch " << num_watch << " out of " << sz << "\n"); + + DEBUG_CODE( + bool is_false = false; + for (unsigned k = 0; k < sz; ++k) { + SASSERT(!is_false || value(p[k].second) == l_false); + SASSERT(k < j == (value(p[k].second) != l_false)); + is_false = value(p[k].second) == l_false; + }); + + if (slack < bound) { + literal lit = p[j].second; + VERIFY(value(lit) == l_false); + for (unsigned i = j + 1; i < sz; ++i) { + if (lvl(lit) < lvl(p[i].second)) { + lit = p[i].second; + } + } + set_conflict(p, lit); + return false; + } + else { + for (unsigned i = 0; i < num_watch; ++i) { + watch_literal(p[i], p); + } + p.set_slack(slack); + p.set_num_watch(num_watch); + + SASSERT(validate_watch(p, null_literal)); + + TRACE("ba", display(tout << "init watch: ", p, true);); + + // slack is tight: + if (slack + slack1 == bound) { + SASSERT(slack1 == 0); + SASSERT(j == num_watch); + for (unsigned i = 0; i < j; ++i) { + assign(p, p[i].second); + } + } + return true; + } + } + + /* + Chai Kuhlmann: + Lw - set of watched literals + Lu - set of unwatched literals that are not false + + Lw = Lw \ { alit } + Sw -= value + a_max = max { a | l in Lw u Lu, l = undef } + while (Sw < k + a_max & Lu != 0) { + a_s = max { a | l in Lu } + Sw += a_s + Lw = Lw u {l_s} + Lu = Lu \ {l_s} + } + if (Sw < k) return conflict + for (li in Lw | Sw < k + ai) + assign li + return no-conflict + + a_max index: index of non-false literal with maximal weight. + */ + + void ba_solver::add_index(pb& p, unsigned index, literal lit) { + if (value(lit) == l_undef) { + m_pb_undef.push_back(index); + if (p[index].first > m_a_max) { + m_a_max = p[index].first; + } + } + } + + /* + \brief propagate assignment to alit in constraint p. + + TBD: + - consider reordering literals in watch list so that the search for watched literal takes average shorter time. + - combine with caching literals that are assigned to 'true' to a cold store where they are not being revisited. + Since 'true' literals may be unassigned (unless they are assigned at level 0) the cache has to be backtrack + friendly (and the overhead of backtracking has to be taken into account). + */ + lbool ba_solver::add_assign(pb& p, literal alit) { + BADLOG(display(verbose_stream() << "assign: " << alit << " watch: " << p.num_watch() << " size: " << p.size(), p, true)); + TRACE("ba", display(tout << "assign: " << alit << "\n", p, true);); + SASSERT(!inconsistent()); + unsigned sz = p.size(); + unsigned bound = p.k(); + unsigned num_watch = p.num_watch(); + unsigned slack = p.slack(); + SASSERT(value(alit) == l_false); + SASSERT(p.lit() == null_literal || value(p.lit()) == l_true); + SASSERT(num_watch <= sz); + SASSERT(num_watch > 0); + unsigned index = 0; + m_a_max = 0; + m_pb_undef.reset(); + for (; index < num_watch; ++index) { + literal lit = p[index].second; + if (lit == alit) { + break; + } + add_index(p, index, lit); + } + if (index == num_watch || num_watch == 0) { + _bad_id = p.id(); + BADLOG( + verbose_stream() << "BAD: " << p.id() << "\n"; + display(verbose_stream(), p, true); + verbose_stream() << "alit: " << alit << "\n"; + verbose_stream() << "num watch " << num_watch << "\n"); + UNREACHABLE(); + exit(0); + return l_undef; + } + + SASSERT(validate_watch(p, null_literal)); + // SASSERT(validate_watch(p, null_literal)); + + SASSERT(index < num_watch); + unsigned index1 = index + 1; + for (; m_a_max == 0 && index1 < num_watch; ++index1) { + add_index(p, index1, p[index1].second); + } + + unsigned val = p[index].first; + SASSERT(value(p[index].second) == l_false); + SASSERT(val <= slack); + slack -= val; + + // find literals to swap with: + for (unsigned j = num_watch; j < sz && slack < bound + m_a_max; ++j) { + literal lit = p[j].second; + if (value(lit) != l_false) { + slack += p[j].first; + SASSERT(!is_watched(p[j].second, p)); + watch_literal(p[j], p); + p.swap(num_watch, j); + add_index(p, num_watch, lit); + BADLOG(verbose_stream() << "add watch: " << lit << " num watch: " << num_watch << " max: " << m_a_max << " slack " << slack << "\n"); + ++num_watch; + } + } + + SASSERT(!inconsistent()); + DEBUG_CODE(for (auto idx : m_pb_undef) { SASSERT(value(p[idx].second) == l_undef); }); + + if (slack < bound) { + // maintain watching the literal + slack += val; + p.set_slack(slack); + p.set_num_watch(num_watch); + SASSERT(validate_watch(p, null_literal)); + BADLOG(display(verbose_stream() << "conflict: " << alit << " watch: " << p.num_watch() << " size: " << p.size(), p, true)); + SASSERT(bound <= slack); + TRACE("ba", tout << "conflict " << alit << "\n";); + set_conflict(p, alit); + return l_false; + } + + if (num_watch == 1) { _bad_id = p.id(); } + + BADLOG(verbose_stream() << "size: " << p.size() << " index: " << index << " num watch: " << num_watch << "\n"); + + // swap out the watched literal. + --num_watch; + SASSERT(num_watch > 0); + p.set_slack(slack); + p.set_num_watch(num_watch); + p.swap(num_watch, index); + + + // + // slack >= bound, but slack - w(l) < bound + // l must be true. + // + if (slack < bound + m_a_max) { + BADLOG(verbose_stream() << "slack " << slack << " " << bound << " " << m_a_max << "\n";); + TRACE("ba", tout << p << "\n"; for(auto j : m_pb_undef) tout << j << " "; tout << "\n";); + for (unsigned index1 : m_pb_undef) { + if (index1 == num_watch) { + index1 = index; + } + wliteral wl = p[index1]; + literal lit = wl.second; + SASSERT(value(lit) == l_undef); + if (slack < bound + wl.first) { + BADLOG(verbose_stream() << "Assign " << lit << " " << wl.first << "\n"); + assign(p, lit); + } + } + } + + SASSERT(validate_watch(p, alit)); // except that alit is still watched. + + TRACE("ba", display(tout << "assign: " << alit << "\n", p, true);); + + BADLOG(verbose_stream() << "unwatch " << alit << " watch: " << p.num_watch() << " size: " << p.size() << " slack: " << p.slack() << " " << inconsistent() << "\n"); + + return l_undef; + } + + void ba_solver::watch_literal(wliteral l, pb& p) { + watch_literal(l.second, p); + } + + void ba_solver::clear_watch(pb& p) { + p.clear_watch(); + for (unsigned i = 0; i < p.num_watch(); ++i) { + unwatch_literal(p[i].second, p); + } + p.set_num_watch(0); + + DEBUG_CODE(for (wliteral wl : p) VERIFY(!is_watched(wl.second, p));); + } + + void ba_solver::recompile(pb& p) { + // IF_VERBOSE(2, verbose_stream() << "re: " << p << "\n";); + SASSERT(p.num_watch() == 0); + m_weights.resize(2*s().num_vars(), 0); + for (wliteral wl : p) { + m_weights[wl.second.index()] += wl.first; + } + unsigned k = p.k(); + unsigned sz = p.size(); + bool all_units = true; + unsigned j = 0; + for (unsigned i = 0; i < sz && 0 < k; ++i) { + literal l = p[i].second; + unsigned w1 = m_weights[l.index()]; + unsigned w2 = m_weights[(~l).index()]; + if (w1 == 0 || w1 < w2) { + continue; + } + else if (k <= w2) { + k = 0; + break; + } + else { + SASSERT(w2 <= w1 && w2 < k); + k -= w2; + w1 -= w2; + m_weights[l.index()] = 0; + m_weights[(~l).index()] = 0; + if (w1 == 0) { + continue; + } + else { + p[j] = wliteral(w1, l); + all_units &= w1 == 1; + ++j; + } + } + } + sz = j; + // clear weights + for (wliteral wl : p) { + m_weights[wl.second.index()] = 0; + m_weights[(~wl.second).index()] = 0; + } + + if (k == 0) { + if (p.lit() != null_literal) { + s().assign(p.lit(), justification()); + } + remove_constraint(p, "recompiled to true"); + return; + } + + else if (k == 1 && p.lit() == null_literal) { + literal_vector lits(sz, p.literals().c_ptr()); + s().mk_clause(sz, lits.c_ptr(), p.learned()); + remove_constraint(p, "recompiled to clause"); + return; + } + + else if (all_units) { + literal_vector lits(sz, p.literals().c_ptr()); + add_at_least(p.lit(), lits, k, p.learned()); + remove_constraint(p, "recompiled to cardinality"); + return; + } + + else { + p.set_size(sz); + p.set_k(k); + SASSERT(p.well_formed()); + + if (clausify(p)) { + return; + } + if (p.lit() == null_literal || value(p.lit()) == l_true) { + init_watch(p); + } + } + } + + void ba_solver::display(std::ostream& out, pb const& p, bool values) const { + if (p.lit() != null_literal) out << p.lit() << " == "; + if (values) { + out << "[watch: " << p.num_watch() << ", slack: " << p.slack() << "]"; + } + if (p.lit() != null_literal && values) { + out << "@(" << value(p.lit()); + if (value(p.lit()) != l_undef) { + out << ":" << lvl(p.lit()); + } + out << "): "; + } + unsigned i = 0; + for (wliteral wl : p) { + literal l = wl.second; + unsigned w = wl.first; + if (i++ == p.num_watch()) out << " | "; + if (w > 1) out << w << " * "; + out << l; + if (values) { + out << "@(" << value(l); + if (value(l) != l_undef) { + out << ":" << lvl(l); + } + out << ") "; + } + else { + out << " "; + } + } + out << ">= " << p.k() << "\n"; + } + + // -------------------- + // xr: + + void ba_solver::clear_watch(xr& x) { + x.clear_watch(); + unwatch_literal(x[0], x); + unwatch_literal(x[1], x); + unwatch_literal(~x[0], x); + unwatch_literal(~x[1], x); + } + + bool ba_solver::parity(xr const& x, unsigned offset) const { + bool odd = false; + unsigned sz = x.size(); + for (unsigned i = offset; i < sz; ++i) { + SASSERT(value(x[i]) != l_undef); + if (value(x[i]) == l_true) { + odd = !odd; + } + } + return odd; + } + + bool ba_solver::init_watch(xr& x) { + clear_watch(x); + if (x.lit() != null_literal && value(x.lit()) == l_false) { + x.negate(); + } + TRACE("ba", display(tout, x, true);); + unsigned sz = x.size(); + unsigned j = 0; + for (unsigned i = 0; i < sz && j < 2; ++i) { + if (value(x[i]) == l_undef) { + x.swap(i, j); + ++j; + } + } + switch (j) { + case 0: + if (!parity(x, 0)) { + unsigned l = lvl(x[0]); + j = 1; + for (unsigned i = 1; i < sz; ++i) { + if (lvl(x[i]) > l) { + j = i; + l = lvl(x[i]); + } + } + SASSERT(x.lit() == null_literal || value(x.lit()) == l_true); + set_conflict(x, x[j]); + } + return false; + case 1: + SASSERT(x.lit() == null_literal || value(x.lit()) == l_true); + assign(x, parity(x, 1) ? ~x[0] : x[0]); + return false; + default: + SASSERT(j == 2); + watch_literal(x[0], x); + watch_literal(x[1], x); + watch_literal(~x[0], x); + watch_literal(~x[1], x); + return true; + } + } + + + lbool ba_solver::add_assign(xr& x, literal alit) { + // literal is assigned + unsigned sz = x.size(); + TRACE("ba", tout << "assign: " << ~alit << "@" << lvl(~alit) << " " << x << "\n"; display(tout, x, true); ); + + SASSERT(x.lit() == null_literal); + SASSERT(value(alit) != l_undef); + unsigned index = 0; + for (; index < 2; ++index) { + if (x[index].var() == alit.var()) break; + } + if (index == 2) { + // literal is no longer watched. + // this can happen as both polarities of literals + // are put in watch lists and they are removed only + // one polarity at a time. + return l_undef; + } + SASSERT(x[index].var() == alit.var()); + + // find a literal to swap with: + for (unsigned i = 2; i < sz; ++i) { + literal lit2 = x[i]; + if (value(lit2) == l_undef) { + x.swap(index, i); + // unwatch_literal(alit, x); + watch_literal(lit2, x); + watch_literal(~lit2, x); + TRACE("ba", tout << "swap in: " << lit2 << " " << x << "\n";); + return l_undef; + } + } + if (index == 0) { + x.swap(0, 1); + } + // alit resides at index 1. + SASSERT(x[1].var() == alit.var()); + if (value(x[0]) == l_undef) { + bool p = parity(x, 1); + assign(x, p ? ~x[0] : x[0]); + } + else if (!parity(x, 0)) { + set_conflict(x, ~x[1]); + } + return inconsistent() ? l_false : l_true; + } + + // --------------------------- + // conflict resolution + + void ba_solver::normalize_active_coeffs() { + reset_active_var_set(); + unsigned i = 0, j = 0, sz = m_active_vars.size(); + for (; i < sz; ++i) { + bool_var v = m_active_vars[i]; + if (!m_active_var_set.contains(v) && get_coeff(v) != 0) { + m_active_var_set.insert(v); + if (j != i) { + m_active_vars[j] = m_active_vars[i]; + } + ++j; + } + } + m_active_vars.shrink(j); + } + + void ba_solver::inc_coeff(literal l, unsigned offset) { + SASSERT(offset > 0); + bool_var v = l.var(); + SASSERT(v != null_bool_var); + m_coeffs.reserve(v + 1, 0); + + int64_t coeff0 = m_coeffs[v]; + if (coeff0 == 0) { + m_active_vars.push_back(v); + } + + int64_t loffset = static_cast(offset); + int64_t inc = l.sign() ? -loffset : loffset; + int64_t coeff1 = inc + coeff0; + m_coeffs[v] = coeff1; + if (coeff1 > INT_MAX || coeff1 < INT_MIN) { + m_overflow = true; + return; + } + + if (coeff0 > 0 && inc < 0) { + inc_bound(std::max((int64_t)0, coeff1) - coeff0); + } + else if (coeff0 < 0 && inc > 0) { + inc_bound(coeff0 - std::min((int64_t)0, coeff1)); + } + int64_t lbound = static_cast(m_bound); + + // reduce coefficient to be no larger than bound. + if (coeff1 > lbound) { + m_coeffs[v] = lbound; + } + else if (coeff1 < 0 && -coeff1 > lbound) { + m_coeffs[v] = -lbound; + } + } + + int64_t ba_solver::get_coeff(bool_var v) const { + return m_coeffs.get(v, 0); + } + + unsigned ba_solver::get_abs_coeff(bool_var v) const { + int64_t c = get_coeff(v); + if (c < INT_MIN+1 || c > UINT_MAX) { + m_overflow = true; + return UINT_MAX; + } + return static_cast(std::abs(c)); + } + + int ba_solver::get_int_coeff(bool_var v) const { + int64_t c = m_coeffs.get(v, 0); + if (c < INT_MIN || c > INT_MAX) { + m_overflow = true; + return 0; + } + return static_cast(c); + } + + void ba_solver::inc_bound(int64_t i) { + if (i < INT_MIN || i > INT_MAX) { + m_overflow = true; + return; + } + int64_t new_bound = m_bound; + new_bound += i; + if (new_bound < 0) { + m_overflow = true; + } + else if (new_bound > UINT_MAX) { + m_overflow = true; + } + else { + m_bound = static_cast(new_bound); + } + } + + void ba_solver::reset_coeffs() { + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + m_coeffs[m_active_vars[i]] = 0; + } + m_active_vars.reset(); + } + + static bool _debug_conflict = false; + static literal _debug_consequent = null_literal; + static unsigned_vector _debug_var2position; + +// #define DEBUG_CODE(_x_) _x_ + + lbool ba_solver::resolve_conflict() { + if (0 == m_num_propagations_since_pop) { + return l_undef; + } + m_overflow = false; + reset_coeffs(); + m_num_marks = 0; + m_bound = 0; + literal consequent = s().m_not_l; + justification js = s().m_conflict; + TRACE("ba", tout << consequent << " " << js << "\n";); + m_conflict_lvl = s().get_max_lvl(consequent, js); + if (consequent != null_literal) { + consequent.neg(); + process_antecedent(consequent, 1); + } + literal_vector const& lits = s().m_trail; + unsigned idx = lits.size() - 1; + unsigned offset = 1; + DEBUG_CODE(active2pb(m_A);); + + do { + + if (m_overflow || offset > (1 << 12)) { + IF_VERBOSE(20, verbose_stream() << "offset: " << offset << "\n"; + DEBUG_CODE(active2pb(m_A); display(verbose_stream(), m_A););); + goto bail_out; + } + + if (offset == 0) { + goto process_next_resolvent; + } + + DEBUG_CODE(TRACE("sat_verbose", display(tout, m_A););); + TRACE("ba", tout << "process consequent: " << consequent << " : "; s().display_justification(tout, js) << "\n";); + SASSERT(offset > 0); + + DEBUG_CODE(justification2pb(js, consequent, offset, m_B);); + + if (_debug_conflict) { + IF_VERBOSE(0, + verbose_stream() << consequent << "\n"; + s().display_justification(verbose_stream(), js); + verbose_stream() << "\n";); + _debug_consequent = consequent; + } + switch(js.get_kind()) { + case justification::NONE: + SASSERT (consequent != null_literal); + inc_bound(offset); + break; + case justification::BINARY: + inc_bound(offset); + SASSERT (consequent != null_literal); + inc_coeff(consequent, offset); + process_antecedent(js.get_literal(), offset); + break; + case justification::TERNARY: + inc_bound(offset); + SASSERT (consequent != null_literal); + inc_coeff(consequent, offset); + process_antecedent(js.get_literal1(), offset); + process_antecedent(js.get_literal2(), offset); + break; + case justification::CLAUSE: { + inc_bound(offset); + clause & c = s().get_clause(js); + unsigned i = 0; + if (consequent != null_literal) { + inc_coeff(consequent, offset); + if (c[0] == consequent) { + i = 1; + } + else { + SASSERT(c[1] == consequent); + process_antecedent(c[0], offset); + i = 2; + } + } + unsigned sz = c.size(); + for (; i < sz; i++) + process_antecedent(c[i], offset); + break; + } + case justification::EXT_JUSTIFICATION: { + constraint& cnstr = index2constraint(js.get_ext_justification_idx()); + ++m_stats.m_num_resolves; + switch (cnstr.tag()) { + case card_t: { + card& c = cnstr.to_card(); + inc_bound(static_cast(offset) * c.k()); + process_card(c, offset); + break; + } + case pb_t: { + pb& p = cnstr.to_pb(); + m_lemma.reset(); + inc_bound(offset); + inc_coeff(consequent, offset); + get_antecedents(consequent, p, m_lemma); + TRACE("ba", display(tout, p, true); tout << m_lemma << "\n";); + if (_debug_conflict) { + verbose_stream() << consequent << " "; + verbose_stream() << "antecedents: " << m_lemma << "\n"; + } + for (literal l : m_lemma) process_antecedent(~l, offset); + break; + } + case xr_t: { + // jus.push_back(js); + m_lemma.reset(); + inc_bound(offset); + inc_coeff(consequent, offset); + get_xr_antecedents(consequent, idx, js, m_lemma); + for (literal l : m_lemma) process_antecedent(~l, offset); + break; + } + default: + UNREACHABLE(); + break; + } + break; + } + default: + UNREACHABLE(); + break; + } + + SASSERT(validate_lemma()); + + DEBUG_CODE( + active2pb(m_C); + VERIFY(validate_resolvent()); + m_A = m_C; + TRACE("ba", display(tout << "conflict: ", m_A););); + + cut(); + + process_next_resolvent: + + // find the next marked variable in the assignment stack + // + bool_var v; + while (true) { + consequent = lits[idx]; + v = consequent.var(); + if (s().is_marked(v)) break; + if (idx == 0) { + IF_VERBOSE(2, verbose_stream() << "did not find marked literal\n";); + goto bail_out; + } + SASSERT(idx > 0); + --idx; + } + + SASSERT(lvl(v) == m_conflict_lvl); + s().reset_mark(v); + --idx; + TRACE("sat_verbose", tout << "Unmark: v" << v << "\n";); + --m_num_marks; + js = s().m_justification[v]; + offset = get_abs_coeff(v); + if (offset > m_bound) { + int64_t bound64 = static_cast(m_bound); + m_coeffs[v] = (get_coeff(v) < 0) ? -bound64 : bound64; + offset = m_bound; + DEBUG_CODE(active2pb(m_A);); + } + SASSERT(value(consequent) == l_true); + } + while (m_num_marks > 0); + + DEBUG_CODE(for (bool_var i = 0; i < static_cast(s().num_vars()); ++i) SASSERT(!s().is_marked(i));); + SASSERT(validate_lemma()); + + normalize_active_coeffs(); + + if (!create_asserting_lemma()) { + goto bail_out; + } + + DEBUG_CODE(VERIFY(validate_conflict(m_lemma, m_A));); + + TRACE("ba", tout << m_lemma << "\n";); + + if (get_config().m_drat) { + svector ps; // TBD fill in + drat_add(m_lemma, ps); + } + + s().m_lemma.reset(); + s().m_lemma.append(m_lemma); + for (unsigned i = 1; i < m_lemma.size(); ++i) { + CTRACE("ba", s().is_marked(m_lemma[i].var()), tout << "marked: " << m_lemma[i] << "\n";); + s().mark(m_lemma[i].var()); + } + + return l_true; + + bail_out: + + m_overflow = false; + + while (m_num_marks > 0) { + bool_var v = lits[idx].var(); + if (s().is_marked(v)) { + s().reset_mark(v); + --m_num_marks; + } + if (idx == 0 && !_debug_conflict) { + _debug_conflict = true; + _debug_var2position.reserve(s().num_vars()); + for (unsigned i = 0; i < lits.size(); ++i) { + _debug_var2position[lits[i].var()] = i; + } + IF_VERBOSE(0, + active2pb(m_A); + uint64_t c = 0; + for (uint64_t c1 : m_A.m_coeffs) c += c1; + verbose_stream() << "sum of coefficients: " << c << "\n"; + display(verbose_stream(), m_A, true); + verbose_stream() << "conflicting literal: " << s().m_not_l << "\n";); + + for (literal l : lits) { + if (s().is_marked(l.var())) { + IF_VERBOSE(0, verbose_stream() << "missing mark: " << l << "\n";); + s().reset_mark(l.var()); + } + } + m_num_marks = 0; + resolve_conflict(); + } + --idx; + } + return l_undef; + } + + bool ba_solver::create_asserting_lemma() { + bool adjusted = false; + + adjust_conflict_level: + int64_t bound64 = m_bound; + int64_t slack = -bound64; + for (bool_var v : m_active_vars) { + slack += get_abs_coeff(v); + } + m_lemma.reset(); + m_lemma.push_back(null_literal); + unsigned num_skipped = 0; + int64_t asserting_coeff = 0; + for (unsigned i = 0; 0 <= slack && i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + int64_t coeff = get_coeff(v); + lbool val = value(v); + bool is_true = val == l_true; + bool append = coeff != 0 && val != l_undef && ((coeff < 0) == is_true); + if (append) { + literal lit(v, !is_true); + if (lvl(lit) == m_conflict_lvl) { + if (m_lemma[0] == null_literal) { + asserting_coeff = std::abs(coeff); + slack -= asserting_coeff; + m_lemma[0] = ~lit; + } + else { + ++num_skipped; + if (asserting_coeff < std::abs(coeff)) { + m_lemma[0] = ~lit; + slack -= (std::abs(coeff) - asserting_coeff); + asserting_coeff = std::abs(coeff); + } + } + } + else if (lvl(lit) < m_conflict_lvl) { + slack -= std::abs(coeff); + m_lemma.push_back(~lit); + } + } + } + if (slack >= 0) { + IF_VERBOSE(20, verbose_stream() << "(sat.card slack: " << slack << " skipped: " << num_skipped << ")\n";); + return false; + } + if (m_overflow) { + return false; + } + if (m_lemma[0] == null_literal) { + if (m_lemma.size() == 1) { + s().set_conflict(justification()); + return false; + } + return false; + unsigned old_level = m_conflict_lvl; + m_conflict_lvl = 0; + for (unsigned i = 1; i < m_lemma.size(); ++i) { + m_conflict_lvl = std::max(m_conflict_lvl, lvl(m_lemma[i])); + } + IF_VERBOSE(1, verbose_stream() << "(sat.backjump :new-level " << m_conflict_lvl << " :old-level " << old_level << ")\n";); + adjusted = true; + goto adjust_conflict_level; + } + if (!adjusted) { + active2card(); + } + return true; + } + + /* + \brief compute a cut for current resolvent. + */ + + void ba_solver::cut() { + + // bypass cut if there is a unit coefficient + for (bool_var v : m_active_vars) { + if (1 == get_abs_coeff(v)) return; + } + + unsigned g = 0; + + for (unsigned i = 0; g != 1 && i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + unsigned coeff = get_abs_coeff(v); + if (coeff == 0) { + continue; + } + if (m_bound < coeff) { + int64_t bound64 = m_bound; + if (get_coeff(v) > 0) { + m_coeffs[v] = bound64; + } + else { + m_coeffs[v] = -bound64; + } + coeff = m_bound; + } + SASSERT(0 < coeff && coeff <= m_bound); + if (g == 0) { + g = coeff; + } + else { + g = u_gcd(g, coeff); + } + } + + if (g >= 2) { + normalize_active_coeffs(); + for (bool_var v : m_active_vars) { + m_coeffs[v] /= static_cast(g); + } + m_bound = (m_bound + g - 1) / g; + ++m_stats.m_num_cut; + } + } + + void ba_solver::process_card(card& c, unsigned offset) { + literal lit = c.lit(); + SASSERT(c.k() <= c.size()); + SASSERT(lit == null_literal || value(lit) != l_undef); + SASSERT(0 < offset); + for (unsigned i = c.k(); i < c.size(); ++i) { + process_antecedent(c[i], offset); + } + for (unsigned i = 0; i < c.k(); ++i) { + inc_coeff(c[i], offset); + } + if (lit != null_literal) { + uint64_t offset1 = static_cast(offset) * c.k(); + if (offset1 > UINT_MAX) { + m_overflow = true; + } + if (value(lit) == l_true) { + process_antecedent(~lit, static_cast(offset1)); + } + else { + process_antecedent(lit, static_cast(offset1)); + } + } + } + + void ba_solver::process_antecedent(literal l, unsigned offset) { + SASSERT(value(l) == l_false); + bool_var v = l.var(); + unsigned level = lvl(v); + + if (level > 0 && !s().is_marked(v) && level == m_conflict_lvl) { + s().mark(v); + TRACE("sat_verbose", tout << "Mark: v" << v << "\n";); + ++m_num_marks; + if (_debug_conflict && _debug_consequent != null_literal && _debug_var2position[_debug_consequent.var()] < _debug_var2position[l.var()]) { + IF_VERBOSE(0, verbose_stream() << "antecedent " << l << " is above consequent in stack\n";); + } + } + inc_coeff(l, offset); + } + + literal ba_solver::get_asserting_literal(literal p) { + if (get_abs_coeff(p.var()) != 0) { + return p; + } + unsigned level = 0; + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + literal lit(v, get_coeff(v) < 0); + if (value(lit) == l_false && lvl(lit) > level) { + p = lit; + level = lvl(lit); + } + } + return p; + } + + ba_solver::ba_solver(): m_solver(0), m_lookahead(0), m_unit_walk(0), m_constraint_id(0), m_ba(*this), m_sort(m_ba) { + TRACE("ba", tout << this << "\n";); + m_num_propagations_since_pop = 0; + } + + ba_solver::~ba_solver() { + m_stats.reset(); + for (constraint* c : m_constraints) { + m_allocator.deallocate(c->obj_size(), c); + } + for (constraint* c : m_learned) { + m_allocator.deallocate(c->obj_size(), c); + } + } + + void ba_solver::add_at_least(bool_var v, literal_vector const& lits, unsigned k) { + literal lit = v == null_bool_var ? null_literal : literal(v, false); + add_at_least(lit, lits, k, false); + } + + ba_solver::constraint* ba_solver::add_at_least(literal lit, literal_vector const& lits, unsigned k, bool learned) { + if (k == 1 && lit == null_literal) { + literal_vector _lits(lits); + s().mk_clause(_lits.size(), _lits.c_ptr(), learned); + return 0; + } + if (!learned && clausify(lit, lits.size(), lits.c_ptr(), k)) { + return 0; + } + void * mem = m_allocator.allocate(card::get_obj_size(lits.size())); + card* c = new (mem) card(next_id(), lit, lits, k); + c->set_learned(learned); + add_constraint(c); + return c; + } + + void ba_solver::add_constraint(constraint* c) { + literal_vector lits(c->literals()); + if (c->learned()) { + m_learned.push_back(c); + } + else { + SASSERT(!m_solver || s().at_base_lvl()); + m_constraints.push_back(c); + } + literal lit = c->lit(); + if (c->learned() && m_solver && !s().at_base_lvl()) { + SASSERT(lit == null_literal); + // gets initialized after backjump. + m_constraint_to_reinit.push_back(c); + } + else if (lit == null_literal) { + init_watch(*c); + } + else { + if (m_solver) m_solver->set_external(lit.var()); + watch_literal(lit, *c); + watch_literal(~lit, *c); + } + SASSERT(c->well_formed()); + } + + + bool ba_solver::init_watch(constraint& c) { + if (inconsistent()) return false; + switch (c.tag()) { + case card_t: return init_watch(c.to_card()); + case pb_t: return init_watch(c.to_pb()); + case xr_t: return init_watch(c.to_xr()); + } + UNREACHABLE(); + return false; + } + + lbool ba_solver::add_assign(constraint& c, literal l) { + switch (c.tag()) { + case card_t: return add_assign(c.to_card(), l); + case pb_t: return add_assign(c.to_pb(), l); + case xr_t: return add_assign(c.to_xr(), l); + } + UNREACHABLE(); + return l_undef; + } + + ba_solver::constraint* ba_solver::add_pb_ge(literal lit, svector const& wlits, unsigned k, bool learned) { + bool units = true; + for (wliteral wl : wlits) units &= wl.first == 1; + if (k == 0 && lit == null_literal) { + return 0; + } + if (units || k == 1) { + literal_vector lits; + for (wliteral wl : wlits) lits.push_back(wl.second); + return add_at_least(lit, lits, k, learned); + } + void * mem = m_allocator.allocate(pb::get_obj_size(wlits.size())); + pb* p = new (mem) pb(next_id(), lit, wlits, k); + p->set_learned(learned); + add_constraint(p); + return p; + } + + void ba_solver::add_pb_ge(bool_var v, svector const& wlits, unsigned k) { + literal lit = v == null_bool_var ? null_literal : literal(v, false); + add_pb_ge(lit, wlits, k, false); + } + + void ba_solver::add_xr(literal_vector const& lits) { + add_xr(lits, false); + } + + ba_solver::constraint* ba_solver::add_xr(literal_vector const& lits, bool learned) { + void * mem = m_allocator.allocate(xr::get_obj_size(lits.size())); + xr* x = new (mem) xr(next_id(), lits); + x->set_learned(learned); + add_constraint(x); + return x; + } + + /* + \brief return true to keep watching literal. + */ + bool ba_solver::propagate(literal l, ext_constraint_idx idx) { + SASSERT(value(l) == l_true); + constraint& c = index2constraint(idx); + if (c.lit() != null_literal && l.var() == c.lit().var()) { + init_watch(c); + return true; + } + else if (c.lit() != null_literal && value(c.lit()) != l_true) { + // else if (c.lit() != null_literal && value(c.lit()) == l_false) { + return true; + } + else { + return l_undef != add_assign(c, ~l); + } + } + + double ba_solver::get_reward(card const& c, literal_occs_fun& literal_occs) const { + unsigned k = c.k(), slack = 0; + bool do_add = get_config().m_lookahead_reward == heule_schur_reward; + double to_add = do_add ? 0: 1; + for (literal l : c) { + switch (value(l)) { + case l_true: --k; if (k == 0) return 0; + case l_undef: + if (do_add) to_add += literal_occs(l); + ++slack; break; + case l_false: break; + } + } + if (k >= slack) return 1; + return pow(0.5, slack - k + 1) * to_add; + } + + double ba_solver::get_reward(pb const& c, literal_occs_fun& occs) const { + unsigned k = c.k(), slack = 0; + bool do_add = get_config().m_lookahead_reward == heule_schur_reward; + double to_add = do_add ? 0 : 1; + double undefs = 0; + for (wliteral wl : c) { + literal l = wl.second; + unsigned w = wl.first; + switch (value(l)) { + case l_true: if (k <= w) return 0; + case l_undef: + if (do_add) to_add += occs(l); + ++undefs; + slack += w; + break; // TBD multiplier factor on this + case l_false: break; + } + } + if (k >= slack || 0 == undefs) return 0; + double avg = slack / undefs; + return pow(0.5, (slack - k + 1)/avg) * to_add; + } + + double ba_solver::get_reward(literal l, ext_justification_idx idx, literal_occs_fun& occs) const { + constraint const& c = index2constraint(idx); + switch (c.tag()) { + case card_t: return get_reward(c.to_card(), occs); + case pb_t: return get_reward(c.to_pb(), occs); + case xr_t: return 0; + default: UNREACHABLE(); return 0; + } + } + + + void ba_solver::ensure_parity_size(bool_var v) { + if (m_parity_marks.size() <= static_cast(v)) { + m_parity_marks.resize(static_cast(v) + 1, 0); + } + } + + unsigned ba_solver::get_parity(bool_var v) { + return m_parity_marks.get(v, 0); + } + + void ba_solver::inc_parity(bool_var v) { + ensure_parity_size(v); + m_parity_marks[v]++; + } + + void ba_solver::reset_parity(bool_var v) { + ensure_parity_size(v); + m_parity_marks[v] = 0; + } + + /** + \brief perform parity resolution on xr premises. + The idea is to collect premises based on xr resolvents. + Variables that are repeated an even number of times cancel out. + */ + void ba_solver::get_xr_antecedents(literal l, unsigned index, justification js, literal_vector& r) { + unsigned level = lvl(l); + bool_var v = l.var(); + SASSERT(js.get_kind() == justification::EXT_JUSTIFICATION); + TRACE("ba", tout << l << ": " << js << "\n"; + for (unsigned i = 0; i <= index; ++i) tout << s().m_trail[i] << " "; tout << "\n"; + s().display_units(tout); + ); + + unsigned num_marks = 0; + unsigned count = 0; + while (true) { + TRACE("ba", tout << "process: " << l << "\n";); + ++count; + if (js.get_kind() == justification::EXT_JUSTIFICATION) { + constraint& c = index2constraint(js.get_ext_justification_idx()); + TRACE("ba", tout << c << "\n";); + if (!c.is_xr()) { + r.push_back(l); + } + else { + xr& x = c.to_xr(); + if (x[1].var() == l.var()) { + x.swap(0, 1); + } + SASSERT(x[0].var() == l.var()); + for (unsigned i = 1; i < x.size(); ++i) { + literal lit(value(x[i]) == l_true ? x[i] : ~x[i]); + inc_parity(lit.var()); + if (lvl(lit) == level) { + TRACE("ba", tout << "mark: " << lit << "\n";); + ++num_marks; + } + else { + m_parity_trail.push_back(lit); + } + } + } + } + else { + r.push_back(l); + } + bool found = false; + while (num_marks > 0) { + l = s().m_trail[index]; + v = l.var(); + unsigned n = get_parity(v); + if (n > 0) { + reset_parity(v); + num_marks -= n; + if (n % 2 == 1) { + found = true; + break; + } + } + --index; + } + if (!found) { + break; + } + --index; + js = s().m_justification[v]; + } + + // now walk the defined literals + + for (unsigned i = 0; i < m_parity_trail.size(); ++i) { + literal lit = m_parity_trail[i]; + if (get_parity(lit.var()) % 2 == 1) { + r.push_back(lit); + } + else { + // IF_VERBOSE(2, verbose_stream() << "skip even parity: " << lit << "\n";); + } + reset_parity(lit.var()); + } + m_parity_trail.reset(); + TRACE("ba", tout << r << "\n";); + } + + /** + \brief retrieve a sufficient set of literals from p that imply l. + + Find partition: + + - Ax + coeff*l + B*y >= k + - all literals in x are false. + - B < k + + Then x is an explanation for l + + */ + + bool ba_solver::assigned_above(literal above, literal below) { + unsigned l = lvl(above); + SASSERT(l == lvl(below)); + if (l == 0) return false; + unsigned start = s().m_scopes[l-1].m_trail_lim; + literal_vector const& lits = s().m_trail; + +#if 0 + IF_VERBOSE(10, verbose_stream() << "level " << l << " scope level " << s().scope_lvl() << " tail lim start: " + << start << " size of lits: " << lits.size() << " num scopes " << s().m_scopes.size() << "\n";); +#endif + + for (unsigned sz = lits.size(); sz-- > start; ) { + if (lits[sz] == above) return true; + if (lits[sz] == below) return false; + } + UNREACHABLE(); + return false; + } + + void ba_solver::get_antecedents(literal l, pb const& p, literal_vector& r) { + TRACE("ba", display(tout << l << " level: " << s().scope_lvl() << " ", p, true);); + SASSERT(p.lit() == null_literal || value(p.lit()) == l_true); + + if (p.lit() != null_literal) { + r.push_back(p.lit()); + } + + unsigned k = p.k(); + + if (_debug_conflict) { + IF_VERBOSE(0, display(verbose_stream(), p, true); + verbose_stream() << "literal: " << l << " value: " << value(l) << " num-watch: " << p.num_watch() << " slack: " << p.slack() << "\n";); + } + + if (value(l) == l_false) { + // The literal comes from a conflict. + // it is forced true, but assigned to false. + unsigned slack = 0; + for (wliteral wl : p) { + if (value(wl.second) != l_false) { + slack += wl.first; + } + } + SASSERT(slack < k); + for (wliteral wl : p) { + literal lit = wl.second; + if (lit != l && value(lit) == l_false) { + unsigned w = wl.first; + if (slack + w < k) { + slack += w; + } + else { + r.push_back(~lit); + } + } + } + } + else { + // comes from a unit propagation + SASSERT(value(l) == l_true); + unsigned coeff = 0, j = 0; + for (; j < p.size(); ++j) { + if (p[j].second == l) { + coeff = p[j].first; + break; + } + } + + ++j; + if (j < p.num_watch()) { + j = p.num_watch(); + } + CTRACE("ba", coeff == 0, display(tout << l << " coeff: " << coeff << "\n", p, true);); + + if (_debug_conflict) { + std::cout << "coeff " << coeff << "\n"; + } + + SASSERT(coeff > 0); + unsigned slack = p.max_sum() - coeff; + + // we need antecedents to be deeper than alit. + for (; j < p.size(); ++j) { + literal lit = p[j].second; + unsigned w = p[j].first; + if (l_false != value(lit)) { + // skip + } + else if (lvl(lit) > lvl(l)) { + // skip + } + else if (lvl(lit) == lvl(l) && assigned_above(~lit, l)) { + // skip + } + else if (slack + w < k) { + slack += w; + } + else { + r.push_back(~lit); + } + } + } + SASSERT(validate_unit_propagation(p, r, l)); + } + + bool ba_solver::is_extended_binary(ext_justification_idx idx, literal_vector & r) { + constraint const& c = index2constraint(idx); + switch (c.tag()) { + case card_t: { + card const& ca = c.to_card(); + if (ca.size() == ca.k() + 1 && ca.lit() == null_literal) { + r.reset(); + for (literal l : ca) r.push_back(l); + return true; + } + else { + return false; + } + } + default: + return false; + } + } + + void ba_solver::simplify(xr& x) { + // no-op + } + + void ba_solver::get_antecedents(literal l, card const& c, literal_vector& r) { + if (l == ~c.lit()) { + for (unsigned i = c.k() - 1; i < c.size(); ++i) { + VERIFY(value(c[i]) == l_false); + r.push_back(~c[i]); + } + return; + } + DEBUG_CODE( + bool found = false; + for (unsigned i = 0; !found && i < c.k(); ++i) { + found = c[i] == l; + } + SASSERT(found);); + + // IF_VERBOSE(0, if (_debug_conflict) verbose_stream() << "ante " << l << " " << c << "\n"); + VERIFY(c.lit() == null_literal || value(c.lit()) != l_false); + if (c.lit() != null_literal) r.push_back(value(c.lit()) == l_true ? c.lit() : ~c.lit()); + for (unsigned i = c.k(); i < c.size(); ++i) { + SASSERT(value(c[i]) == l_false); + r.push_back(~c[i]); + } + } + + void ba_solver::get_antecedents(literal l, xr const& x, literal_vector& r) { + if (x.lit() != null_literal) r.push_back(x.lit()); + // TRACE("ba", display(tout << l << " ", x, true);); + SASSERT(x.lit() == null_literal || value(x.lit()) == l_true); + SASSERT(x[0].var() == l.var() || x[1].var() == l.var()); + if (x[0].var() == l.var()) { + SASSERT(value(x[1]) != l_undef); + r.push_back(value(x[1]) == l_true ? x[1] : ~x[1]); + } + else { + SASSERT(value(x[0]) != l_undef); + r.push_back(value(x[0]) == l_true ? x[0] : ~x[0]); + } + for (unsigned i = 2; i < x.size(); ++i) { + SASSERT(value(x[i]) != l_undef); + r.push_back(value(x[i]) == l_true ? x[i] : ~x[i]); + } + } + + // ---------------------------- + // constraint generic methods + + void ba_solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) { + get_antecedents(l, index2constraint(idx), r); + } + + bool ba_solver::is_watched(literal lit, constraint const& c) const { + return get_wlist(~lit).contains(watched(c.index())); + } + + void ba_solver::unwatch_literal(literal lit, constraint& c) { + get_wlist(~lit).erase(watched(c.index())); + } + + void ba_solver::watch_literal(literal lit, constraint& c) { + if (c.is_pure() && lit == ~c.lit()) return; + get_wlist(~lit).push_back(watched(c.index())); + } + + void ba_solver::get_antecedents(literal l, constraint const& c, literal_vector& r) { + switch (c.tag()) { + case card_t: get_antecedents(l, c.to_card(), r); break; + case pb_t: get_antecedents(l, c.to_pb(), r); break; + case xr_t: get_antecedents(l, c.to_xr(), r); break; + default: UNREACHABLE(); break; + } + } + + void ba_solver::nullify_tracking_literal(constraint& c) { + if (c.lit() != null_literal) { + unwatch_literal(c.lit(), c); + unwatch_literal(~c.lit(), c); + c.nullify_literal(); + } + } + + void ba_solver::clear_watch(constraint& c) { + switch (c.tag()) { + case card_t: + clear_watch(c.to_card()); + break; + case pb_t: + clear_watch(c.to_pb()); + break; + case xr_t: + clear_watch(c.to_xr()); + break; + default: + UNREACHABLE(); + } + } + + void ba_solver::remove_constraint(constraint& c, char const* reason) { + IF_VERBOSE(21, display(verbose_stream() << "remove " << reason << " ", c, true);); + nullify_tracking_literal(c); + clear_watch(c); + c.set_removed(); + m_constraint_removed = true; + } + + // -------------------------------- + // validation + + bool ba_solver::validate_unit_propagation(constraint const& c, literal l) const { + return true; + switch (c.tag()) { + case card_t: return validate_unit_propagation(c.to_card(), l); + case pb_t: return validate_unit_propagation(c.to_pb(), l); + case xr_t: return true; + default: UNREACHABLE(); break; + } + return false; + } + + bool ba_solver::validate_conflict(constraint const& c) const { + return eval(c) == l_false; + } + + lbool ba_solver::eval(constraint const& c) const { + lbool v1 = c.lit() == null_literal ? l_true : value(c.lit()); + switch (c.tag()) { + case card_t: return eval(v1, eval(c.to_card())); + case pb_t: return eval(v1, eval(c.to_pb())); + case xr_t: return eval(v1, eval(c.to_xr())); + default: UNREACHABLE(); break; + } + return l_undef; + } + + lbool ba_solver::eval(model const& m, constraint const& c) const { + lbool v1 = c.lit() == null_literal ? l_true : value(m, c.lit()); + switch (c.tag()) { + case card_t: return eval(v1, eval(m, c.to_card())); + case pb_t: return eval(v1, eval(m, c.to_pb())); + case xr_t: return eval(v1, eval(m, c.to_xr())); + default: UNREACHABLE(); break; + } + return l_undef; + } + + lbool ba_solver::eval(lbool a, lbool b) const { + if (a == l_undef || b == l_undef) return l_undef; + return (a == b) ? l_true : l_false; + } + + lbool ba_solver::eval(card const& c) const { + unsigned trues = 0, undefs = 0; + for (literal l : c) { + switch (value(l)) { + case l_true: trues++; break; + case l_undef: undefs++; break; + default: break; + } + } + if (trues + undefs < c.k()) return l_false; + if (trues >= c.k()) return l_true; + return l_undef; + } + + lbool ba_solver::eval(model const& m, card const& c) const { + unsigned trues = 0, undefs = 0; + for (literal l : c) { + switch (value(m, l)) { + case l_true: trues++; break; + case l_undef: undefs++; break; + default: break; + } + } + if (trues + undefs < c.k()) return l_false; + if (trues >= c.k()) return l_true; + return l_undef; + } + + lbool ba_solver::eval(model const& m, pb const& p) const { + unsigned trues = 0, undefs = 0; + for (wliteral wl : p) { + switch (value(m, wl.second)) { + case l_true: trues += wl.first; break; + case l_undef: undefs += wl.first; break; + default: break; + } + } + if (trues + undefs < p.k()) return l_false; + if (trues >= p.k()) return l_true; + return l_undef; + } + + lbool ba_solver::eval(pb const& p) const { + unsigned trues = 0, undefs = 0; + for (wliteral wl : p) { + switch (value(wl.second)) { + case l_true: trues += wl.first; break; + case l_undef: undefs += wl.first; break; + default: break; + } + } + if (trues + undefs < p.k()) return l_false; + if (trues >= p.k()) return l_true; + return l_undef; + } + + lbool ba_solver::eval(xr const& x) const { + bool odd = false; + + for (auto l : x) { + switch (value(l)) { + case l_true: odd = !odd; break; + case l_false: break; + default: return l_undef; + } + } + return odd ? l_true : l_false; + } + + lbool ba_solver::eval(model const& m, xr const& x) const { + bool odd = false; + + for (auto l : x) { + switch (value(m, l)) { + case l_true: odd = !odd; break; + case l_false: break; + default: return l_undef; + } + } + return odd ? l_true : l_false; + } + + bool ba_solver::validate() { + if (!validate_watch_literals()) { + return false; + } + for (constraint* c : m_constraints) { + if (!validate_watched_constraint(*c)) + return false; + } + for (constraint* c : m_learned) { + if (!validate_watched_constraint(*c)) + return false; + } + return true; + } + + bool ba_solver::validate_watch_literals() const { + for (unsigned v = 0; v < s().num_vars(); ++v) { + literal lit(v, false); + if (lvl(lit) == 0) continue; + if (!validate_watch_literal(lit)) return false; + if (!validate_watch_literal(~lit)) return false; + } + return true; + } + + bool ba_solver::validate_watch_literal(literal lit) const { + if (lvl(lit) == 0) return true; + for (auto const & w : get_wlist(lit)) { + if (w.get_kind() == watched::EXT_CONSTRAINT) { + constraint const& c = index2constraint(w.get_ext_constraint_idx()); + if (!c.is_watching(~lit) && lit.var() != c.lit().var()) { + IF_VERBOSE(0, display(verbose_stream() << lit << " " << lvl(lit) << " is not watched in " << c << "\n", c, true);); + UNREACHABLE(); + return false; + } + } + } + return true; + } + + bool ba_solver::validate_watched_constraint(constraint const& c) const { + if (c.is_pb() && !validate_watch(c.to_pb(), null_literal)) { + return false; + } + if (c.lit() != null_literal && value(c.lit()) != l_true) return true; + SASSERT(c.lit() == null_literal || lvl(c.lit()) == 0 || (is_watched(c.lit(), c) && is_watched(~c.lit(), c))); + if (eval(c) == l_true) { + return true; + } + literal_vector lits(c.literals()); + for (literal l : lits) { + if (lvl(l) == 0) continue; + bool found = is_watched(l, c); + if (found != c.is_watching(l)) { + + IF_VERBOSE(0, + verbose_stream() << "Discrepancy of watched literal: " << l << " id: " << c.id() + << " clause: " << c << (found?" is watched, but shouldn't be":" not watched, but should be") << "\n"; + s().display_watch_list(verbose_stream() << l << ": ", get_wlist(l)) << "\n"; + s().display_watch_list(verbose_stream() << ~l << ": ", get_wlist(~l)) << "\n"; + verbose_stream() << "value: " << value(l) << " level: " << lvl(l) << "\n"; + display(verbose_stream(), c, true); + if (c.lit() != null_literal) verbose_stream() << value(c.lit()) << "\n";); + + IF_VERBOSE(0, s().display_watches(verbose_stream())); + + UNREACHABLE(); + exit(1); + return false; + } + } + return true; + } + + bool ba_solver::validate_watch(pb const& p, literal alit) const { + for (unsigned i = 0; i < p.size(); ++i) { + literal l = p[i].second; + if (l != alit && lvl(l) != 0 && is_watched(l, p) != (i < p.num_watch())) { + IF_VERBOSE(0, display(verbose_stream(), p, true); + verbose_stream() << "literal " << l << " at position " << i << " " << is_watched(l, p) << "\n";); + UNREACHABLE(); + return false; + } + } + unsigned slack = 0; + for (unsigned i = 0; i < p.num_watch(); ++i) { + slack += p[i].first; + } + if (slack != p.slack()) { + IF_VERBOSE(0, display(verbose_stream(), p, true);); + UNREACHABLE(); + return false; + } + return true; + } + + + /** + \brief Lex on (glue, size) + */ + struct constraint_glue_psm_lt { + bool operator()(ba_solver::constraint const * c1, ba_solver::constraint const * c2) const { + return + (c1->glue() < c2->glue()) || + (c1->glue() == c2->glue() && + (c1->psm() < c2->psm() || + (c1->psm() == c2->psm() && c1->size() < c2->size()))); + } + }; + + void ba_solver::update_psm(constraint& c) const { + unsigned r = 0; + switch (c.tag()) { + case card_t: + for (literal l : c.to_card()) { + if (s().m_phase[l.var()] == (l.sign() ? NEG_PHASE : POS_PHASE)) ++r; + } + break; + case pb_t: + for (wliteral l : c.to_pb()) { + if (s().m_phase[l.second.var()] == (l.second.sign() ? NEG_PHASE : POS_PHASE)) ++r; + } + break; + default: + break; + } + c.set_psm(r); + } + + void ba_solver::gc() { + if (m_learned.size() >= 2 * m_constraints.size()) { + for (auto & c : m_learned) update_psm(*c); + std::stable_sort(m_learned.begin(), m_learned.end(), constraint_glue_psm_lt()); + gc_half("glue-psm"); + cleanup_constraints(m_learned, true); + } + } + + void ba_solver::gc_half(char const* st_name) { + TRACE("ba", tout << "gc\n";); + unsigned sz = m_learned.size(); + unsigned new_sz = sz/2; + unsigned removed = 0; + for (unsigned i = new_sz; i < sz; i++) { + constraint* c = m_learned[i]; + if (!m_constraint_to_reinit.contains(c)) { + remove_constraint(*c, "gc"); + ++removed; + } + } + m_stats.m_num_gc += removed; + m_learned.shrink(new_sz); + IF_VERBOSE(2, verbose_stream() << "(sat-gc :strategy " << st_name << " :deleted " << removed << ")\n";); + + } + + lbool ba_solver::add_assign(card& c, literal alit) { + // literal is assigned to false. + unsigned sz = c.size(); + unsigned bound = c.k(); + TRACE("ba", tout << "assign: " << c.lit() << ": " << ~alit << "@" << lvl(~alit) << "\n";); + + SASSERT(0 < bound && bound <= sz); + if (bound == sz) { + if (c.lit() != null_literal && value(c.lit()) == l_undef) { + assign(c, ~c.lit()); + return inconsistent() ? l_false : l_true; + } + set_conflict(c, alit); + return l_false; + } + SASSERT(value(alit) == l_false); + VERIFY(c.lit() == null_literal || value(c.lit()) != l_false); + unsigned index = 0; + for (index = 0; index <= bound; ++index) { + if (c[index] == alit) { + break; + } + } + if (index == bound + 1) { + // literal is no longer watched. + return l_undef; + } + VERIFY(index <= bound); + VERIFY(c[index] == alit); + + // find a literal to swap with: + for (unsigned i = bound + 1; i < sz; ++i) { + literal lit2 = c[i]; + if (value(lit2) != l_false) { + c.swap(index, i); + watch_literal(lit2, c); + return l_undef; + } + } + + // conflict + if (bound != index && value(c[bound]) == l_false) { + TRACE("ba", tout << "conflict " << c[bound] << " " << alit << "\n";); + if (c.lit() != null_literal && value(c.lit()) == l_undef) { + if (index + 1 < bound) c.swap(index, bound - 1); + assign(c, ~c.lit()); + return inconsistent() ? l_false : l_true; + } + set_conflict(c, alit); + return l_false; + } + + if (index != bound) { + c.swap(index, bound); + } + + // TRACE("ba", tout << "no swap " << index << " " << alit << "\n";); + // there are no literals to swap with, + // prepare for unit propagation by swapping the false literal into + // position bound. Then literals in positions 0..bound-1 have to be + // assigned l_true. + + if (c.lit() != null_literal && value(c.lit()) == l_undef) { + return l_true; + } + + for (unsigned i = 0; i < bound; ++i) { + assign(c, c[i]); + } + + if (c.learned() && c.glue() > 2) { + unsigned glue; + if (s().num_diff_false_levels_below(c.size(), c.begin(), c.glue()-1, glue)) { + c.set_glue(glue); + } + } + + return inconsistent() ? l_false : l_true; + } + + void ba_solver::asserted(literal l) { + } + + + check_result ba_solver::check() { return CR_DONE; } + + void ba_solver::push() { + m_constraint_to_reinit_lim.push_back(m_constraint_to_reinit.size()); + } + + void ba_solver::pop(unsigned n) { + TRACE("sat_verbose", tout << "pop:" << n << "\n";); + unsigned new_lim = m_constraint_to_reinit_lim.size() - n; + m_constraint_to_reinit_last_sz = m_constraint_to_reinit_lim[new_lim]; + m_constraint_to_reinit_lim.shrink(new_lim); + m_num_propagations_since_pop = 0; + } + + void ba_solver::pop_reinit() { + unsigned sz = m_constraint_to_reinit_last_sz; + for (unsigned i = sz; i < m_constraint_to_reinit.size(); ++i) { + constraint* c = m_constraint_to_reinit[i]; + if (!init_watch(*c) && !s().at_base_lvl()) { + m_constraint_to_reinit[sz++] = c; + } + } + m_constraint_to_reinit.shrink(sz); + } + + + void ba_solver::simplify(constraint& c) { + SASSERT(s().at_base_lvl()); + switch (c.tag()) { + case card_t: + simplify(c.to_card()); + break; + case pb_t: + simplify(c.to_pb()); + break; + case xr_t: + simplify(c.to_xr()); + break; + default: + UNREACHABLE(); + } + } + + void ba_solver::simplify() { + if (!s().at_base_lvl()) s().pop_to_base_level(); + unsigned trail_sz; + do { + trail_sz = s().init_trail_size(); + m_simplify_change = false; + m_clause_removed = false; + m_constraint_removed = false; + for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) simplify(*m_constraints[i]); + for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) simplify(*m_learned[i]); + init_use_lists(); + remove_unused_defs(); + set_non_external(); + if (get_config().m_elim_vars) elim_pure(); + for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) subsumption(*m_constraints[i]); + for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) subsumption(*m_learned[i]); + cleanup_clauses(); + cleanup_constraints(); + update_pure(); + } + while (m_simplify_change || trail_sz < s().init_trail_size()); + + IF_VERBOSE(1, verbose_stream() << "(ba.simplify" + << " :vars " << s().num_vars() - trail_sz + << " :constraints " << m_constraints.size() + << " :lemmas " << m_learned.size() + << " :subsumes " << m_stats.m_num_bin_subsumes + + m_stats.m_num_clause_subsumes + + m_stats.m_num_pb_subsumes + << " :gc " << m_stats.m_num_gc + << ")\n";); + + // IF_VERBOSE(0, s().display(verbose_stream())); + // mutex_reduction(); + // if (s().m_clauses.size() < 80000) lp_lookahead_reduction(); + } + + /* + * ~lit does not occur in clauses + * ~lit is only in one constraint use list + * lit == C + * -> ignore assignemnts to ~lit for C + * + * ~lit does not occur in clauses + * lit is only in one constraint use list + * lit == C + * -> negate: ~lit == ~C + */ + void ba_solver::update_pure() { + // return; + for (constraint* cp : m_constraints) { + literal lit = cp->lit(); + if (lit != null_literal && + !cp->is_pure() && + value(lit) == l_undef && + get_wlist(~lit).size() == 1 && + m_clause_use_list.get(lit).empty()) { + clear_watch(*cp); + cp->negate(); + lit.neg(); + } + if (lit != null_literal && + !cp->is_pure() && + m_cnstr_use_list[(~lit).index()].size() == 1 && + get_wlist(lit).size() == 1 && + m_clause_use_list.get(~lit).empty()) { + cp->set_pure(); + get_wlist(~lit).erase(watched(cp->index())); // just ignore assignments to false + } + } + } + + void ba_solver::mutex_reduction() { + literal_vector lits; + for (unsigned v = 0; v < s().num_vars(); ++v) { + lits.push_back(literal(v, false)); + lits.push_back(literal(v, true)); + } + vector mutexes; + s().find_mutexes(lits, mutexes); + for (literal_vector& mux : mutexes) { + if (mux.size() > 2) { + IF_VERBOSE(1, verbose_stream() << "mux: " << mux << "\n";); + for (unsigned i = 0; i < mux.size(); ++i) mux[i].neg(); + add_at_least(null_literal, mux, mux.size() - 1, false); + } + } + } + + // ------------------------- + // sorting networks + literal ba_solver::ba_sort::mk_false() { + return ~mk_true(); + } + + literal ba_solver::ba_sort::mk_true() { + if (m_true == null_literal) { + bool_var v = s.s().mk_var(false, false); + m_true = literal(v, false); + s.s().mk_clause(1,&m_true); + } + VERIFY(m_true != null_literal); + return m_true; + } + + literal ba_solver::ba_sort::mk_not(literal l) { + return ~l; + } + + literal ba_solver::ba_sort::fresh(char const*) { + bool_var v = s.s().mk_var(false, true); + return literal(v, false); + } + + literal ba_solver::ba_sort::mk_max(literal l1, literal l2) { + VERIFY(l1 != null_literal); + VERIFY(l2 != null_literal); + if (l1 == m_true) return l1; + if (l2 == m_true) return l2; + if (l1 == ~m_true) return l2; + if (l2 == ~m_true) return l1; + literal max = fresh("max"); + s.s().mk_clause(~l1, max); + s.s().mk_clause(~l2, max); + s.s().mk_clause(~max, l1, l2); + return max; + } + + literal ba_solver::ba_sort::mk_min(literal l1, literal l2) { + return ~mk_max(~l1, ~l2); + } + + void ba_solver::ba_sort::mk_clause(unsigned n, literal const* lits) { + m_lits.reset(); + m_lits.append(n, lits); + s.s().mk_clause(n, m_lits.c_ptr()); + } + + std::ostream& ba_solver::ba_sort::pp(std::ostream& out, literal l) const { + return out << l; + } + + + // ------------------------------- + // set literals equivalent + + bool ba_solver::set_root(literal l, literal r) { + if (s().is_assumption(l.var())) { + return false; + } + m_root_vars.reserve(s().num_vars(), false); + for (unsigned i = m_roots.size(); i < 2 * s().num_vars(); ++i) { + m_roots.push_back(to_literal(i)); + } + m_roots[l.index()] = r; + m_roots[(~l).index()] = ~r; + m_root_vars[l.var()] = true; + return true; + } + + void ba_solver::flush_roots() { + if (m_roots.empty()) return; + + // validate(); + m_visited.resize(s().num_vars()*2, false); + m_constraint_removed = false; + for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) + flush_roots(*m_constraints[i]); + for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) + flush_roots(*m_learned[i]); + cleanup_constraints(); + // validate(); + } + + void ba_solver::recompile(constraint& c) { + if (c.id() == _bad_id) { + IF_VERBOSE(0, display(verbose_stream() << "recompile\n", c, true);); + } + switch (c.tag()) { + case card_t: + recompile(c.to_card()); + break; + case pb_t: + recompile(c.to_pb()); + break; + case xr_t: + NOT_IMPLEMENTED_YET(); + break; + default: + UNREACHABLE(); + } + } + + void ba_solver::recompile(card& c) { + SASSERT(c.lit() == null_literal || is_watched(c.lit(), c)); + + // pre-condition is that the literals, except c.lit(), in c are unwatched. + if (c.id() == _bad_id) std::cout << "recompile: " << c << "\n"; + // IF_VERBOSE(0, verbose_stream() << c << "\n";); + m_weights.resize(2*s().num_vars(), 0); + for (literal l : c) { + ++m_weights[l.index()]; + } + unsigned k = c.k(); + bool all_units = true; + unsigned sz = c.size(); + unsigned_vector coeffs; + literal_vector lits; + unsigned j = 0; + for (unsigned i = 0; i < sz && 0 < k; ++i) { + literal l = c[i]; + unsigned w = m_weights[l.index()]; + unsigned w2 = m_weights[(~l).index()]; + if (w == 0 || w < w2) { + continue; + } + else if (k <= w2) { + k = 0; + break; + } + else { + SASSERT(w2 <= w && w2 < k); + k -= w2; + w -= w2; + m_weights[(~l).index()] = 0; + m_weights[l.index()] = 0; + if (w == 0) { + continue; + } + else { + all_units &= (w == 1); + coeffs.push_back(w); + c[j++] = l; + } + } + } + sz = j; + + // clear weights + for (literal l : c) { + m_weights[l.index()] = 0; + m_weights[(~l).index()] = 0; + } + + if (k == 0 && c.lit() == null_literal) { + remove_constraint(c, "recompiled to true"); + return; + } + + if (k == 1 && c.lit() == null_literal) { + literal_vector lits(sz, c.literals().c_ptr()); + s().mk_clause(sz, lits.c_ptr(), c.learned()); + remove_constraint(c, "recompiled to clause"); + return; + } + + if (sz == 0) { + if (c.lit() == null_literal) { + if (k > 0) { + s().mk_clause(0, nullptr, true); + } + } + else if (k > 0) { + literal lit = ~c.lit(); + s().mk_clause(1, &lit, c.learned()); + } + else { + literal lit = c.lit(); + s().mk_clause(1, &lit, c.learned()); + } + remove_constraint(c, "recompiled to clause"); + return; + } + if (all_units && sz < k) { + // IF_VERBOSE(0, verbose_stream() << "all units " << sz << " " << k << "\n"); + if (c.lit() == null_literal) { + s().mk_clause(0, nullptr, true); + } + else { + literal lit = ~c.lit(); + s().mk_clause(1, &lit, c.learned()); + } + remove_constraint(c, "recompiled to clause"); + return; + } + // IF_VERBOSE(0, verbose_stream() << "csz: " << c.size() << " ck:" << c.k() << " sz:" << sz << " k:" << k << "\n"); + VERIFY(!all_units || c.size() - c.k() >= sz - k); + c.set_size(sz); + c.set_k(k); + + if (all_units && clausify(c)) { + return; + } + + if (!all_units) { + TRACE("ba", tout << "replacing by pb: " << c << "\n";); + m_wlits.reset(); + for (unsigned i = 0; i < sz; ++i) { + m_wlits.push_back(wliteral(coeffs[i], c[i])); + } + literal root = c.lit(); + remove_constraint(c, "recompiled to pb"); + add_pb_ge(root, m_wlits, k, c.learned()); + } + else { + if (c.lit() == null_literal || value(c.lit()) == l_true) { + init_watch(c); + } + SASSERT(c.lit() == null_literal || is_watched(c.lit(), c)); + SASSERT(c.well_formed()); + } + } + + bool ba_solver::clausify(literal lit, unsigned n, literal const* lits, unsigned k) { + return false; + bool is_def = lit != null_literal; + if ((!is_def || !s().was_eliminated(lit)) && + !std::any_of(lits, lits + n, [&](literal l) { return s().was_eliminated(l); })) { + literal def_lit = m_sort.ge(is_def, k, n, lits); + if (is_def) { + s().mk_clause(~lit, def_lit); + s().mk_clause( lit, ~def_lit); + } + return true; + } + return false; + } + + bool ba_solver::clausify(xr& x) { + return false; + } + + bool ba_solver::clausify(card& c) { + return false; + if (get_config().m_card_solver) + return false; + + // + // TBD: conditions for when to clausify are TBD and + // handling of conditional cardinality as well. + // + if (!c.learned() && clausify(c.lit(), c.size(), c.begin(), c.k())) { + IF_VERBOSE(0, verbose_stream() << "clausify " << c << "\n";); + // compiled + } + remove_constraint(c, "recompiled to clauses"); + return true; + } + + bool ba_solver::clausify(pb& p) { + return false; + if (get_config().m_card_solver) + return false; + + bool ok = !p.learned(); + bool is_def = p.lit() != null_literal; + for (wliteral wl : p) { + ok &= !s().was_eliminated(wl.second); + } + ok &= !is_def || !s().was_eliminated(p.lit()); + if (!ok) { + remove_constraint(p, "recompiled to clauses"); + return true; + } + + if (is_cardinality(p, m_lemma)) { + literal lit = m_sort.ge(is_def, p.k(), m_lemma.size(), m_lemma.c_ptr()); + if (is_def) { + s().mk_clause(p.lit(), ~lit); + s().mk_clause(~p.lit(), lit); + } + remove_constraint(p, "recompiled to clauses"); + return true; + } + return false; + } + + bool ba_solver::is_cardinality(pb const& p, literal_vector& lits) { + lits.reset(); + p.size(); + for (wliteral wl : p) { + if (lits.size() > 2*p.size() + wl.first) { + return false; + } + for (unsigned i = 0; i < wl.first; ++i) { + lits.push_back(wl.second); + } + } + return true; + } + + void ba_solver::split_root(constraint& c) { + switch (c.tag()) { + case card_t: split_root(c.to_card()); break; + case pb_t: split_root(c.to_pb()); break; + case xr_t: NOT_IMPLEMENTED_YET(); break; + } + } + + void ba_solver::flush_roots(constraint& c) { + if (c.lit() != null_literal && !is_watched(c.lit(), c)) { + watch_literal(c.lit(), c); + watch_literal(~c.lit(), c); + } + SASSERT(c.lit() == null_literal || is_watched(c.lit(), c)); + bool found = c.lit() != null_literal && m_root_vars[c.lit().var()]; + for (unsigned i = 0; !found && i < c.size(); ++i) { + found = m_root_vars[c.get_lit(i).var()]; + } + if (!found) return; + clear_watch(c); + + // this could create duplicate literals + for (unsigned i = 0; i < c.size(); ++i) { + literal lit = m_roots[c.get_lit(i).index()]; + c.set_lit(i, lit); + } + + literal root = c.lit(); + if (root != null_literal && m_roots[root.index()] != root) { + root = m_roots[root.index()]; + nullify_tracking_literal(c); + c.update_literal(root); + watch_literal(root, c); + watch_literal(~root, c); + } + + bool found_dup = false; + bool found_root = false; + for (unsigned i = 0; i < c.size(); ++i) { + literal l = c.get_lit(i); + if (is_marked(l)) { + found_dup = true; + break; + } + else { + mark_visited(l); + mark_visited(~l); + } + } + for (unsigned i = 0; i < c.size(); ++i) { + literal l = c.get_lit(i); + unmark_visited(l); + unmark_visited(~l); + found_root |= l.var() == root.var(); + } + + if (found_root) { + split_root(c); + c.negate(); + split_root(c); + remove_constraint(c, "flush roots"); + } + else if (found_dup) { + recompile(c); + } + else { + if (c.lit() == null_literal || value(c.lit()) != l_undef) init_watch(c); + SASSERT(c.well_formed()); + } + } + + unsigned ba_solver::get_num_unblocked_bin(literal l) { + return s().m_simplifier.num_nonlearned_bin(l); + } + + /* + \brief garbage collection. + This entails + - finding pure literals, + - setting literals that are not used in the extension to non-external. + - subsumption + - resolution + - blocked literals + */ + void ba_solver::init_use_lists() { + m_visited.resize(s().num_vars()*2, false); + m_clause_use_list.init(s().num_vars()); + m_cnstr_use_list.reset(); + m_cnstr_use_list.resize(2*s().num_vars()); + for (clause* c : s().m_clauses) { + if (!c->frozen()) + m_clause_use_list.insert(*c); + } + for (constraint* cp : m_constraints) { + literal lit = cp->lit(); + if (lit != null_literal) { + m_cnstr_use_list[lit.index()].push_back(cp); + m_cnstr_use_list[(~lit).index()].push_back(cp); + } + switch (cp->tag()) { + case card_t: { + card& c = cp->to_card(); + for (literal l : c) { + m_cnstr_use_list[l.index()].push_back(&c); + if (lit != null_literal) m_cnstr_use_list[(~l).index()].push_back(&c); + } + break; + } + case pb_t: { + pb& p = cp->to_pb(); + for (wliteral wl : p) { + literal l = wl.second; + m_cnstr_use_list[l.index()].push_back(&p); + if (lit != null_literal) m_cnstr_use_list[(~l).index()].push_back(&p); + } + break; + } + case xr_t: { + xr& x = cp->to_xr(); + for (literal l : x) { + m_cnstr_use_list[l.index()].push_back(&x); + m_cnstr_use_list[(~l).index()].push_back(&x); + } + break; + } + } + } + } + + void ba_solver::remove_unused_defs() { + // remove constraints where indicator literal isn't used. + for (constraint* cp : m_constraints) { + constraint& c = *cp; + literal lit = c.lit(); + switch (c.tag()) { + case card_t: + case pb_t: { + if (lit != null_literal && + value(lit) == l_undef && + use_count(lit) == 1 && + use_count(~lit) == 1 && + get_num_unblocked_bin(lit) == 0 && + get_num_unblocked_bin(~lit) == 0) { + remove_constraint(c, "unused def"); + } + break; + } + default: + break; + } + } + } + + unsigned ba_solver::set_non_external() { + sat_simplifier_params p(s().m_params); + // set variables to be non-external if they are not used in theory constraints. + unsigned ext = 0; + bool incremental_mode = s().get_config().m_incremental && !p.override_incremental(); + incremental_mode |= s().tracking_assumptions(); + for (unsigned v = 0; !incremental_mode && v < s().num_vars(); ++v) { + literal lit(v, false); + if (s().is_external(v) && + m_cnstr_use_list[lit.index()].empty() && + m_cnstr_use_list[(~lit).index()].empty()) { + s().set_non_external(v); + ++ext; + } + } + // ensure that lemmas use only non-eliminated variables + for (constraint* cp : m_learned) { + constraint& c = *cp; + if (c.was_removed()) continue; + SASSERT(c.lit() == null_literal); + for (unsigned i = 0; i < c.size(); ++i) { + bool_var v = c.get_lit(i).var(); + if (s().was_eliminated(v)) { + remove_constraint(c, "contains eliminated var"); + break; + } + } + } + return ext; + } + + bool ba_solver::elim_pure(literal lit) { + if (value(lit) == l_undef && !m_cnstr_use_list[lit.index()].empty() && + use_count(~lit) == 0 && get_num_unblocked_bin(~lit) == 0) { + IF_VERBOSE(100, verbose_stream() << "pure literal: " << lit << "\n";); + s().assign(lit, justification()); + return true; + } + return false; + } + + unsigned ba_solver::elim_pure() { + // eliminate pure literals + unsigned pure_literals = 0; + for (unsigned v = 0; v < s().num_vars(); ++v) { + literal lit(v, false); + if (value(v) != l_undef) continue; + if (m_cnstr_use_list[lit.index()].empty() && + m_cnstr_use_list[(~lit).index()].empty()) continue; + + if (elim_pure(lit) || elim_pure(~lit)) { + ++pure_literals; + } + } + return pure_literals; + } + + void ba_solver::subsumption(constraint& cnstr) { + if (cnstr.was_removed()) return; + switch (cnstr.tag()) { + case card_t: { + card& c = cnstr.to_card(); + if (c.k() > 1) subsumption(c); + break; + } + case pb_t: { + pb& p = cnstr.to_pb(); + if (p.k() > 1) subsumption(p); + break; + } + default: + break; + } + } + + void ba_solver::cleanup_clauses() { + if (!m_clause_removed) return; + // version in simplify first clears + // all watch literals, then reinserts them. + // this ensures linear time cleanup. + clause_vector::iterator it = s().m_clauses.begin(); + clause_vector::iterator end = s().m_clauses.end(); + clause_vector::iterator it2 = it; + for (; it != end; ++it) { + clause* c = *it; + if (c->was_removed() && s().can_delete(*c)) { + s().detach_clause(*c); + s().del_clause(*c); + } + else { + if (it2 != it) { + *it2 = *it; + } + ++it2; + } + } + s().m_clauses.set_end(it2); + } + + void ba_solver::cleanup_constraints() { + if (!m_constraint_removed) return; + cleanup_constraints(m_constraints, false); + cleanup_constraints(m_learned, true); + m_constraint_removed = false; + } + + void ba_solver::cleanup_constraints(ptr_vector& cs, bool learned) { + ptr_vector::iterator it = cs.begin(); + ptr_vector::iterator it2 = it; + ptr_vector::iterator end = cs.end(); + for (; it != end; ++it) { + constraint& c = *(*it); + if (c.was_removed()) { + clear_watch(c); + nullify_tracking_literal(c); + m_allocator.deallocate(c.obj_size(), &c); + } + else if (learned && !c.learned()) { + m_constraints.push_back(&c); + } + else { + if (it != it2) { + *it2 = *it; + } + ++it2; + } + } + cs.set_end(it2); + } + + /* + \brief subsumption between two cardinality constraints + - A >= k subsumes A + B >= k' for k' <= k + - A + A' >= k subsumes A + B >= k' for k' + |A'| <= k + - A + lit >= k self subsumes A + ~lit + B >= k' into A + B >= k' for k' <= k + - version that generalizes self-subsumption to more than one literal + A + ~L + B >= k' => A + B >= k' if A + A' + L >= k and k' + |L| + |A'| <= k + */ + bool ba_solver::subsumes(card& c1, card& c2, literal_vector & comp) { + if (c2.lit() != null_literal) return false; + + unsigned c2_exclusive = 0; + unsigned common = 0; + comp.reset(); + for (literal l : c2) { + if (is_marked(l)) { + ++common; + } + else if (is_marked(~l)) { + comp.push_back(l); + } + else { + ++c2_exclusive; + } + } + + unsigned c1_exclusive = c1.size() - common - comp.size(); + return c1_exclusive + c2.k() + comp.size() <= c1.k(); + } + + /* + \brief L + A >= k subsumes L + C if |A| < k + A + L + B >= k self-subsumes A + ~L + C >= 1 + if k + 1 - |B| - |C| - |L| > 0 + */ + bool ba_solver::subsumes(card& c1, clause& c2, bool& self) { + unsigned common = 0, complement = 0, c2_exclusive = 0; + self = false; + + for (literal l : c2) { + if (is_marked(l)) { + ++common; + } + else if (is_marked(~l)) { + ++complement; + } + else { + ++c2_exclusive; + } + } + unsigned c1_exclusive = c1.size() - common - complement; + if (complement > 0 && c1.k() + 1 > c1_exclusive + c2_exclusive + common) { + self = true; + return true; + } + return c1.size() - common < c1.k(); + } + + /* + \brief Ax >= k subsumes By >= k' if + all coefficients in A are <= B and k >= k' + */ + bool ba_solver::subsumes(pb const& p1, pb_base const& p2) { + if (p1.k() < p2.k() || p1.size() > p2.size()) return false; + unsigned num_sub = 0; + for (unsigned i = 0; i < p2.size(); ++i) { + literal l = p2.get_lit(i); + if (is_marked(l) && m_weights[l.index()] <= p2.get_coeff(i)) { + ++num_sub; + } + } + return num_sub == p1.size(); + } + + void ba_solver::subsumes(pb& p1, literal lit) { + for (constraint* c : m_cnstr_use_list[lit.index()]) { + if (c == &p1 || c->was_removed()) continue; + bool s = false; + switch (c->tag()) { + case card_t: + s = subsumes(p1, c->to_card()); + break; + case pb_t: + s = subsumes(p1, c->to_pb()); + break; + default: + break; + } + if (s) { + ++m_stats.m_num_pb_subsumes; + p1.set_learned(false); + remove_constraint(*c, "subsumed"); + } + } + } + + literal ba_solver::get_min_occurrence_literal(card const& c) { + unsigned occ_count = UINT_MAX; + literal lit = null_literal; + for (literal l : c) { + unsigned occ_count1 = m_cnstr_use_list[l.index()].size(); + if (occ_count1 < occ_count) { + lit = l; + occ_count = occ_count1; + } + } + return lit; + } + + void ba_solver::card_subsumption(card& c1, literal lit) { + literal_vector slit; + for (constraint* c : m_cnstr_use_list[lit.index()]) { + if (!c->is_card() || c == &c1 || c->was_removed()) { + continue; + } + card& c2 = c->to_card(); + + SASSERT(c1.index() != c2.index()); + if (subsumes(c1, c2, slit)) { + if (slit.empty()) { + TRACE("ba", tout << "subsume cardinality\n" << c1.index() << ":" << c1 << "\n" << c2.index() << ":" << c2 << "\n";); + remove_constraint(c2, "subsumed"); + ++m_stats.m_num_pb_subsumes; + c1.set_learned(false); + } + else { + TRACE("ba", tout << "self subsume cardinality\n";); + IF_VERBOSE(11, + verbose_stream() << "self-subsume cardinality\n"; + verbose_stream() << c1 << "\n"; + verbose_stream() << c2 << "\n";); + clear_watch(c2); + unsigned j = 0; + for (unsigned i = 0; i < c2.size(); ++i) { + if (!is_marked(~c2[i])) { + c2[j++] = c2[i]; + } + } + c2.set_size(j); + init_watch(c2); + m_simplify_change = true; + } + } + } + } + + void ba_solver::clause_subsumption(card& c1, literal lit, clause_vector& removed_clauses) { + SASSERT(!c1.was_removed()); + clause_use_list& occurs = m_clause_use_list.get(lit); + clause_use_list::iterator it = occurs.mk_iterator(); + while (!it.at_end()) { + clause& c2 = it.curr(); + bool self; + if (!c2.was_removed() && subsumes(c1, c2, self)) { + if (self) { + // self-subsumption is TBD + } + else { + TRACE("ba", tout << "remove\n" << c1 << "\n" << c2 << "\n";); + removed_clauses.push_back(&c2); + ++m_stats.m_num_clause_subsumes; + c1.set_learned(false); + } + } + it.next(); + } + } + + void ba_solver::binary_subsumption(card& c1, literal lit) { + if (c1.k() + 1 != c1.size()) return; + SASSERT(is_marked(lit)); + SASSERT(!c1.was_removed()); + watch_list & wlist = get_wlist(~lit); + watch_list::iterator it = wlist.begin(); + watch_list::iterator it2 = it; + watch_list::iterator end = wlist.end(); + for (; it != end; ++it) { + watched w = *it; + if (w.is_binary_clause() && is_marked(w.get_literal())) { + ++m_stats.m_num_bin_subsumes; + IF_VERBOSE(10, verbose_stream() << c1 << " subsumes (" << lit << " " << w.get_literal() << ")\n";); + if (!w.is_learned()) { + c1.set_learned(false); + } + } + else { + if (it != it2) { + *it2 = *it; + } + ++it2; + } + } + wlist.set_end(it2); + } + + void ba_solver::subsumption(card& c1) { + if (c1.was_removed() || c1.lit() != null_literal) { + return; + } + clause_vector removed_clauses; + for (literal l : c1) mark_visited(l); + for (unsigned i = 0; i < std::min(c1.size(), c1.k() + 1); ++i) { + literal lit = c1[i]; + card_subsumption(c1, lit); + clause_subsumption(c1, lit, removed_clauses); + binary_subsumption(c1, lit); + } + for (literal l : c1) unmark_visited(l); + m_clause_removed |= !removed_clauses.empty(); + for (clause *c : removed_clauses) { + c->set_removed(true); + m_clause_use_list.erase(*c); + } + } + + void ba_solver::subsumption(pb& p1) { + if (p1.was_removed() || p1.lit() != null_literal) { + return; + } + for (wliteral l : p1) { + SASSERT(m_weights[l.second.index()] == 0); + m_weights.setx(l.second.index(), l.first, 0); + mark_visited(l.second); + } + for (unsigned i = 0; i < p1.num_watch(); ++i) { + subsumes(p1, p1[i].second); + } + for (wliteral l : p1) { + m_weights[l.second.index()] = 0; + unmark_visited(l.second); + } + } + + void ba_solver::clauses_modifed() {} + + lbool ba_solver::get_phase(bool_var v) { return l_undef; } + + /* + \brief lit <=> conjunction of unconstrained lits + */ + void ba_solver::assert_unconstrained(literal lit, literal_vector const& lits) { + if (lit == null_literal) { + for (literal l : lits) { + if (value(l) == l_undef) { + s().assign(l, justification()); + } + } + } + else { + // add clauses for: lit <=> conjunction of undef literals + SASSERT(value(lit) == l_undef); + literal_vector cl; + cl.push_back(lit); + for (literal l : lits) { + if (value(l) == l_undef) { + s().mk_clause(~lit, l); + cl.push_back(~l); + } + } + s().mk_clause(cl); + } + } + + extension* ba_solver::copy(solver* s) { + ba_solver* result = alloc(ba_solver); + result->set_solver(s); + copy_core(result, false); + return result; + } + + extension* ba_solver::copy(lookahead* s, bool learned) { + ba_solver* result = alloc(ba_solver); + result->set_lookahead(s); + copy_core(result, learned); + return result; + } + + void ba_solver::copy_core(ba_solver* result, bool learned) { + copy_constraints(result, m_constraints); + if (learned) copy_constraints(result, m_learned); + } + + void ba_solver::copy_constraints(ba_solver* result, ptr_vector const& constraints) { + literal_vector lits; + svector wlits; + for (constraint* cp : constraints) { + switch (cp->tag()) { + case card_t: { + card const& c = cp->to_card(); + lits.reset(); + for (literal l : c) lits.push_back(l); + result->add_at_least(c.lit(), lits, c.k(), c.learned()); + break; + } + case pb_t: { + pb const& p = cp->to_pb(); + wlits.reset(); + for (wliteral w : p) { + wlits.push_back(w); + } + result->add_pb_ge(p.lit(), wlits, p.k(), p.learned()); + break; + } + case xr_t: { + xr const& x = cp->to_xr(); + lits.reset(); + for (literal l : x) lits.push_back(l); + result->add_xr(lits, x.learned()); + break; + } + default: + UNREACHABLE(); + } + } + } + + void ba_solver::init_use_list(ext_use_list& ul) { + ul.init(s().num_vars()); + for (constraint const* cp : m_constraints) { + ext_constraint_idx idx = cp->index(); + if (cp->lit() != null_literal) { + ul.insert(cp->lit(), idx); + ul.insert(~cp->lit(), idx); + } + switch (cp->tag()) { + case card_t: { + card const& c = cp->to_card(); + for (literal l : c) { + ul.insert(l, idx); + } + break; + } + case pb_t: { + pb const& p = cp->to_pb(); + for (wliteral w : p) { + ul.insert(w.second, idx); + } + break; + } + case xr_t: { + xr const& x = cp->to_xr(); + for (literal l : x) { + ul.insert(l, idx); + ul.insert(~l, idx); + } + break; + } + default: + UNREACHABLE(); + } + } + } + + // + // literal is used in a clause (C or l), it + // it occurs negatively in constraint c. + // all literals in C are marked + // + bool ba_solver::is_blocked(literal l, ext_constraint_idx idx) { + constraint const& c = index2constraint(idx); + simplifier& sim = s().m_simplifier; + if (c.lit() != null_literal) return false; + switch (c.tag()) { + case card_t: { + card const& ca = c.to_card(); + unsigned weight = 0; + for (literal l2 : ca) { + if (sim.is_marked(~l2)) ++weight; + } + return weight >= ca.k(); + } + case pb_t: { + pb const& p = c.to_pb(); + unsigned weight = 0, offset = 0; + for (wliteral l2 : p) { + if (~l2.second == l) { + offset = l2.first; + break; + } + } + SASSERT(offset != 0); + for (wliteral l2 : p) { + if (sim.is_marked(~l2.second)) { + weight += std::min(offset, l2.first); + } + } + return weight >= p.k(); + } + default: + break; + } + return false; + } + + + void ba_solver::find_mutexes(literal_vector& lits, vector & mutexes) { + literal_set slits(lits); + bool change = false; + for (constraint* cp : m_constraints) { + if (!cp->is_card()) continue; + card const& c = cp->to_card(); + if (c.size() == c.k() + 1) { + literal_vector mux; + for (literal lit : c) { + if (slits.contains(~lit)) { + mux.push_back(~lit); + } + } + if (mux.size() <= 1) { + continue; + } + + for (literal m : mux) { + slits.remove(m); + } + change = true; + mutexes.push_back(mux); + } + } + if (!change) return; + lits.reset(); + for (literal l : slits) { + lits.push_back(l); + } + } + + void ba_solver::display(std::ostream& out, ineq& ineq, bool values) const { + for (unsigned i = 0; i < ineq.m_lits.size(); ++i) { + out << ineq.m_coeffs[i] << "*" << ineq.m_lits[i] << " "; + if (values) out << value(ineq.m_lits[i]) << " "; + } + out << ">= " << ineq.m_k << "\n"; + } + + void ba_solver::display(std::ostream& out, xr const& x, bool values) const { + out << "xr: "; + for (literal l : x) { + out << l; + if (values) { + out << "@(" << value(l); + if (value(l) != l_undef) { + out << ":" << lvl(l); + } + out << ") "; + } + else { + out << " "; + } + } + out << "\n"; + } + + void ba_solver::display_lit(std::ostream& out, literal lit, unsigned sz, bool values) const { + if (lit != null_literal) { + if (values) { + out << lit << "[" << sz << "]"; + out << "@(" << value(lit); + if (value(lit) != l_undef) { + out << ":" << lvl(lit); + } + out << "): "; + } + else { + out << lit << " == "; + } + } + } + + void ba_solver::display(std::ostream& out, card const& c, bool values) const { + display_lit(out, c.lit(), c.size(), values); + for (unsigned i = 0; i < c.size(); ++i) { + literal l = c[i]; + out << l; + if (values) { + out << "@(" << value(l); + if (value(l) != l_undef) { + out << ":" << lvl(l); + } + out << ") "; + } + else { + out << " "; + } + } + out << ">= " << c.k() << "\n"; + } + + std::ostream& ba_solver::display(std::ostream& out) const { + for (constraint const* c : m_constraints) { + out << (*c) << "\n"; + } + if (!m_learned.empty()) { + out << "learned:\n"; + } + for (constraint const* c : m_learned) { + out << (*c) << "\n"; + } + return out; + } + + std::ostream& ba_solver::display_justification(std::ostream& out, ext_justification_idx idx) const { + return out << index2constraint(idx); + } + + void ba_solver::display(std::ostream& out, constraint const& c, bool values) const { + switch (c.tag()) { + case card_t: display(out, c.to_card(), values); break; + case pb_t: display(out, c.to_pb(), values); break; + case xr_t: display(out, c.to_xr(), values); break; + default: UNREACHABLE(); break; + } + } + + void ba_solver::collect_statistics(statistics& st) const { + st.update("ba propagations", m_stats.m_num_propagations); + st.update("ba conflicts", m_stats.m_num_conflicts); + st.update("ba resolves", m_stats.m_num_resolves); + st.update("ba cuts", m_stats.m_num_cut); + st.update("ba gc", m_stats.m_num_gc); + } + + bool ba_solver::validate_unit_propagation(card const& c, literal alit) const { + (void) alit; + if (c.lit() != null_literal && value(c.lit()) != l_true) return false; + for (unsigned i = c.k(); i < c.size(); ++i) { + if (value(c[i]) != l_false) return false; + } + return true; + } + + bool ba_solver::validate_unit_propagation(pb const& p, literal alit) const { + if (p.lit() != null_literal && value(p.lit()) != l_true) { + return false; + } + + unsigned sum = 0; + TRACE("ba", display(tout << "validate: " << alit << "\n", p, true);); + for (wliteral wl : p) { + literal lit = wl.second; + lbool val = value(lit); + if (val != l_false && lit != alit) { + sum += wl.first; + } + } + return sum < p.k(); + } + + bool ba_solver::validate_unit_propagation(pb const& p, literal_vector const& r, literal alit) const { + // all elements of r are true, + for (literal l : r) { + if (value(l) != l_true) { + IF_VERBOSE(0, verbose_stream() << "value of " << l << " is " << value(l) << "\n"; + display(verbose_stream(), p, true);); + return false; + } + if (value(alit) == l_true && lvl(l) > lvl(alit)) { + IF_VERBOSE(0, + verbose_stream() << "level of premise " << l << " is " << lvl(l) << "\n"; + verbose_stream() << "level of asserting literal " << alit << " is " << lvl(alit) << "\n"; + display(verbose_stream(), p, true);); + return false; + } + // if (value(alit) == l_true && lvl(l) == lvl(alit)) { + // std::cout << "same level " << alit << " " << l << "\n"; + // } + } + // the sum of elements not in r or alit add up to less than k. + unsigned sum = 0; + // + // a*x + b*alit + c*r >= k + // sum a < k + // val(r) = false + // hence alit has to be true. + for (wliteral wl : p) { + literal lit = wl.second; + if (lit != alit && !r.contains(~lit)) { + sum += wl.first; + } + } + if (sum >= p.k()) { + IF_VERBOSE(0, + verbose_stream() << "sum is " << sum << " >= " << p.k() << "\n"; + display(verbose_stream(), p, true); + verbose_stream() << "id: " << p.id() << "\n"; + sum = 0; + for (wliteral wl : p) sum += wl.first; + verbose_stream() << "overall sum " << sum << "\n"; + verbose_stream() << "asserting literal: " << alit << "\n"; + verbose_stream() << "reason: " << r << "\n";); + return false; + } + for (wliteral wl : p) { + if (alit == wl.second) { + return true; + } + } + IF_VERBOSE(0, verbose_stream() << alit << " not found among literals\n";); + return false; + } + + bool ba_solver::validate_unit_propagation(xr const& x, literal alit) const { + if (value(x.lit()) != l_true) return false; + for (unsigned i = 1; i < x.size(); ++i) { + if (value(x[i]) == l_undef) return false; + } + return true; + } + + bool ba_solver::validate_lemma() { + int64_t bound64 = m_bound; + int64_t val = -bound64; + reset_active_var_set(); + for (bool_var v : m_active_vars) { + if (m_active_var_set.contains(v)) continue; + int64_t coeff = get_coeff(v); + if (coeff == 0) continue; + m_active_var_set.insert(v); + literal lit(v, false); + if (coeff < 0 && value(lit) != l_true) { + val -= coeff; + } + else if (coeff > 0 && value(lit) != l_false) { + val += coeff; + } + } + CTRACE("ba", val >= 0, active2pb(m_A); display(tout, m_A);); + return val < 0; + } + + void ba_solver::reset_active_var_set() { + while (!m_active_var_set.empty()) m_active_var_set.erase(); + } + + void ba_solver::active2pb(ineq& p) { + reset_active_var_set(); + p.reset(m_bound); + for (bool_var v : m_active_vars) { + if (m_active_var_set.contains(v)) continue; + int64_t coeff = get_coeff(v); + if (coeff == 0) continue; + m_active_var_set.insert(v); + literal lit(v, coeff < 0); + p.m_lits.push_back(lit); + p.m_coeffs.push_back(std::abs(coeff)); + } + } + + ba_solver::constraint* ba_solver::active2constraint() { + reset_active_var_set(); + m_wlits.reset(); + uint64_t sum = 0; + if (m_bound == 1) return 0; + if (m_overflow) return 0; + + for (bool_var v : m_active_vars) { + int coeff = get_int_coeff(v); + if (m_active_var_set.contains(v) || coeff == 0) continue; + m_active_var_set.insert(v); + literal lit(v, coeff < 0); + m_wlits.push_back(wliteral(get_abs_coeff(v), lit)); + sum += get_abs_coeff(v); + } + + if (m_overflow || sum >= UINT_MAX/2) { + return 0; + } + else { + return add_pb_ge(null_literal, m_wlits, m_bound, true); + } + } + + /* + Chai Kuhlmann: + + a1*l1 + ... + a_n*l_n >= k + s.t. + a1 >= a2 >= .. >= a_n + + let m be such that + + sum_{i = 1}^{m-1} a_i < k <= sum_{i = 1}^{m} + + then + + l1 + ... + l_n >= m + + furthermore, for the largest n' <= n, such that + + sum_{i = n'+1}^n a_i + sum_{i = 1}^{m-1} a_i < k + + then + + l1 + ... + l_n' >= m + + */ + struct compare_wlit { + bool operator()(ba_solver::wliteral l1, ba_solver::wliteral l2) const { + return l1.first > l2.first; + } + }; + + + ba_solver::constraint* ba_solver::active2card() { + normalize_active_coeffs(); + m_wlits.reset(); + for (bool_var v : m_active_vars) { + int coeff = get_int_coeff(v); + m_wlits.push_back(std::make_pair(get_abs_coeff(v), literal(v, coeff < 0))); + } + std::sort(m_wlits.begin(), m_wlits.end(), compare_wlit()); + unsigned k = 0; + uint64_t sum = 0, sum0 = 0; + for (wliteral wl : m_wlits) { + if (sum >= m_bound) break; + sum0 = sum; + sum += wl.first; + ++k; + } + if (k == 1) { + return 0; + } + while (!m_wlits.empty()) { + wliteral wl = m_wlits.back(); + if (wl.first + sum0 >= m_bound) break; + m_wlits.pop_back(); + sum0 += wl.first; + } + + unsigned slack = 0; + unsigned max_level = 0; + unsigned num_max_level = 0; + for (wliteral wl : m_wlits) { + if (value(wl.second) != l_false) ++slack; + unsigned level = lvl(wl.second); + if (level > max_level) { + max_level = level; + num_max_level = 1; + } + else if (max_level == level) { + ++num_max_level; + } + } + if (m_overflow) return 0; + + if (slack >= k) { +#if 0 + return active2constraint(); + active2pb(m_A); + std::cout << "not asserting\n"; + display(std::cout, m_A, true); +#endif + return 0; + } + + // produce asserting cardinality constraint + literal_vector lits; + for (wliteral wl : m_wlits) { + lits.push_back(wl.second); + } + constraint* c = add_at_least(null_literal, lits, k, true); + + if (c) { + // IF_VERBOSE(0, verbose_stream() << *c << "\n";); + lits.reset(); + for (wliteral wl : m_wlits) { + if (value(wl.second) == l_false) lits.push_back(wl.second); + } + unsigned glue = s().num_diff_levels(lits.size(), lits.c_ptr()); + c->set_glue(glue); + } + return c; + } + + + void ba_solver::justification2pb(justification const& js, literal lit, unsigned offset, ineq& ineq) { + switch (js.get_kind()) { + case justification::NONE: + ineq.reset(offset); + ineq.push(lit, offset); + break; + case justification::BINARY: + ineq.reset(offset); + ineq.push(lit, offset); + ineq.push(js.get_literal(), offset); + break; + case justification::TERNARY: + ineq.reset(offset); + ineq.push(lit, offset); + ineq.push(js.get_literal1(), offset); + ineq.push(js.get_literal2(), offset); + break; + case justification::CLAUSE: { + ineq.reset(offset); + clause & c = s().get_clause(js); + for (literal l : c) ineq.push(l, offset); + break; + } + case justification::EXT_JUSTIFICATION: { + ext_justification_idx index = js.get_ext_justification_idx(); + constraint& cnstr = index2constraint(index); + switch (cnstr.tag()) { + case card_t: { + card& c = cnstr.to_card(); + ineq.reset(offset*c.k()); + for (literal l : c) ineq.push(l, offset); + if (c.lit() != null_literal) ineq.push(~c.lit(), offset*c.k()); + break; + } + case pb_t: { + pb& p = cnstr.to_pb(); + ineq.reset(p.k()); + for (wliteral wl : p) ineq.push(wl.second, wl.first); + if (p.lit() != null_literal) ineq.push(~p.lit(), p.k()); + break; + } + case xr_t: { + xr& x = cnstr.to_xr(); + literal_vector ls; + get_antecedents(lit, x, ls); + ineq.reset(offset); + for (literal l : ls) ineq.push(~l, offset); + literal lxr = x.lit(); + if (lxr != null_literal) ineq.push(~lxr, offset); + break; + } + default: + UNREACHABLE(); + break; + } + break; + } + default: + UNREACHABLE(); + break; + } + } + + + // validate that m_A & m_B implies m_C + + bool ba_solver::validate_resolvent() { + return true; + u_map coeffs; + uint64_t k = m_A.m_k + m_B.m_k; + for (unsigned i = 0; i < m_A.m_lits.size(); ++i) { + uint64_t coeff = m_A.m_coeffs[i]; + SASSERT(!coeffs.contains(m_A.m_lits[i].index())); + coeffs.insert(m_A.m_lits[i].index(), coeff); + } + for (unsigned i = 0; i < m_B.m_lits.size(); ++i) { + uint64_t coeff1 = m_B.m_coeffs[i], coeff2; + literal lit = m_B.m_lits[i]; + if (coeffs.find((~lit).index(), coeff2)) { + if (coeff1 == coeff2) { + coeffs.remove((~lit).index()); + k += coeff1; + } + else if (coeff1 < coeff2) { + coeffs.insert((~lit).index(), coeff2 - coeff1); + k += coeff1; + } + else { + SASSERT(coeff2 < coeff1); + coeffs.remove((~lit).index()); + coeffs.insert(lit.index(), coeff1 - coeff2); + k += coeff2; + } + } + else if (coeffs.find(lit.index(), coeff2)) { + coeffs.insert(lit.index(), coeff1 + coeff2); + } + else { + coeffs.insert(lit.index(), coeff1); + } + } + // C is above the sum of A and B + for (unsigned i = 0; i < m_C.m_lits.size(); ++i) { + literal lit = m_C.m_lits[i]; + uint64_t coeff; + if (coeffs.find(lit.index(), coeff)) { + if (coeff > m_C.m_coeffs[i] && m_C.m_coeffs[i] < m_C.m_k) { + goto violated; + } + coeffs.remove(lit.index()); + } + } + if (!coeffs.empty()) goto violated; + if (m_C.m_k > k) goto violated; + SASSERT(coeffs.empty()); + SASSERT(m_C.m_k <= k); + return true; + + violated: + // last ditch effort by translating to SAT. + solver s0(s().m_params, s().rlimit()); + u_map translation; + literal l1 = translate_to_sat(s0, translation, m_A); + if (l1 == null_literal) return true; + literal l2 = translate_to_sat(s0, translation, m_B); + if (l2 == null_literal) return true; + ineq notC = negate(m_B); + literal l3 = translate_to_sat(s0, translation, notC); + if (l3 == null_literal) return true; + s0.assign(l1, justification()); + s0.assign(l2, justification()); + s0.assign(l3, justification()); + lbool is_sat = s0.check(); + TRACE("ba", s0.display(tout << "trying sat encoding");); + if (is_sat == l_false) return true; + + IF_VERBOSE(0, + display(verbose_stream(), m_A); + display(verbose_stream(), m_B); + display(verbose_stream(), m_C); + for (auto& e : coeffs) { + verbose_stream() << to_literal(e.m_key) << ": " << e.m_value << "\n"; + }); + + UNREACHABLE(); + return false; + } + + /** + \brief translate PB inequality to SAT formula. + */ + literal ba_solver::translate_to_sat(solver& s, u_map& translation, ineq const& pb) { + SASSERT(pb.m_k > 0); + if (pb.m_lits.size() > 1) { + ineq a, b; + a.reset(pb.m_k); + b.reset(pb.m_k); + for (unsigned i = 0; i < pb.m_lits.size()/2; ++i) { + a.push(pb.m_lits[i], pb.m_coeffs[i]); + } + for (unsigned i = pb.m_lits.size()/2; i < pb.m_lits.size(); ++i) { + b.push(pb.m_lits[i], pb.m_coeffs[i]); + } + bool_var v = s.mk_var(); + literal lit(v, false); + literal_vector lits; + lits.push_back(~lit); + push_lit(lits, translate_to_sat(s, translation, a)); + push_lit(lits, translate_to_sat(s, translation, b)); + push_lit(lits, translate_to_sat(s, translation, a, b)); + s.mk_clause(lits); + return lit; + } + if (pb.m_coeffs[0] >= pb.m_k) { + return translate_to_sat(s, translation, pb.m_lits[0]); + } + else { + return null_literal; + } + } + + /* + \brief encode the case where Sum(a) >= k-1 & Sum(b) >= 1 \/ ... \/ Sum(a) >= 1 & Sum(b) >= k-1 + */ + literal ba_solver::translate_to_sat(solver& s, u_map& translation, ineq& a, ineq& b) { + uint64_t k0 = a.m_k; + literal_vector lits; + for (unsigned k = 1; k < a.m_k - 1; ++k) { + a.m_k = k; b.m_k = k0 - k; + literal lit1 = translate_to_sat(s, translation, a); + literal lit2 = translate_to_sat(s, translation, b); + if (lit1 != null_literal && lit2 != null_literal) { + bool_var v = s.mk_var(); + literal lit(v, false); + s.mk_clause(~lit, lit1); + s.mk_clause(~lit, lit2); + lits.push_back(lit); + } + } + a.m_k = k0; + b.m_k = k0; + switch (lits.size()) { + case 0: return null_literal; + case 1: return lits[0]; + default: { + bool_var v = s.mk_var(); + literal lit(v, false); + lits.push_back(~lit); + s.mk_clause(lits); + return lit; + } + } + } + + literal ba_solver::translate_to_sat(solver& s, u_map& translation, literal lit) { + bool_var v; + if (!translation.find(lit.var(), v)) { + v = s.mk_var(); + translation.insert(lit.var(), v); + } + return literal(v, lit.sign()); + } + + ba_solver::ineq ba_solver::negate(ineq const& a) const { + ineq result; + uint64_t sum = 0; + for (unsigned i = 0; i < a.m_lits.size(); ++i) { + result.push(~a.m_lits[i], a.m_coeffs[i]); + sum += a.m_coeffs[i]; + } + SASSERT(sum >= a.m_k + 1); + result.m_k = sum + 1 - a.m_k; + return result; + } + + void ba_solver::push_lit(literal_vector& lits, literal lit) { + if (lit != null_literal) { + lits.push_back(lit); + } + } + + bool ba_solver::validate_conflict(literal_vector const& lits, ineq& p) { + for (literal l : lits) { + if (value(l) != l_false) { + TRACE("ba", tout << "literal " << l << " is not false\n";); + return false; + } + if (!p.m_lits.contains(l)) { + TRACE("ba", tout << "lemma contains literal " << l << " not in inequality\n";); + return false; + } + } + uint64_t value = 0; + for (unsigned i = 0; i < p.m_lits.size(); ++i) { + uint64_t coeff = p.m_coeffs[i]; + if (!lits.contains(p.m_lits[i])) { + value += coeff; + } + } + CTRACE("ba", value >= p.m_k, tout << "slack: " << value << " bound " << p.m_k << "\n"; + display(tout, p); + tout << lits << "\n";); + return value < p.m_k; + } + + bool ba_solver::check_model(model const& m) const { + bool ok = true; + for (constraint const* c : m_constraints) { + if (c->is_pure() && c->lit() != null_literal && m[c->lit().var()] == (c->lit().sign() ? l_true : l_false)) { + continue; + } + switch (eval(m, *c)) { + case l_false: + IF_VERBOSE(0, verbose_stream() << "failed checking " << c->id() << ": " << *c << "\n";); + ok = false; + break; + case l_true: + break; + case l_undef: + IF_VERBOSE(0, verbose_stream() << "undef " << c->id() << ": " << *c << "\n";); + break; + } + } + return ok; + } + + +}; + diff --git a/src/sat/ba_solver.h b/src/sat/ba_solver.h new file mode 100644 index 000000000..07e7cfd58 --- /dev/null +++ b/src/sat/ba_solver.h @@ -0,0 +1,531 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + ba_solver.h + +Abstract: + + Cardinality extensions, + Pseudo Booleans, + Xors + +Author: + + Nikolaj Bjorner (nbjorner) 2017-01-30 + +Revision History: + +--*/ +#ifndef BA_SOLVER_H_ +#define BA_SOLVER_H_ + +#include "sat/sat_extension.h" +#include "sat/sat_solver.h" +#include "sat/sat_lookahead.h" +#include "sat/sat_unit_walk.h" +#include "util/scoped_ptr_vector.h" +#include "util/sorting_network.h" + +namespace sat { + + class ba_solver : public extension { + + friend class local_search; + + struct stats { + unsigned m_num_propagations; + unsigned m_num_conflicts; + unsigned m_num_resolves; + unsigned m_num_bin_subsumes; + unsigned m_num_clause_subsumes; + unsigned m_num_pb_subsumes; + unsigned m_num_cut; + unsigned m_num_gc; + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + }; + + public: + enum tag_t { + card_t, + pb_t, + xr_t + }; + + class card; + class pb; + class xr; + + class constraint { + protected: + tag_t m_tag; + bool m_removed; + literal m_lit; + literal m_watch; + unsigned m_glue; + unsigned m_psm; + unsigned m_size; + size_t m_obj_size; + bool m_learned; + unsigned m_id; + bool m_pure; // is the constraint pure (only positive occurrences) + public: + constraint(tag_t t, unsigned id, literal l, unsigned sz, size_t osz): + m_tag(t), m_removed(false), m_lit(l), m_watch(null_literal), m_glue(0), m_psm(0), m_size(sz), m_obj_size(osz), m_learned(false), m_id(id), m_pure(false) {} + ext_constraint_idx index() const { return reinterpret_cast(this); } + unsigned id() const { return m_id; } + tag_t tag() const { return m_tag; } + literal lit() const { return m_lit; } + unsigned size() const { return m_size; } + void set_size(unsigned sz) { SASSERT(sz <= m_size); m_size = sz; } + void update_literal(literal l) { m_lit = l; } + bool was_removed() const { return m_removed; } + void set_removed() { m_removed = true; } + void nullify_literal() { m_lit = null_literal; } + unsigned glue() const { return m_glue; } + void set_glue(unsigned g) { m_glue = g; } + unsigned psm() const { return m_psm; } + void set_psm(unsigned p) { m_psm = p; } + void set_learned(bool f) { m_learned = f; } + bool learned() const { return m_learned; } + bool is_watched() const { return m_watch == m_lit && m_lit != null_literal; } + void set_watch() { m_watch = m_lit; } + void clear_watch() { m_watch = null_literal; } + bool is_clear() const { return m_watch == null_literal && m_lit != null_literal; } + bool is_pure() const { return m_pure; } + void set_pure() { m_pure = true; } + + size_t obj_size() const { return m_obj_size; } + card& to_card(); + pb& to_pb(); + xr& to_xr(); + card const& to_card() const; + pb const& to_pb() const; + xr const& to_xr() const; + bool is_card() const { return m_tag == card_t; } + bool is_pb() const { return m_tag == pb_t; } + bool is_xr() const { return m_tag == xr_t; } + + virtual bool is_watching(literal l) const { UNREACHABLE(); return false; }; + virtual literal_vector literals() const { UNREACHABLE(); return literal_vector(); } + virtual void swap(unsigned i, unsigned j) { UNREACHABLE(); } + virtual literal get_lit(unsigned i) const { UNREACHABLE(); return null_literal; } + virtual void set_lit(unsigned i, literal l) { UNREACHABLE(); } + virtual bool well_formed() const { return true; } + virtual void negate() { UNREACHABLE(); } + }; + + friend std::ostream& operator<<(std::ostream& out, constraint const& c); + + // base class for pb and cardinality constraints + class pb_base : public constraint { + protected: + unsigned m_k; + public: + pb_base(tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k): constraint(t, id, l, sz, osz), m_k(k) {} + virtual void set_k(unsigned k) { m_k = k; } + virtual unsigned get_coeff(unsigned i) const { UNREACHABLE(); return 0; } + unsigned k() const { return m_k; } + virtual bool well_formed() const; + }; + + class card : public pb_base { + literal m_lits[0]; + public: + static size_t get_obj_size(unsigned num_lits) { return sizeof(card) + num_lits * sizeof(literal); } + card(unsigned id, literal lit, literal_vector const& lits, unsigned k); + literal operator[](unsigned i) const { return m_lits[i]; } + literal& operator[](unsigned i) { return m_lits[i]; } + literal const* begin() const { return m_lits; } + literal const* end() const { return static_cast(m_lits) + m_size; } + virtual void negate(); + virtual void swap(unsigned i, unsigned j) { std::swap(m_lits[i], m_lits[j]); } + virtual literal_vector literals() const { return literal_vector(m_size, m_lits); } + virtual bool is_watching(literal l) const; + virtual literal get_lit(unsigned i) const { return m_lits[i]; } + virtual void set_lit(unsigned i, literal l) { m_lits[i] = l; } + virtual unsigned get_coeff(unsigned i) const { return 1; } + }; + + + typedef std::pair wliteral; + + class pb : public pb_base { + unsigned m_slack; + unsigned m_num_watch; + unsigned m_max_sum; + wliteral m_wlits[0]; + void update_max_sum(); + public: + static size_t get_obj_size(unsigned num_lits) { return sizeof(pb) + num_lits * sizeof(wliteral); } + pb(unsigned id, literal lit, svector const& wlits, unsigned k); + literal lit() const { return m_lit; } + wliteral operator[](unsigned i) const { return m_wlits[i]; } + wliteral& operator[](unsigned i) { return m_wlits[i]; } + wliteral const* begin() const { return m_wlits; } + wliteral const* end() const { return begin() + m_size; } + + unsigned slack() const { return m_slack; } + void set_slack(unsigned s) { m_slack = s; } + unsigned num_watch() const { return m_num_watch; } + unsigned max_sum() const { return m_max_sum; } + void set_num_watch(unsigned s) { m_num_watch = s; } + bool is_cardinality() const; + virtual void negate(); + virtual void set_k(unsigned k) { m_k = k; update_max_sum(); } + virtual void swap(unsigned i, unsigned j) { std::swap(m_wlits[i], m_wlits[j]); } + virtual literal_vector literals() const { literal_vector lits; for (auto wl : *this) lits.push_back(wl.second); return lits; } + virtual bool is_watching(literal l) const; + virtual literal get_lit(unsigned i) const { return m_wlits[i].second; } + virtual void set_lit(unsigned i, literal l) { m_wlits[i].second = l; } + virtual unsigned get_coeff(unsigned i) const { return m_wlits[i].first; } + }; + + class xr : public constraint { + literal m_lits[0]; + public: + static size_t get_obj_size(unsigned num_lits) { return sizeof(xr) + num_lits * sizeof(literal); } + xr(unsigned id, literal_vector const& lits); + literal operator[](unsigned i) const { return m_lits[i]; } + literal const* begin() const { return m_lits; } + literal const* end() const { return begin() + m_size; } + virtual void negate() { m_lits[0].neg(); } + virtual void swap(unsigned i, unsigned j) { std::swap(m_lits[i], m_lits[j]); } + virtual bool is_watching(literal l) const; + virtual literal_vector literals() const { return literal_vector(size(), begin()); } + virtual literal get_lit(unsigned i) const { return m_lits[i]; } + virtual void set_lit(unsigned i, literal l) { m_lits[i] = l; } + virtual bool well_formed() const; + }; + + + protected: + + struct ineq { + literal_vector m_lits; + svector m_coeffs; + uint64_t m_k; + ineq(): m_k(0) {} + void reset(uint64_t k) { m_lits.reset(); m_coeffs.reset(); m_k = k; } + void push(literal l, uint64_t c) { m_lits.push_back(l); m_coeffs.push_back(c); } + }; + + solver* m_solver; + lookahead* m_lookahead; + unit_walk* m_unit_walk; + stats m_stats; + small_object_allocator m_allocator; + + + ptr_vector m_constraints; + ptr_vector m_learned; + ptr_vector m_constraint_to_reinit; + unsigned_vector m_constraint_to_reinit_lim; + unsigned m_constraint_to_reinit_last_sz; + unsigned m_constraint_id; + + // conflict resolution + unsigned m_num_marks; + unsigned m_conflict_lvl; + svector m_coeffs; + svector m_active_vars; + unsigned m_bound; + tracked_uint_set m_active_var_set; + literal_vector m_lemma; + literal_vector m_skipped; + unsigned m_num_propagations_since_pop; + unsigned_vector m_parity_marks; + literal_vector m_parity_trail; + + unsigned_vector m_pb_undef; + + struct ba_sort { + typedef sat::literal pliteral; + typedef sat::literal_vector pliteral_vector; + + ba_solver& s; + pliteral m_true; + pliteral_vector m_lits; + + + ba_sort(ba_solver& s): s(s), m_true(null_literal) {} + pliteral mk_false(); + pliteral mk_true(); + pliteral mk_not(pliteral l); + pliteral fresh(char const*); + pliteral mk_max(pliteral l1, pliteral l2); + pliteral mk_min(pliteral l1, pliteral l2); + void mk_clause(unsigned n, literal const* lits); + std::ostream& pp(std::ostream& out, pliteral l) const; + }; + ba_sort m_ba; + psort_nw m_sort; + + void ensure_parity_size(bool_var v); + unsigned get_parity(bool_var v); + void inc_parity(bool_var v); + void reset_parity(bool_var v); + + solver& s() const { return *m_solver; } + + + // simplification routines + + svector m_visited; + vector> m_cnstr_use_list; + use_list m_clause_use_list; + bool m_simplify_change; + bool m_clause_removed; + bool m_constraint_removed; + literal_vector m_roots; + svector m_root_vars; + unsigned_vector m_weights; + svector m_wlits; + bool subsumes(card& c1, card& c2, literal_vector& comp); + bool subsumes(card& c1, clause& c2, bool& self); + bool subsumed(card& c1, literal l1, literal l2); + bool subsumes(pb const& p1, pb_base const& p2); + void subsumes(pb& p1, literal lit); + void subsumption(pb& p1); + void binary_subsumption(card& c1, literal lit); + void clause_subsumption(card& c1, literal lit, clause_vector& removed_clauses); + void card_subsumption(card& c1, literal lit); + void mark_visited(literal l) { m_visited[l.index()] = true; } + void unmark_visited(literal l) { m_visited[l.index()] = false; } + bool is_marked(literal l) const { return m_visited[l.index()] != 0; } + unsigned get_num_unblocked_bin(literal l); + literal get_min_occurrence_literal(card const& c); + void init_use_lists(); + void remove_unused_defs(); + unsigned set_non_external(); + unsigned elim_pure(); + bool elim_pure(literal lit); + void subsumption(constraint& c1); + void subsumption(card& c1); + void gc_half(char const* _method); + void update_psm(constraint& c) const; + void mutex_reduction(); + void update_pure(); + + unsigned use_count(literal lit) const { return m_cnstr_use_list[lit.index()].size() + m_clause_use_list.get(lit).size(); } + + void cleanup_clauses(); + void cleanup_constraints(); + void cleanup_constraints(ptr_vector& cs, bool learned); + void remove_constraint(constraint& c, char const* reason); + + // constraints + constraint& index2constraint(size_t idx) const { return *reinterpret_cast(idx); } + void pop_constraint(); + void unwatch_literal(literal w, constraint& c); + void watch_literal(literal w, constraint& c); + void watch_literal(wliteral w, pb& p); + bool is_watched(literal l, constraint const& c) const; + void add_constraint(constraint* c); + bool init_watch(constraint& c); + void init_watch(bool_var v); + void clear_watch(constraint& c); + lbool add_assign(constraint& c, literal l); + void simplify(constraint& c); + void nullify_tracking_literal(constraint& c); + void set_conflict(constraint& c, literal lit); + void assign(constraint& c, literal lit); + bool assigned_above(literal above, literal below); + void get_antecedents(literal l, constraint const& c, literal_vector & r); + bool validate_conflict(constraint const& c) const; + bool validate_unit_propagation(constraint const& c, literal alit) const; + void attach_constraint(constraint const& c); + void detach_constraint(constraint const& c); + lbool eval(constraint const& c) const; + lbool eval(model const& m, constraint const& c) const; + lbool eval(lbool a, lbool b) const; + void assert_unconstrained(literal lit, literal_vector const& lits); + void flush_roots(constraint& c); + void recompile(constraint& c); + void split_root(constraint& c); + unsigned next_id() { return m_constraint_id++; } + + + // cardinality + bool init_watch(card& c); + lbool add_assign(card& c, literal lit); + void clear_watch(card& c); + void reset_coeffs(); + void reset_marked_literals(); + void get_antecedents(literal l, card const& c, literal_vector & r); + void flush_roots(card& c); + void recompile(card& c); + bool clausify(card& c); + bool clausify(literal lit, unsigned n, literal const* lits, unsigned k); + lbool eval(card const& c) const; + lbool eval(model const& m, card const& c) const; + double get_reward(card const& c, literal_occs_fun& occs) const; + + + // xr specific functionality + void clear_watch(xr& x); + bool init_watch(xr& x); + bool parity(xr const& x, unsigned offset) const; + lbool add_assign(xr& x, literal alit); + void get_xr_antecedents(literal l, unsigned index, justification js, literal_vector& r); + void get_antecedents(literal l, xr const& x, literal_vector & r); + void simplify(xr& x); + bool clausify(xr& x); + void flush_roots(xr& x); + lbool eval(xr const& x) const; + lbool eval(model const& m, xr const& x) const; + + // pb functionality + unsigned m_a_max; + bool init_watch(pb& p); + lbool add_assign(pb& p, literal alit); + void add_index(pb& p, unsigned index, literal lit); + void clear_watch(pb& p); + void get_antecedents(literal l, pb const& p, literal_vector & r); + void split_root(pb_base& p); + void simplify(pb_base& p); + void simplify2(pb& p); + bool is_cardinality(pb const& p); + void flush_roots(pb& p); + void recompile(pb& p); + bool clausify(pb& p); + bool is_cardinality(pb const& p, literal_vector& lits); + lbool eval(pb const& p) const; + lbool eval(model const& m, pb const& p) const; + double get_reward(pb const& p, literal_occs_fun& occs) const; + + // access solver + inline lbool value(bool_var v) const { return value(literal(v, false)); } + inline lbool value(literal lit) const { return m_lookahead ? m_lookahead->value(lit) : m_solver->value(lit); } + inline lbool value(model const& m, literal l) const { return l.sign() ? ~m[l.var()] : m[l.var()]; } + + inline unsigned lvl(literal lit) const { return m_lookahead || m_unit_walk ? 0 : m_solver->lvl(lit); } + inline unsigned lvl(bool_var v) const { return m_lookahead || m_unit_walk ? 0 : m_solver->lvl(v); } + inline bool inconsistent() const { + if (m_lookahead) return m_lookahead->inconsistent(); + if (m_unit_walk) return m_unit_walk->inconsistent(); + return m_solver->inconsistent(); + } + inline watch_list& get_wlist(literal l) { return m_lookahead ? m_lookahead->get_wlist(l) : m_solver->get_wlist(l); } + inline watch_list const& get_wlist(literal l) const { return m_lookahead ? m_lookahead->get_wlist(l) : m_solver->get_wlist(l); } + inline void assign(literal l, justification j) { + if (m_lookahead) m_lookahead->assign(l); + else if (m_unit_walk) m_unit_walk->assign(l); + else m_solver->assign(l, j); + } + inline void set_conflict(justification j, literal l) { + if (m_lookahead) m_lookahead->set_conflict(); + else if (m_unit_walk) m_unit_walk->set_conflict(); + else m_solver->set_conflict(j, l); + } + inline config const& get_config() const { return m_lookahead ? m_lookahead->get_config() : m_solver->get_config(); } + inline void drat_add(literal_vector const& c, svector const& premises) { if (m_solver) m_solver->m_drat.add(c, premises); } + + + mutable bool m_overflow; + void reset_active_var_set(); + void normalize_active_coeffs(); + void inc_coeff(literal l, unsigned offset); + int64_t get_coeff(bool_var v) const; + unsigned get_abs_coeff(bool_var v) const; + int get_int_coeff(bool_var v) const; + unsigned get_bound() const; + void inc_bound(int64_t i); + + literal get_asserting_literal(literal conseq); + void process_antecedent(literal l, unsigned offset); + void process_card(card& c, unsigned offset); + void cut(); + bool create_asserting_lemma(); + + // validation utilities + bool validate_conflict(card const& c) const; + bool validate_conflict(xr const& x) const; + bool validate_conflict(pb const& p) const; + bool validate_assign(literal_vector const& lits, literal lit); + bool validate_lemma(); + bool validate_unit_propagation(card const& c, literal alit) const; + bool validate_unit_propagation(pb const& p, literal alit) const; + bool validate_unit_propagation(pb const& p, literal_vector const& r, literal alit) const; + bool validate_unit_propagation(xr const& x, literal alit) const; + bool validate_conflict(literal_vector const& lits, ineq& p); + bool validate_watch_literals() const; + bool validate_watch_literal(literal lit) const; + bool validate_watched_constraint(constraint const& c) const; + bool validate_watch(pb const& p, literal alit) const; + bool is_watching(literal lit, constraint const& c) const; + literal translate_to_sat(solver& s, u_map& translation, ineq const& pb); + literal translate_to_sat(solver& s, u_map& translation, ineq& a, ineq& b); + literal translate_to_sat(solver& s, u_map& translation, literal lit); + ineq negate(ineq const& a) const; + void push_lit(literal_vector& lits, literal lit); + + ineq m_A, m_B, m_C; + void active2pb(ineq& p); + constraint* active2constraint(); + constraint* active2card(); + void justification2pb(justification const& j, literal lit, unsigned offset, ineq& p); + bool validate_resolvent(); + + void display(std::ostream& out, ineq& p, bool values = false) const; + void display(std::ostream& out, card const& c, bool values) const; + void display(std::ostream& out, pb const& p, bool values) const; + void display(std::ostream& out, xr const& c, bool values) const; + void display_lit(std::ostream& out, literal l, unsigned sz, bool values) const; + + constraint* add_at_least(literal l, literal_vector const& lits, unsigned k, bool learned); + constraint* add_pb_ge(literal l, svector const& wlits, unsigned k, bool learned); + constraint* add_xr(literal_vector const& lits, bool learned); + + void copy_core(ba_solver* result, bool learned); + void copy_constraints(ba_solver* result, ptr_vector const& constraints); + + public: + ba_solver(); + virtual ~ba_solver(); + virtual void set_solver(solver* s) { m_solver = s; } + virtual void set_lookahead(lookahead* l) { m_lookahead = l; } + virtual void set_unit_walk(unit_walk* u) { m_unit_walk = u; } + void add_at_least(bool_var v, literal_vector const& lits, unsigned k); + void add_pb_ge(bool_var v, svector const& wlits, unsigned k); + void add_xr(literal_vector const& lits); + + virtual bool propagate(literal l, ext_constraint_idx idx); + virtual lbool resolve_conflict(); + virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r); + virtual void asserted(literal l); + virtual check_result check(); + virtual void push(); + virtual void pop(unsigned n); + virtual void simplify(); + virtual void clauses_modifed(); + virtual lbool get_phase(bool_var v); + virtual bool set_root(literal l, literal r); + virtual void flush_roots(); + virtual std::ostream& display(std::ostream& out) const; + virtual std::ostream& display_justification(std::ostream& out, ext_justification_idx idx) const; + virtual void collect_statistics(statistics& st) const; + virtual extension* copy(solver* s); + virtual extension* copy(lookahead* s, bool learned); + virtual void find_mutexes(literal_vector& lits, vector & mutexes); + virtual void pop_reinit(); + virtual void gc(); + virtual double get_reward(literal l, ext_justification_idx idx, literal_occs_fun& occs) const; + virtual bool is_extended_binary(ext_justification_idx idx, literal_vector & r); + virtual void init_use_list(ext_use_list& ul); + virtual bool is_blocked(literal l, ext_constraint_idx idx); + virtual bool check_model(model const& m) const; + + ptr_vector const & constraints() const { return m_constraints; } + void display(std::ostream& out, constraint const& c, bool values) const; + + virtual bool validate(); + + + }; + +}; + +#endif diff --git a/src/sat/dimacs.cpp b/src/sat/dimacs.cpp index 512be5f4b..463418b23 100644 --- a/src/sat/dimacs.cpp +++ b/src/sat/dimacs.cpp @@ -24,10 +24,12 @@ Revision History: class stream_buffer { std::istream & m_stream; int m_val; + unsigned m_line; public: stream_buffer(std::istream & s): - m_stream(s) { + m_stream(s), + m_line(0) { m_val = m_stream.get(); } @@ -37,7 +39,10 @@ public: void operator ++() { m_val = m_stream.get(); + if (m_val == '\n') ++m_line; } + + unsigned line() const { return m_line; } }; template @@ -76,7 +81,7 @@ int parse_int(Buffer & in) { } if (*in < '0' || *in > '9') { - std::cerr << "(error, \"unexpected char: " << *in << "\")\n"; + std::cerr << "(error, \"unexpected char: " << *in << " line: " << in.line() << "\")\n"; exit(3); exit(ERR_PARSER); } diff --git a/src/sat/sat_allocator.h b/src/sat/sat_allocator.h new file mode 100644 index 000000000..06585ebed --- /dev/null +++ b/src/sat/sat_allocator.h @@ -0,0 +1,103 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + sat_allocator.h + +Abstract: + + Small object allocator suitable for clauses + +Author: + + Nikolaj bjorner (nbjorner) 2018-04-26. + +Revision History: +--*/ + +#ifndef SAT_ALLOCATOR_H_ +#define SAT_ALLOCATOR_H_ + +#include "util/vector.h" +#include "util/machine.h" + +class sat_allocator { + static const unsigned CHUNK_SIZE = (1 << 16); + static const unsigned SMALL_OBJ_SIZE = 512; + static const unsigned MASK = ((1 << PTR_ALIGNMENT) - 1); + static const unsigned NUM_FREE = 1 + (SMALL_OBJ_SIZE >> PTR_ALIGNMENT); + struct chunk { + char * m_curr; + char m_data[CHUNK_SIZE]; + chunk():m_curr(m_data) {} + }; + char const * m_id; + size_t m_alloc_size; + ptr_vector m_chunks; + void * m_chunk_ptr; + ptr_vector m_free[NUM_FREE]; + + unsigned align_size(size_t sz) const { + return free_slot_id(sz) << PTR_ALIGNMENT; + } + unsigned free_slot_id(size_t size) const { + return (static_cast(size >> PTR_ALIGNMENT) + ((0 != (size & MASK)) ? 1u : 0u)); + } +public: + sat_allocator(char const * id = "unknown"): m_id(id), m_alloc_size(0), m_chunk_ptr(nullptr) {} + ~sat_allocator() { reset(); } + void reset() { + for (chunk * ch : m_chunks) dealloc(ch); + m_chunks.reset(); + for (unsigned i = 0; i < NUM_FREE; ++i) m_free[i].reset(); + m_alloc_size = 0; + m_chunk_ptr = nullptr; + } + void * allocate(size_t size) { + m_alloc_size += size; + if (size >= SMALL_OBJ_SIZE) { + return memory::allocate(size); + } + unsigned slot_id = free_slot_id(size); + if (!m_free[slot_id].empty()) { + void* result = m_free[slot_id].back(); + m_free[slot_id].pop_back(); + return result; + } + if (m_chunks.empty()) { + m_chunks.push_back(alloc(chunk)); + m_chunk_ptr = m_chunks.back(); + } + + unsigned sz = align_size(size); + if ((char*)m_chunk_ptr + sz > (char*)m_chunks.back() + CHUNK_SIZE) { + m_chunks.push_back(alloc(chunk)); + m_chunk_ptr = m_chunks.back(); + } + void * result = m_chunk_ptr; + m_chunk_ptr = (char*)m_chunk_ptr + sz; + return result; + } + + void deallocate(size_t size, void * p) { + m_alloc_size -= size; + if (size >= SMALL_OBJ_SIZE) { + memory::deallocate(p); + } + else { + m_free[free_slot_id(size)].push_back(p); + } + } + size_t get_allocation_size() const { return m_alloc_size; } + + char const* id() const { return m_id; } +}; + +inline void * operator new(size_t s, sat_allocator & r) { return r.allocate(s); } +inline void * operator new[](size_t s, sat_allocator & r) { return r.allocate(s); } +inline void operator delete(void * p, sat_allocator & r) { UNREACHABLE(); } +inline void operator delete[](void * p, sat_allocator & r) { UNREACHABLE(); } + +#endif /* SAT_ALLOCATOR_H_ */ + diff --git a/src/sat/sat_asymm_branch.cpp b/src/sat/sat_asymm_branch.cpp index 8d5778f0b..941426321 100644 --- a/src/sat/sat_asymm_branch.cpp +++ b/src/sat/sat_asymm_branch.cpp @@ -19,6 +19,7 @@ Revision History: #include "sat/sat_asymm_branch.h" #include "sat/sat_asymm_branch_params.hpp" #include "sat/sat_solver.h" +#include "sat/sat_big.h" #include "util/stopwatch.h" #include "util/trace.h" @@ -26,9 +27,11 @@ namespace sat { asymm_branch::asymm_branch(solver & _s, params_ref const & p): s(_s), + m_params(p), m_counter(0) { updt_params(p); reset_statistics(); + m_calls = 0; } struct clause_size_lt { @@ -39,43 +42,75 @@ namespace sat { asymm_branch & m_asymm_branch; stopwatch m_watch; unsigned m_elim_literals; + unsigned m_elim_learned_literals; + unsigned m_tr; report(asymm_branch & a): m_asymm_branch(a), - m_elim_literals(a.m_elim_literals) { + m_elim_literals(a.m_elim_literals), + m_elim_learned_literals(a.m_elim_learned_literals), + m_tr(a.m_tr) { m_watch.start(); } ~report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, - verbose_stream() << " (sat-asymm-branch :elim-literals " - << (m_asymm_branch.m_elim_literals - m_elim_literals) + unsigned num_learned = (m_asymm_branch.m_elim_learned_literals - m_elim_learned_literals); + unsigned num_total = (m_asymm_branch.m_elim_literals - m_elim_literals); + verbose_stream() + << " (sat-asymm-branch :elim-literals " << (num_total - num_learned) + << " :elim-learned-literals " << num_learned + << " :hte " << (m_asymm_branch.m_tr - m_tr) << " :cost " << m_asymm_branch.m_counter << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; - - void asymm_branch::operator()(bool force) { - if (!m_asymm_branch) - return; - s.propagate(false); // must propagate, since it uses s.push() - if (s.m_inconsistent) - return; - if (!force && m_counter > 0) - return; - CASSERT("asymm_branch", s.check_invariant()); - TRACE("asymm_branch_detail", s.display(tout);); - report rpt(*this); - svector saved_phase(s.m_phase); - m_counter = 0; // counter is moving down to capture propagate cost. - int limit = -static_cast(m_asymm_branch_limit); - std::stable_sort(s.m_clauses.begin(), s.m_clauses.end(), clause_size_lt()); - m_counter -= s.m_clauses.size(); - SASSERT(s.m_qhead == s.m_trail.size()); - clause_vector::iterator it = s.m_clauses.begin(); + + void asymm_branch::process_bin(big& big) { + m_tr += big.reduce_tr(s); + } + + bool asymm_branch::process(big& big, bool learned) { + unsigned elim0 = m_elim_literals; + unsigned eliml0 = m_elim_learned_literals; + for (unsigned i = 0; i < m_asymm_branch_rounds; ++i) { + unsigned elim = m_elim_literals + m_tr; + big.init(s, learned); + process(&big, s.m_clauses); + process(&big, s.m_learned); + process_bin(big); + s.propagate(false); + if (s.m_inconsistent) + break; + unsigned num_elim = m_elim_literals + m_tr - elim; + IF_VERBOSE(1, verbose_stream() << "(sat-asymm-branch-step :elim " << num_elim << ")\n";); + if (num_elim == 0) + break; + } + IF_VERBOSE(1, if (m_elim_learned_literals > eliml0) + verbose_stream() << "(sat-asymm-branch :elim " << m_elim_learned_literals - eliml0 << ")\n";); + return m_elim_literals > elim0; + } + + bool asymm_branch::process(bool learned) { + unsigned eliml0 = m_elim_learned_literals; + unsigned elim = m_elim_literals; + process(nullptr, s.m_clauses); + s.propagate(false); + IF_VERBOSE(1, if (m_elim_learned_literals > eliml0) + verbose_stream() << "(sat-asymm-branch :elim " << m_elim_learned_literals - eliml0 << ")\n";); + return m_elim_literals > elim; + } + + + void asymm_branch::process(big* big, clause_vector& clauses) { + int64_t limit = -m_asymm_branch_limit; + std::stable_sort(clauses.begin(), clauses.end(), clause_size_lt()); + m_counter -= clauses.size(); + clause_vector::iterator it = clauses.begin(); clause_vector::iterator it2 = it; - clause_vector::iterator end = s.m_clauses.end(); + clause_vector::iterator end = clauses.end(); try { for (; it != end; ++it) { if (s.inconsistent()) { @@ -84,44 +119,339 @@ namespace sat { } break; } - SASSERT(s.m_qhead == s.m_trail.size()); - if (m_counter < limit || s.inconsistent()) { + clause & c = *(*it); + if (m_counter < limit || s.inconsistent() || c.was_removed()) { *it2 = *it; ++it2; continue; } - s.checkpoint(); - clause & c = *(*it); - m_counter -= c.size(); - if (!process(c)) + s.checkpoint(); + if (big ? !process_sampled(*big, c) : !process(c)) { continue; // clause was removed + } *it2 = *it; ++it2; } - s.m_clauses.set_end(it2); + clauses.set_end(it2); } catch (solver_exception & ex) { // put m_clauses in a consistent state... for (; it != end; ++it, ++it2) { *it2 = *it; } - s.m_clauses.set_end(it2); + clauses.set_end(it2); m_counter = -m_counter; throw ex; } - m_counter = -m_counter; - s.m_phase = saved_phase; + } + + + void asymm_branch::operator()(bool force) { + ++m_calls; + if (m_calls <= m_asymm_branch_delay) + return; + if (!m_asymm_branch && !m_asymm_branch_all && !m_asymm_branch_sampled) + return; + s.propagate(false); // must propagate, since it uses s.push() + if (s.m_inconsistent) + return; + if (!force && m_counter > 0) { + m_counter /= 100; + return; + } CASSERT("asymm_branch", s.check_invariant()); + TRACE("asymm_branch_detail", s.display(tout);); + report rpt(*this); + svector saved_phase(s.m_phase); + + bool change = true; + unsigned counter = 0; + while (change && counter < 2) { + ++counter; + change = false; + if (m_asymm_branch_sampled) { + big big(s.m_rand); + if (process(big, true)) change = true; + } + if (m_asymm_branch_sampled) { + big big(s.m_rand); + if (process(big, false)) change = true; + } + if (m_asymm_branch) { + m_counter = 0; + if (process(true)) change = true; + m_counter = -m_counter; + } + } + + s.m_phase = saved_phase; + m_asymm_branch_limit *= 2; + if (m_asymm_branch_limit > UINT_MAX) + m_asymm_branch_limit = UINT_MAX; + + CASSERT("asymm_branch", s.check_invariant()); + } + + /** + \brief try asymmetric branching on all literals in clause. + */ + + bool asymm_branch::process_all(clause & c) { + scoped_detach scoped_d(s, c); // clause must not be used for propagation + unsigned sz = c.size(); + SASSERT(sz > 0); + unsigned i = 0, new_sz = sz; + for (i = sz; i-- > 0; ) { + if (flip_literal_at(c, i, new_sz)) + return cleanup(scoped_d, c, i, new_sz); + } + return true; + } + + struct asymm_branch::compare_left { + big& s; + compare_left(big& s): s(s) {} + bool operator()(literal u, literal v) const { + return s.get_left(u) < s.get_left(v); + } + }; + + void asymm_branch::sort(big& big, clause const& c) { + sort(big, c.begin(), c.end()); + } + + void asymm_branch::radix_sort(big& big, literal_vector& lits) { + const unsigned d = 4; + const unsigned w = 20; // 1M variable cap + unsigned sz = lits.size(); + m_tmp.reserve(sz); + for (unsigned p = 0; p < w/d; ++p) { + unsigned on[16]; + memset(on, 0, 16*sizeof(unsigned)); + for (literal l : lits) on[(big.get_left(l) >> 4*p) & 15]++; + for (unsigned i = 1; i < 16; ++i) on[i] += on[i-1]; + for (unsigned i = sz; i-- > 0; ) + m_tmp[--on[(big.get_left(lits[i]) >> 4*p) & 15]] = lits[i]; + for (unsigned i = sz; i-- > 0; ) lits[i] = m_tmp[i]; + } + } + + void asymm_branch::sort(big& big, literal const* begin, literal const* end) { + m_pos.reset(); m_neg.reset(); + for (; begin != end; ++begin) { + literal l = *begin; + m_pos.push_back(l); + m_neg.push_back(~l); + } + compare_left cmp(big); + std::sort(m_pos.begin(), m_pos.end(), cmp); + std::sort(m_neg.begin(), m_neg.end(), cmp); + + // alternative: worse + // radix_sort(big, m_pos); + // radix_sort(big, m_neg); + + IF_VERBOSE(100, + for (literal l : m_pos) verbose_stream() << big.get_left(l) << " "; + verbose_stream() << "\n"; + for (literal l : m_neg) verbose_stream() << big.get_left(l) << " "; + verbose_stream() << "\n"; + ); + } + + bool asymm_branch::uhte(big& big, clause & c) { + unsigned pindex = 0, nindex = 0; + literal lpos = m_pos[pindex++]; + literal lneg = m_neg[nindex++]; + while (true) { + if (big.get_left(lneg) > big.get_left(lpos)) { + if (pindex == m_pos.size()) return false; + lpos = m_pos[pindex++]; + } + else if (big.get_right(lneg) < big.get_right(lpos) || + (m_pos.size() == 2 && (lpos == ~lneg || big.get_parent(lpos) == lneg))) { + if (nindex == m_neg.size()) return false; + lneg = m_neg[nindex++]; + } + else { + return true; + } + } + return false; + } + + void asymm_branch::minimize(big& big, literal_vector& lemma) { + big.ensure_big(s, true); + sort(big, lemma.begin(), lemma.end()); + uhle(big); + if (!m_to_delete.empty()) { + unsigned j = 0; + for (unsigned i = 0; i < lemma.size(); ++i) { + literal l = lemma[i]; + if (!m_to_delete.contains(l)) { + lemma[j++] = l; + } + } + lemma.shrink(j); + } + } + + void asymm_branch::uhle(big& big) { + m_to_delete.reset(); + if (m_to_delete.empty()) { + int right = big.get_right(m_pos.back()); + for (unsigned i = m_pos.size() - 1; i-- > 0; ) { + literal lit = m_pos[i]; + int right2 = big.get_right(lit); + if (right2 > right) { + // lit => last, so lit can be deleted + m_to_delete.push_back(lit); + } + else { + right = right2; + } + } + } + if (m_to_delete.empty()) { + int right = big.get_right(m_neg[0]); + for (unsigned i = 1; i < m_neg.size(); ++i) { + literal lit = m_neg[i]; + int right2 = big.get_right(lit); + if (right > right2) { + // ~first => ~lit + m_to_delete.push_back(~lit); + } + else { + right = right2; + } + } + } + } + + bool asymm_branch::uhle(scoped_detach& scoped_d, big& big, clause & c) { + uhle(big); + if (!m_to_delete.empty()) { + unsigned j = 0; + for (unsigned i = 0; i < c.size(); ++i) { + literal lit = c[i]; + switch (s.value(lit)) { + case l_true: + scoped_d.del_clause(); + return false; + case l_false: + break; + default: + if (!m_to_delete.contains(lit)) { + c[j++] = lit; + } + break; + } + } + return re_attach(scoped_d, c, j); + } + else { + return true; + } + } + + + bool asymm_branch::propagate_literal(clause const& c, literal l) { + SASSERT(!s.inconsistent()); + TRACE("asymm_branch_detail", tout << "assigning: " << l << "\n";); + s.assign(l, justification()); + s.propagate_core(false); // must not use propagate(), since check_missed_propagation may fail for c + return s.inconsistent(); + } + + bool asymm_branch::flip_literal_at(clause const& c, unsigned flip_index, unsigned& new_sz) { + VERIFY(s.m_trail.size() == s.m_qhead); + bool found_conflict = false; + unsigned i = 0, sz = c.size(); + s.push(); + for (i = 0; !found_conflict && i < sz; i++) { + if (i == flip_index) continue; + found_conflict = propagate_literal(c, ~c[i]); + } + if (!found_conflict) { + SASSERT(sz == i); + found_conflict = propagate_literal(c, c[flip_index]); + } + s.pop(1); + new_sz = i; + return found_conflict; + } + + bool asymm_branch::cleanup(scoped_detach& scoped_d, clause& c, unsigned skip_idx, unsigned new_sz) { + unsigned j = 0; + for (unsigned i = 0; i < new_sz; i++) { + if (skip_idx == i) continue; + literal l = c[i]; + switch (s.value(l)) { + case l_undef: + if (i != j) { + std::swap(c[i], c[j]); + } + j++; + break; + case l_false: + break; + case l_true: + UNREACHABLE(); + break; + } + } + new_sz = j; + return re_attach(scoped_d, c, new_sz); + } + + bool asymm_branch::re_attach(scoped_detach& scoped_d, clause& c, unsigned new_sz) { + VERIFY(s.m_trail.size() == s.m_qhead); + m_elim_literals += c.size() - new_sz; + if (c.is_learned()) { + m_elim_learned_literals += c.size() - new_sz; + } + + switch(new_sz) { + case 0: + s.set_conflict(justification()); + return false; + case 1: + TRACE("asymm_branch", tout << "produced unit clause: " << c[0] << "\n";); + s.assign(c[0], justification()); + s.propagate_core(false); + scoped_d.del_clause(); + return false; // check_missed_propagation() may fail, since m_clauses is not in a consistent state. + case 2: + SASSERT(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); + VERIFY(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); + s.mk_bin_clause(c[0], c[1], c.is_learned()); + if (s.m_trail.size() > s.m_qhead) s.propagate_core(false); + scoped_d.del_clause(); + return false; + default: + c.shrink(new_sz); + if (s.m_config.m_drat) s.m_drat.add(c, true); + // if (s.m_config.m_drat) s.m_drat.del(c0); // TBD + return true; + } + } + + bool asymm_branch::process_sampled(big& big, clause & c) { + scoped_detach scoped_d(s, c); + sort(big, c); + if (uhte(big, c)) { + // don't touch hidden tautologies. + // ATE takes care of them. + return true; + } + return uhle(scoped_d, big, c); } bool asymm_branch::process(clause & c) { TRACE("asymm_branch_detail", tout << "processing: " << c << "\n";); SASSERT(s.scope_lvl() == 0); - SASSERT(s.m_qhead == s.m_trail.size()); -#ifdef Z3DEBUG - unsigned trail_sz = s.m_trail.size(); -#endif SASSERT(!s.inconsistent()); + unsigned sz = c.size(); SASSERT(sz > 0); unsigned i; @@ -133,83 +463,38 @@ namespace sat { return false; } } + m_counter -= c.size(); + + if (m_asymm_branch_all) return process_all(c); + // try asymmetric branching // clause must not be used for propagation - solver::scoped_detach scoped_d(s, c); - s.push(); - for (i = 0; i < sz - 1; i++) { - literal l = c[i]; - SASSERT(!s.inconsistent()); - TRACE("asymm_branch_detail", tout << "assigning: " << ~l << "\n";); - s.assign(~l, justification()); - s.propagate_core(false); // must not use propagate(), since check_missed_propagation may fail for c - if (s.inconsistent()) - break; - } - s.pop(1); + scoped_detach scoped_d(s, c); + unsigned new_sz = c.size(); + unsigned flip_position = m_rand(c.size()); + bool found_conflict = flip_literal_at(c, flip_position, new_sz); SASSERT(!s.inconsistent()); SASSERT(s.scope_lvl() == 0); - SASSERT(trail_sz == s.m_trail.size()); - SASSERT(s.m_qhead == s.m_trail.size()); - if (i == sz - 1) { + if (!found_conflict) { // clause size can't be reduced. return true; } - // clause can be reduced - unsigned new_sz = i+1; - SASSERT(new_sz >= 1); - SASSERT(new_sz < sz); - TRACE("asymm_branch", tout << c << "\nnew_size: " << new_sz << "\n"; - for (unsigned i = 0; i < c.size(); i++) tout << static_cast(s.value(c[i])) << " "; tout << "\n";); - // cleanup reduced clause - unsigned j = 0; - for (i = 0; i < new_sz; i++) { - literal l = c[i]; - switch (s.value(l)) { - case l_undef: - c[j] = l; - j++; - break; - case l_false: - break; - case l_true: - UNREACHABLE(); - break; - } - } - new_sz = j; - m_elim_literals += sz - new_sz; - switch(new_sz) { - case 0: - s.set_conflict(justification()); - return false; - case 1: - TRACE("asymm_branch", tout << "produced unit clause: " << c[0] << "\n";); - s.assign(c[0], justification()); - s.propagate_core(false); - scoped_d.del_clause(); - SASSERT(s.inconsistent() || s.m_qhead == s.m_trail.size()); - return false; // check_missed_propagation() may fail, since m_clauses is not in a consistent state. - case 2: - SASSERT(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); - s.mk_bin_clause(c[0], c[1], false); - scoped_d.del_clause(); - SASSERT(s.m_qhead == s.m_trail.size()); - return false; - default: - c.shrink(new_sz); - SASSERT(s.m_qhead == s.m_trail.size()); - return true; + else { + // clause can be reduced + return cleanup(scoped_d, c, flip_position, new_sz); } } void asymm_branch::updt_params(params_ref const & _p) { sat_asymm_branch_params p(_p); - m_asymm_branch = p.asymm_branch(); - m_asymm_branch_rounds = p.asymm_branch_rounds(); - m_asymm_branch_limit = p.asymm_branch_limit(); - if (m_asymm_branch_limit > INT_MAX) - m_asymm_branch_limit = INT_MAX; + m_asymm_branch = p.asymm_branch(); + m_asymm_branch_rounds = p.asymm_branch_rounds(); + m_asymm_branch_delay = p.asymm_branch_delay(); + m_asymm_branch_sampled = p.asymm_branch_sampled(); + m_asymm_branch_limit = p.asymm_branch_limit(); + m_asymm_branch_all = p.asymm_branch_all(); + if (m_asymm_branch_limit > UINT_MAX) + m_asymm_branch_limit = UINT_MAX; } void asymm_branch::collect_param_descrs(param_descrs & d) { @@ -218,10 +503,13 @@ namespace sat { void asymm_branch::collect_statistics(statistics & st) const { st.update("elim literals", m_elim_literals); + st.update("tr", m_tr); } void asymm_branch::reset_statistics() { m_elim_literals = 0; + m_elim_learned_literals = 0; + m_tr = 0; } }; diff --git a/src/sat/sat_asymm_branch.h b/src/sat/sat_asymm_branch.h index 9e28d1600..4d032022c 100644 --- a/src/sat/sat_asymm_branch.h +++ b/src/sat/sat_asymm_branch.h @@ -20,31 +20,79 @@ Revision History: #define SAT_ASYMM_BRANCH_H_ #include "sat/sat_types.h" +#include "sat/sat_big.h" #include "util/statistics.h" #include "util/params.h" namespace sat { class solver; + class scoped_detach; class asymm_branch { struct report; - solver & s; - int m_counter; - + solver & s; + params_ref m_params; + int64_t m_counter; + random_gen m_rand; + unsigned m_calls; + // config - bool m_asymm_branch; - unsigned m_asymm_branch_rounds; - unsigned m_asymm_branch_limit; + bool m_asymm_branch; + unsigned m_asymm_branch_rounds; + unsigned m_asymm_branch_delay; + bool m_asymm_branch_sampled; + bool m_asymm_branch_all; + int64_t m_asymm_branch_limit; // stats - unsigned m_elim_literals; + unsigned m_elim_literals; + unsigned m_elim_learned_literals; + unsigned m_tr; + + literal_vector m_pos, m_neg; // literals (complements of literals) in clauses sorted by discovery time (m_left in BIG). + svector> m_pos1, m_neg1; + literal_vector m_to_delete; + literal_vector m_tmp; + + struct compare_left; + + void sort(big& big, literal const* begin, literal const* end); + void sort(big & big, clause const& c); + void radix_sort(big & big, literal_vector& lits); + + bool uhle(scoped_detach& scoped_d, big & big, clause & c); + + void uhle(big & big); + + bool uhte(big & big, clause & c); + + bool re_attach(scoped_detach& scoped_d, clause& c, unsigned new_sz); + + bool process(bool learned); + + bool process(big& big, bool learned); bool process(clause & c); + + bool process_sampled(big& big, clause & c); + + void process(big* big, clause_vector & c); + + bool process_all(clause & c); + + void process_bin(big& big); + + bool flip_literal_at(clause const& c, unsigned flip_index, unsigned& new_sz); + + bool cleanup(scoped_detach& scoped_d, clause& c, unsigned skip_index, unsigned new_sz); + + bool propagate_literal(clause const& c, literal l); + public: asymm_branch(solver & s, params_ref const & p); - void operator()(bool force = false); + void operator()(bool force); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); @@ -52,7 +100,11 @@ namespace sat { void collect_statistics(statistics & st) const; void reset_statistics(); - void dec(unsigned c) { m_counter -= c; } + void minimize(big& big, literal_vector& lemma); + + void init_search() { m_calls = 0; } + + inline void dec(unsigned c) { m_counter -= c; } }; }; diff --git a/src/sat/sat_asymm_branch_params.pyg b/src/sat/sat_asymm_branch_params.pyg index 8940c64a6..5ee140ab9 100644 --- a/src/sat/sat_asymm_branch_params.pyg +++ b/src/sat/sat_asymm_branch_params.pyg @@ -2,5 +2,8 @@ def_module_params(module_name='sat', class_name='sat_asymm_branch_params', export=True, params=(('asymm_branch', BOOL, True, 'asymmetric branching'), - ('asymm_branch.rounds', UINT, 32, 'maximum number of rounds of asymmetric branching'), - ('asymm_branch.limit', UINT, 100000000, 'approx. maximum number of literals visited during asymmetric branching'))) + ('asymm_branch.rounds', UINT, 2, 'maximal number of rounds to run asymmetric branch simplifications if progress is made'), + ('asymm_branch.delay', UINT, 1, 'number of simplification rounds to wait until invoking asymmetric branch simplification'), + ('asymm_branch.sampled', BOOL, True, 'use sampling based asymmetric branching based on binary implication graph'), + ('asymm_branch.limit', UINT, 100000000, 'approx. maximum number of literals visited during asymmetric branching'), + ('asymm_branch.all', BOOL, False, 'asymmetric branching on all literals per clause'))) diff --git a/src/sat/sat_bdd.cpp b/src/sat/sat_bdd.cpp new file mode 100644 index 000000000..74af4bf5e --- /dev/null +++ b/src/sat/sat_bdd.cpp @@ -0,0 +1,894 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_bdd.cpp + +Abstract: + + Simple BDD package modeled after BuDDy, which is modeled after CUDD. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-13 + +Revision History: + +--*/ + +#include "sat/sat_bdd.h" +#include "util/trace.h" +#include "util/stopwatch.h" + +namespace sat { + + bdd_manager::bdd_manager(unsigned num_vars) { + m_cost_metric = bdd_cost; + m_cost_bdd = 0; + for (BDD a = 0; a < 2; ++a) { + for (BDD b = 0; b < 2; ++b) { + for (unsigned op = bdd_and_op; op < bdd_not_op; ++op) { + unsigned index = a + 2*b + 4*op; + m_apply_const.reserve(index+1); + m_apply_const[index] = apply_const(a, b, static_cast(op)); + } + } + } + + // add dummy nodes for operations, and true, false bdds. + for (unsigned i = 0; i <= bdd_no_op + 2; ++i) { + m_nodes.push_back(bdd_node(0,0,0)); + m_nodes.back().m_refcount = max_rc; + m_nodes.back().m_index = m_nodes.size()-1; + } + + m_spare_entry = nullptr; + m_max_num_bdd_nodes = 1 << 24; // up to 16M nodes + m_mark_level = 0; + alloc_free_nodes(1024 + num_vars); + m_disable_gc = false; + m_is_new_node = false; + + // add variables + for (unsigned i = 0; i < num_vars; ++i) { + reserve_var(i); + } + } + + bdd_manager::~bdd_manager() { + if (m_spare_entry) { + m_alloc.deallocate(sizeof(*m_spare_entry), m_spare_entry); + } + for (auto* e : m_op_cache) { + SASSERT(e != m_spare_entry); + m_alloc.deallocate(sizeof(*e), e); + } + } + + bdd_manager::BDD bdd_manager::apply_const(BDD a, BDD b, bdd_op op) { + SASSERT(is_const(a) && is_const(b)); + switch (op) { + case bdd_and_op: + return (a == true_bdd && b == true_bdd) ? true_bdd : false_bdd; + case bdd_or_op: + return (a == true_bdd || b == true_bdd) ? true_bdd : false_bdd; + case bdd_xor_op: + return (a == b) ? false_bdd : true_bdd; + default: + return false_bdd; + } + } + + bdd_manager::BDD bdd_manager::apply(BDD arg1, BDD arg2, bdd_op op) { + bool first = true; + SASSERT(well_formed()); + while (true) { + try { + return apply_rec(arg1, arg2, op); + } + catch (mem_out) { + try_reorder(); + if (!first) throw; + first = false; + } + } + SASSERT(well_formed()); + } + + + bdd bdd_manager::mk_true() { return bdd(true_bdd, this); } + bdd bdd_manager::mk_false() { return bdd(false_bdd, this); } + bdd bdd_manager::mk_and(bdd const& a, bdd const& b) { return bdd(apply(a.root, b.root, bdd_and_op), this); } + bdd bdd_manager::mk_or(bdd const& a, bdd const& b) { return bdd(apply(a.root, b.root, bdd_or_op), this); } + bdd bdd_manager::mk_xor(bdd const& a, bdd const& b) { return bdd(apply(a.root, b.root, bdd_xor_op), this); } + bdd bdd_manager::mk_exists(unsigned v, bdd const& b) { return mk_exists(1, &v, b); } + bdd bdd_manager::mk_forall(unsigned v, bdd const& b) { return mk_forall(1, &v, b); } + + + bool bdd_manager::check_result(op_entry*& e1, op_entry const* e2, BDD a, BDD b, BDD c) { + if (e1 != e2) { + SASSERT(e2->m_result != -1); + push_entry(e1); + e1 = nullptr; + return true; + } + else { + e1->m_bdd1 = a; + e1->m_bdd2 = b; + e1->m_op = c; + SASSERT(e1->m_result == -1); + return false; + } + } + + bdd_manager::BDD bdd_manager::apply_rec(BDD a, BDD b, bdd_op op) { + switch (op) { + case bdd_and_op: + if (a == b) return a; + if (is_false(a) || is_false(b)) return false_bdd; + if (is_true(a)) return b; + if (is_true(b)) return a; + break; + case bdd_or_op: + if (a == b) return a; + if (is_false(a)) return b; + if (is_false(b)) return a; + if (is_true(a) || is_true(b)) return true_bdd; + break; + case bdd_xor_op: + if (a == b) return false_bdd; + if (is_false(a)) return b; + if (is_false(b)) return a; + break; + default: + UNREACHABLE(); + break; + } + if (is_const(a) && is_const(b)) { + return m_apply_const[a + 2*b + 4*op]; + } + op_entry * e1 = pop_entry(a, b, op); + op_entry const* e2 = m_op_cache.insert_if_not_there(e1); + if (check_result(e1, e2, a, b, op)) { + SASSERT(!m_free_nodes.contains(e2->m_result)); + return e2->m_result; + } + // SASSERT(well_formed()); + BDD r; + if (level(a) == level(b)) { + push(apply_rec(lo(a), lo(b), op)); + push(apply_rec(hi(a), hi(b), op)); + r = make_node(level(a), read(2), read(1)); + } + else if (level(a) > level(b)) { + push(apply_rec(lo(a), b, op)); + push(apply_rec(hi(a), b, op)); + r = make_node(level(a), read(2), read(1)); + } + else { + push(apply_rec(a, lo(b), op)); + push(apply_rec(a, hi(b), op)); + r = make_node(level(b), read(2), read(1)); + } + pop(2); + e1->m_result = r; + // SASSERT(well_formed()); + SASSERT(!m_free_nodes.contains(r)); + return r; + } + + void bdd_manager::push(BDD b) { + m_bdd_stack.push_back(b); + } + + void bdd_manager::pop(unsigned num_scopes) { + m_bdd_stack.shrink(m_bdd_stack.size() - num_scopes); + } + + bdd_manager::BDD bdd_manager::read(unsigned index) { + return m_bdd_stack[m_bdd_stack.size() - index]; + } + + bdd_manager::op_entry* bdd_manager::pop_entry(BDD l, BDD r, BDD op) { + op_entry* result = nullptr; + if (m_spare_entry) { + result = m_spare_entry; + m_spare_entry = nullptr; + result->m_bdd1 = l; + result->m_bdd2 = r; + result->m_op = op; + } + else { + void * mem = m_alloc.allocate(sizeof(op_entry)); + result = new (mem) op_entry(l, r, op); + } + result->m_result = -1; + return result; + } + + void bdd_manager::push_entry(op_entry* e) { + SASSERT(!m_spare_entry); + m_spare_entry = e; + } + + bdd_manager::BDD bdd_manager::make_node(unsigned lvl, BDD l, BDD h) { + m_is_new_node = false; + if (l == h) { + return l; + } + SASSERT(is_const(l) || level(l) < lvl); + SASSERT(is_const(h) || level(h) < lvl); + + bdd_node n(lvl, l, h); + node_table::entry* e = m_node_table.insert_if_not_there2(n); + if (e->get_data().m_index != 0) { + unsigned result = e->get_data().m_index; + return result; + } + e->get_data().m_refcount = 0; + bool do_gc = m_free_nodes.empty(); + if (do_gc && !m_disable_gc) { + gc(); + e = m_node_table.insert_if_not_there2(n); + e->get_data().m_refcount = 0; + } + if (do_gc && m_free_nodes.size()*3 < m_nodes.size()) { + if (m_nodes.size() > m_max_num_bdd_nodes) { + throw mem_out(); + } + alloc_free_nodes(m_nodes.size()/2); + } + + SASSERT(!m_free_nodes.empty()); + unsigned result = m_free_nodes.back(); + m_free_nodes.pop_back(); + e->get_data().m_index = result; + m_nodes[result] = e->get_data(); + m_is_new_node = true; + SASSERT(!m_free_nodes.contains(result)); + SASSERT(m_nodes[result].m_index == result); + return result; + } + + void bdd_manager::try_cnf_reorder(bdd const& b) { + m_cost_bdd = b.root; + m_cost_metric = cnf_cost; + try_reorder(); + m_cost_metric = bdd_cost; + m_cost_bdd = 0; + } + + void bdd_manager::try_reorder() { + gc(); + for (auto* e : m_op_cache) { + m_alloc.deallocate(sizeof(*e), e); + } + m_op_cache.reset(); + init_reorder(); + for (unsigned i = 0; i < m_var2level.size(); ++i) { + sift_var(i); + } + SASSERT(m_op_cache.empty()); + SASSERT(well_formed()); + } + + double bdd_manager::current_cost() { + switch (m_cost_metric) { + case bdd_cost: + return m_nodes.size() - m_free_nodes.size(); + case cnf_cost: + return cnf_size(m_cost_bdd); + case dnf_cost: + return dnf_size(m_cost_bdd); + default: + UNREACHABLE(); + return 0; + } + } + + bool bdd_manager::is_bad_cost(double current_cost, double best_cost) const { + return current_cost > 1.1 * best_cost; + } + + void bdd_manager::sift_var(unsigned v) { + unsigned lvl = m_var2level[v]; + unsigned start = lvl; + double best_cost = current_cost(); + bool first = true; + unsigned max_lvl = m_level2nodes.size()-1; + if (lvl*2 < max_lvl) { + goto go_down; + } + go_up: + TRACE("bdd", tout << "sift up " << lvl << "\n";); + while (lvl < max_lvl) { + sift_up(lvl++); + double cost = current_cost(); + if (is_bad_cost(cost, best_cost)) break; + best_cost = std::min(cost, best_cost); + } + if (first) { + first = false; + while (lvl != start) { + sift_up(--lvl); + } + goto go_down; + } + else { + while (current_cost() != best_cost) { + sift_up(--lvl); + } + return; + } + go_down: + TRACE("bdd", tout << "sift down " << lvl << "\n";); + while (lvl > 0) { + sift_up(--lvl); + double cost = current_cost(); + if (is_bad_cost(cost, best_cost)) break; + best_cost = std::min(cost, best_cost); + } + if (first) { + first = false; + while (lvl != start) { + sift_up(lvl++); + } + goto go_up; + } + else { + while (current_cost() != best_cost) { + sift_up(lvl++); + } + return; + } + } + + void bdd_manager::sift_up(unsigned lvl) { + if (m_level2nodes[lvl].empty()) return; + // SASSERT(well_formed()); + // exchange level and level + 1. + m_S.reset(); + m_T.reset(); + m_to_free.reset(); + m_disable_gc = true; + + for (unsigned n : m_level2nodes[lvl + 1]) { + BDD l = lo(n); + BDD h = hi(n); + if (l == 0 && h == 0) continue; + if ((is_const(l) || level(l) != lvl) && + (is_const(h) || level(h) != lvl)) { + m_S.push_back(n); + } + else { + reorder_decref(l); + reorder_decref(h); + m_T.push_back(n); + } + TRACE("bdd", tout << "remove " << n << "\n";); + m_node_table.remove(m_nodes[n]); + } + m_level2nodes[lvl + 1].reset(); + m_level2nodes[lvl + 1].append(m_T); + + for (unsigned n : m_level2nodes[lvl]) { + bdd_node& node = m_nodes[n]; + m_node_table.remove(node); + node.m_level = lvl + 1; + if (m_reorder_rc[n] == 0) { + m_to_free.push_back(n); + } + else { + TRACE("bdd", tout << "set level " << n << " to " << lvl + 1 << "\n";); + m_node_table.insert(node); + m_level2nodes[lvl + 1].push_back(n); + } + } + m_level2nodes[lvl].reset(); + m_level2nodes[lvl].append(m_S); + + for (unsigned n : m_S) { + m_nodes[n].m_level = lvl; + m_node_table.insert(m_nodes[n]); + } + + for (unsigned n : m_T) { + BDD l = lo(n); + BDD h = hi(n); + if (l == 0 && h == 0) continue; + BDD a, b, c, d; + if (level(l) == lvl + 1) { + a = lo(l); + b = hi(l); + } + else { + a = b = l; + } + if (level(h) == lvl + 1) { + c = lo(h); + d = hi(h); + } + else { + c = d = h; + } + + unsigned ac = make_node(lvl, a, c); + if (is_new_node()) { + m_level2nodes[lvl].push_back(ac); + m_reorder_rc.reserve(ac+1); + reorder_incref(a); + reorder_incref(c); + } + unsigned bd = make_node(lvl, b, d); + if (is_new_node()) { + m_level2nodes[lvl].push_back(bd); + m_reorder_rc.reserve(bd+1); + reorder_incref(b); + reorder_incref(d); + } + m_nodes[n].m_lo = ac; + m_nodes[n].m_hi = bd; + reorder_incref(ac); + reorder_incref(bd); + TRACE("bdd", tout << "transform " << n << " " << " " << a << " " << b << " " << c << " " << d << " " << ac << " " << bd << "\n";); + m_node_table.insert(m_nodes[n]); + } + unsigned v = m_level2var[lvl]; + unsigned w = m_level2var[lvl+1]; + std::swap(m_level2var[lvl], m_level2var[lvl+1]); + std::swap(m_var2level[v], m_var2level[w]); + m_disable_gc = false; + + // add orphaned nodes to free-list + for (unsigned i = 0; i < m_to_free.size(); ++i) { + unsigned n = m_to_free[i]; + bdd_node& node = m_nodes[n]; + if (!node.is_internal()) { + SASSERT(!m_free_nodes.contains(n)); + SASSERT(node.m_refcount == 0); + m_free_nodes.push_back(n); + m_node_table.remove(node); + BDD l = lo(n); + BDD h = hi(n); + node.set_internal(); + + reorder_decref(l); + if (!m_nodes[l].is_internal() && m_reorder_rc[l] == 0) { + m_to_free.push_back(l); + } + reorder_decref(h); + if (!m_nodes[h].is_internal() && m_reorder_rc[h] == 0) { + m_to_free.push_back(h); + } + } + } + TRACE("bdd", tout << "sift " << lvl << "\n"; display(tout); ); + DEBUG_CODE( + for (unsigned i = 0; i < m_level2nodes.size(); ++i) { + for (unsigned n : m_level2nodes[i]) { + bdd_node const& node = m_nodes[n]; + SASSERT(node.m_level == i); + } + }); + + TRACE("bdd", + for (unsigned i = 0; i < m_nodes.size(); ++i) { + if (m_reorder_rc[i] != 0) { + tout << i << " " << m_reorder_rc[i] << "\n"; + }}); + + // SASSERT(well_formed()); + } + + void bdd_manager::init_reorder() { + m_level2nodes.reset(); + unsigned sz = m_nodes.size(); + m_reorder_rc.fill(sz, 0); + for (unsigned i = 0; i < sz; ++i) { + if (m_nodes[i].m_refcount > 0) + m_reorder_rc[i] = UINT_MAX; + } + for (unsigned i = 0; i < sz; ++i) { + bdd_node const& n = m_nodes[i]; + if (n.is_internal()) continue; + unsigned lvl = n.m_level; + SASSERT(i == m_nodes[i].m_index); + m_level2nodes.reserve(lvl + 1); + m_level2nodes[lvl].push_back(i); + reorder_incref(n.m_lo); + reorder_incref(n.m_hi); + } + TRACE("bdd", + display(tout); + for (unsigned i = 0; i < sz; ++i) { + bdd_node const& n = m_nodes[i]; + if (n.is_internal()) continue; + unsigned lvl = n.m_level; + tout << i << " lvl: " << lvl << " rc: " << m_reorder_rc[i] << " lo " << n.m_lo << " hi " << n.m_hi << "\n"; + } + ); + } + + void bdd_manager::reorder_incref(unsigned n) { + if (m_reorder_rc[n] != UINT_MAX) m_reorder_rc[n]++; + } + + void bdd_manager::reorder_decref(unsigned n) { + if (m_reorder_rc[n] != UINT_MAX) m_reorder_rc[n]--; + } + + void bdd_manager::reserve_var(unsigned i) { + while (m_var2level.size() <= i) { + unsigned v = m_var2level.size(); + m_var2bdd.push_back(make_node(v, false_bdd, true_bdd)); + m_var2bdd.push_back(make_node(v, true_bdd, false_bdd)); + m_nodes[m_var2bdd[2*v]].m_refcount = max_rc; + m_nodes[m_var2bdd[2*v+1]].m_refcount = max_rc; + m_var2level.push_back(v); + m_level2var.push_back(v); + } + } + + bdd bdd_manager::mk_var(unsigned i) { + reserve_var(i); + return bdd(m_var2bdd[2*i], this); + } + + bdd bdd_manager::mk_nvar(unsigned i) { + reserve_var(i); + return bdd(m_var2bdd[2*i+1], this); + } + + bdd bdd_manager::mk_not(bdd b) { + bool first = true; + while (true) { + try { + return bdd(mk_not_rec(b.root), this); + } + catch (mem_out) { + try_reorder(); + if (!first) throw; + first = false; + } + } + } + + bdd_manager::BDD bdd_manager::mk_not_rec(BDD b) { + if (is_true(b)) return false_bdd; + if (is_false(b)) return true_bdd; + op_entry* e1 = pop_entry(b, b, bdd_not_op); + op_entry const* e2 = m_op_cache.insert_if_not_there(e1); + if (check_result(e1, e2, b, b, bdd_not_op)) + return e2->m_result; + push(mk_not_rec(lo(b))); + push(mk_not_rec(hi(b))); + BDD r = make_node(level(b), read(2), read(1)); + pop(2); + e1->m_result = r; + return r; + } + + bdd bdd_manager::mk_ite(bdd const& c, bdd const& t, bdd const& e) { + bool first = true; + while (true) { + try { + return bdd(mk_ite_rec(c.root, t.root, e.root), this); + } + catch (mem_out) { + try_reorder(); + if (!first) throw; + first = false; + } + } + } + + bdd_manager::BDD bdd_manager::mk_ite_rec(BDD a, BDD b, BDD c) { + if (is_true(a)) return b; + if (is_false(a)) return c; + if (b == c) return b; + if (is_true(b)) return apply(a, c, bdd_or_op); + if (is_false(c)) return apply(a, b, bdd_and_op); + if (is_false(b)) return apply(mk_not_rec(a), c, bdd_and_op); + if (is_true(c)) return apply(mk_not_rec(a), b, bdd_or_op); + SASSERT(!is_const(a) && !is_const(b) && !is_const(c)); + op_entry * e1 = pop_entry(a, b, c); + op_entry const* e2 = m_op_cache.insert_if_not_there(e1); + if (check_result(e1, e2, a, b, c)) + return e2->m_result; + unsigned la = level(a), lb = level(b), lc = level(c); + BDD r; + BDD a1, b1, c1, a2, b2, c2; + unsigned lvl = la; + if (la >= lb && la >= lc) { + a1 = lo(a), a2 = hi(a); + lvl = la; + } + else { + a1 = a, a2 = a; + } + if (lb >= la && lb >= lc) { + b1 = lo(b), b2 = hi(b); + lvl = lb; + } + else { + b1 = b, b2 = b; + } + if (lc >= la && lc >= lb) { + c1 = lo(c), c2 = hi(c); + lvl = lc; + } + else { + c1 = c, c2 = c; + } + push(mk_ite_rec(a1, b1, c1)); + push(mk_ite_rec(a2, b2, c2)); + r = make_node(lvl, read(2), read(1)); + pop(2); + e1->m_result = r; + return r; + } + + bdd bdd_manager::mk_exists(unsigned n, unsigned const* vars, bdd const& b) { + // SASSERT(well_formed()); + return bdd(mk_quant(n, vars, b.root, bdd_or_op), this); + } + + bdd bdd_manager::mk_forall(unsigned n, unsigned const* vars, bdd const& b) { + return bdd(mk_quant(n, vars, b.root, bdd_and_op), this); + } + + bdd_manager::BDD bdd_manager::mk_quant(unsigned n, unsigned const* vars, BDD b, bdd_op op) { + BDD result = b; + for (unsigned i = 0; i < n; ++i) { + result = mk_quant_rec(m_var2level[vars[i]], result, op); + } + return result; + } + + bdd_manager::BDD bdd_manager::mk_quant_rec(unsigned l, BDD b, bdd_op op) { + unsigned lvl = level(b); + BDD r; + if (is_const(b)) { + r = b; + } + else if (lvl == l) { + r = apply(lo(b), hi(b), op); + } + else if (lvl < l) { + r = b; + } + else { + BDD a = level2bdd(l); + bdd_op q_op = op == bdd_and_op ? bdd_and_proj_op : bdd_or_proj_op; + op_entry * e1 = pop_entry(a, b, q_op); + op_entry const* e2 = m_op_cache.insert_if_not_there(e1); + if (check_result(e1, e2, a, b, q_op)) { + r = e2->m_result; + } + else { + SASSERT(e1->m_result == -1); + push(mk_quant_rec(l, lo(b), op)); + push(mk_quant_rec(l, hi(b), op)); + r = make_node(lvl, read(2), read(1)); + pop(2); + e1->m_result = r; + } + } + SASSERT(r != UINT_MAX); + return r; + } + + double bdd_manager::count(BDD b, unsigned z) { + init_mark(); + m_count.resize(m_nodes.size()); + m_count[0] = z; + m_count[1] = 1-z; + set_mark(0); + set_mark(1); + m_todo.push_back(b); + while (!m_todo.empty()) { + BDD r = m_todo.back(); + if (is_marked(r)) { + m_todo.pop_back(); + } + else if (!is_marked(lo(r))) { + SASSERT (is_const(r) || r != lo(r)); + m_todo.push_back(lo(r)); + } + else if (!is_marked(hi(r))) { + SASSERT (is_const(r) || r != hi(r)); + m_todo.push_back(hi(r)); + } + else { + m_count[r] = m_count[lo(r)] + m_count[hi(r)]; + set_mark(r); + m_todo.pop_back(); + } + } + return m_count[b]; + } + + unsigned bdd_manager::bdd_size(bdd const& b) { + init_mark(); + set_mark(0); + set_mark(1); + unsigned sz = 0; + m_todo.push_back(b.root); + while (!m_todo.empty()) { + BDD r = m_todo.back(); + m_todo.pop_back(); + if (!is_marked(r)) { + ++sz; + set_mark(r); + if (!is_marked(lo(r))) { + m_todo.push_back(lo(r)); + } + if (!is_marked(hi(r))) { + m_todo.push_back(hi(r)); + } + } + } + return sz; + } + + void bdd_manager::alloc_free_nodes(unsigned n) { + for (unsigned i = 0; i < n; ++i) { + m_free_nodes.push_back(m_nodes.size()); + m_nodes.push_back(bdd_node()); + m_nodes.back().m_index = m_nodes.size() - 1; + } + m_free_nodes.reverse(); + } + + void bdd_manager::gc() { + m_free_nodes.reset(); + IF_VERBOSE(13, verbose_stream() << "(bdd :gc " << m_nodes.size() << ")\n";); + svector reachable(m_nodes.size(), false); + for (unsigned i = m_bdd_stack.size(); i-- > 0; ) { + reachable[m_bdd_stack[i]] = true; + m_todo.push_back(m_bdd_stack[i]); + } + for (unsigned i = m_nodes.size(); i-- > 2; ) { + if (m_nodes[i].m_refcount > 0) { + reachable[i] = true; + m_todo.push_back(i); + } + } + while (!m_todo.empty()) { + BDD b = m_todo.back(); + m_todo.pop_back(); + SASSERT(reachable[b]); + if (is_const(b)) continue; + if (!reachable[lo(b)]) { + reachable[lo(b)] = true; + m_todo.push_back(lo(b)); + } + if (!reachable[hi(b)]) { + reachable[hi(b)] = true; + m_todo.push_back(hi(b)); + } + } + for (unsigned i = m_nodes.size(); i-- > 2; ) { + if (!reachable[i]) { + m_nodes[i].set_internal(); + SASSERT(m_nodes[i].m_refcount == 0); + m_free_nodes.push_back(i); + } + } + // sort free nodes so that adjacent nodes are picked in order of use + std::sort(m_free_nodes.begin(), m_free_nodes.end()); + m_free_nodes.reverse(); + + ptr_vector to_delete, to_keep; + for (auto* e : m_op_cache) { + if (e->m_result != -1) { + to_delete.push_back(e); + } + else { + to_keep.push_back(e); + } + } + m_op_cache.reset(); + for (op_entry* e : to_delete) { + m_alloc.deallocate(sizeof(*e), e); + } + for (op_entry* e : to_keep) { + m_op_cache.insert(e); + } + + m_node_table.reset(); + // re-populate node cache + for (unsigned i = m_nodes.size(); i-- > 2; ) { + if (reachable[i]) { + SASSERT(m_nodes[i].m_index == i); + m_node_table.insert(m_nodes[i]); + } + } + SASSERT(well_formed()); + } + + void bdd_manager::init_mark() { + m_mark.resize(m_nodes.size()); + ++m_mark_level; + if (m_mark_level == 0) { + m_mark.fill(0); + ++m_mark_level; + } + } + + std::ostream& bdd_manager::display(std::ostream& out, bdd const& b) { + init_mark(); + m_todo.push_back(b.root); + m_reorder_rc.reserve(m_nodes.size()); + while (!m_todo.empty()) { + BDD r = m_todo.back(); + if (is_marked(r)) { + m_todo.pop_back(); + } + else if (lo(r) == 0 && hi(r) == 0) { + set_mark(r); + m_todo.pop_back(); + } + else if (!is_marked(lo(r))) { + m_todo.push_back(lo(r)); + } + else if (!is_marked(hi(r))) { + m_todo.push_back(hi(r)); + } + else { + out << r << " : " << var(r) << " @ " << level(r) << " " << lo(r) << " " << hi(r) << " " << m_reorder_rc[r] << "\n"; + set_mark(r); + m_todo.pop_back(); + } + } + return out; + } + + bool bdd_manager::well_formed() { + bool ok = true; + for (unsigned n : m_free_nodes) { + ok &= (lo(n) == 0 && hi(n) == 0 && m_nodes[n].m_refcount == 0); + if (!ok) { + IF_VERBOSE(0, + verbose_stream() << "free node is not internal " << n << " " << lo(n) << " " << hi(n) << " " << m_nodes[n].m_refcount << "\n"; + display(verbose_stream());); + UNREACHABLE(); + return false; + } + } + for (bdd_node const& n : m_nodes) { + if (n.is_internal()) continue; + unsigned lvl = n.m_level; + BDD lo = n.m_lo; + BDD hi = n.m_hi; + ok &= is_const(lo) || level(lo) < lvl; + ok &= is_const(hi) || level(hi) < lvl; + ok &= is_const(lo) || !m_nodes[lo].is_internal(); + ok &= is_const(hi) || !m_nodes[hi].is_internal(); + if (!ok) { + IF_VERBOSE(0, display(verbose_stream() << n.m_index << " lo " << lo << " hi " << hi << "\n");); + UNREACHABLE(); + return false; + } + } + return ok; + } + + std::ostream& bdd_manager::display(std::ostream& out) { + m_reorder_rc.reserve(m_nodes.size()); + for (unsigned i = 0; i < m_nodes.size(); ++i) { + bdd_node const& n = m_nodes[i]; + if (n.is_internal()) continue; + out << i << " : v" << m_level2var[n.m_level] << " " << n.m_lo << " " << n.m_hi << " rc " << m_reorder_rc[i] << "\n"; + } + for (unsigned i = 0; i < m_level2nodes.size(); ++i) { + out << "level: " << i << " : " << m_level2nodes[i] << "\n"; + } + return out; + } + + bdd& bdd::operator=(bdd const& other) { unsigned r1 = root; root = other.root; m->inc_ref(root); m->dec_ref(r1); return *this; } + std::ostream& operator<<(std::ostream& out, bdd const& b) { return b.display(out); } + +} diff --git a/src/sat/sat_bdd.h b/src/sat/sat_bdd.h new file mode 100644 index 000000000..41d1115b9 --- /dev/null +++ b/src/sat/sat_bdd.h @@ -0,0 +1,258 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_bdd + +Abstract: + + Simple BDD package modeled after BuDDy, which is modeled after CUDD. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-13 + +Revision History: + +--*/ +#ifndef SAT_BDD_H_ +#define SAT_BDD_H_ + +#include "util/vector.h" +#include "util/map.h" +#include "util/small_object_allocator.h" + +namespace sat { + + class bdd; + + class bdd_manager { + friend bdd; + + typedef unsigned BDD; + + enum bdd_op { + bdd_and_op = 2, + bdd_or_op = 3, + bdd_xor_op = 4, + bdd_not_op = 5, + bdd_and_proj_op = 6, + bdd_or_proj_op = 7, + bdd_no_op = 8, + }; + + struct bdd_node { + bdd_node(unsigned level, BDD lo, BDD hi): + m_refcount(0), + m_level(level), + m_lo(lo), + m_hi(hi), + m_index(0) + {} + bdd_node(): m_refcount(0), m_level(0), m_lo(0), m_hi(0), m_index(0) {} + unsigned m_refcount : 10; + unsigned m_level : 22; + BDD m_lo; + BDD m_hi; + unsigned m_index; + unsigned hash() const { return mk_mix(m_level, m_lo, m_hi); } + bool is_internal() const { return m_lo == 0 && m_hi == 0; } + void set_internal() { m_lo = 0; m_hi = 0; } + }; + + enum cost_metric { + cnf_cost, + dnf_cost, + bdd_cost + }; + + struct hash_node { + unsigned operator()(bdd_node const& n) const { return n.hash(); } + }; + + struct eq_node { + bool operator()(bdd_node const& a, bdd_node const& b) const { + return a.m_lo == b.m_lo && a.m_hi == b.m_hi && a.m_level == b.m_level; + } + }; + + typedef hashtable node_table; + + struct op_entry { + op_entry(BDD l, BDD r, BDD op): + m_bdd1(l), + m_bdd2(r), + m_op(op), + m_result(0) + {} + + BDD m_bdd1; + BDD m_bdd2; + BDD m_op; + BDD m_result; + unsigned hash() const { return mk_mix(m_bdd1, m_bdd2, m_op); } + }; + + struct hash_entry { + unsigned operator()(op_entry* e) const { return e->hash(); } + }; + + struct eq_entry { + bool operator()(op_entry * a, op_entry * b) const { + return a->m_bdd1 == b->m_bdd2 && a->m_bdd2 == b->m_bdd2 && a->m_op == b->m_op; + } + }; + + typedef ptr_hashtable op_table; + + svector m_nodes; + op_table m_op_cache; + node_table m_node_table; + unsigned_vector m_apply_const; + svector m_bdd_stack; + op_entry* m_spare_entry; + svector m_var2bdd; + unsigned_vector m_var2level, m_level2var; + unsigned_vector m_free_nodes; + small_object_allocator m_alloc; + mutable svector m_mark; + mutable unsigned m_mark_level; + mutable svector m_count; + mutable svector m_todo; + bool m_disable_gc; + bool m_is_new_node; + unsigned m_max_num_bdd_nodes; + unsigned_vector m_S, m_T, m_to_free; // used for reordering + vector m_level2nodes; + unsigned_vector m_reorder_rc; + cost_metric m_cost_metric; + BDD m_cost_bdd; + + BDD make_node(unsigned level, BDD l, BDD r); + bool is_new_node() const { return m_is_new_node; } + + BDD apply_const(BDD a, BDD b, bdd_op op); + BDD apply(BDD arg1, BDD arg2, bdd_op op); + BDD mk_quant(unsigned n, unsigned const* vars, BDD b, bdd_op op); + + BDD apply_rec(BDD arg1, BDD arg2, bdd_op op); + BDD mk_not_rec(BDD b); + BDD mk_ite_rec(BDD a, BDD b, BDD c); + BDD mk_quant_rec(unsigned lvl, BDD b, bdd_op op); + + void push(BDD b); + void pop(unsigned num_scopes); + BDD read(unsigned index); + + op_entry* pop_entry(BDD l, BDD r, BDD op); + void push_entry(op_entry* e); + bool check_result(op_entry*& e1, op_entry const* e2, BDD a, BDD b, BDD c); + + double count(BDD b, unsigned z); + + void alloc_free_nodes(unsigned n); + void init_mark(); + void set_mark(unsigned i) { m_mark[i] = m_mark_level; } + bool is_marked(unsigned i) { return m_mark[i] == m_mark_level; } + + void init_reorder(); + void reorder_incref(unsigned n); + void reorder_decref(unsigned n); + void sift_up(unsigned level); + void sift_var(unsigned v); + double current_cost(); + bool is_bad_cost(double new_cost, double best_cost) const; + + static const BDD false_bdd = 0; + static const BDD true_bdd = 1; + static const unsigned max_rc = (1 << 10) - 1; + + inline bool is_true(BDD b) const { return b == true_bdd; } + inline bool is_false(BDD b) const { return b == false_bdd; } + inline bool is_const(BDD b) const { return b <= 1; } + inline unsigned level(BDD b) const { return m_nodes[b].m_level; } + inline unsigned var(BDD b) const { return m_level2var[level(b)]; } + inline BDD lo(BDD b) const { return m_nodes[b].m_lo; } + inline BDD hi(BDD b) const { return m_nodes[b].m_hi; } + inline void inc_ref(BDD b) { if (m_nodes[b].m_refcount != max_rc) m_nodes[b].m_refcount++; VERIFY(!m_free_nodes.contains(b)); } + inline void dec_ref(BDD b) { if (m_nodes[b].m_refcount != max_rc) m_nodes[b].m_refcount--; VERIFY(!m_free_nodes.contains(b)); } + inline BDD level2bdd(unsigned l) const { return m_var2bdd[m_level2var[l]]; } + + double dnf_size(BDD b) { return count(b, 0); } + double cnf_size(BDD b) { return count(b, 1); } + unsigned bdd_size(bdd const& b); + + bdd mk_not(bdd b); + bdd mk_and(bdd const& a, bdd const& b); + bdd mk_or(bdd const& a, bdd const& b); + bdd mk_xor(bdd const& a, bdd const& b); + + void reserve_var(unsigned v); + bool well_formed(); + + public: + struct mem_out {}; + + bdd_manager(unsigned nodes); + ~bdd_manager(); + + void set_max_num_nodes(unsigned n) { m_max_num_bdd_nodes = n; } + + bdd mk_var(unsigned i); + bdd mk_nvar(unsigned i); + + bdd mk_true(); + bdd mk_false(); + + bdd mk_exists(unsigned n, unsigned const* vars, bdd const & b); + bdd mk_forall(unsigned n, unsigned const* vars, bdd const & b); + bdd mk_exists(unsigned v, bdd const& b); + bdd mk_forall(unsigned v, bdd const& b); + bdd mk_ite(bdd const& c, bdd const& t, bdd const& e); + + std::ostream& display(std::ostream& out); + std::ostream& display(std::ostream& out, bdd const& b); + + void gc(); + void try_reorder(); + void try_cnf_reorder(bdd const& b); + }; + + class bdd { + friend class bdd_manager; + unsigned root; + bdd_manager* m; + bdd(unsigned root, bdd_manager* m): root(root), m(m) { m->inc_ref(root); } + public: + bdd(bdd & other): root(other.root), m(other.m) { m->inc_ref(root); } + bdd(bdd && other): root(0), m(other.m) { std::swap(root, other.root); } + bdd& operator=(bdd const& other); + ~bdd() { m->dec_ref(root); } + bdd lo() const { return bdd(m->lo(root), m); } + bdd hi() const { return bdd(m->hi(root), m); } + unsigned var() const { return m->var(root); } + + bool is_true() const { return root == bdd_manager::true_bdd; } + bool is_false() const { return root == bdd_manager::false_bdd; } + + bdd operator!() { return m->mk_not(*this); } + bdd operator&&(bdd const& other) { return m->mk_and(*this, other); } + bdd operator||(bdd const& other) { return m->mk_or(*this, other); } + bdd operator^(bdd const& other) { return m->mk_xor(*this, other); } + bdd operator|=(bdd const& other) { return *this = *this || other; } + bdd operator&=(bdd const& other) { return *this = *this && other; } + std::ostream& display(std::ostream& out) const { return m->display(out, *this); } + bool operator==(bdd const& other) const { return root == other.root; } + bool operator!=(bdd const& other) const { return root != other.root; } + double cnf_size() const { return m->cnf_size(root); } + double dnf_size() const { return m->dnf_size(root); } + unsigned bdd_size() const { return m->bdd_size(*this); } + }; + + std::ostream& operator<<(std::ostream& out, bdd const& b); + +} + + +#endif diff --git a/src/sat/sat_big.cpp b/src/sat/sat_big.cpp new file mode 100644 index 000000000..35898a110 --- /dev/null +++ b/src/sat/sat_big.cpp @@ -0,0 +1,289 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + sat_big.cpp + +Abstract: + + binary implication graph structure. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-12-13. + +Revision History: + +--*/ +#include "sat/sat_big.h" +#include "sat/sat_solver.h" + +namespace sat { + + big::big(random_gen& rand): + m_rand(rand) { + } + + void big::init(solver& s, bool learned) { + init_adding_edges(s.num_vars(), learned); + unsigned num_lits = m_num_vars * 2; + literal_vector lits, r; + SASSERT(num_lits == m_dag.size() && num_lits == m_roots.size()); + size_t_map seen_idx; + for (unsigned l_idx = 0; l_idx < num_lits; l_idx++) { + literal u = to_literal(l_idx); + if (s.was_eliminated(u.var())) + continue; + auto& edges = m_dag[l_idx]; + for (watched const& w : s.get_wlist(l_idx)) { + if (learned ? w.is_binary_clause() : w.is_binary_non_learned_clause()) { + literal v = w.get_literal(); + m_roots[v.index()] = false; + edges.push_back(v); + } +#if 0 + if (w.is_ext_constraint() && + s.m_ext && + learned && + !seen_idx.contains(w.get_ext_constraint_idx()) && + s.m_ext->is_extended_binary(w.get_ext_constraint_idx(), r)) { + seen_idx.insert(w.get_ext_constraint_idx(), true); + for (unsigned i = 0; i < r.size(); ++i) { + literal u = r[i]; + for (unsigned j = i + 1; j < r.size(); ++j) { + // add ~r[i] -> r[j] + literal v = r[j]; + literal u = ~r[j]; + m_roots[v.index()] = false; + m_dag[u.index()].push_back(v); + // add ~r[j] -> r[i] + v.neg(); + u.neg(); + m_roots[u.index()] = false; + m_dag[v.index()].push_back(u); + } + } + } +#endif + } + } + done_adding_edges(); + } + + void big::reinit() { + done_adding_edges(); + } + + void big::init_adding_edges(unsigned num_vars, bool learned) { + m_learned = learned; + m_num_vars = num_vars; + unsigned num_lits = m_num_vars * 2; + m_dag.reset(); + m_roots.reset(); + m_dag.resize(num_lits, 0); + m_roots.resize(num_lits, true); + } + + void big::add_edge(literal u, literal v) { + m_dag[u.index()].push_back(v); + } + + void big::done_adding_edges() { + for (auto& edges : m_dag) { + shuffle(edges.size(), edges.c_ptr(), m_rand); + } + init_dfs_num(); + } + + + struct big::pframe { + literal m_parent; + literal m_child; + pframe(literal p, literal c): + m_parent(p), m_child(c) {} + literal child() const { return m_child; } + literal parent() const { return m_parent; } + }; + + void big::init_dfs_num() { + unsigned num_lits = m_num_vars * 2; + m_left.reset(); + m_right.reset(); + m_root.reset(); + m_parent.reset(); + m_left.resize(num_lits, 0); + m_right.resize(num_lits, -1); + m_root.resize(num_lits, null_literal); + m_parent.resize(num_lits, null_literal); + for (unsigned i = 0; i < num_lits; ++i) { + m_root[i] = to_literal(i); + m_parent[i] = to_literal(i); + } + svector todo; + // retrieve literals that have no predecessors + for (unsigned l_idx = 0; l_idx < num_lits; l_idx++) { + literal u(to_literal(l_idx)); + if (m_roots[u.index()]) { + todo.push_back(pframe(null_literal, u)); + } + } + shuffle(todo.size(), todo.c_ptr(), m_rand); + int dfs_num = 0; + while (!todo.empty()) { + literal u = todo.back().child(); + if (m_left[u.index()] > 0) { + // already visited + if (m_right[u.index()] < 0) { + m_right[u.index()] = ++dfs_num; + } + todo.pop_back(); + } + else { + SASSERT(m_left[u.index()] == 0); + m_left[u.index()] = ++dfs_num; + literal p = todo.back().parent(); + if (p != null_literal) { + m_root[u.index()] = m_root[p.index()]; + m_parent[u.index()] = p; + } + for (literal v : m_dag[u.index()]) { + if (m_left[v.index()] == 0) { + todo.push_back(pframe(u, v)); + } + } + } + } + for (unsigned i = 0; i < num_lits; ++i) { + if (m_right[i] < 0) { + VERIFY(m_left[i] == 0); + m_left[i] = ++dfs_num; + m_right[i] = ++dfs_num; + } + } + DEBUG_CODE(for (unsigned i = 0; i < num_lits; ++i) { VERIFY(m_left[i] < m_right[i]);}); + } + + // svector> big::s_del_bin; + + bool big::in_del(literal u, literal v) const { + if (u.index() > v.index()) std::swap(u, v); + return m_del_bin.contains(std::make_pair(u, v)); + } + + void big::add_del(literal u, literal v) { + if (u.index() > v.index()) std::swap(u, v); + m_del_bin.push_back(std::make_pair(u, v)); + } + + unsigned big::reduce_tr(solver& s) { + unsigned idx = 0; + unsigned elim = 0; + m_del_bin.reset(); + for (watch_list & wlist : s.m_watches) { + if (s.inconsistent()) break; + literal u = to_literal(idx++); + watch_list::iterator it = wlist.begin(); + watch_list::iterator itprev = it; + watch_list::iterator end = wlist.end(); + for (; it != end; ++it) { + watched& w = *it; + if (learned() ? w.is_binary_learned_clause() : w.is_binary_clause()) { + literal v = w.get_literal(); + if (u != get_parent(v) && safe_reach(u, v)) { + ++elim; + add_del(~u, v); + if (s.get_config().m_drat) s.m_drat.del(~u, v); + s.m_mc.stackv().reset(); // TBD: brittle code + s.add_ate(~u, v); + if (find_binary_watch(wlist, ~v)) { + IF_VERBOSE(10, verbose_stream() << "binary: " << ~u << "\n"); + s.assign(~u, justification()); + } + // could turn non-learned non-binary tautology into learned binary. + s.get_wlist(~v).erase(watched(~u, w.is_learned())); + continue; + } + } + *itprev = *it; + itprev++; + } + wlist.set_end(itprev); + } + +#if 0 + s_del_bin.append(m_del_bin); + IF_VERBOSE(1, + display(verbose_stream() << "Learned: " << learned() << ":"); + verbose_stream() << "del-bin\n"; + for (auto p : m_del_bin) { + verbose_stream() << p.first << " " << p.second << "\n"; + if (safe_reach(~p.first, p.second)) { + display_path(verbose_stream(), ~p.first, p.second) << " " << "\n"; + } + else { + display_path(verbose_stream(), ~p.second, p.first) << " " << "\n"; + } + } + ); +#endif + s.propagate(false); + return elim; + } + + bool big::safe_reach(literal u, literal v) { + if (!reaches(u, v)) return false; + while (u != v) { + literal w = next(u, v); + if (in_del(~u, w)) { + return false; + } + u = w; + } + return true; + } + + literal big::next(literal u, literal v) const { + SASSERT(reaches(u, v)); + literal result = null_literal; + int left = m_right[u.index()]; + // identify the path from the reachability graph + for (literal w : m_dag[u.index()]) { + if (reaches(u, w) && + (w == v || reaches(w, v)) && + m_left[w.index()] < left) { + left = m_left[w.index()]; + result = w; + } + } + SASSERT(result != null_literal); + return result; + } + + std::ostream& big::display_path(std::ostream& out, literal u, literal v) const { + while (u != v) { + out << u << " -> "; + u = next(u, v); + } + return out << v; + } + + void big::display(std::ostream& out) const { + unsigned idx = 0; + for (auto& next : m_dag) { + if (!next.empty()) { + out << to_literal(idx) << " : " << m_left[idx] << ":" << m_right[idx] << " -> " << next << "\n"; +#if 0 + for (literal n : next) { + out << n << "[" << m_left[n.index()] << ":" << m_right[n.index()] << "] "; + } + out << "\n"; +#endif + } + ++idx; + } + } + + + +}; diff --git a/src/sat/sat_big.h b/src/sat/sat_big.h new file mode 100644 index 000000000..898ddd1e8 --- /dev/null +++ b/src/sat/sat_big.h @@ -0,0 +1,88 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_big.h + +Abstract: + + binary implication graph structure. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-12-13. + +Revision History: + +--*/ +#ifndef SAT_BIG_H_ +#define SAT_BIG_H_ + +#include "sat/sat_types.h" +#include "util/statistics.h" +#include "util/params.h" + +namespace sat { + class solver; + + class big { + random_gen& m_rand; + unsigned m_num_vars; + vector m_dag; + svector m_roots; + svector m_left, m_right; + literal_vector m_root, m_parent; + bool m_learned; + + svector> m_del_bin; + + + void init_dfs_num(); + struct pframe; + + bool safe_reach(literal u, literal v); + literal next(literal u, literal v) const; + + std::ostream& display_path(std::ostream& out, literal u, literal v) const; + + void add_del(literal u, literal v); + bool in_del(literal u, literal v) const; + + public: + + // static svector> s_del_bin; + + big(random_gen& rand); + /** + \brief initialize a BIG from a solver. + */ + void init(solver& s, bool learned); + + void reinit(); + + /** + \brief initialize a BIG externally by adding implications. + */ + void init_adding_edges(unsigned num_vars, bool learned); + void add_edge(literal u, literal v); + void done_adding_edges(); + + void ensure_big(solver& s, bool learned) { if (m_left.empty()) init(s, learned); } + + unsigned reduce_tr(solver& s); + + // does it include learned binaries? + bool learned() const { return m_learned; } + int get_left(literal l) const { return m_left[l.index()]; } + int get_right(literal l) const { return m_right[l.index()]; } + literal get_parent(literal l) const { return m_parent[l.index()]; } + literal get_root(literal l) const { return m_root[l.index()]; } + bool reaches(literal u, literal v) const { return m_left[u.index()] < m_left[v.index()] && m_right[v.index()] < m_right[u.index()]; } + bool connected(literal u, literal v) const { return reaches(u, v) || reaches(~v, ~u); } + void display(std::ostream& out) const; + + }; +}; + +#endif diff --git a/src/sat/sat_clause.cpp b/src/sat/sat_clause.cpp index 67f894566..3cbd3015b 100644 --- a/src/sat/sat_clause.cpp +++ b/src/sat/sat_clause.cpp @@ -32,9 +32,9 @@ namespace sat { m_used(false), m_frozen(false), m_reinit_stack(false), - m_inact_rounds(0) { - m_psm = 0; - m_glue = 0; + m_inact_rounds(0), + m_glue(255), + m_psm(255) { memcpy(m_lits, lits, sizeof(literal) * sz); mark_strengthened(); SASSERT(check_approx()); @@ -61,15 +61,15 @@ namespace sat { } bool clause::contains(literal l) const { - for (unsigned i = 0; i < m_size; i++) - if (m_lits[i] == l) + for (literal l2 : *this) + if (l2 == l) return true; return false; } bool clause::contains(bool_var v) const { - for (unsigned i = 0; i < m_size; i++) - if (m_lits[i].var() == v) + for (literal l : *this) + if (l.var() == v) return true; return false; } @@ -87,9 +87,16 @@ namespace sat { mark_strengthened(); } + void clause::shrink(unsigned num_lits) { + SASSERT(num_lits <= m_size); + if (num_lits < m_size) { + m_size = num_lits; + mark_strengthened(); + } + } + bool clause::satisfied_by(model const & m) const { - for (unsigned i = 0; i < m_size; i++) { - literal l = m_lits[i]; + for (literal l : *this) { if (l.sign()) { if (m[l.var()] == l_false) return true; @@ -102,6 +109,27 @@ namespace sat { return false; } + clause_offset clause::get_new_offset() const { + unsigned o1 = m_lits[0].index(); +#if defined(_AMD64_) || defined(_M_IA64) + if (sizeof(clause_offset) == 8) { + unsigned o2 = m_lits[1].index(); + return (clause_offset)o1 + (((clause_offset)o2) << 32); + } +#endif + return (clause_offset)o1; + } + + void clause::set_new_offset(clause_offset offset) { + m_lits[0] = to_literal(static_cast(offset)); +#if defined(_AMD64_) || defined(_M_IA64) + if (sizeof(offset) == 8) { + m_lits[1] = to_literal(static_cast(offset >> 32)); + } +#endif + } + + void tmp_clause::set(unsigned num_lits, literal const * lits, bool learned) { if (m_clause && m_clause->m_capacity < num_lits) { dealloc_svect(m_clause); @@ -125,94 +153,46 @@ namespace sat { clause_allocator::clause_allocator(): m_allocator("clause-allocator") { -#if defined(_AMD64_) - m_num_segments = 0; -#endif + } + + void clause_allocator::finalize() { + m_allocator.reset(); } clause * clause_allocator::get_clause(clause_offset cls_off) const { -#if defined(_AMD64_) -#if defined (Z3DEBUG) - clause const* result; - if (((cls_off & c_alignment_mask) == c_last_segment)) { - unsigned id = cls_off >> c_cls_alignment; - bool check = m_last_seg_id2cls.find(id, result); - SASSERT(check); - return const_cast(result); - } -#endif - return reinterpret_cast(m_segments[cls_off & c_alignment_mask] + (static_cast(cls_off) & ~c_alignment_mask)); -#else + SASSERT(cls_off == reinterpret_cast(reinterpret_cast(cls_off))); return reinterpret_cast(cls_off); -#endif } -#if defined(_AMD64_) - unsigned clause_allocator::get_segment(clause const* cls) { - size_t ptr = reinterpret_cast(cls); - - SASSERT((ptr & c_alignment_mask) == 0); - ptr &= 0xFFFFFFFF00000000ull; // Keep only high part - unsigned i = 0; - for (i = 0; i < m_num_segments; ++i) - if (m_segments[i] == ptr) - return i; - i = m_num_segments; - SASSERT(i <= c_last_segment); -#if defined(Z3DEBUG) - if (i == c_last_segment) { - if (!m_last_seg_id2cls.contains(cls->id())) - m_last_seg_id2cls.insert(cls->id(), cls); - } - else { - ++m_num_segments; - m_segments[i] = ptr; - } -#else - if (i == c_last_segment) { - throw default_exception("segment out of range"); - } - m_segments[i] = ptr; - ++m_num_segments; -#endif - - return i; - } -#endif - clause_offset clause_allocator::get_offset(clause const * cls) const { -#if defined(_AMD64_) - unsigned segment = const_cast(this)->get_segment(cls); -#if defined(Z3DEBUG) - SASSERT(segment <= c_last_segment); - if (segment == c_last_segment) { - SASSERT(m_last_seg_id2cls.contains(cls->id())); - return (cls->id() << c_cls_alignment) | c_last_segment; - } -#endif - return static_cast(reinterpret_cast(cls)) + segment; -#else + SASSERT(cls == reinterpret_cast(reinterpret_cast(cls))); return reinterpret_cast(cls); -#endif } clause * clause_allocator::mk_clause(unsigned num_lits, literal const * lits, bool learned) { size_t size = clause::get_obj_size(num_lits); void * mem = m_allocator.allocate(size); clause * cls = new (mem) clause(m_id_gen.mk(), num_lits, lits, learned); - TRACE("sat", tout << "alloc: " << cls->id() << " " << *cls << " " << (learned?"l":"a") << "\n";); + TRACE("sat_clause", tout << "alloc: " << cls->id() << " " << *cls << " " << (learned?"l":"a") << "\n";); SASSERT(!learned || cls->is_learned()); return cls; } + clause * clause_allocator::copy_clause(clause const& other) { + size_t size = clause::get_obj_size(other.size()); + void * mem = m_allocator.allocate(size); + clause * cls = new (mem) clause(m_id_gen.mk(), other.size(), other.m_lits, other.is_learned()); + cls->m_reinit_stack = other.on_reinit_stack(); + cls->m_glue = other.glue(); + cls->m_psm = other.psm(); + cls->m_frozen = other.frozen(); + cls->m_approx = other.approx(); + return cls; + } + void clause_allocator::del_clause(clause * cls) { - TRACE("sat", tout << "delete: " << cls->id() << " " << *cls << "\n";); + TRACE("sat_clause", tout << "delete: " << cls->id() << " " << *cls << "\n";); m_id_gen.recycle(cls->id()); -#if defined(_AMD64_) -#if defined(Z3DEBUG) - m_last_seg_id2cls.remove(cls->id()); -#endif -#endif size_t size = clause::get_obj_size(cls->m_capacity); cls->~clause(); m_allocator.deallocate(size, cls); @@ -232,10 +212,8 @@ namespace sat { } std::ostream & operator<<(std::ostream & out, clause_vector const & cs) { - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - out << *(*it) << "\n"; + for (clause *cp : cs) { + out << *cp << "\n"; } return out; } @@ -257,12 +235,12 @@ namespace sat { } std::ostream & operator<<(std::ostream & out, clause_wrapper const & c) { - out << "("; - for (unsigned i = 0; i < c.size(); i++) { - if (i > 0) out << " "; - out << c[i]; + if (c.is_binary()) { + out << "(" << c[0] << " " << c[1] << ")"; + } + else { + out << c.get_clause()->id() << ": " << *c.get_clause(); } - out << ")"; return out; } diff --git a/src/sat/sat_clause.h b/src/sat/sat_clause.h index b7cf5e577..f95696b3d 100644 --- a/src/sat/sat_clause.h +++ b/src/sat/sat_clause.h @@ -19,10 +19,11 @@ Revision History: #ifndef SAT_CLAUSE_H_ #define SAT_CLAUSE_H_ -#include "sat/sat_types.h" #include "util/small_object_allocator.h" #include "util/id_gen.h" #include "util/map.h" +#include "sat/sat_types.h" +#include "sat/sat_allocator.h" #ifdef _MSC_VER #pragma warning(disable : 4200) @@ -33,6 +34,10 @@ namespace sat { class clause_allocator; + class clause; + + std::ostream & operator<<(std::ostream & out, clause const & c); + class clause { friend class clause_allocator; friend class tmp_clause; @@ -57,11 +62,12 @@ namespace sat { public: unsigned id() const { return m_id; } unsigned size() const { return m_size; } + unsigned capacity() const { return m_capacity; } literal & operator[](unsigned idx) { SASSERT(idx < m_size); return m_lits[idx]; } literal const & operator[](unsigned idx) const { SASSERT(idx < m_size); return m_lits[idx]; } bool is_learned() const { return m_learned; } - void unset_learned() { SASSERT(is_learned()); m_learned = false; } - void shrink(unsigned num_lits) { SASSERT(num_lits <= m_size); if (num_lits < m_size) { m_size = num_lits; mark_strengthened(); } } + void set_learned(bool l) { SASSERT(is_learned() != l); m_learned = l; } + void shrink(unsigned num_lits); bool strengthened() const { return m_strengthened; } void mark_strengthened() { m_strengthened = true; update_approx(); } void unmark_strengthened() { m_strengthened = false; } @@ -73,6 +79,8 @@ namespace sat { bool check_approx() const; // for debugging literal * begin() { return m_lits; } literal * end() { return m_lits + m_size; } + literal const * begin() const { return m_lits; } + literal const * end() const { return m_lits + m_size; } bool contains(literal l) const; bool contains(bool_var v) const; bool satisfied_by(model const & m) const; @@ -90,12 +98,13 @@ namespace sat { unsigned glue() const { return m_glue; } void set_psm(unsigned psm) { m_psm = psm > 255 ? 255 : psm; } unsigned psm() const { return m_psm; } + clause_offset get_new_offset() const; + void set_new_offset(clause_offset off); bool on_reinit_stack() const { return m_reinit_stack; } void set_reinit_stack(bool f) { m_reinit_stack = f; } }; - std::ostream & operator<<(std::ostream & out, clause const & c); std::ostream & operator<<(std::ostream & out, clause_vector const & cs); class bin_clause { @@ -127,24 +136,16 @@ namespace sat { \brief Simple clause allocator that allows uint (32bit integers) to be used to reference clauses (even in 64bit machines). */ class clause_allocator { - small_object_allocator m_allocator; - id_gen m_id_gen; -#if defined(_AMD64_) - unsigned get_segment(clause const* cls); - static const unsigned c_cls_alignment = 3; - static const unsigned c_last_segment = (1ull << c_cls_alignment) - 1ull; - static const size_t c_alignment_mask = (1ull << c_cls_alignment) - 1ull; - unsigned m_num_segments; - size_t m_segments[c_last_segment]; -#if defined(Z3DEBUG) - u_map m_last_seg_id2cls; -#endif -#endif + sat_allocator m_allocator; + id_gen m_id_gen; public: clause_allocator(); + void finalize(); + size_t get_allocation_size() const { return m_allocator.get_allocation_size(); } clause * get_clause(clause_offset cls_off) const; clause_offset get_offset(clause const * ptr) const; clause * mk_clause(unsigned num_lits, literal const * lits, bool learned); + clause * copy_clause(clause const& other); void del_clause(clause * cls); }; @@ -160,8 +161,18 @@ namespace sat { }; unsigned m_l2_idx; public: - clause_wrapper(literal l1, literal l2):m_l1_idx(l1.to_uint()), m_l2_idx(l2.to_uint()) {} - clause_wrapper(clause & c):m_cls(&c), m_l2_idx(null_literal.to_uint()) {} + explicit clause_wrapper(literal l1, literal l2):m_l1_idx(l1.to_uint()), m_l2_idx(l2.to_uint()) {} + explicit clause_wrapper(clause & c):m_cls(&c), m_l2_idx(null_literal.to_uint()) {} + clause_wrapper& operator=(clause_wrapper const& other) { + if (other.is_binary()) { + m_l1_idx = other.m_l1_idx; + } + else { + m_cls = other.m_cls; + } + m_l2_idx = other.m_l2_idx; + return *this; + } bool is_binary() const { return m_l2_idx != null_literal.to_uint(); } unsigned size() const { return is_binary() ? 2 : m_cls->size(); } @@ -176,6 +187,7 @@ namespace sat { bool contains(literal l) const; bool contains(bool_var v) const; clause * get_clause() const { SASSERT(!is_binary()); return m_cls; } + bool was_removed() const { return !is_binary() && get_clause()->was_removed(); } }; typedef svector clause_wrapper_vector; diff --git a/src/sat/sat_clause_use_list.cpp b/src/sat/sat_clause_use_list.cpp index 5ee5b4cda..7ca0aa2c6 100644 --- a/src/sat/sat_clause_use_list.cpp +++ b/src/sat/sat_clause_use_list.cpp @@ -22,17 +22,20 @@ Revision History: namespace sat { bool clause_use_list::check_invariant() const { -#ifdef LAZY_USE_LIST unsigned sz = 0; - for (unsigned i = 0; i < m_clauses.size(); i++) - if (!m_clauses[i]->was_removed()) + for (clause* c : m_clauses) + if (!c->was_removed()) sz++; SASSERT(sz == m_size); -#endif + unsigned redundant = 0; + for (clause* c : m_clauses) + if (c->is_learned()) + redundant++; + SASSERT(redundant == m_num_redundant); + return true; } -#ifdef LAZY_USE_LIST void clause_use_list::iterator::consume() { while (true) { if (m_i == m_size) @@ -44,14 +47,11 @@ namespace sat { m_i++; } } -#endif clause_use_list::iterator::~iterator() { -#ifdef LAZY_USE_LIST while (m_i < m_size) next(); m_clauses.shrink(m_j); -#endif } }; diff --git a/src/sat/sat_clause_use_list.h b/src/sat/sat_clause_use_list.h index 121345f21..90c2d7779 100644 --- a/src/sat/sat_clause_use_list.h +++ b/src/sat/sat_clause_use_list.h @@ -24,30 +24,30 @@ Revision History: namespace sat { -#define LAZY_USE_LIST - /** \brief Clause use list with delayed deletion. */ class clause_use_list { clause_vector m_clauses; -#ifdef LAZY_USE_LIST unsigned m_size; -#endif + unsigned m_num_redundant; public: clause_use_list() { STRACE("clause_use_list_bug", tout << "[cul_created] " << this << "\n";); -#ifdef LAZY_USE_LIST m_size = 0; -#endif + m_num_redundant = 0; } unsigned size() const { -#ifdef LAZY_USE_LIST return m_size; -#else - return m_clauses.size(); -#endif + } + + unsigned num_redundant() const { + return m_num_redundant; + } + + unsigned num_irredundant() const { + return m_size - m_num_redundant; } bool empty() const { return size() == 0; } @@ -57,58 +57,59 @@ namespace sat { SASSERT(!m_clauses.contains(&c)); SASSERT(!c.was_removed()); m_clauses.push_back(&c); -#ifdef LAZY_USE_LIST m_size++; -#endif + if (c.is_learned()) ++m_num_redundant; } void erase_not_removed(clause & c) { STRACE("clause_use_list_bug", tout << "[cul_erase_not_removed] " << this << " " << &c << "\n";); -#ifdef LAZY_USE_LIST SASSERT(m_clauses.contains(&c)); SASSERT(!c.was_removed()); m_clauses.erase(&c); m_size--; -#else - m_clauses.erase(&c); -#endif + if (c.is_learned()) --m_num_redundant; } void erase(clause & c) { STRACE("clause_use_list_bug", tout << "[cul_erase] " << this << " " << &c << "\n";); -#ifdef LAZY_USE_LIST SASSERT(m_clauses.contains(&c)); SASSERT(c.was_removed()); m_size--; -#else - m_clauses.erase(&c); -#endif + if (c.is_learned()) --m_num_redundant; + } + + void block(clause const& c) { + SASSERT(c.is_learned()); + ++m_num_redundant; + SASSERT(check_invariant()); + } + + void unblock(clause const& c) { + SASSERT(!c.is_learned()); + --m_num_redundant; + SASSERT(check_invariant()); } void reset() { m_clauses.finalize(); -#ifdef LAZY_USE_LIST m_size = 0; -#endif + m_num_redundant = 0; } bool check_invariant() const; // iterate & compress - class iterator { + class iterator { clause_vector & m_clauses; unsigned m_size; unsigned m_i; -#ifdef LAZY_USE_LIST unsigned m_j; void consume(); -#endif + public: iterator(clause_vector & v):m_clauses(v), m_size(v.size()), m_i(0) { -#ifdef LAZY_USE_LIST m_j = 0; consume(); -#endif } ~iterator(); bool at_end() const { return m_i == m_size; } @@ -117,14 +118,21 @@ namespace sat { SASSERT(!at_end()); SASSERT(!m_clauses[m_i]->was_removed()); m_i++; -#ifdef LAZY_USE_LIST m_j++; consume(); -#endif } }; iterator mk_iterator() const { return iterator(const_cast(this)->m_clauses); } + + std::ostream& display(std::ostream& out) const { + iterator it = mk_iterator(); + while (!it.at_end()) { + out << it.curr() << "\n"; + it.next(); + } + return out; + } }; }; diff --git a/src/sat/sat_cleaner.cpp b/src/sat/sat_cleaner.cpp index 9dd53d8f6..c0c6fabe4 100644 --- a/src/sat/sat_cleaner.cpp +++ b/src/sat/sat_cleaner.cpp @@ -99,7 +99,9 @@ namespace sat { m_elim_literals++; break; case l_undef: - c[j] = c[i]; + if (i != j) { + std::swap(c[j], c[i]); + } j++; break; } @@ -115,19 +117,13 @@ namespace sat { else { unsigned new_sz = j; CTRACE("sat_cleaner_bug", new_sz < 2, tout << "new_sz: " << new_sz << "\n"; - if (c.size() > 0) tout << "unit: " << c[0] << "\n";); - SASSERT(c.frozen() || new_sz >= 2); + if (c.size() > 0) tout << "unit: " << c[0] << "\n"; + s.display_watches(tout);); if (new_sz == 0) { - // It can only happen with frozen clauses. - // active clauses would have signed the conflict. - SASSERT(c.frozen()); s.set_conflict(justification()); s.del_clause(c); } else if (new_sz == 1) { - // It can only happen with frozen clauses. - // active clauses would have propagated the literal - SASSERT(c.frozen()); s.assign(c[0], justification()); s.del_clause(c); } @@ -142,11 +138,13 @@ namespace sat { c.shrink(new_sz); *it2 = *it; it2++; - if (!c.frozen()) { - if (new_sz == 3) - s.attach_ter_clause(c); - else - s.attach_nary_clause(c); + if (!c.frozen()) { + s.attach_clause(c); + } + if (s.m_config.m_drat) { + // for optimization, could also report deletion + // of previous version of clause. + s.m_drat.add(c, true); } } } @@ -184,6 +182,7 @@ namespace sat { CASSERT("cleaner_bug", s.check_invariant()); unsigned trail_sz = s.m_trail.size(); s.propagate(false); // make sure that everything was propagated. + TRACE("sat_cleaner_bug", s.display(tout); s.display_watches(tout);); if (s.m_inconsistent) return false; if (m_last_num_units == trail_sz) diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index c67511275..c7f2377c9 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -19,23 +19,13 @@ Revision History: #include "sat/sat_config.h" #include "sat/sat_types.h" #include "sat/sat_params.hpp" +#include "sat/sat_simplifier_params.hpp" + namespace sat { - config::config(params_ref const & p): - m_restart_max(0), - m_always_true("always_true"), - m_always_false("always_false"), - m_caching("caching"), - m_random("random"), - m_geometric("geometric"), - m_luby("luby"), - m_dyn_psm("dyn_psm"), - m_psm("psm"), - m_glue("glue"), - m_glue_psm("glue_psm"), - m_psm_glue("psm_glue") { - m_num_parallel = 1; + config::config(params_ref const & p) { + m_incremental = false; // ad-hoc parameter updt_params(p); } @@ -44,31 +34,42 @@ namespace sat { m_max_memory = megabytes_to_bytes(p.max_memory()); symbol s = p.restart(); - if (s == m_luby) + if (s == symbol("luby")) m_restart = RS_LUBY; - else if (s == m_geometric) + else if (s == symbol("geometric")) m_restart = RS_GEOMETRIC; + else if (s == symbol("ema")) + m_restart = RS_EMA; + else if (s == symbol("static")) + m_restart = RS_STATIC; else throw sat_param_exception("invalid restart strategy"); + m_fast_glue_avg = p.restart_emafastglue(); + m_slow_glue_avg = p.restart_emaslowglue(); + m_restart_margin = p.restart_margin(); + m_restart_fast = p.restart_fast(); s = p.phase(); - if (s == m_always_false) + if (s == symbol("always_false")) m_phase = PS_ALWAYS_FALSE; - else if (s == m_always_true) + else if (s == symbol("always_true")) m_phase = PS_ALWAYS_TRUE; - else if (s == m_caching) + else if (s == symbol("caching")) m_phase = PS_CACHING; - else if (s == m_random) + else if (s == symbol("random")) m_phase = PS_RANDOM; else throw sat_param_exception("invalid phase selection strategy"); m_phase_caching_on = p.phase_caching_on(); m_phase_caching_off = p.phase_caching_off(); + m_phase_sticky = p.phase_sticky(); m_restart_initial = p.restart_initial(); m_restart_factor = p.restart_factor(); m_restart_max = p.restart_max(); + m_propagate_prefetch = p.propagate_prefetch(); + m_inprocess_max = p.inprocess_max(); m_random_freq = p.random_freq(); m_random_seed = p.random_seed(); @@ -78,43 +79,127 @@ namespace sat { m_burst_search = p.burst_search(); m_max_conflicts = p.max_conflicts(); - m_num_parallel = p.parallel_threads(); - + m_num_threads = p.threads(); + m_local_search = p.local_search(); + m_local_search_threads = p.local_search_threads(); + if (p.local_search_mode() == symbol("gsat")) + m_local_search_mode = local_search_mode::gsat; + else + m_local_search_mode = local_search_mode::wsat; + m_unit_walk = p.unit_walk(); + m_unit_walk_threads = p.unit_walk_threads(); + m_lookahead_simplify = p.lookahead_simplify(); + m_lookahead_simplify_bca = p.lookahead_simplify_bca(); + if (p.lookahead_reward() == symbol("heule_schur")) + m_lookahead_reward = heule_schur_reward; + else if (p.lookahead_reward() == symbol("heuleu")) + m_lookahead_reward = heule_unit_reward; + else if (p.lookahead_reward() == symbol("ternary")) + m_lookahead_reward = ternary_reward; + else if (p.lookahead_reward() == symbol("unit")) + m_lookahead_reward = unit_literal_reward; + else if (p.lookahead_reward() == symbol("march_cu")) + m_lookahead_reward = march_cu_reward; + else + throw sat_param_exception("invalid reward type supplied: accepted heuristics are 'ternary', 'heuleu', 'unit' or 'heule_schur'"); + + if (p.lookahead_cube_cutoff() == symbol("depth")) + m_lookahead_cube_cutoff = depth_cutoff; + else if (p.lookahead_cube_cutoff() == symbol("freevars")) + m_lookahead_cube_cutoff = freevars_cutoff; + else if (p.lookahead_cube_cutoff() == symbol("psat")) + m_lookahead_cube_cutoff = psat_cutoff; + else if (p.lookahead_cube_cutoff() == symbol("adaptive_freevars")) + m_lookahead_cube_cutoff = adaptive_freevars_cutoff; + else if (p.lookahead_cube_cutoff() == symbol("adaptive_psat")) + m_lookahead_cube_cutoff = adaptive_psat_cutoff; + else + throw sat_param_exception("invalid cutoff type supplied: accepted cutoffs are 'depth', 'freevars', 'psat', 'adaptive_freevars' and 'adaptive_psat'"); + m_lookahead_cube_fraction = p.lookahead_cube_fraction(); + m_lookahead_cube_depth = p.lookahead_cube_depth(); + m_lookahead_cube_freevars = p.lookahead_cube_freevars(); + m_lookahead_cube_psat_var_exp = p.lookahead_cube_psat_var_exp(); + m_lookahead_cube_psat_clause_base = p.lookahead_cube_psat_clause_base(); + m_lookahead_cube_psat_trigger = p.lookahead_cube_psat_trigger(); + m_lookahead_global_autarky = p.lookahead_global_autarky(); + m_lookahead_use_learned = p.lookahead_use_learned(); + + // These parameters are not exposed - m_simplify_mult1 = _p.get_uint("simplify_mult1", 300); + m_next_simplify1 = _p.get_uint("next_simplify", 30000); m_simplify_mult2 = _p.get_double("simplify_mult2", 1.5); m_simplify_max = _p.get_uint("simplify_max", 500000); // -------------------------------- + m_simplify_delay = p.simplify_delay(); s = p.gc(); - if (s == m_dyn_psm) { - m_gc_strategy = GC_DYN_PSM; - m_gc_initial = p.gc_initial(); - m_gc_increment = p.gc_increment(); - m_gc_small_lbd = p.gc_small_lbd(); - m_gc_k = p.gc_k(); - if (m_gc_k > 255) - m_gc_k = 255; - } - else { - if (s == m_glue_psm) - m_gc_strategy = GC_GLUE_PSM; - else if (s == m_glue) - m_gc_strategy = GC_GLUE; - else if (s == m_psm) - m_gc_strategy = GC_PSM; - else if (s == m_psm_glue) - m_gc_strategy = GC_PSM_GLUE; - else - throw sat_param_exception("invalid gc strategy"); - m_gc_initial = p.gc_initial(); - m_gc_increment = p.gc_increment(); - } + if (s == symbol("dyn_psm")) + m_gc_strategy = GC_DYN_PSM; + else if (s == symbol("glue_psm")) + m_gc_strategy = GC_GLUE_PSM; + else if (s == symbol("glue")) + m_gc_strategy = GC_GLUE; + else if (s == symbol("psm")) + m_gc_strategy = GC_PSM; + else if (s == symbol("psm_glue")) + m_gc_strategy = GC_PSM_GLUE; + else + throw sat_param_exception("invalid gc strategy"); + m_gc_initial = p.gc_initial(); + m_gc_increment = p.gc_increment(); + m_gc_small_lbd = p.gc_small_lbd(); + m_gc_k = std::min(255u, p.gc_k()); + m_gc_burst = p.gc_burst(); + m_gc_defrag = p.gc_defrag(); + m_minimize_lemmas = p.minimize_lemmas(); m_core_minimize = p.core_minimize(); m_core_minimize_partial = p.core_minimize_partial(); + m_drat_check_unsat = p.drat_check_unsat(); + m_drat_check_sat = p.drat_check_sat(); + m_drat_file = p.drat_file(); + m_drat = (m_drat_check_unsat || m_drat_file != symbol("") || m_drat_check_sat) && p.threads() == 1; m_dyn_sub_res = p.dyn_sub_res(); - m_dimacs_display = p.dimacs_display(); + + // Parameters used in Liang, Ganesh, Poupart, Czarnecki AAAI 2016. + m_branching_heuristic = BH_VSIDS; + if (p.branching_heuristic() == symbol("vsids")) + m_branching_heuristic = BH_VSIDS; + else if (p.branching_heuristic() == symbol("chb")) + m_branching_heuristic = BH_CHB; + else if (p.branching_heuristic() == symbol("lrb")) + m_branching_heuristic = BH_LRB; + else + throw sat_param_exception("invalid branching heuristic: accepted heuristics are 'vsids', 'lrb' or 'chb'"); + + m_anti_exploration = p.branching_anti_exploration(); + m_step_size_init = 0.40; + m_step_size_dec = 0.000001; + m_step_size_min = 0.06; + m_reward_multiplier = 0.9; + m_reward_offset = 1000000.0; + + m_variable_decay = p.variable_decay(); + + // PB parameters + s = p.pb_solver(); + if (s == symbol("circuit")) + m_pb_solver = PB_CIRCUIT; + else if (s == symbol("sorting")) + m_pb_solver = PB_SORTING; + else if (s == symbol("totalizer")) + m_pb_solver = PB_TOTALIZER; + else if (s == symbol("solver")) + m_pb_solver = PB_SOLVER; + else if (s == symbol("segmented")) + m_pb_solver = PB_SEGMENTED; + else + throw sat_param_exception("invalid PB solver: solver, totalizer, circuit, sorting, segmented"); + + m_card_solver = p.cardinality_solver(); + + sat_simplifier_params sp(_p); + m_elim_vars = sp.elim_vars(); } void config::collect_param_descrs(param_descrs & r) { diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index 36f22e83f..37efe69ed 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -33,7 +33,9 @@ namespace sat { enum restart_strategy { RS_GEOMETRIC, - RS_LUBY + RS_LUBY, + RS_EMA, + RS_STATIC }; enum gc_strategy { @@ -44,51 +46,121 @@ namespace sat { GC_PSM_GLUE }; + enum branching_heuristic { + BH_VSIDS, + BH_CHB, + BH_LRB + }; + + enum pb_solver { + PB_SOLVER, + PB_CIRCUIT, + PB_SORTING, + PB_TOTALIZER, + PB_SEGMENTED + }; + + enum reward_t { + ternary_reward, + unit_literal_reward, + heule_schur_reward, + heule_unit_reward, + march_cu_reward + }; + + enum cutoff_t { + depth_cutoff, + freevars_cutoff, + psat_cutoff, + adaptive_freevars_cutoff, + adaptive_psat_cutoff + }; + + enum local_search_mode { + gsat, + wsat + }; + struct config { unsigned long long m_max_memory; phase_selection m_phase; unsigned m_phase_caching_on; unsigned m_phase_caching_off; + bool m_phase_sticky; + bool m_propagate_prefetch; restart_strategy m_restart; + bool m_restart_fast; unsigned m_restart_initial; double m_restart_factor; // for geometric case + double m_restart_margin; // for ema unsigned m_restart_max; + double m_fast_glue_avg; + double m_slow_glue_avg; + unsigned m_inprocess_max; double m_random_freq; unsigned m_random_seed; unsigned m_burst_search; unsigned m_max_conflicts; - unsigned m_num_parallel; + unsigned m_num_threads; + unsigned m_local_search_threads; + bool m_local_search; + local_search_mode m_local_search_mode; + unsigned m_unit_walk_threads; + bool m_unit_walk; + bool m_lookahead_simplify; + bool m_lookahead_simplify_bca; + cutoff_t m_lookahead_cube_cutoff; + double m_lookahead_cube_fraction; + unsigned m_lookahead_cube_depth; + double m_lookahead_cube_freevars; + double m_lookahead_cube_psat_var_exp; + double m_lookahead_cube_psat_clause_base; + double m_lookahead_cube_psat_trigger; + reward_t m_lookahead_reward; + bool m_lookahead_global_autarky; + bool m_lookahead_use_learned; - unsigned m_simplify_mult1; + bool m_incremental; + unsigned m_next_simplify1; double m_simplify_mult2; unsigned m_simplify_max; + unsigned m_simplify_delay; + + unsigned m_variable_decay; gc_strategy m_gc_strategy; unsigned m_gc_initial; unsigned m_gc_increment; unsigned m_gc_small_lbd; unsigned m_gc_k; + bool m_gc_burst; + bool m_gc_defrag; + bool m_minimize_lemmas; bool m_dyn_sub_res; bool m_core_minimize; bool m_core_minimize_partial; - - bool m_dimacs_display; - - symbol m_always_true; - symbol m_always_false; - symbol m_caching; - symbol m_random; - symbol m_geometric; - symbol m_luby; + bool m_drat; + symbol m_drat_file; + bool m_drat_check_unsat; + bool m_drat_check_sat; - symbol m_dyn_psm; - symbol m_psm; - symbol m_glue; - symbol m_glue_psm; - symbol m_psm_glue; + pb_solver m_pb_solver; + bool m_card_solver; + // branching heuristic settings. + branching_heuristic m_branching_heuristic; + bool m_anti_exploration; + double m_step_size_init; + double m_step_size_dec; + double m_step_size_min; + double m_reward_multiplier; + double m_reward_offset; + + // simplifier configurations used outside of sat_simplifier + bool m_elim_vars; + config(params_ref const & p); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); diff --git a/src/sat/sat_drat.cpp b/src/sat/sat_drat.cpp new file mode 100644 index 000000000..00a3fa076 --- /dev/null +++ b/src/sat/sat_drat.cpp @@ -0,0 +1,502 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_drat.cpp + +Abstract: + + Produce DRAT proofs. + + Check them using a very simple forward checker + that interacts with external plugins. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-2-3 + +Notes: + +--*/ +#include "sat_solver.h" +#include "sat_drat.h" + + +namespace sat { + drat::drat(solver& s): + s(s), + m_out(0), + m_inconsistent(false), + m_check_unsat(false), + m_check_sat(false), + m_check(false) + { + if (s.m_config.m_drat && s.m_config.m_drat_file != symbol()) { + m_out = alloc(std::ofstream, s.m_config.m_drat_file.str().c_str()); + } + } + + drat::~drat() { + dealloc(m_out); + for (unsigned i = 0; i < m_proof.size(); ++i) { + clause* c = m_proof[i]; + if (c && (c->size() == 2 || m_status[i] == status::deleted || m_status[i] == status::external)) { + s.dealloc_clause(c); + } + } + } + + void drat::updt_config() { + m_check_unsat = s.m_config.m_drat_check_unsat; + m_check_sat = s.m_config.m_drat_check_sat; + m_check = m_check_unsat || m_check_sat; + } + + std::ostream& operator<<(std::ostream& out, drat::status st) { + switch (st) { + case drat::status::learned: return out << "l"; + case drat::status::asserted: return out << "a"; + case drat::status::deleted: return out << "d"; + case drat::status::external: return out << "e"; + default: return out; + } + } + + void drat::dump(unsigned n, literal const* c, status st) { + switch (st) { + case status::asserted: return; + case status::external: return; // requires extension to drat format. + case status::learned: break; + case status::deleted: (*m_out) << "d "; break; + } + for (unsigned i = 0; i < n; ++i) (*m_out) << c[i] << " "; + (*m_out) << "0\n"; + } + + bool drat::is_cleaned(clause& c) const { + literal last = null_literal; + unsigned n = c.size(); + for (unsigned i = 0; i < n; ++i) { + if (c[i] == last) return true; + last = c[i]; + } + return false; + } + + void drat::trace(std::ostream& out, unsigned n, literal const* c, status st) { + out << st << " "; + literal last = null_literal; + for (unsigned i = 0; i < n; ++i) { + if (c[i] != last) { + out << c[i] << " "; + last = c[i]; + } + } + out << "\n"; + } + + void drat::append(literal l, status st) { + IF_VERBOSE(20, trace(verbose_stream(), 1, &l, st);); + if (st == status::learned) { + verify(1, &l); + } + if (st == status::deleted) { + return; + } + assign_propagate(l); + } + + void drat::append(literal l1, literal l2, status st) { + literal lits[2] = { l1, l2 }; + IF_VERBOSE(20, trace(verbose_stream(), 2, lits, st);); + if (st == status::deleted) { + // noop + // don't record binary as deleted. + } + else { + if (st == status::learned) { + verify(2, lits); + } + clause* c = s.alloc_clause(2, lits, st == status::learned); + m_proof.push_back(c); + m_status.push_back(st); + unsigned idx = m_watched_clauses.size(); + m_watched_clauses.push_back(watched_clause(c, l1, l2)); + m_watches[(~l1).index()].push_back(idx); + m_watches[(~l2).index()].push_back(idx); + + if (value(l1) == l_false && value(l2) == l_false) { + m_inconsistent = true; + } + else if (value(l1) == l_false) { + assign_propagate(l2); + } + else if (value(l2) == l_false) { + assign_propagate(l1); + } + } + } + + void drat::append(clause& c, status st) { + unsigned n = c.size(); + IF_VERBOSE(20, trace(verbose_stream(), n, c.begin(), st);); + + if (st == status::learned) { + verify(n, c.begin()); + } + + m_status.push_back(st); + m_proof.push_back(&c); + if (st == status::deleted) { + del_watch(c, c[0]); + del_watch(c, c[1]); + return; + } + unsigned num_watch = 0; + literal l1, l2; + for (unsigned i = 0; i < n; ++i) { + if (value(c[i]) != l_false) { + if (num_watch == 0) { + l1 = c[i]; + ++num_watch; + } + else { + l2 = c[i]; + ++num_watch; + break; + } + } + } + switch (num_watch) { + case 0: + m_inconsistent = true; + break; + case 1: + assign_propagate(l1); + break; + default: { + SASSERT(num_watch == 2); + unsigned idx = m_watched_clauses.size(); + m_watched_clauses.push_back(watched_clause(&c, l1, l2)); + m_watches[(~l1).index()].push_back(idx); + m_watches[(~l2).index()].push_back(idx); + break; + } + } + } + + void drat::del_watch(clause& c, literal l) { + watch& w = m_watches[(~l).index()]; + for (unsigned i = 0; i < w.size(); ++i) { + if (m_watched_clauses[w[i]].m_clause == &c) { + w[i] = w.back(); + w.pop_back(); + break; + } + } + } + + void drat::declare(literal l) { + unsigned n = static_cast(l.var()); + while (m_assignment.size() <= n) { + m_assignment.push_back(l_undef); + m_watches.push_back(watch()); + m_watches.push_back(watch()); + } + } + + bool drat::is_drup(unsigned n, literal const* c) { + if (m_inconsistent || n == 0) return true; + unsigned num_units = m_units.size(); + for (unsigned i = 0; !m_inconsistent && i < n; ++i) { + assign_propagate(~c[i]); + } + if (!m_inconsistent) { + DEBUG_CODE(validate_propagation();); + } + for (unsigned i = 0; i < m_units.size(); ++i) { + SASSERT(m_assignment[m_units[i].var()] != l_undef); + } + + for (unsigned i = num_units; i < m_units.size(); ++i) { + m_assignment[m_units[i].var()] = l_undef; + } + m_units.resize(num_units); + bool ok = m_inconsistent; + m_inconsistent = false; + return ok; + } + + bool drat::is_drat(unsigned n, literal const* c) { + if (m_inconsistent || n == 0) return true; + for (unsigned i = 0; i < n; ++i) { + if (is_drat(n, c, i)) return true; + } + return false; + } + + void drat::validate_propagation() const { + for (unsigned i = 0; i < m_proof.size(); ++i) { + status st = m_status[i]; + if (m_proof[i] && st != status::deleted) { + clause& c = *m_proof[i]; + unsigned num_undef = 0, num_true = 0; + for (unsigned j = 0; j < c.size(); ++j) { + switch (value(c[j])) { + case l_false: break; + case l_true: num_true++; break; + case l_undef: num_undef++; break; + } + } + CTRACE("sat", num_true == 0 && num_undef == 1, display(tout);); + SASSERT(num_true != 0 || num_undef != 1); + } + } + } + + bool drat::is_drat(unsigned n, literal const* c, unsigned pos) { + SASSERT(pos < n); + literal l = c[pos]; + literal_vector lits(n, c); + SASSERT(lits.size() == n); + for (unsigned i = 0; i < m_proof.size(); ++i) { + status st = m_status[i]; + if (m_proof[i] && (st == status::asserted || st == status::external)) { + clause& c = *m_proof[i]; + unsigned j = 0; + for (; j < c.size() && c[j] != ~l; ++j) {} + if (j != c.size()) { + lits.append(j, c.begin()); + lits.append(c.size() - j - 1, c.begin() + j + 1); + if (!is_drup(lits.size(), lits.c_ptr())) return false; + lits.resize(n); + } + } + } + return true; + + } + + void drat::verify(unsigned n, literal const* c) { + if (m_check_unsat && !is_drup(n, c) && !is_drat(n, c)) { + std::cout << "Verification failed\n"; + UNREACHABLE(); + //display(std::cout); + TRACE("sat", + tout << literal_vector(n, c) << "\n"; + display(tout); + s.display(tout);); + UNREACHABLE(); + exit(0); + } + } + + void drat::display(std::ostream& out) const { + out << "units: " << m_units << "\n"; + for (unsigned i = 0; i < m_assignment.size(); ++i) { + lbool v = value(literal(i, false)); + if (v != l_undef) out << i << ": " << v << "\n"; + } + for (unsigned i = 0; i < m_proof.size(); ++i) { + clause* c = m_proof[i]; + if (m_status[i] != status::deleted && c) { + unsigned num_true = 0; + unsigned num_undef = 0; + for (unsigned j = 0; j < c->size(); ++j) { + switch (value((*c)[j])) { + case l_true: num_true++; break; + case l_undef: num_undef++; break; + default: break; + } + } + if (num_true == 0 && num_undef == 0) { + out << "False "; + } + if (num_true == 0 && num_undef == 1) { + out << "Unit "; + } + out << m_status[i] << " " << i << ": " << *c << "\n"; + } + } + for (unsigned i = 0; i < m_assignment.size(); ++i) { + watch const& w1 = m_watches[2*i]; + watch const& w2 = m_watches[2*i + 1]; + if (!w1.empty()) { + out << i << " |-> "; + for (unsigned i = 0; i < w1.size(); ++i) out << *(m_watched_clauses[w1[i]].m_clause) << " "; + out << "\n"; + } + if (!w2.empty()) { + out << "-" << i << " |-> "; + for (unsigned i = 0; i < w2.size(); ++i) out << *(m_watched_clauses[w2[i]].m_clause) << " "; + out << "\n"; + } + } + } + + lbool drat::value(literal l) const { + lbool val = m_assignment.get(l.var(), l_undef); + return val == l_undef || !l.sign() ? val : ~val; + } + + void drat::assign(literal l) { + lbool new_value = l.sign() ? l_false : l_true; + lbool old_value = value(l); +// TRACE("sat", tout << "assign " << l << " := " << new_value << " from " << old_value << "\n";); + switch (old_value) { + case l_false: + m_inconsistent = true; + break; + case l_true: + break; + case l_undef: + m_assignment.setx(l.var(), new_value, l_undef); + m_units.push_back(l); + break; + } + } + + void drat::assign_propagate(literal l) { + unsigned num_units = m_units.size(); + assign(l); + for (unsigned i = num_units; !m_inconsistent && i < m_units.size(); ++i) { + propagate(m_units[i]); + } + } + + void drat::propagate(literal l) { + watch& clauses = m_watches[l.index()]; + watch::iterator it = clauses.begin(); + watch::iterator it2 = it; + watch::iterator end = clauses.end(); + for (; it != end; ++it) { + unsigned idx = *it; + watched_clause& wc = m_watched_clauses[idx]; + clause& c = *wc.m_clause; + + //TRACE("sat", tout << "Propagate " << l << " " << c << " watch: " << wc.m_l1 << " " << wc.m_l2 << "\n";); + if (wc.m_l1 == ~l) { + std::swap(wc.m_l1, wc.m_l2); + } + + SASSERT(wc.m_l2 == ~l); + if (value(wc.m_l1) == l_true) { + *it2 = *it; + it2++; + } + else { + bool done = false; + for (unsigned i = 0; !done && i < c.size(); ++i) { + literal lit = c[i]; + if (lit != wc.m_l1 && lit != wc.m_l2 && value(lit) != l_false) { + wc.m_l2 = lit; + m_watches[(~lit).index()].push_back(idx); + done = true; + } + } + if (done) { + continue; + } + else if (value(wc.m_l1) == l_false) { + m_inconsistent = true; + goto end_process_watch; + } + else { + *it2 = *it; + it2++; + assign(wc.m_l1); + } + } + } + end_process_watch: + for (; it != end; ++it, ++it2) + *it2 = *it; + clauses.set_end(it2); + } + + drat::status drat::get_status(bool learned) const { + return learned || s.m_searching ? status::learned : status::asserted; + } + + void drat::add() { + if (m_out) (*m_out) << "0\n"; + if (m_check_unsat) { + SASSERT(m_inconsistent); + } + } + void drat::add(literal l, bool learned) { + declare(l); + status st = get_status(learned); + if (m_out) dump(1, &l, st); + if (m_check) append(l, st); + } + void drat::add(literal l1, literal l2, bool learned) { + declare(l1); + declare(l2); + literal ls[2] = {l1, l2}; + status st = get_status(learned); + if (m_out) dump(2, ls, st); + if (m_check) append(l1, l2, st); + } + void drat::add(clause& c, bool learned) { + TRACE("sat", tout << "add: " << c << "\n";); + for (unsigned i = 0; i < c.size(); ++i) declare(c[i]); + status st = get_status(learned); + if (m_out) dump(c.size(), c.begin(), st); + if (m_check_unsat) append(c, get_status(learned)); + } + void drat::add(literal_vector const& lits, svector const& premises) { + if (m_check) { + switch (lits.size()) { + case 0: add(); break; + case 1: append(lits[0], status::external); break; + default: { + clause* c = s.alloc_clause(lits.size(), lits.c_ptr(), true); + append(*c, status::external); + break; + } + } + } + } + void drat::add(literal_vector const& c) { + for (unsigned i = 0; i < c.size(); ++i) declare(c[i]); + if (m_out) dump(c.size(), c.begin(), status::learned); + if (m_check) { + switch (c.size()) { + case 0: add(); break; + case 1: append(c[0], status::learned); break; + default: { + verify(c.size(), c.begin()); + clause* cl = s.alloc_clause(c.size(), c.c_ptr(), true); + append(*cl, status::external); + break; + } + } + } + } + + void drat::del(literal l) { + if (m_out) dump(1, &l, status::deleted); + if (m_check_unsat) append(l, status::deleted); + } + void drat::del(literal l1, literal l2) { + literal ls[2] = {l1, l2}; + if (m_out) dump(2, ls, status::deleted); + if (m_check) + append(l1, l2, status::deleted); + } + void drat::del(clause& c) { + TRACE("sat", tout << "del: " << c << "\n";); + if (m_out) dump(c.size(), c.begin(), status::deleted); + if (m_check) { + clause* c1 = s.alloc_clause(c.size(), c.begin(), c.is_learned()); + append(*c1, status::deleted); + } + } + + void drat::check_model(model const& m) { + std::cout << "check model on " << m_proof.size() << "\n"; + } + +} diff --git a/src/sat/sat_drat.h b/src/sat/sat_drat.h new file mode 100644 index 000000000..64d796839 --- /dev/null +++ b/src/sat/sat_drat.h @@ -0,0 +1,101 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_drat.h + +Abstract: + + Produce DRAT proofs. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-2-3 + +Notes: + +--*/ +#ifndef SAT_DRAT_H_ +#define SAT_DRAT_H_ + +namespace sat { + class drat { + public: + struct s_ext {}; + struct s_unit {}; + struct premise { + enum { t_clause, t_unit, t_ext } m_type; + union { + clause* m_clause; + unsigned m_literal; + }; + premise(s_ext, literal l): m_type(t_ext), m_literal(l.index()) {} + premise(s_unit, literal l): m_type(t_unit), m_literal(l.index()) {} + premise(clause* c): m_type(t_clause), m_clause(c) {} + }; + private: + enum status { asserted, learned, deleted, external }; + struct watched_clause { + clause* m_clause; + literal m_l1, m_l2; + watched_clause(clause* c, literal l1, literal l2): + m_clause(c), m_l1(l1), m_l2(l2) {} + }; + svector m_watched_clauses; + typedef svector watch; + solver& s; + std::ostream* m_out; + ptr_vector m_proof; + svector m_status; + literal_vector m_units; + vector m_watches; + svector m_assignment; + bool m_inconsistent; + bool m_check_unsat, m_check_sat, m_check; + + void dump(unsigned n, literal const* c, status st); + void append(literal l, status st); + void append(literal l1, literal l2, status st); + void append(clause& c, status st); + + friend std::ostream& operator<<(std::ostream & out, status st); + status get_status(bool learned) const; + + void declare(literal l); + void assign(literal l); + void propagate(literal l); + void assign_propagate(literal l); + void del_watch(clause& c, literal l); + void verify(unsigned n, literal const* c); + bool is_drup(unsigned n, literal const* c); + bool is_drat(unsigned n, literal const* c); + bool is_drat(unsigned n, literal const* c, unsigned pos); + lbool value(literal l) const; + void trace(std::ostream& out, unsigned n, literal const* c, status st); + void display(std::ostream& out) const; + void validate_propagation() const; + + public: + drat(solver& s); + ~drat(); + + void updt_config(); + void add(); + void add(literal l, bool learned); + void add(literal l1, literal l2, bool learned); + void add(clause& c, bool learned); + void add(literal_vector const& c, svector const& premises); + void add(literal_vector const& c); // add learned clause + + bool is_cleaned(clause& c) const; + void del(literal l); + void del(literal l1, literal l2); + void del(clause& c); + + void check_model(model const& m); + }; + +}; + +#endif diff --git a/src/sat/sat_elim_eqs.cpp b/src/sat/sat_elim_eqs.cpp index 4cb2fa8ae..870aa7fe2 100644 --- a/src/sat/sat_elim_eqs.cpp +++ b/src/sat/sat_elim_eqs.cpp @@ -33,19 +33,18 @@ namespace sat { return roots[l.var()]; } - void elim_eqs::cleanup_bin_watches(literal_vector const & roots) { - vector::iterator it = m_solver.m_watches.begin(); - vector::iterator end = m_solver.m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - watch_list & wlist = *it; - literal l1 = ~to_literal(l_idx); + void elim_eqs::cleanup_bin_watches(literal_vector const & roots) { + unsigned l_idx = 0; + m_new_bin.reset(); + for (watch_list & wlist : m_solver.m_watches) { + literal l1 = ~to_literal(l_idx++); literal r1 = norm(roots, l1); - watch_list::iterator it2 = wlist.begin(); - watch_list::iterator itprev = it2; - watch_list::iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_clause()) { - literal l2 = it2->get_literal(); + watch_list::iterator it = wlist.begin(); + watch_list::iterator itprev = it; + watch_list::iterator end = wlist.end(); + for (; it != end; ++it) { + if (it->is_binary_clause()) { + literal l2 = it->get_literal(); literal r2 = norm(roots, l2); if (r1 == r2) { m_solver.assign(r1, justification()); @@ -58,18 +57,33 @@ namespace sat { // consume tautology continue; } +#if 0 if (l1 != r1) { // add half r1 => r2, the other half ~r2 => ~r1 is added when traversing l2 - m_solver.m_watches[(~r1).index()].push_back(watched(r2, it2->is_learned())); + m_solver.m_watches[(~r1).index()].push_back(watched(r2, it->is_learned())); continue; } - it2->set_literal(r2); // keep it + it->set_literal(r2); // keep it. +#else + if (l1 != r1 || l2 != r2) { + if (r1.index() < r2.index()) { + m_new_bin.push_back(bin(r1, r2, it->is_learned())); + } + continue; + } + // keep it +#endif } - *itprev = *it2; + *itprev = *it; itprev++; } wlist.set_end(itprev); } + + for (auto const& b : m_new_bin) { + m_solver.mk_bin_clause(b.l1, b.l2, b.learned); + } + m_new_bin.reset(); } void elim_eqs::cleanup_clauses(literal_vector const & roots, clause_vector & cs) { @@ -78,7 +92,7 @@ namespace sat { clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); - TRACE("elim_eqs", tout << "processing: " << c << "\n";); + TRACE("sats", tout << "processing: " << c << "\n";); unsigned sz = c.size(); unsigned i; for (i = 0; i < sz; i++) { @@ -96,12 +110,20 @@ namespace sat { if (!c.frozen()) m_solver.detach_clause(c); // apply substitution - for (i = 0; i < sz; i++) { - SASSERT(!m_solver.was_eliminated(c[i].var())); - c[i] = norm(roots, c[i]); + for (i = 0; i < sz; i++) { + literal lit = c[i]; + c[i] = norm(roots, lit); + VERIFY(c[i] == norm(roots, c[i])); + VERIFY(!m_solver.was_eliminated(c[i].var()) || lit == c[i]); } std::sort(c.begin(), c.end()); - TRACE("elim_eqs", tout << "after normalization/sorting: " << c << "\n";); + for (literal l : c) VERIFY(l == norm(roots, l)); + TRACE("sats", tout << "after normalization/sorting: " << c << "\n"; tout.flush();); + DEBUG_CODE({ + for (literal l : c) { + CTRACE("sat", l != norm(roots, l), tout << l << " " << norm(roots, l) << "\n"; tout.flush();); + SASSERT(l == norm(roots, l)); + } }); // remove duplicates, and check if it is a tautology literal l_prev = null_literal; unsigned j = 0; @@ -117,11 +139,11 @@ namespace sat { break; // clause was satisfied if (val == l_false) continue; // skip - c[j] = l; + c[j] = l; j++; } if (i < sz) { - // clause is a tautology or was simplified + // clause is a tautology or was simplified to true m_solver.del_clause(c); continue; } @@ -136,16 +158,7 @@ namespace sat { return; } TRACE("elim_eqs", tout << "after removing duplicates: " << c << " j: " << j << "\n";); - if (j < sz) - c.shrink(j); - else - c.update_approx(); - SASSERT(c.size() == j); - DEBUG_CODE({ - for (unsigned i = 0; i < c.size(); i++) { - SASSERT(c[i] == norm(roots, c[i])); - } - }); + SASSERT(j >= 1); switch (j) { case 1: @@ -158,10 +171,21 @@ namespace sat { break; default: SASSERT(*it == &c); + if (j < sz) { + if (m_solver.m_config.m_drat) m_solver.m_drat.del(c); + c.shrink(j); + if (m_solver.m_config.m_drat) m_solver.m_drat.add(c, true); + } + else + c.update_approx(); + + DEBUG_CODE(for (literal l : c) VERIFY(l == norm(roots, l));); + *it2 = *it; it2++; - if (!c.frozen()) + if (!c.frozen()) { m_solver.attach_clause(c); + } break; } } @@ -170,14 +194,12 @@ namespace sat { void elim_eqs::save_elim(literal_vector const & roots, bool_var_vector const & to_elim) { model_converter & mc = m_solver.m_mc; - bool_var_vector::const_iterator it = to_elim.begin(); - bool_var_vector::const_iterator end = to_elim.end(); - for (; it != end; ++it) { - bool_var v = *it; + for (bool_var v : to_elim) { literal l(v, false); literal r = roots[v]; SASSERT(v != r.var()); - if (m_solver.is_external(v)) { + bool root_ok = !m_solver.is_external(v) || m_solver.set_root(l, r); + if (m_solver.is_assumption(v) || (m_solver.is_external(v) && (m_solver.is_incremental() || !root_ok))) { // cannot really eliminate v, since we have to notify extension of future assignments m_solver.mk_bin_clause(~l, r, false); m_solver.mk_bin_clause(l, ~r, false); @@ -190,28 +212,34 @@ namespace sat { mc.insert(e, l, ~r); } } + m_solver.flush_roots(); } - bool elim_eqs::check_clauses(literal_vector const & roots) const { - clause_vector * vs[2] = { &m_solver.m_clauses, &m_solver.m_learned }; - for (unsigned i = 0; i < 2; i++) { - clause_vector & cs = *(vs[i]); - clause_vector::iterator it = cs.begin(); - clause_vector::iterator end = cs.end(); - for (; it != end; ++it) { - clause & c = *(*it); - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - CTRACE("elim_eqs_bug", m_solver.was_eliminated(c[i].var()), tout << "lit: " << c[i] << " " << norm(roots, c[i]) << "\n"; - tout << c << "\n";); - SASSERT(!m_solver.was_eliminated(c[i].var())); - } + bool elim_eqs::check_clause(clause const& c, literal_vector const& roots) const { + for (literal l : c) { + CTRACE("elim_eqs_bug", m_solver.was_eliminated(l.var()), tout << "lit: " << l << " " << norm(roots, l) << "\n"; + tout << c << "\n";); + if (m_solver.was_eliminated(l.var())) { + IF_VERBOSE(0, verbose_stream() << c << " contains eliminated literal " << l << " " << norm(roots, l) << "\n";); + UNREACHABLE(); } } return true; } + + bool elim_eqs::check_clauses(literal_vector const & roots) const { + for (clause * cp : m_solver.m_clauses) + if (!check_clause(*cp, roots)) + return false; + for (clause * cp : m_solver.m_learned) + if (!check_clause(*cp, roots)) + return false; + return true; + } + void elim_eqs::operator()(literal_vector const & roots, bool_var_vector const & to_elim) { + TRACE("elim_eqs", tout << "before bin cleanup\n"; m_solver.display(tout);); cleanup_bin_watches(roots); TRACE("elim_eqs", tout << "after bin cleanup\n"; m_solver.display(tout);); cleanup_clauses(roots, m_solver.m_clauses); @@ -221,5 +249,6 @@ namespace sat { save_elim(roots, to_elim); m_solver.propagate(false); SASSERT(check_clauses(roots)); + TRACE("elim_eqs", tout << "after full cleanup\n"; m_solver.display(tout);); } }; diff --git a/src/sat/sat_elim_eqs.h b/src/sat/sat_elim_eqs.h index 0422b60df..143fcbb3f 100644 --- a/src/sat/sat_elim_eqs.h +++ b/src/sat/sat_elim_eqs.h @@ -25,11 +25,18 @@ namespace sat { class solver; class elim_eqs { + struct bin { + literal l1, l2; + bool learned; + bin(literal l1, literal l2, bool learned): l1(l1), l2(l2), learned(learned) {} + }; + svector m_new_bin; solver & m_solver; void save_elim(literal_vector const & roots, bool_var_vector const & to_elim); void cleanup_clauses(literal_vector const & roots, clause_vector & cs); void cleanup_bin_watches(literal_vector const & roots); bool check_clauses(literal_vector const & roots) const; + bool check_clause(clause const& c, literal_vector const& roots) const; public: elim_eqs(solver & s); void operator()(literal_vector const & roots, bool_var_vector const & to_elim); diff --git a/src/sat/sat_elim_vars.cpp b/src/sat/sat_elim_vars.cpp new file mode 100644 index 000000000..299fbace1 --- /dev/null +++ b/src/sat/sat_elim_vars.cpp @@ -0,0 +1,336 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_elim_vars.cpp + +Abstract: + + Helper class for eliminating variables + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-14 + +Revision History: + +--*/ + +#include "sat/sat_simplifier.h" +#include "sat/sat_elim_vars.h" +#include "sat/sat_solver.h" + +namespace sat{ + + elim_vars::elim_vars(simplifier& s) : simp(s), s(s.s), m(20) { + m_mark_lim = 0; + m_max_literals = 11; + m_miss = 0; + m_hit1 = 0; + m_hit2 = 0; + } + + bool elim_vars::operator()(bool_var v) { + if (s.value(v) != l_undef) + return false; + + literal pos_l(v, false); + literal neg_l(v, true); + unsigned num_bin_pos = simp.num_nonlearned_bin(pos_l); + if (num_bin_pos > m_max_literals) return false; + unsigned num_bin_neg = simp.num_nonlearned_bin(neg_l); + if (num_bin_neg > m_max_literals) return false; + clause_use_list & pos_occs = simp.m_use_list.get(pos_l); + clause_use_list & neg_occs = simp.m_use_list.get(neg_l); + unsigned clause_size = num_bin_pos + num_bin_neg + pos_occs.num_irredundant() + neg_occs.num_irredundant(); + if (clause_size == 0) { + return false; + } + reset_mark(); + mark_var(v); + if (!mark_literals(pos_occs)) return false; + if (!mark_literals(neg_occs)) return false; + if (!mark_literals(pos_l)) return false; + if (!mark_literals(neg_l)) return false; + + // associate index with each variable. + sort_marked(); + bdd b1 = elim_var(v); + double sz1 = b1.cnf_size(); + if (sz1 > 2*clause_size) { + ++m_miss; + return false; + } + if (sz1 <= clause_size) { + ++m_hit1; + return elim_var(v, b1); + } + m.try_cnf_reorder(b1); + sz1 = b1.cnf_size(); + if (sz1 <= clause_size) { + ++m_hit2; + return elim_var(v, b1); + } + ++m_miss; + return false; + } + + bool elim_vars::elim_var(bool_var v, bdd const& b) { + literal pos_l(v, false); + literal neg_l(v, true); + clause_use_list & pos_occs = simp.m_use_list.get(pos_l); + clause_use_list & neg_occs = simp.m_use_list.get(neg_l); + + // eliminate variable + simp.m_pos_cls.reset(); + simp.m_neg_cls.reset(); + simp.collect_clauses(pos_l, simp.m_pos_cls); + simp.collect_clauses(neg_l, simp.m_neg_cls); + VERIFY(!simp.is_external(v)); + + model_converter::entry & mc_entry = s.m_mc.mk(model_converter::ELIM_VAR, v); + simp.save_clauses(mc_entry, simp.m_pos_cls); + simp.save_clauses(mc_entry, simp.m_neg_cls); + s.m_eliminated[v] = true; + ++s.m_stats.m_elim_var_bdd; + simp.remove_bin_clauses(pos_l); + simp.remove_bin_clauses(neg_l); + simp.remove_clauses(pos_occs, pos_l); + simp.remove_clauses(neg_occs, neg_l); + pos_occs.reset(); + neg_occs.reset(); + literal_vector lits; + add_clauses(v, b, lits); + return true; + } + + bdd elim_vars::elim_var(bool_var v) { + unsigned index = 0; + for (bool_var w : m_vars) { + m_var2index[w] = index++; + } + literal pos_l(v, false); + literal neg_l(v, true); + clause_use_list & pos_occs = simp.m_use_list.get(pos_l); + clause_use_list & neg_occs = simp.m_use_list.get(neg_l); + + bdd b1 = make_clauses(pos_l); + bdd b2 = make_clauses(neg_l); + bdd b3 = make_clauses(pos_occs); + bdd b4 = make_clauses(neg_occs); + bdd b0 = b1 && b2 && b3 && b4; + bdd b = m.mk_exists(m_var2index[v], b0); + TRACE("elim_vars", + tout << "eliminate " << v << "\n"; + for (watched const& w : simp.get_wlist(~pos_l)) { + if (w.is_binary_non_learned_clause()) { + tout << pos_l << " " << w.get_literal() << "\n"; + } + } + m.display(tout, b1); + for (watched const& w : simp.get_wlist(~neg_l)) { + if (w.is_binary_non_learned_clause()) { + tout << neg_l << " " << w.get_literal() << "\n"; + } + } + m.display(tout, b2); + clause_use_list::iterator itp = pos_occs.mk_iterator(); + while (!itp.at_end()) { + clause const& c = itp.curr(); + tout << c << "\n"; + itp.next(); + } + m.display(tout, b3); + clause_use_list::iterator itn = neg_occs.mk_iterator(); + while (!itn.at_end()) { + clause const& c = itn.curr(); + tout << c << "\n"; + itn.next(); + } + m.display(tout, b4); + tout << "eliminated:\n"; + tout << b << "\n"; + tout << b.cnf_size() << "\n"; + ); + + return b; + } + + void elim_vars::add_clauses(bool_var v0, bdd const& b, literal_vector& lits) { + if (b.is_true()) { + // no-op + } + else if (b.is_false()) { + SASSERT(lits.size() > 0); + literal_vector c(lits); + if (simp.cleanup_clause(c)) + return; + + if (v0 == 39063) IF_VERBOSE(0, verbose_stream() << "bdd: " << c << "\n"); + switch (c.size()) { + case 0: + s.set_conflict(justification()); + break; + case 1: + simp.propagate_unit(c[0]); + break; + case 2: + s.m_stats.m_mk_bin_clause++; + simp.add_non_learned_binary_clause(c[0], c[1]); + simp.back_subsumption1(c[0], c[1], false); + break; + default: { + if (c.size() == 3) + s.m_stats.m_mk_ter_clause++; + else + s.m_stats.m_mk_clause++; + clause* cp = s.alloc_clause(c.size(), c.c_ptr(), false); + s.m_clauses.push_back(cp); + simp.m_use_list.insert(*cp); + if (simp.m_sub_counter > 0) + simp.back_subsumption1(*cp); + else + simp.back_subsumption0(*cp); + break; + } + } + } + else { + unsigned v = m_vars[b.var()]; + lits.push_back(literal(v, false)); + add_clauses(v0, b.lo(), lits); + lits.pop_back(); + lits.push_back(literal(v, true)); + add_clauses(v0, b.hi(), lits); + lits.pop_back(); + } + } + + + void elim_vars::get_clauses(bdd const& b, literal_vector & lits, clause_vector& clauses, literal_vector& units) { + if (b.is_true()) { + return; + } + if (b.is_false()) { + if (lits.size() > 1) { + clause* c = s.alloc_clause(lits.size(), lits.c_ptr(), false); + clauses.push_back(c); + } + else { + units.push_back(lits.back()); + } + return; + } + + // if (v hi lo) + // (v | lo) & (!v | hi) + // if (v T lo) -> (v | lo) + unsigned v = m_vars[b.var()]; + lits.push_back(literal(v, false)); + get_clauses(b.lo(), lits, clauses, units); + lits.pop_back(); + lits.push_back(literal(v, true)); + get_clauses(b.hi(), lits, clauses, units); + lits.pop_back(); + } + + void elim_vars::reset_mark() { + m_vars.reset(); + m_mark.resize(s.num_vars()); + m_var2index.resize(s.num_vars()); + m_occ.resize(s.num_vars()); + ++m_mark_lim; + if (m_mark_lim == 0) { + ++m_mark_lim; + m_mark.fill(0); + } + } + + class elim_vars::compare_occ { + elim_vars& ev; + public: + compare_occ(elim_vars& ev):ev(ev) {} + + bool operator()(bool_var v1, bool_var v2) const { + return ev.m_occ[v1] < ev.m_occ[v2]; + } + }; + + void elim_vars::sort_marked() { + std::sort(m_vars.begin(), m_vars.end(), compare_occ(*this)); + } + + void elim_vars::shuffle_vars() { + unsigned sz = m_vars.size(); + for (unsigned i = 0; i < sz; ++i) { + unsigned x = m_rand(sz); + unsigned y = m_rand(sz); + std::swap(m_vars[x], m_vars[y]); + } + } + + void elim_vars::mark_var(bool_var v) { + if (m_mark[v] != m_mark_lim) { + m_mark[v] = m_mark_lim; + m_vars.push_back(v); + m_occ[v] = 1; + } + else { + ++m_occ[v]; + } + } + + bool elim_vars::mark_literals(clause_use_list & occs) { + clause_use_list::iterator it = occs.mk_iterator(); + while (!it.at_end()) { + clause const& c = it.curr(); + for (literal l : c) { + mark_var(l.var()); + } + if (num_vars() > m_max_literals) return false; + it.next(); + } + return true; + } + + bool elim_vars::mark_literals(literal lit) { + watch_list& wl = simp.get_wlist(lit); + for (watched const& w : wl) { + if (w.is_binary_non_learned_clause()) { + mark_var(w.get_literal().var()); + } + } + return num_vars() <= m_max_literals; + } + + bdd elim_vars::make_clauses(clause_use_list & occs) { + bdd result = m.mk_true(); + for (auto it = occs.mk_iterator(); !it.at_end(); it.next()) { + clause const& c = it.curr(); + bdd cl = m.mk_false(); + for (literal l : c) { + cl |= mk_literal(l); + } + result &= cl; + } + return result; + } + + bdd elim_vars::make_clauses(literal lit) { + bdd result = m.mk_true(); + watch_list& wl = simp.get_wlist(~lit); + for (watched const& w : wl) { + if (w.is_binary_non_learned_clause()) { + result &= (mk_literal(lit) || mk_literal(w.get_literal())); + } + } + return result; + } + + bdd elim_vars::mk_literal(literal l) { + return l.sign() ? m.mk_nvar(m_var2index[l.var()]) : m.mk_var(m_var2index[l.var()]); + } + +}; + diff --git a/src/sat/sat_elim_vars.h b/src/sat/sat_elim_vars.h new file mode 100644 index 000000000..b62fdc5db --- /dev/null +++ b/src/sat/sat_elim_vars.h @@ -0,0 +1,74 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_elim_vars.h + +Abstract: + + Helper class for eliminating variables + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-14 + +Revision History: + +--*/ +#ifndef SAT_ELIM_VARS_H_ +#define SAT_ELIM_VARS_H_ + +#include "sat/sat_types.h" +#include "sat/sat_bdd.h" + +namespace sat { + class solver; + class simplifier; + + class elim_vars { + class compare_occ; + + simplifier& simp; + solver& s; + bdd_manager m; + random_gen m_rand; + + + svector m_vars; + unsigned_vector m_mark; + unsigned m_mark_lim; + unsigned_vector m_var2index; + unsigned_vector m_occ; + unsigned m_miss; + unsigned m_hit1; + unsigned m_hit2; + + unsigned m_max_literals; + + unsigned num_vars() const { return m_vars.size(); } + void reset_mark(); + void mark_var(bool_var v); + void sort_marked(); + void shuffle_vars(); + bool mark_literals(clause_use_list & occs); + bool mark_literals(literal lit); + bdd make_clauses(clause_use_list & occs); + bdd make_clauses(literal lit); + bdd mk_literal(literal l); + void get_clauses(bdd const& b, literal_vector& lits, clause_vector& clauses, literal_vector& units); + void add_clauses(bool_var v, bdd const& b, literal_vector& lits); + bool elim_var(bool_var v, bdd const& b); + bdd elim_var(bool_var v); + + public: + elim_vars(simplifier& s); + bool operator()(bool_var v); + unsigned hit2() const { return m_hit1; } // first round bdd construction is minimal + unsigned hit1() const { return m_hit2; } // minimal after reshufling + unsigned miss() const { return m_miss; } // not-minimal + }; + +}; + +#endif diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index 80144e00d..e687ab2b0 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -21,6 +21,7 @@ Revision History: #include "sat/sat_types.h" #include "util/params.h" +#include "util/statistics.h" namespace sat { @@ -28,17 +29,57 @@ namespace sat { CR_DONE, CR_CONTINUE, CR_GIVEUP }; + class literal_occs_fun { + public: + virtual double operator()(literal l) = 0; + }; + + + typedef svector ext_constraint_list; + + class ext_use_list { + vector m_use_list; + public: + void init(unsigned num_vars) { m_use_list.reset(); m_use_list.resize(num_vars*2); } + void insert(literal l, ext_constraint_idx idx) { get(l).push_back(idx); } + ext_constraint_list & get(literal l) { return m_use_list[l.index()]; } + ext_constraint_list const & get(literal l) const { return m_use_list[l.index()]; } + void finalize() { m_use_list.finalize(); } + }; + class extension { public: - virtual void propagate(literal l, ext_constraint_idx idx, bool & keep) = 0; + virtual ~extension() {} + virtual void set_solver(solver* s) = 0; + virtual void set_lookahead(lookahead* s) = 0; + virtual void set_unit_walk(unit_walk* u) = 0; + virtual bool propagate(literal l, ext_constraint_idx idx) = 0; + virtual double get_reward(literal l, ext_constraint_idx idx, literal_occs_fun& occs) const = 0; virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) = 0; + virtual bool is_extended_binary(ext_justification_idx idx, literal_vector & r) = 0; virtual void asserted(literal l) = 0; virtual check_result check() = 0; + virtual lbool resolve_conflict() { return l_undef; } // stores result in sat::solver::m_lemma virtual void push() = 0; virtual void pop(unsigned n) = 0; virtual void simplify() = 0; + // have a way to replace l by r in all constraints + virtual bool set_root(literal l, literal r) { return false; } + virtual void flush_roots() {} virtual void clauses_modifed() = 0; virtual lbool get_phase(bool_var v) = 0; + virtual std::ostream& display(std::ostream& out) const = 0; + virtual std::ostream& display_justification(std::ostream& out, ext_justification_idx idx) const = 0; + virtual void collect_statistics(statistics& st) const = 0; + virtual extension* copy(solver* s) = 0; + virtual extension* copy(lookahead* s, bool learned) = 0; + virtual void find_mutexes(literal_vector& lits, vector & mutexes) = 0; + virtual void gc() = 0; + virtual void pop_reinit() = 0; + virtual bool validate() = 0; + virtual void init_use_list(ext_use_list& ul) = 0; + virtual bool is_blocked(literal l, ext_constraint_idx) = 0; + virtual bool check_model(model const& m) const = 0; }; }; diff --git a/src/sat/sat_iff3_finder.cpp b/src/sat/sat_iff3_finder.cpp index e889af164..32bf70414 100644 --- a/src/sat/sat_iff3_finder.cpp +++ b/src/sat/sat_iff3_finder.cpp @@ -136,9 +136,9 @@ namespace sat { TRACE("iff3_finder", tout << "visiting: " << x << "\n"; tout << "pos:\n"; - display(tout, s.m_cls_allocator, pos_wlist); + s.display_watch_list(tout, pos_wlist); tout << "\nneg:\n"; - display(tout, s.m_cls_allocator, neg_wlist); + s.display_watch_list(tout, neg_wlist); tout << "\n--------------\n";); // traverse the ternary clauses x \/ l1 \/ l2 bool_var curr_v1 = null_bool_var; diff --git a/src/sat/sat_integrity_checker.cpp b/src/sat/sat_integrity_checker.cpp index 027cd8a6f..32feaa2c7 100644 --- a/src/sat/sat_integrity_checker.cpp +++ b/src/sat/sat_integrity_checker.cpp @@ -34,13 +34,11 @@ namespace sat { // for nary clauses static bool contains_watched(watch_list const & wlist, clause const & c, clause_offset cls_off) { - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { - if (it->is_clause()) { - if (it->get_clause_offset() == cls_off) { + for (watched const& w : wlist) { + if (w.is_clause()) { + if (w.get_clause_offset() == cls_off) { // the blocked literal must be in the clause. - SASSERT(c.contains(it->get_blocked_literal())); + VERIFY(c.contains(w.get_blocked_literal())); return true; } } @@ -52,12 +50,12 @@ namespace sat { bool integrity_checker::check_clause(clause const & c) const { SASSERT(!c.was_removed()); for (unsigned i = 0; i < c.size(); i++) { - SASSERT(c[i].var() <= s.num_vars()); + VERIFY(c[i].var() <= s.num_vars()); CTRACE("sat_bug", s.was_eliminated(c[i].var()), tout << "l: " << c[i].var() << "\n"; tout << "c: " << c << "\n"; s.display(tout);); - SASSERT(!s.was_eliminated(c[i].var())); + VERIFY(!s.was_eliminated(c[i].var())); } SASSERT(c.check_approx()); @@ -68,7 +66,7 @@ namespace sat { if (c.size() == 3) { CTRACE("sat_ter_watch_bug", !contains_watched(s.get_wlist(~c[0]), c[1], c[2]), tout << c << "\n"; tout << "watch_list:\n"; - sat::display(tout, s.m_cls_allocator, s.get_wlist(~c[0])); + s.display_watch_list(tout, s.get_wlist(~c[0])); tout << "\n";); VERIFY(contains_watched(s.get_wlist(~c[0]), c[1], c[2])); VERIFY(contains_watched(s.get_wlist(~c[1]), c[0], c[2])); @@ -90,7 +88,7 @@ namespace sat { CTRACE("sat_bug", s.value(c[i]) != l_false, tout << c << " status: " << s.status(c) << "\n"; for (unsigned i = 0; i < c.size(); i++) tout << "val(" << i << "): " << s.value(c[i]) << "\n";); - SASSERT(s.value(c[i]) == l_false); + VERIFY(s.value(c[i]) == l_false); } } } @@ -102,9 +100,9 @@ namespace sat { return true; } - bool integrity_checker::check_clauses(clause * const * begin, clause * const * end) const { + bool integrity_checker::check_clauses(clause * const * begin, clause * const * end) const { for (clause * const * it = begin; it != end; ++it) { - SASSERT(check_clause(*(*it))); + VERIFY(check_clause(*(*it))); } return true; } @@ -130,95 +128,94 @@ namespace sat { } bool integrity_checker::check_bool_vars() const { - SASSERT(s.m_watches.size() == s.num_vars() * 2); - SASSERT(s.m_assignment.size() == s.num_vars() * 2); - SASSERT(s.m_lit_mark.size() == s.num_vars() * 2); - SASSERT(s.m_justification.size() == s.num_vars()); - SASSERT(s.m_decision.size() == s.num_vars()); - SASSERT(s.m_eliminated.size() == s.num_vars()); - SASSERT(s.m_external.size() == s.num_vars()); - SASSERT(s.m_level.size() == s.num_vars()); - SASSERT(s.m_mark.size() == s.num_vars()); - SASSERT(s.m_activity.size() == s.num_vars()); - SASSERT(s.m_phase.size() == s.num_vars()); - SASSERT(s.m_prev_phase.size() == s.num_vars()); - SASSERT(s.m_assigned_since_gc.size() == s.num_vars()); + VERIFY(s.m_watches.size() == s.num_vars() * 2); + VERIFY(s.m_assignment.size() == s.num_vars() * 2); + VERIFY(s.m_lit_mark.size() == s.num_vars() * 2); + VERIFY(s.m_justification.size() == s.num_vars()); + VERIFY(s.m_decision.size() == s.num_vars()); + VERIFY(s.m_eliminated.size() == s.num_vars()); + VERIFY(s.m_external.size() == s.num_vars()); + VERIFY(s.m_level.size() == s.num_vars()); + VERIFY(s.m_mark.size() == s.num_vars()); + VERIFY(s.m_activity.size() == s.num_vars()); + VERIFY(s.m_phase.size() == s.num_vars()); + VERIFY(s.m_prev_phase.size() == s.num_vars()); + VERIFY(s.m_assigned_since_gc.size() == s.num_vars()); for (bool_var v = 0; v < s.num_vars(); v++) { if (s.was_eliminated(v)) { - SASSERT(s.get_wlist(literal(v, false)).empty()); - SASSERT(s.get_wlist(literal(v, true)).empty()); + VERIFY(s.get_wlist(literal(v, false)).empty()); + VERIFY(s.get_wlist(literal(v, true)).empty()); + } + } + return true; + } + + bool integrity_checker::check_watches(literal l) const { + return check_watches(l, s.get_wlist(~l)); + } + + bool integrity_checker::check_watches(literal l, watch_list const& wlist) const { + for (watched const& w : wlist) { + switch (w.get_kind()) { + case watched::BINARY: + VERIFY(!s.was_eliminated(w.get_literal().var())); + CTRACE("sat_watched_bug", !s.get_wlist(~(w.get_literal())).contains(watched(l, w.is_learned())), + tout << "l: " << l << " l2: " << w.get_literal() << "\n"; + tout << "was_eliminated1: " << s.was_eliminated(l.var()); + tout << " was_eliminated2: " << s.was_eliminated(w.get_literal().var()); + tout << " learned: " << w.is_learned() << "\n"; + s.display_watch_list(tout, wlist); + tout << "\n"; + s.display_watch_list(tout, s.get_wlist(~(w.get_literal()))); + tout << "\n";); + VERIFY(find_binary_watch(s.get_wlist(~(w.get_literal())), l)); + break; + case watched::TERNARY: + VERIFY(!s.was_eliminated(w.get_literal1().var())); + VERIFY(!s.was_eliminated(w.get_literal2().var())); + VERIFY(w.get_literal1().index() < w.get_literal2().index()); + break; + case watched::CLAUSE: + VERIFY(!s.get_clause(w.get_clause_offset()).was_removed()); + break; + default: + break; } } return true; } bool integrity_checker::check_watches() const { - DEBUG_CODE( - vector::const_iterator it = s.m_watches.begin(); - vector::const_iterator end = s.m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - literal l = ~to_literal(l_idx); - watch_list const & wlist = *it; + unsigned l_idx = 0; + for (watch_list const& wlist : s.m_watches) { + literal l = ~to_literal(l_idx++); CTRACE("sat_bug", s.was_eliminated(l.var()) && !wlist.empty(), tout << "l: " << l << "\n"; s.display_watches(tout); s.display(tout);); - SASSERT(!s.was_eliminated(l.var()) || wlist.empty()); - for (watched const& w : wlist) { - switch (w.get_kind()) { - case watched::BINARY: - SASSERT(!s.was_eliminated(w.get_literal().var())); - CTRACE("sat_watched_bug", !s.get_wlist(~(w.get_literal())).contains(watched(l, w.is_learned())), - tout << "l: " << l << " l2: " << w.get_literal() << "\n"; - tout << "was_eliminated1: " << s.was_eliminated(l.var()); - tout << " was_eliminated2: " << s.was_eliminated(w.get_literal().var()); - tout << " learned: " << w.is_learned() << "\n"; - sat::display(tout, s.m_cls_allocator, wlist); - tout << "\n"; - sat::display(tout, s.m_cls_allocator, s.get_wlist(~(w.get_literal()))); - tout << "\n";); - SASSERT(s.get_wlist(~(w.get_literal())).contains(watched(l, w.is_learned()))); - break; - case watched::TERNARY: - SASSERT(!s.was_eliminated(w.get_literal1().var())); - SASSERT(!s.was_eliminated(w.get_literal2().var())); - SASSERT(w.get_literal1().index() < w.get_literal2().index()); - break; - case watched::CLAUSE: - SASSERT(!s.m_cls_allocator.get_clause(w.get_clause_offset())->was_removed()); - break; - default: - break; - } - } - }); + VERIFY(!s.was_eliminated(l.var()) || wlist.empty()); + if (!check_watches(l, wlist)) + return false; + } return true; } bool integrity_checker::check_reinit_stack() const { - clause_wrapper_vector::const_iterator it = s.m_clauses_to_reinit.begin(); - clause_wrapper_vector::const_iterator end = s.m_clauses_to_reinit.end(); - for (; it != end; ++it) { - if (it->is_binary()) - continue; - SASSERT(it->get_clause()->on_reinit_stack()); + for (auto const& c : s.m_clauses_to_reinit) { + VERIFY(c.is_binary() || c.get_clause()->on_reinit_stack()); } return true; } bool integrity_checker::check_disjoint_clauses() const { uint_set ids; - clause_vector::const_iterator it = s.m_clauses.begin(); - clause_vector::const_iterator end = s.m_clauses.end(); - for (; it != end; ++it) { - ids.insert((*it)->id()); + for (clause* cp : s.m_clauses) { + ids.insert(cp->id()); } - it = s.m_learned.begin(); - end = s.m_learned.end(); - for (; it != end; ++it) { - if (ids.contains((*it)->id())) { - TRACE("sat", tout << "Repeated clause: " << (*it)->id() << "\n";); + for (clause* cp : s.m_learned) { + if (ids.contains(cp->id())) { + TRACE("sat", tout << "Repeated clause: " << cp->id() << "\n";); return false; } } @@ -228,12 +225,12 @@ namespace sat { bool integrity_checker::operator()() const { if (s.inconsistent()) return true; - SASSERT(check_clauses()); - SASSERT(check_learned_clauses()); - SASSERT(check_watches()); - SASSERT(check_bool_vars()); - SASSERT(check_reinit_stack()); - SASSERT(check_disjoint_clauses()); + VERIFY(check_clauses()); + VERIFY(check_learned_clauses()); + VERIFY(check_watches()); + VERIFY(check_bool_vars()); + VERIFY(check_reinit_stack()); + VERIFY(check_disjoint_clauses()); return true; } }; diff --git a/src/sat/sat_integrity_checker.h b/src/sat/sat_integrity_checker.h index 640fce068..10fd2203c 100644 --- a/src/sat/sat_integrity_checker.h +++ b/src/sat/sat_integrity_checker.h @@ -21,6 +21,7 @@ Revision History: #define SAT_INTEGRITY_CHECKER_H_ #include "sat/sat_types.h" +#include "sat/sat_watched.h" namespace sat { class integrity_checker { @@ -35,6 +36,8 @@ namespace sat { bool check_assignment() const; bool check_bool_vars() const; bool check_watches() const; + bool check_watches(literal l, watch_list const& wlist) const; + bool check_watches(literal l) const; bool check_reinit_stack() const; bool check_disjoint_clauses() const; bool operator()() const; diff --git a/src/sat/sat_justification.h b/src/sat/sat_justification.h index 00da9f30e..497d636c8 100644 --- a/src/sat/sat_justification.h +++ b/src/sat/sat_justification.h @@ -25,25 +25,26 @@ namespace sat { public: enum kind { NONE, BINARY, TERNARY, CLAUSE, EXT_JUSTIFICATION }; private: - unsigned m_val1; + size_t m_val1; unsigned m_val2; justification(ext_justification_idx idx, kind k):m_val1(idx), m_val2(k) {} + unsigned val1() const { return static_cast(m_val1); } public: justification():m_val1(0), m_val2(NONE) {} explicit justification(literal l):m_val1(l.to_uint()), m_val2(BINARY) {} justification(literal l1, literal l2):m_val1(l1.to_uint()), m_val2(TERNARY + (l2.to_uint() << 3)) {} explicit justification(clause_offset cls_off):m_val1(cls_off), m_val2(CLAUSE) {} - justification mk_ext_justification(ext_justification_idx idx) { return justification(idx, EXT_JUSTIFICATION); } + static justification mk_ext_justification(ext_justification_idx idx) { return justification(idx, EXT_JUSTIFICATION); } kind get_kind() const { return static_cast(m_val2 & 7); } bool is_none() const { return m_val2 == NONE; } bool is_binary_clause() const { return m_val2 == BINARY; } - literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(m_val1); } + literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(val1()); } bool is_ternary_clause() const { return get_kind() == TERNARY; } - literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(m_val1); } + literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(val1()); } literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 3); } bool is_clause() const { return m_val2 == CLAUSE; } diff --git a/src/sat/sat_local_search.cpp b/src/sat/sat_local_search.cpp new file mode 100644 index 000000000..af2447fc2 --- /dev/null +++ b/src/sat/sat_local_search.cpp @@ -0,0 +1,1092 @@ +/*++ + Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_local_search.cpp + +Abstract: + + Local search module for cardinality clauses. + +Author: + + Sixue Liu 2017-2-21 + +Notes: + +--*/ + +#include "sat/sat_local_search.h" +#include "sat/sat_solver.h" +#include "sat/ba_solver.h" +#include "sat/sat_params.hpp" +#include "util/timer.h" + +namespace sat { + + void local_search::init() { + + for (unsigned i = 0; i < m_assumptions.size(); ++i) { + add_clause(1, m_assumptions.c_ptr() + i); + } + + // add sentinel variable. + m_vars.push_back(var_info()); + + if (m_config.phase_sticky()) { + for (var_info& vi : m_vars) + if (!vi.m_unit) + vi.m_value = vi.m_bias < 100; + } + else { + for (var_info& vi : m_vars) + if (!vi.m_unit) + vi.m_value = (0 == (m_rand() % 2)); + } + + m_best_solution.resize(num_vars() + 1, false); + m_index_in_unsat_stack.resize(num_constraints(), 0); + coefficient_in_ob_constraint.resize(num_vars() + 1, 0); + + if (m_config.mode() == local_search_mode::gsat) { + uint_set is_neighbor; + for (bool_var v = 0; v < num_vars(); ++v) { + is_neighbor.reset(); + bool pol = true; + var_info& vi = m_vars[v]; + for (unsigned k = 0; k < 2; pol = !pol, k++) { + for (auto const& wi : m_vars[v].m_watch[pol]) { + constraint const& c = m_constraints[wi.m_constraint_id]; + for (literal lit : c) { + bool_var w = lit.var(); + if (w == v || is_neighbor.contains(w)) continue; + is_neighbor.insert(w); + vi.m_neighbors.push_back(w); + } + } + } + } + } + + for (auto const& c : ob_constraint) { + coefficient_in_ob_constraint[c.var_id] = c.coefficient; + } + + set_parameters(); + } + + void local_search::init_cur_solution() { + for (var_info& vi : m_vars) { + // use bias with a small probability + if (!vi.m_unit) { + if (m_rand() % 10 < 5 || m_config.phase_sticky()) { + vi.m_value = ((unsigned)(m_rand() % 100) < vi.m_bias); + } + else { + vi.m_value = (m_rand() % 2) == 0; + } + } + } + } + + // figure out slack, and init unsat stack + void local_search::init_slack() { + for (unsigned v = 0; v < num_vars(); ++v) { + bool is_true = cur_solution(v); + coeff_vector& truep = m_vars[v].m_watch[is_true]; + for (auto const& coeff : truep) { + unsigned c = coeff.m_constraint_id; + constraint& cn = m_constraints[c]; + cn.m_slack -= coeff.m_coeff; + } + } + for (unsigned c = 0; c < num_constraints(); ++c) { + // violate the at-most-k constraint + if (m_constraints[c].m_slack < 0) + unsat(c); + } + } + + // figure out variables scores and slack_scores + void local_search::init_scores() { + for (unsigned v = 0; v < num_vars(); ++v) { + bool is_true = cur_solution(v); + coeff_vector& truep = m_vars[v].m_watch[is_true]; + coeff_vector& falsep = m_vars[v].m_watch[!is_true]; + for (auto const& coeff : falsep) { + constraint& c = m_constraints[coeff.m_constraint_id]; + //SASSERT(falsep[i].m_coeff == 1); + // will --slack + if (c.m_slack <= 0) { + dec_slack_score(v); + if (c.m_slack == 0) + dec_score(v); + } + } + for (auto const& coeff : truep) { + //SASSERT(coeff.m_coeff == 1); + constraint& c = m_constraints[coeff.m_constraint_id]; + // will --true_terms_count[c] + // will ++slack + if (c.m_slack <= -1) { + inc_slack_score(v); + if (c.m_slack == -1) + inc_score(v); + } + } + } + } + + // init goodvars + void local_search::init_goodvars() { + m_goodvar_stack.reset(); + for (unsigned v = 0; v < num_vars(); ++v) { + if (score(v) > 0) { // && conf_change[v] == true + m_vars[v].m_in_goodvar_stack = true; + m_goodvar_stack.push_back(v); + } + } + } + + void local_search::reinit() { + + IF_VERBOSE(1, verbose_stream() << "(sat-local-search reinit)\n";); + if (true || !m_is_pb) { + // + // the following methods does NOT converge for pseudo-boolean + // can try other way to define "worse" and "better" + // the current best noise is below 1000 + // + if (m_best_unsat_rate > m_last_best_unsat_rate) { + // worse + m_noise -= m_noise * 2 * m_noise_delta; + m_best_unsat_rate *= 1000.0; + } + else { + // better + m_noise += (10000 - m_noise) * m_noise_delta; + } + } + + for (constraint & c : m_constraints) { + c.m_slack = c.m_k; + } + + // init unsat stack + m_is_unsat = false; + m_unsat_stack.reset(); + + // init solution using the bias + init_cur_solution(); + + // init variable information + // the last variable is the virtual variable + + m_vars.back().m_score = INT_MIN; + m_vars.back().m_conf_change = false; + m_vars.back().m_slack_score = INT_MIN; + m_vars.back().m_cscc = 0; + m_vars.back().m_time_stamp = m_max_steps + 1; + for (unsigned i = 0; i < num_vars(); ++i) { + m_vars[i].m_time_stamp = 0; + m_vars[i].m_cscc = 1; + m_vars[i].m_conf_change = true; + m_vars[i].m_in_goodvar_stack = false; + m_vars[i].m_score = 0; + m_vars[i].m_slack_score = 0; + } + init_slack(); + init_scores(); + init_goodvars(); + set_best_unsat(); + + for (bool_var v : m_units) { + propagate(literal(v, !cur_solution(v))); + if (m_is_unsat) break; + } + if (m_is_unsat) { + IF_VERBOSE(0, verbose_stream() << "unsat during reinit\n"); + } + } + + bool local_search::propagate(literal lit) { + bool unit = is_unit(lit); + VERIFY(is_true(lit)); + m_prop_queue.reset(); + add_propagation(lit); + for (unsigned i = 0; i < m_prop_queue.size() && i < m_vars.size(); ++i) { + literal lit2 = m_prop_queue[i]; + if (!is_true(lit2)) { + if (is_unit(lit2)) { + return false; + } + flip_walksat(lit2.var()); + add_propagation(lit2); + } + } + if (m_prop_queue.size() >= m_vars.size()) { + IF_VERBOSE(0, verbose_stream() << "propagation loop\n"); + return false; + } + if (unit) { + for (literal lit : m_prop_queue) { + VERIFY(is_true(lit)); + add_unit(lit); + } + } + return true; + } + + void local_search::add_propagation(literal l) { + VERIFY(is_true(l)); + for (literal lit : m_vars[l.var()].m_bin[l.sign()]) { + if (!is_true(lit)) { + m_prop_queue.push_back(lit); + } + } + } + + void local_search::set_best_unsat() { + m_best_unsat = m_unsat_stack.size(); + if (m_best_unsat == 1) { + constraint const& c = m_constraints[m_unsat_stack[0]]; + IF_VERBOSE(2, display(verbose_stream() << "single unsat:", c)); + } + } + + void local_search::calculate_and_update_ob() { + unsigned i, v; + int objective_value = 0; + for (i = 0; i < ob_constraint.size(); ++i) { + v = ob_constraint[i].var_id; + if (cur_solution(v)) + objective_value += ob_constraint[i].coefficient; + } + if (objective_value > m_best_objective_value) { + m_best_solution.reset(); + for (unsigned v = 0; v < num_vars(); ++v) { + m_best_solution.push_back(cur_solution(v)); + } + m_best_objective_value = objective_value; + } + } + + bool local_search::all_objectives_are_met() const { + for (unsigned i = 0; i < ob_constraint.size(); ++i) { + bool_var v = ob_constraint[i].var_id; + if (!cur_solution(v)) return false; + } + return true; + } + + void local_search::verify_solution() const { + IF_VERBOSE(0, verbose_stream() << "verifying solution\n"); + for (constraint const& c : m_constraints) + verify_constraint(c); + } + + void local_search::verify_unsat_stack() const { + for (unsigned i : m_unsat_stack) { + constraint const& c = m_constraints[i]; + VERIFY(c.m_k < constraint_value(c)); + } + } + + unsigned local_search::constraint_coeff(constraint const& c, literal l) const { + for (auto const& pb : m_vars[l.var()].m_watch[is_pos(l)]) { + if (pb.m_constraint_id == c.m_id) return pb.m_coeff; + } + UNREACHABLE(); + return 0; + } + + + unsigned local_search::constraint_value(constraint const& c) const { + unsigned value = 0; + for (literal t : c) { + if (is_true(t)) { + value += constraint_coeff(c, t); + } + } + return value; + } + + void local_search::verify_constraint(constraint const& c) const { + unsigned value = constraint_value(c); + IF_VERBOSE(11, display(verbose_stream() << "verify ", c);); + TRACE("sat", display(verbose_stream() << "verify ", c);); + if (c.m_k < value) { + IF_VERBOSE(0, display(verbose_stream() << "violated constraint: ", c) << "value: " << value << "\n";); + } + } + + void local_search::add_clause(unsigned sz, literal const* c) { + add_cardinality(sz, c, sz - 1); + } + + // ~c <= k + void local_search::add_cardinality(unsigned sz, literal const* c, unsigned k) { + if (sz == 1 && k == 0) { + add_unit(c[0]); + return; + } + if (k == 1 && sz == 2) { + // IF_VERBOSE(0, verbose_stream() << "bin: " << ~c[0] << " + " << ~c[1] << " <= 1\n"); + for (unsigned i = 0; i < 2; ++i) { + literal t(c[i]), s(c[1-i]); + m_vars.reserve(t.var() + 1); + m_vars[t.var()].m_bin[is_pos(t)].push_back(s); + } + } + unsigned id = m_constraints.size(); + m_constraints.push_back(constraint(k, id)); + for (unsigned i = 0; i < sz; ++i) { + m_vars.reserve(c[i].var() + 1); + literal t(~c[i]); + m_vars[t.var()].m_watch[is_pos(t)].push_back(pbcoeff(id, 1)); + m_constraints.back().push(t); + } + + } + + // c * coeffs <= k + void local_search::add_pb(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k) { + if (sz == 1 && k == 0) { + add_unit(~c[0]); + return; + } + unsigned id = m_constraints.size(); + m_constraints.push_back(constraint(k, id)); + for (unsigned i = 0; i < sz; ++i) { + m_vars.reserve(c[i].var() + 1); + literal t(c[i]); + m_vars[t.var()].m_watch[is_pos(t)].push_back(pbcoeff(id, coeffs[i])); + m_constraints.back().push(t); + } + } + + void local_search::add_unit(literal lit) { + bool_var v = lit.var(); + if (is_unit(lit)) return; + VERIFY(!m_units.contains(v)); + m_vars[v].m_bias = lit.sign() ? 0 : 100; + m_vars[v].m_value = !lit.sign(); + m_vars[v].m_unit = true; + m_units.push_back(v); + verify_unsat_stack(); + } + + local_search::local_search() : + m_is_unsat(false), + m_par(nullptr) { + } + + void local_search::import(solver& s, bool _init) { + m_is_pb = false; + m_vars.reset(); + m_constraints.reset(); + m_units.reset(); + m_unsat_stack.reset(); + + m_vars.reserve(s.num_vars()); + if (m_config.phase_sticky()) { + unsigned v = 0; + for (var_info& vi : m_vars) { + if (!vi.m_unit) + vi.m_bias = s.m_phase[v] == POS_PHASE ? 100 : 0; + ++v; + } + } + + // copy units + unsigned trail_sz = s.init_trail_size(); + for (unsigned i = 0; i < trail_sz; ++i) { + add_clause(1, s.m_trail.c_ptr() + i); + } + + // copy binary clauses + { + unsigned sz = s.m_watches.size(); + for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { + literal l1 = ~to_literal(l_idx); + watch_list const & wlist = s.m_watches[l_idx]; + for (watched const& w : wlist) { + if (!w.is_binary_non_learned_clause()) + continue; + literal l2 = w.get_literal(); + if (l1.index() > l2.index()) + continue; + literal ls[2] = { l1, l2 }; + add_clause(2, ls); + } + } + } + + // copy clauses + for (clause* c : s.m_clauses) { + add_clause(c->size(), c->begin()); + } + m_num_non_binary_clauses = s.m_clauses.size(); + + // copy cardinality clauses + ba_solver* ext = dynamic_cast(s.get_extension()); + if (ext) { + unsigned_vector coeffs; + literal_vector lits; + for (ba_solver::constraint* cp : ext->m_constraints) { + switch (cp->tag()) { + case ba_solver::card_t: { + ba_solver::card const& c = cp->to_card(); + unsigned n = c.size(); + unsigned k = c.k(); + + if (c.lit() == null_literal) { + // c.lits() >= k + // <=> + // ~c.lits() <= n - k + lits.reset(); + for (unsigned j = 0; j < n; ++j) lits.push_back(c[j]); + add_cardinality(lits.size(), lits.c_ptr(), n - k); + } + else { + // + // c.lit() <=> c.lits() >= k + // + // (c.lits() < k) or c.lit() + // = (c.lits() + (n - k + 1)*~c.lit()) <= n + // + // ~c.lit() or (c.lits() >= k) + // = ~c.lit() or (~c.lits() <= n - k) + // = k*c.lit() + ~c.lits() <= n + // + m_is_pb = true; + lits.reset(); + coeffs.reset(); + for (literal l : c) lits.push_back(l), coeffs.push_back(1); + lits.push_back(~c.lit()); coeffs.push_back(n - k + 1); + add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), n); + + lits.reset(); + coeffs.reset(); + for (literal l : c) lits.push_back(~l), coeffs.push_back(1); + lits.push_back(c.lit()); coeffs.push_back(k); + add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), n); + } + break; + } + case ba_solver::pb_t: { + ba_solver::pb const& p = cp->to_pb(); + lits.reset(); + coeffs.reset(); + m_is_pb = true; + unsigned sum = 0; + for (ba_solver::wliteral wl : p) sum += wl.first; + + if (p.lit() == null_literal) { + // w1 + .. + w_n >= k + // <=> + // ~wl + ... + ~w_n <= sum_of_weights - k + for (ba_solver::wliteral wl : p) lits.push_back(~(wl.second)), coeffs.push_back(wl.first); + add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), sum - p.k()); + } + else { + // lit <=> w1 + .. + w_n >= k + // <=> + // lit or w1 + .. + w_n <= k - 1 + // ~lit or w1 + .. + w_n >= k + // <=> + // (sum - k + 1)*~lit + w1 + .. + w_n <= sum + // k*lit + ~wl + ... + ~w_n <= sum + lits.push_back(p.lit()), coeffs.push_back(p.k()); + for (ba_solver::wliteral wl : p) lits.push_back(~(wl.second)), coeffs.push_back(wl.first); + add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), sum); + + lits.reset(); + coeffs.reset(); + lits.push_back(~p.lit()), coeffs.push_back(sum + 1 - p.k()); + for (ba_solver::wliteral wl : p) lits.push_back(wl.second), coeffs.push_back(wl.first); + add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), sum); + } + break; + } + case ba_solver::xr_t: + NOT_IMPLEMENTED_YET(); + break; + } + } + } + if (_init) { + init(); + } + } + + local_search::~local_search() { + } + + void local_search::add_soft(bool_var v, int weight) { + ob_constraint.push_back(ob_term(v, weight)); + } + + lbool local_search::check() { + return check(0, 0); + } + +#define PROGRESS(tries, flips) \ + if (tries % 10 == 0 || m_unsat_stack.empty()) { \ + IF_VERBOSE(1, verbose_stream() << "(sat-local-search" \ + << " :flips " << flips \ + << " :noise " << m_noise \ + << " :unsat " << /*m_unsat_stack.size()*/ m_best_unsat \ + << " :constraints " << m_constraints.size() \ + << " :time " << (timer.get_seconds() < 0.001 ? 0.0 : timer.get_seconds()) << ")\n";); \ + } + + void local_search::walksat() { + m_best_unsat_rate = 1; + m_last_best_unsat_rate = 1; + + reinit(); + timer timer; + timer.start(); + unsigned step = 0, total_flips = 0, tries = 0; + PROGRESS(tries, total_flips); + + for (tries = 1; !m_unsat_stack.empty() && m_limit.inc(); ++tries) { + for (step = 0; step < m_max_steps && !m_unsat_stack.empty(); ++step) { + pick_flip_walksat(); + if (m_unsat_stack.size() < m_best_unsat) { + set_best_unsat(); + m_last_best_unsat_rate = m_best_unsat_rate; + m_best_unsat_rate = (double)m_unsat_stack.size() / num_constraints(); + } + if (m_is_unsat) return; + } + total_flips += step; + PROGRESS(tries, total_flips); + if (m_par && m_par->get_phase(*this)) { + reinit(); + } + if (tries % 10 == 0 && !m_unsat_stack.empty()) { + reinit(); + } + } + } + + void local_search::gsat() { + reinit(); + bool_var flipvar; + timer timer; + timer.start(); + unsigned tries, step = 0, total_flips = 0; + for (tries = 1; m_limit.inc() && !m_unsat_stack.empty(); ++tries) { + reinit(); + for (step = 1; step <= m_max_steps; ) { + // feasible + if (m_unsat_stack.empty()) { + calculate_and_update_ob(); + if (m_best_objective_value >= m_best_known_value) { + break; + } + } + if (m_unsat_stack.size() < m_best_unsat) { + set_best_unsat(); + } + flipvar = pick_var_gsat(); + flip_gsat(flipvar); + m_vars[flipvar].m_time_stamp = step++; + } + total_flips += step; + PROGRESS(tries, total_flips); + + // tell the SAT solvers about the phase of variables. + if (m_par && tries % 10 == 0) { + m_par->get_phase(*this); + } + } + } + + lbool local_search::check(unsigned sz, literal const* assumptions, parallel* p) { + flet _p(m_par, p); + m_model.reset(); + m_assumptions.reset(); + m_assumptions.append(sz, assumptions); + init(); + + switch (m_config.mode()) { + case local_search_mode::gsat: + gsat(); + break; + case local_search_mode::wsat: + walksat(); + break; + } + + + // remove unit clauses from assumptions. + m_constraints.shrink(num_constraints() - sz); + + TRACE("sat", display(tout);); + + lbool result; + if (m_is_unsat) { + // result = l_false; + result = l_undef; + } + else if (m_unsat_stack.empty() && all_objectives_are_met()) { + verify_solution(); + extract_model(); + result = l_true; + } + else { + result = l_undef; + } + IF_VERBOSE(1, verbose_stream() << "(sat-local-search " << result << ")\n";); + IF_VERBOSE(20, display(verbose_stream());); + return result; + } + + + void local_search::sat(unsigned c) { + unsigned last_unsat_constraint = m_unsat_stack.back(); + int index = m_index_in_unsat_stack[c]; + m_unsat_stack[index] = last_unsat_constraint; + m_index_in_unsat_stack[last_unsat_constraint] = index; + m_unsat_stack.pop_back(); + } + + // swap the deleted one with the last one and pop + void local_search::unsat(unsigned c) { + m_index_in_unsat_stack[c] = m_unsat_stack.size(); + m_unsat_stack.push_back(c); + } + + void local_search::pick_flip_walksat() { + reflip: + bool_var best_var = null_bool_var; + unsigned n = 1; + bool_var v = null_bool_var; + unsigned num_unsat = m_unsat_stack.size(); + constraint const& c = m_constraints[m_unsat_stack[m_rand() % m_unsat_stack.size()]]; + // VERIFY(c.m_k < constraint_value(c)); + unsigned reflipped = 0; + bool is_core = m_unsat_stack.size() <= 10; + // TBD: dynamic noise strategy + //if (m_rand() % 100 < 98) { + if (m_rand() % 10000 <= m_noise) { + // take this branch with 98% probability. + // find the first one, to fast break the rest + unsigned best_bsb = 0; + literal_vector::const_iterator cit = c.m_literals.begin(), cend = c.m_literals.end(); + literal l; + for (; (cit != cend) && (!is_true(*cit) || is_unit(*cit)); ++cit) { } + if (cit == cend) { + if (c.m_k < constraint_value(c)) { + IF_VERBOSE(0, display(verbose_stream() << "unsat clause\n", c)); + m_is_unsat = true; + return; + } + goto reflip; + } + l = *cit; + best_var = v = l.var(); + bool tt = cur_solution(v); + coeff_vector const& falsep = m_vars[v].m_watch[!tt]; + for (pbcoeff const& pbc : falsep) { + int slack = constraint_slack(pbc.m_constraint_id); + if (slack < 0) + ++best_bsb; + else if (slack < static_cast(pbc.m_coeff)) + best_bsb += num_unsat; + } + ++cit; + for (; cit != cend; ++cit) { + l = *cit; + if (is_true(l) && !is_unit(l)) { + v = l.var(); + unsigned bsb = 0; + coeff_vector const& falsep = m_vars[v].m_watch[!cur_solution(v)]; + auto it = falsep.begin(), end = falsep.end(); + for (; it != end; ++it) { + int slack = constraint_slack(it->m_constraint_id); + if (slack < 0) { + if (bsb == best_bsb) { + break; + } + else { + ++bsb; + } + } + else if (slack < static_cast(it->m_coeff)) { + bsb += num_unsat; + if (bsb > best_bsb) { + break; + } + } + } + if (it == end) { + if (bsb < best_bsb) { + best_bsb = bsb; + best_var = v; + n = 1; + } + else {// if (bsb == best_bb) + ++n; + if (m_rand() % n == 0) { + best_var = v; + } + } + } + } + } + } + else { + for (literal l : c) { + if (is_true(l) && !is_unit(l)) { + if (m_rand() % n == 0) { + best_var = l.var(); + } + ++n; + } + } + } + if (best_var == null_bool_var) { + IF_VERBOSE(1, verbose_stream() << "(sat.local_search :unsat)\n"); + return; + } + if (is_unit(best_var)) { + goto reflip; + } + flip_walksat(best_var); + literal lit(best_var, !cur_solution(best_var)); + if (!propagate(lit)) { + if (is_true(lit)) { + flip_walksat(best_var); + } + add_unit(~lit); + if (!propagate(~lit)) { + IF_VERBOSE(0, verbose_stream() << "unsat\n"); + m_is_unsat = true; + return; + } + goto reflip; + } + + if (false && is_core && c.m_k < constraint_value(c)) { + ++reflipped; + goto reflip; + } + } + + void local_search::flip_walksat(bool_var flipvar) { + VERIFY(!is_unit(flipvar)); + m_vars[flipvar].m_value = !cur_solution(flipvar); + + bool flip_is_true = cur_solution(flipvar); + coeff_vector const& truep = m_vars[flipvar].m_watch[flip_is_true]; + coeff_vector const& falsep = m_vars[flipvar].m_watch[!flip_is_true]; + + for (auto const& pbc : truep) { + unsigned ci = pbc.m_constraint_id; + constraint& c = m_constraints[ci]; + int old_slack = c.m_slack; + c.m_slack -= pbc.m_coeff; + if (c.m_slack < 0 && old_slack >= 0) { // from non-negative to negative: sat -> unsat + unsat(ci); + } + } + for (auto const& pbc : falsep) { + unsigned ci = pbc.m_constraint_id; + constraint& c = m_constraints[ci]; + int old_slack = c.m_slack; + c.m_slack += pbc.m_coeff; + if (c.m_slack >= 0 && old_slack < 0) { // from negative to non-negative: unsat -> sat + sat(ci); + } + } + + // verify_unsat_stack(); + } + + void local_search::flip_gsat(bool_var flipvar) { + // already changed truth value!!!! + m_vars[flipvar].m_value = !cur_solution(flipvar); + + unsigned v; + int org_flipvar_score = score(flipvar); + int org_flipvar_slack_score = slack_score(flipvar); + + bool flip_is_true = cur_solution(flipvar); + coeff_vector& truep = m_vars[flipvar].m_watch[flip_is_true]; + coeff_vector& falsep = m_vars[flipvar].m_watch[!flip_is_true]; + + // update related clauses and neighbor vars + for (unsigned i = 0; i < truep.size(); ++i) { + constraint & c = m_constraints[truep[i].m_constraint_id]; + //++true_terms_count[c]; + --c.m_slack; + switch (c.m_slack) { + case -2: // from -1 to -2 + for (literal l : c) { + v = l.var(); + // flipping the slack increasing var will no longer satisfy this constraint + if (is_true(l)) { + //score[v] -= constraint_weight[c]; + dec_score(v); + } + } + break; + case -1: // from 0 to -1: sat -> unsat + for (literal l : c) { + v = l.var(); + inc_cscc(v); + //score[v] += constraint_weight[c]; + inc_score(v); + // slack increasing var + if (is_true(l)) + inc_slack_score(v); + } + unsat(truep[i].m_constraint_id); + break; + case 0: // from 1 to 0 + for (literal l : c) { + v = l.var(); + // flip the slack decreasing var will falsify this constraint + if (is_false(l)) { + // score[v] -= constraint_weight[c]; + dec_score(v); + dec_slack_score(v); + } + } + break; + default: + break; + } + } + for (pbcoeff const& f : falsep) { + constraint& c = m_constraints[f.m_constraint_id]; + //--true_terms_count[c]; + ++c.m_slack; + switch (c.m_slack) { + case 1: // from 0 to 1 + for (literal l : c) { + v = l.var(); + // flip the slack decreasing var will no long falsify this constraint + if (is_false(l)) { + //score[v] += constraint_weight[c]; + inc_score(v); + inc_slack_score(v); + } + } + break; + case 0: // from -1 to 0: unsat -> sat + for (literal l : c) { + v = l.var(); + inc_cscc(v); + //score[v] -= constraint_weight[c]; + dec_score(v); + // slack increasing var no longer sat this var + if (is_true(l)) + dec_slack_score(v); + } + sat(f.m_constraint_id); + break; + case -1: // from -2 to -1 + for (literal l : c) { + v = l.var(); + // flip the slack increasing var will satisfy this constraint + if (is_true(l)) { + //score[v] += constraint_weight[c]; + inc_score(v); + } + } + break; + default: + break; + } + } + m_vars[flipvar].m_score = -org_flipvar_score; + m_vars[flipvar].m_slack_score = -org_flipvar_slack_score; + m_vars[flipvar].m_conf_change = false; + m_vars[flipvar].m_cscc = 0; + + /* update CCD */ + // remove the vars no longer goodvar in goodvar stack + for (unsigned i = m_goodvar_stack.size(); i > 0;) { + --i; + v = m_goodvar_stack[i]; + if (score(v) <= 0) { + m_goodvar_stack[i] = m_goodvar_stack.back(); + m_goodvar_stack.pop_back(); + m_vars[v].m_in_goodvar_stack = false; + } + } + // update all flipvar's neighbor's conf_change to true, add goodvar/okvar + + var_info& vi = m_vars[flipvar]; + for (auto v : vi.m_neighbors) { + m_vars[v].m_conf_change = true; + if (score(v) > 0 && !already_in_goodvar_stack(v)) { + m_goodvar_stack.push_back(v); + m_vars[v].m_in_goodvar_stack = true; + } + } + } + + bool local_search::tie_breaker_sat(bool_var v, bool_var best_var) { + // most improvement on objective value + int v_imp = cur_solution(v) ? -coefficient_in_ob_constraint.get(v, 0) : coefficient_in_ob_constraint.get(v, 0); + int b_imp = cur_solution(best_var) ? -coefficient_in_ob_constraint.get(best_var, 0) : coefficient_in_ob_constraint.get(best_var, 0); + // std::cout << v_imp << "\n"; + // break tie 1: max imp + // break tie 2: conf_change + // break tie 3: time_stamp + + return + (v_imp > b_imp) || + ((v_imp == b_imp) && + ((conf_change(v) && !conf_change(best_var)) || + ((conf_change(v) == conf_change(best_var)) && + (time_stamp(v) < time_stamp(best_var))))); + } + + bool local_search::tie_breaker_ccd(bool_var v, bool_var best_var) { + // break tie 1: max score + // break tie 2: max slack_score + // break tie 3: cscc + // break tie 4: oldest one + return + ((score(v) > score(best_var)) || + ((score(v) == score(best_var)) && + ((slack_score(v) > slack_score(best_var)) || + ((slack_score(v) == slack_score(best_var)) && + ((cscc(v) > cscc(best_var)) || + ((cscc(v) == cscc(best_var)) && + (time_stamp(v) < time_stamp(best_var)))))))); + } + + bool_var local_search::pick_var_gsat() { + bool_var best_var = m_vars.size()-1; // sentinel variable + // SAT Mode + if (m_unsat_stack.empty()) { + //std::cout << "as\t"; + for (auto const& c : ob_constraint) { + bool_var v = c.var_id; + if (tie_breaker_sat(v, best_var)) + best_var = v; + } + return best_var; + } + + // Unsat Mode: CCD > RD + // CCD mode + if (!m_goodvar_stack.empty()) { + //++ccd; + best_var = m_goodvar_stack[0]; + for (bool_var v : m_goodvar_stack) { + if (tie_breaker_ccd(v, best_var)) + best_var = v; + } + return best_var; + } + + // Diversification Mode + constraint const& c = m_constraints[m_unsat_stack[m_rand() % m_unsat_stack.size()]]; // a random unsat constraint + // Within c, from all slack increasing var, choose the oldest one + for (literal l : c) { + bool_var v = l.var(); + if (is_true(l) && time_stamp(v) < time_stamp(best_var)) + best_var = v; + } + return best_var; + } + + void local_search::set_parameters() { + m_rand.set_seed(m_config.random_seed()); + m_best_known_value = m_config.best_known_value(); + + switch (m_config.mode()) { + case local_search_mode::gsat: + m_max_steps = 2 * num_vars(); + break; + case local_search_mode::wsat: + m_max_steps = std::min(static_cast(20 * num_vars()), static_cast(1 << 17)); // cut steps off at 100K + break; + } + + TRACE("sat", + tout << "seed:\t" << m_config.random_seed() << '\n'; + tout << "best_known_value:\t" << m_config.best_known_value() << '\n'; + tout << "max_steps:\t" << m_max_steps << '\n'; + ); + } + + void local_search::print_info(std::ostream& out) { + for (unsigned v = 0; v < num_vars(); ++v) { + out << "v" << v << "\t" + << m_vars[v].m_neighbors.size() << '\t' + << cur_solution(v) << '\t' + << conf_change(v) << '\t' + << score(v) << '\t' + << slack_score(v) << '\n'; + } + } + + void local_search::extract_model() { + m_model.reset(); + for (unsigned v = 0; v < num_vars(); ++v) { + m_model.push_back(cur_solution(v) ? l_true : l_false); + } + } + + std::ostream& local_search::display(std::ostream& out) const { + for (constraint const& c : m_constraints) { + display(out, c); + } + for (bool_var v = 0; v < num_vars(); ++v) { + display(out, v, m_vars[v]); + } + return out; + } + + std::ostream& local_search::display(std::ostream& out, constraint const& c) const { + for (literal l : c) { + unsigned coeff = constraint_coeff(c, l); + if (coeff > 1) out << coeff << " * "; + out << l << " "; + } + return out << " <= " << c.m_k << " lhs value: " << constraint_value(c) << "\n"; + } + + std::ostream& local_search::display(std::ostream& out, unsigned v, var_info const& vi) const { + return out << "v" << v << " := " << (vi.m_value?"true":"false") << " bias: " << vi.m_bias << "\n"; + } + + bool local_search::check_goodvar() { + unsigned g = 0; + for (unsigned v = 0; v < num_vars(); ++v) { + if (conf_change(v) && score(v) > 0) { + ++g; + if (!already_in_goodvar_stack(v)) + std::cout << "3\n"; + } + } + if (g == m_goodvar_stack.size()) + return true; + else { + if (g < m_goodvar_stack.size()) + std::cout << "1\n"; + else + std::cout << "2\n"; // delete too many + return false; + } + } + + void local_search::set_phase(bool_var v, lbool f) { + unsigned& bias = m_vars[v].m_bias; + if (f == l_true && bias < 100) bias++; + if (f == l_false && bias > 0) bias--; + // f == l_undef ? + } + +} diff --git a/src/sat/sat_local_search.h b/src/sat/sat_local_search.h new file mode 100644 index 000000000..8a63898c3 --- /dev/null +++ b/src/sat/sat_local_search.h @@ -0,0 +1,299 @@ +/*++ + Copyright (c) 2017 Microsoft Corporation + + Module Name: + + sat_local_search.h + + Abstract: + + Local search module for cardinality clauses. + + Author: + + Sixue Liu 2017-2-21 + + Notes: + + --*/ +#ifndef _SAT_LOCAL_SEARCH_H_ +#define _SAT_LOCAL_SEARCH_H_ + +#include "util/vector.h" +#include "sat/sat_types.h" +#include "sat/sat_config.h" +#include "util/rlimit.h" + +namespace sat { + + class parallel; + + class local_search_config { + unsigned m_random_seed; + int m_best_known_value; + local_search_mode m_mode; + bool m_phase_sticky; + public: + local_search_config() { + m_random_seed = 0; + m_best_known_value = INT_MAX; + m_mode = local_search_mode::wsat; + m_phase_sticky = false; + } + + unsigned random_seed() const { return m_random_seed; } + unsigned best_known_value() const { return m_best_known_value; } + local_search_mode mode() const { return m_mode; } + bool phase_sticky() const { return m_phase_sticky; } + + void set_random_seed(unsigned s) { m_random_seed = s; } + void set_best_known_value(unsigned v) { m_best_known_value = v; } + + void set_config(config const& cfg) { + m_mode = cfg.m_local_search_mode; + m_random_seed = cfg.m_random_seed; + m_phase_sticky = cfg.m_phase_sticky; + } + }; + + + class local_search { + + struct pbcoeff { + unsigned m_constraint_id; + unsigned m_coeff; + pbcoeff(unsigned id, unsigned coeff): + m_constraint_id(id), m_coeff(coeff) {} + }; + typedef svector bool_vector; + typedef svector coeff_vector; + + // data structure for a term in objective function + struct ob_term { + bool_var var_id; // variable id, begin with 1 + int coefficient; // non-zero integer + ob_term(bool_var v, int c): var_id(v), coefficient(c) {} + }; + + struct var_info { + bool m_value; // current solution + unsigned m_bias; // bias for current solution in percentage. + // if bias is 0, then value is always false, if 100, then always true + bool m_unit; // is this a unit literal + bool m_conf_change; // whether its configure changes since its last flip + bool m_in_goodvar_stack; + int m_score; + int m_slack_score; + int m_time_stamp; // the flip time stamp + int m_cscc; // how many times its constraint state configure changes since its last flip + bool_var_vector m_neighbors; // neighborhood variables + coeff_vector m_watch[2]; + literal_vector m_bin[2]; + var_info(): + m_value(true), + m_bias(50), + m_unit(false), + m_conf_change(true), + m_in_goodvar_stack(false), + m_score(0), + m_slack_score(0), + m_cscc(0) + {} + }; + + struct constraint { + unsigned m_id; + unsigned m_k; + int m_slack; + unsigned m_size; + literal_vector m_literals; + constraint(unsigned k, unsigned id) : m_id(id), m_k(k), m_slack(0), m_size(0) {} + void push(literal l) { m_literals.push_back(l); ++m_size; } + unsigned size() const { return m_size; } + literal const& operator[](unsigned idx) const { return m_literals[idx]; } + literal const* begin() const { return m_literals.begin(); } + literal const* end() const { return m_literals.end(); } + }; + + local_search_config m_config; + + // objective function: maximize + svector ob_constraint; // the objective function *constraint*, sorted in decending order + + // information about the variable + int_vector coefficient_in_ob_constraint; // var! initialized to be 0 + + + vector m_vars; + svector m_units; + + inline int score(bool_var v) const { return m_vars[v].m_score; } + inline void inc_score(bool_var v) { m_vars[v].m_score++; } + inline void dec_score(bool_var v) { m_vars[v].m_score--; } + + inline int slack_score(bool_var v) const { return m_vars[v].m_slack_score; } + inline void inc_slack_score(bool_var v) { m_vars[v].m_slack_score++; } + inline void dec_slack_score(bool_var v) { m_vars[v].m_slack_score--; } + + inline bool already_in_goodvar_stack(bool_var v) const { return m_vars[v].m_in_goodvar_stack; } + inline bool conf_change(bool_var v) const { return m_vars[v].m_conf_change; } + inline int time_stamp(bool_var v) const { return m_vars[v].m_time_stamp; } + inline int cscc(bool_var v) const { return m_vars[v].m_cscc; } + inline void inc_cscc(bool_var v) { m_vars[v].m_cscc++; } + + inline bool cur_solution(bool_var v) const { return m_vars[v].m_value; } + + inline void set_best_unsat(); + /* TBD: other scores */ + + + vector m_constraints; + + literal_vector m_assumptions; + literal_vector m_prop_queue; + + unsigned m_num_non_binary_clauses; + bool m_is_pb; + + inline bool is_pos(literal t) const { return !t.sign(); } + inline bool is_true(bool_var v) const { return cur_solution(v); } + inline bool is_true(literal l) const { return cur_solution(l.var()) != l.sign(); } + inline bool is_false(literal l) const { return cur_solution(l.var()) == l.sign(); } + inline bool is_unit(bool_var v) const { return m_vars[v].m_unit; } + inline bool is_unit(literal l) const { return m_vars[l.var()].m_unit; } + + unsigned num_constraints() const { return m_constraints.size(); } // constraint index from 1 to num_constraint + + + unsigned constraint_slack(unsigned ci) const { return m_constraints[ci].m_slack; } + + // unsat constraint stack + bool m_is_unsat; + unsigned_vector m_unsat_stack; // store all the unsat constraits + unsigned_vector m_index_in_unsat_stack; // which position is a contraint in the unsat_stack + + // configuration changed decreasing variables (score>0 and conf_change==true) + bool_var_vector m_goodvar_stack; + + + // information about solution + unsigned m_best_unsat; + double m_best_unsat_rate; + double m_last_best_unsat_rate; + int m_objective_value; // the objective function value corresponds to the current solution + bool_vector m_best_solution; // !var: the best solution so far + int m_best_objective_value = -1; // the objective value corresponds to the best solution so far + // for non-known instance, set as maximal + int m_best_known_value = INT_MAX; // best known value for this instance + + unsigned m_max_steps = (1 << 30); + + // dynamic noise + double m_noise = 9800; // normalized by 10000 + double m_noise_delta = 0.05; + + reslimit m_limit; + random_gen m_rand; + parallel* m_par; + model m_model; + + void init(); + void reinit(); + void reinit_orig(); + void init_cur_solution(); + void init_slack(); + void init_scores(); + void init_goodvars(); + + bool_var pick_var_gsat(); + + void flip_gsat(bool_var v); + + void pick_flip_walksat(); + + void flip_walksat(bool_var v); + + bool propagate(literal lit); + + void add_propagation(literal lit); + + void walksat(); + + void gsat(); + + void unsat(unsigned c); + + void sat(unsigned c); + + bool tie_breaker_sat(bool_var v1, bool_var v2); + + bool tie_breaker_ccd(bool_var v1, bool_var v2); + + void set_parameters(); + + void calculate_and_update_ob(); + + bool all_objectives_are_met() const; + + void verify_solution() const; + + void verify_unsat_stack() const; + + void verify_constraint(constraint const& c) const; + + unsigned constraint_value(constraint const& c) const; + + unsigned constraint_coeff(constraint const& c, literal l) const; + + void print_info(std::ostream& out); + + void extract_model(); + + bool check_goodvar(); + + void add_clause(unsigned sz, literal const* c); + + void add_unit(literal lit); + + std::ostream& display(std::ostream& out) const; + + std::ostream& display(std::ostream& out, constraint const& c) const; + + std::ostream& display(std::ostream& out, unsigned v, var_info const& vi) const; + + public: + + local_search(); + + reslimit& rlimit() { return m_limit; } + + ~local_search(); + + void add_soft(bool_var v, int weight); + + void add_cardinality(unsigned sz, literal const* c, unsigned k); + + void add_pb(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k); + + lbool check(); + + lbool check(unsigned sz, literal const* assumptions, parallel* p = 0); + + local_search_config& config() { return m_config; } + + unsigned num_vars() const { return m_vars.size() - 1; } // var index from 1 to num_vars + + unsigned num_non_binary_clauses() const { return m_num_non_binary_clauses; } + + void import(solver& s, bool init); + + void set_phase(bool_var v, lbool f); + + bool get_phase(bool_var v) const { return is_true(v); } + + model& get_model() { return m_model; } + + }; +} + +#endif diff --git a/src/sat/sat_lookahead.cpp b/src/sat/sat_lookahead.cpp new file mode 100644 index 000000000..3833e2a52 --- /dev/null +++ b/src/sat/sat_lookahead.cpp @@ -0,0 +1,2494 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_lookahead.h + +Abstract: + + Lookahead SAT solver in the style of March. + Thanks also to the presentation in sat11.w. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-2-11 + +Notes: + +--*/ + +#include +#include "sat/sat_solver.h" +#include "sat/sat_extension.h" +#include "sat/sat_lookahead.h" +#include "sat/sat_scc.h" +#include "util/union_find.h" + +namespace sat { + lookahead::scoped_ext::scoped_ext(lookahead& p): p(p) { + if (p.m_s.m_ext) p.m_s.m_ext->set_lookahead(&p); + } + + lookahead::scoped_ext::~scoped_ext() { + if (p.m_s.m_ext) p.m_s.m_ext->set_lookahead(0); + } + + lookahead::scoped_assumptions::scoped_assumptions(lookahead& p, literal_vector const& lits): p(p), lits(lits) { + for (auto l : lits) { + p.push(l, p.c_fixed_truth); + } + } + lookahead::scoped_assumptions::~scoped_assumptions() { + for (auto l : lits) { + (void)l; + p.pop(); + } + } + + void lookahead::flip_prefix() { + if (m_trail_lim.size() < 64) { + uint64_t mask = (1ull << m_trail_lim.size()); + m_prefix = mask | (m_prefix & (mask - 1)); + } + } + + void lookahead::prune_prefix() { + if (m_trail_lim.size() < 64) { + m_prefix &= (1ull << m_trail_lim.size()) - 1; + } + } + + void lookahead::update_prefix(literal l) { + bool_var x = l.var(); + unsigned p = m_vprefix[x].m_prefix; + unsigned pl = m_vprefix[x].m_length; + unsigned mask = (1 << std::min(31u, pl)) - 1; + if (pl >= m_trail_lim.size() || (p & mask) != (m_prefix & mask)) { + m_vprefix[x].m_length = m_trail_lim.size(); + m_vprefix[x].m_prefix = static_cast(m_prefix); + } + } + + bool lookahead::active_prefix(bool_var x) { + unsigned lvl = m_trail_lim.size(); + unsigned p = m_vprefix[x].m_prefix; + unsigned l = m_vprefix[x].m_length; + if (l > lvl) return false; + if (l == lvl || l >= 31) return m_prefix == p; + unsigned mask = ((1 << std::min(l,31u)) - 1); + return (m_prefix & mask) == (p & mask); + } + + void lookahead::add_binary(literal l1, literal l2) { + TRACE("sat", tout << "binary: " << l1 << " " << l2 << "\n";); + SASSERT(l1 != l2); + // don't add tautologies and don't add already added binaries + if (~l1 == l2) return; + if (!m_binary[(~l1).index()].empty() && m_binary[(~l1).index()].back() == l2) return; + m_binary[(~l1).index()].push_back(l2); + m_binary[(~l2).index()].push_back(l1); + m_binary_trail.push_back((~l1).index()); + ++m_stats.m_add_binary; + if (m_s.m_config.m_drat) validate_binary(l1, l2); + } + + void lookahead::del_binary(unsigned idx) { + // TRACE("sat", display(tout << "Delete " << to_literal(idx) << "\n");); + literal_vector & lits = m_binary[idx]; + SASSERT(!lits.empty()); + literal l = lits.back(); + lits.pop_back(); + SASSERT(!m_binary[(~l).index()].empty()); + IF_VERBOSE(0, if (m_binary[(~l).index()].back() != ~to_literal(idx)) verbose_stream() << "pop bad literal: " << idx << " " << (~l).index() << "\n";); + SASSERT(m_binary[(~l).index()].back() == ~to_literal(idx)); + m_binary[(~l).index()].pop_back(); + ++m_stats.m_del_binary; + } + + + void lookahead::validate_binary(literal l1, literal l2) { + if (m_search_mode == lookahead_mode::searching) { + m_assumptions.push_back(l1); + m_assumptions.push_back(l2); + m_drat.add(m_assumptions); + m_assumptions.pop_back(); + m_assumptions.pop_back(); + } + } + + void lookahead::inc_bstamp() { + ++m_bstamp_id; + if (m_bstamp_id == 0) { + ++m_bstamp_id; + m_bstamp.fill(0); + } + } + + void lookahead::inc_istamp() { + ++m_istamp_id; + if (m_istamp_id == 0) { + ++m_istamp_id; + for (unsigned i = 0; i < m_lits.size(); ++i) { + m_lits[i].m_double_lookahead = 0; + } + } + } + + void lookahead::set_bstamps(literal l) { + inc_bstamp(); + set_bstamp(l); + literal_vector const& conseq = m_binary[l.index()]; + for (literal l : conseq) { + set_bstamp(l); + } + } + + /** + \brief add one-step transitive closure of binary implications + return false if we learn a unit literal. + \pre all implicants of ~u are stamped. + u \/ v is true + **/ + + bool lookahead::add_tc1(literal u, literal v) { + unsigned sz = m_binary[v.index()].size(); + for (unsigned i = 0; i < sz; ++i) { + literal w = m_binary[v.index()][i]; + // ~v \/ w + if (!is_fixed(w)) { + if (is_stamped(~w)) { + // u \/ v, ~v \/ w, u \/ ~w => u is unit + TRACE("sat", tout << "tc1: " << u << "\n";); + propagated(u); + return false; + } + if (m_num_tc1 < m_config.m_tc1_limit) { + ++m_num_tc1; + IF_VERBOSE(30, verbose_stream() << "tc1: " << u << " " << w << "\n";); + add_binary(u, w); + } + } + } + return true; + } + + + /** + \brief main routine for adding a new binary clause dynamically. + */ + void lookahead::try_add_binary(literal u, literal v) { + SASSERT(m_search_mode == lookahead_mode::searching); + SASSERT(u.var() != v.var()); + if (!is_undef(u) || !is_undef(v)) { + IF_VERBOSE(0, verbose_stream() << "adding assigned binary " << v << " " << u << "\n";); + } + set_bstamps(~u); + if (is_stamped(~v)) { + TRACE("sat", tout << "try_add_binary: " << u << "\n";); + propagated(u); // u \/ ~v, u \/ v => u is a unit literal + } + else if (!is_stamped(v) && add_tc1(u, v)) { + // u \/ v is not in index + set_bstamps(~v); + if (is_stamped(~u)) { + TRACE("sat", tout << "try_add_binary: " << v << "\n";); + propagated(v); // v \/ ~u, u \/ v => v is a unit literal + } + else if (add_tc1(v, u)) { + update_prefix(u); + update_prefix(v); + add_binary(u, v); + } + } + } + + // ------------------------------------- + // pre-selection + // see also 91 - 102 sat11.w + + + void lookahead::pre_select() { + IF_VERBOSE(10, verbose_stream() << "(sat-lookahead :freevars " << m_freevars.size() << ")\n";); + m_lookahead.reset(); + for (bool_var x : m_freevars) { // tree lookahead leaves literals fixed in lower truth levels + literal l(x, false); + set_undef(l); + set_undef(~l); + } + if (select(scope_lvl())) { + get_scc(); + if (inconsistent()) return; + find_heights(); + construct_lookahead_table(); + } + } + + + bool lookahead::select(unsigned level) { + init_pre_selection(level); + unsigned level_cand = std::max(m_config.m_level_cand, m_freevars.size() / 50); + unsigned max_num_cand = (level > 0 && m_config.m_preselect) ? level_cand / level : m_freevars.size(); + max_num_cand = std::max(m_config.m_min_cutoff, max_num_cand); + + double sum = 0; + for (bool newbies = false; ; newbies = true) { + sum = init_candidates(level, newbies); + if (!m_candidates.empty()) break; + if (is_sat()) { + return false; + } + } + SASSERT(!m_candidates.empty()); + // cut number of candidates down to max_num_cand. + // step 1. cut it to at most 2*max_num_cand. + // step 2. use a heap to sift through the rest. + bool progress = true; + while (progress && m_candidates.size() >= max_num_cand * 2) { + progress = false; + double mean = sum / (double)(m_candidates.size() + 0.0001); + sum = 0; + for (unsigned i = 0; i < m_candidates.size() && m_candidates.size() >= max_num_cand * 2; ++i) { + if (m_candidates[i].m_rating >= mean) { + sum += m_candidates[i].m_rating; + } + else { + m_candidates[i] = m_candidates.back(); + m_candidates.pop_back(); + --i; + progress = true; + } + } + } + TRACE("sat", display_candidates(tout);); + SASSERT(!m_candidates.empty()); + heap_sort(); + while (m_candidates.size() > max_num_cand) { + m_candidates.pop_back(); + } + SASSERT(!m_candidates.empty() && m_candidates.size() <= max_num_cand); + TRACE("sat", display_candidates(tout);); + return true; + } + + void lookahead::heap_sort() { + if (m_candidates.size() > 1) { + heapify(); + for (unsigned i = m_candidates.size() - 1; i > 0; --i) { + candidate c = m_candidates[i]; + m_candidates[i] = m_candidates[0]; + m_candidates[0] = c; + sift_down(0, i); + } + } + SASSERT(validate_heap_sort()); + } + + void lookahead::heapify() { + unsigned i = 1 + (m_candidates.size() - 2) / 2; + while(i > 0) { + sift_down(--i, m_candidates.size()); + } + } + + void lookahead::sift_down(unsigned j, unsigned sz) { + unsigned i = j; + candidate c = m_candidates[j]; + for (unsigned k = 2 * j + 1; k < sz; i = k, k = 2 * k + 1) { + // pick smallest child + if (k + 1 < sz && m_candidates[k].m_rating > m_candidates[k + 1].m_rating) { + ++k; + } + if (c.m_rating <= m_candidates[k].m_rating) break; + m_candidates[i] = m_candidates[k]; + } + if (i > j) m_candidates[i] = c; + } + + /** + * \brief validate that the result of heap sort sorts the candidates + * in descending order of their rating. + */ + bool lookahead::validate_heap_sort() { + for (unsigned i = 0; i + 1 < m_candidates.size(); ++i) + if (m_candidates[i].m_rating < m_candidates[i + 1].m_rating) + return false; + return true; + } + + double lookahead::init_candidates(unsigned level, bool newbies) { + m_candidates.reset(); + double sum = 0; + unsigned skip_candidates = 0; + bool autarky = get_config().m_lookahead_global_autarky; + for (bool_var x : m_freevars) { + SASSERT(is_undef(x)); + if (!m_select_lookahead_vars.empty()) { + if (m_select_lookahead_vars.contains(x)) { + if (!autarky || newbies || in_reduced_clause(x)) { + m_candidates.push_back(candidate(x, m_rating[x])); + sum += m_rating[x]; + } + else { + skip_candidates++; + } + } + } + else if (newbies || active_prefix(x)) { + m_candidates.push_back(candidate(x, m_rating[x])); + sum += m_rating[x]; + } + } + TRACE("sat", display_candidates(tout << "sum: " << sum << "\n");); + if (skip_candidates > 0) { + IF_VERBOSE(1, verbose_stream() << "(sat-lookahead :candidates " << m_candidates.size() << " :skipped " << skip_candidates << ")\n";); + } + return sum; + } + + + std::ostream& lookahead::display_candidates(std::ostream& out) const { + for (unsigned i = 0; i < m_candidates.size(); ++i) { + out << "var: " << m_candidates[i].m_var << " rating: " << m_candidates[i].m_rating << "\n"; + } + return out; + } + + bool lookahead::is_unsat() const { + for (unsigned idx = 0; idx < m_binary.size(); ++idx) { + literal l = to_literal(idx); + for (literal lit : m_binary[idx]) { + if (is_true(l) && is_false(lit)) + return true; + } + } + // check if there is a clause whose literals are false. + // every clause is terminated by a null-literal. + for (nary* n : m_nary_clauses) { + bool all_false = true; + for (literal l : *n) { + all_false &= is_false(l); + } + if (all_false) return true; + } + // check if there is a ternary whose literals are false. + for (unsigned idx = 0; idx < m_ternary.size(); ++idx) { + literal lit = to_literal(idx); + if (is_false(lit)) { + unsigned sz = m_ternary_count[lit.index()]; + for (binary const& b : m_ternary[lit.index()]) { + if (sz-- == 0) break; + if (is_false(b.m_u) && is_false(b.m_v)) + return true; + } + } + } + return false; + } + + bool lookahead::is_sat() const { + for (bool_var x : m_freevars) { + literal l(x, false); + literal_vector const& lits1 = m_binary[l.index()]; + for (literal lit1 : lits1) { + if (!is_true(lit1)) { + TRACE("sat", tout << l << " " << lit1 << "\n";); + return false; + } + } + l.neg(); + literal_vector const& lits2 = m_binary[l.index()]; + for (literal lit2 : lits2) { + if (!is_true(lit2)) { + TRACE("sat", tout << l << " " << lit2 << "\n";); + return false; + } + } + } + // check if there is a clause whose literals are false. + // every clause is terminated by a null-literal. + for (nary * n : m_nary_clauses) { + bool no_true = true; + for (literal l : *n) { + no_true &= !is_true(l); + } + if (no_true) return false; + } + // check if there is a ternary whose literals are false. + for (unsigned idx = 0; idx < m_ternary.size(); ++idx) { + literal lit = to_literal(idx); + if (!is_true(lit)) { + unsigned sz = m_ternary_count[lit.index()]; + for (binary const& b : m_ternary[lit.index()]) { + if (sz-- == 0) break; + if (!is_true(b.m_u) && !is_true(b.m_v)) + return false; + } + } + } + return true; + } + + bool lookahead::missed_propagation() const { + if (inconsistent()) return false; + for (literal l1 : m_trail) { + SASSERT(is_true(l1)); + for (literal l2 : m_binary[l1.index()]) { + VERIFY(is_true(l2)); + if (is_undef(l2)) return true; + } + unsigned sz = m_ternary_count[(~l1).index()]; + for (binary b : m_ternary[(~l1).index()]) { + if (sz-- == 0) break; + if (!(is_true(b.m_u) || is_true(b.m_v) || (is_undef(b.m_v) && is_undef(b.m_u)))) { + IF_VERBOSE(0, verbose_stream() << b.m_u << " " << b.m_v << "\n" + << get_level(b.m_u) << " " << get_level(b.m_v) << " level: " << m_level << "\n";); + UNREACHABLE(); + } + if ((is_false(b.m_u) && is_undef(b.m_v)) || (is_false(b.m_v) && is_undef(b.m_u))) + return true; + } + } + for (nary * n : m_nary_clauses) { + if (n->size() == 1 && !is_true(n->get_head())) { + for (literal lit : *n) { + VERIFY(!is_undef(lit)); + if (is_undef(lit)) return true; + } + } + } + return false; + } + + bool lookahead::missed_conflict() const { + if (inconsistent()) return false; + for (literal l1 : m_trail) { + for (literal l2 : m_binary[l1.index()]) { + if (is_false(l2)) + return true; + } + unsigned sz = m_ternary_count[(~l1).index()]; + for (binary b : m_ternary[(~l1).index()]) { + if (sz-- == 0) break; + if ((is_false(b.m_u) && is_false(b.m_v))) + return true; + } + } + for (nary * n : m_nary_clauses) { + if (n->size() == 0) + return true; + } + return false; + } + + void lookahead::init_pre_selection(unsigned level) { + switch (m_config.m_reward_type) { + case ternary_reward: { + unsigned max_level = m_config.m_max_hlevel; + if (level <= 1) { + ensure_H(2); + h_scores(m_H[0], m_H[1]); + for (unsigned j = 0; j < 2; ++j) { + for (unsigned i = 0; i < 2; ++i) { + h_scores(m_H[i + 1], m_H[(i + 2) % 3]); + } + } + m_heur = &m_H[1]; + } + else if (level < max_level) { + ensure_H(level); + h_scores(m_H[level-1], m_H[level]); + m_heur = &m_H[level]; + } + else { + ensure_H(max_level); + h_scores(m_H[max_level-1], m_H[max_level]); + m_heur = &m_H[max_level]; + } + break; + } + case heule_schur_reward: + heule_schur_scores(); + break; + case heule_unit_reward: + heule_unit_scores(); + break; + case march_cu_reward: + march_cu_scores(); + break; + case unit_literal_reward: + heule_schur_scores(); + break; + } + } + + void lookahead::heule_schur_scores() { + if (m_rating_throttle++ % 10 != 0) return; + for (bool_var x : m_freevars) { + literal l(x, false); + m_rating[l.var()] = heule_schur_score(l) * heule_schur_score(~l); + } + } + + double lookahead::heule_schur_score(literal l) { + double sum = 0; + for (literal lit : m_binary[l.index()]) { + if (is_undef(lit)) sum += literal_occs(lit) / 4.0; + } + unsigned sz = m_ternary_count[(~l).index()]; + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + sum += (literal_occs(b.m_u) + literal_occs(b.m_v)) / 8.0; + } + sz = m_nary_count[(~l).index()]; + for (nary * n : m_nary[(~l).index()]) { + if (sz-- == 0) break; + double to_add = 0; + for (literal lit : *n) { + if (!is_fixed(lit) && lit != ~l) { + to_add += literal_occs(lit); + } + } + unsigned len = n->size(); + sum += pow(0.5, len) * to_add / len; + } + return sum; + } + + void lookahead::heule_unit_scores() { + if (m_rating_throttle++ % 10 != 0) return; + for (bool_var x : m_freevars) { + literal l(x, false); + m_rating[l.var()] = heule_unit_score(l) * heule_unit_score(~l); + } + } + + double lookahead::heule_unit_score(literal l) { + double sum = 0; + for (literal lit : m_binary[l.index()]) { + if (is_undef(lit)) sum += 0.5; + } + sum += 0.25 * m_ternary_count[(~l).index()]; + unsigned sz = m_nary_count[(~l).index()]; + for (nary * n : m_nary[(~l).index()]) { + if (sz-- == 0) break; + sum += pow(0.5, n->size()); + } + return sum; + } + + void lookahead::march_cu_scores() { + for (bool_var x : m_freevars) { + literal l(x, false); + double pos = march_cu_score(l), neg = march_cu_score(~l); + m_rating[l.var()] = 1024 * pos * neg + pos + neg + 1; + } + } + + double lookahead::march_cu_score(literal l) { + double sum = 1.0 + literal_big_occs(~l); + for (literal lit : m_binary[l.index()]) { + if (is_undef(lit)) sum += literal_big_occs(lit); + } + return sum; + } + + void lookahead::ensure_H(unsigned level) { + while (m_H.size() <= level) { + m_H.push_back(svector()); + m_H.back().resize(m_num_vars * 2, 0); + } + } + + void lookahead::h_scores(svector& h, svector& hp) { + double sum = 0; + for (bool_var x : m_freevars) { + literal l(x, false); + sum += h[l.index()] + h[(~l).index()]; + } + if (sum == 0) sum = 0.0001; + double factor = 2 * m_freevars.size() / sum; + double sqfactor = factor * factor; + double afactor = factor * m_config.m_alpha; + for (bool_var x : m_freevars) { + literal l(x, false); + double pos = l_score(l, h, factor, sqfactor, afactor); + double neg = l_score(~l, h, factor, sqfactor, afactor); + hp[l.index()] = pos; + hp[(~l).index()] = neg; + m_rating[l.var()] = pos * neg; + } + } + + double lookahead::l_score(literal l, svector const& h, double factor, double sqfactor, double afactor) { + double sum = 0, tsum = 0; + for (literal lit : m_binary[l.index()]) { + if (is_undef(lit)) sum += h[lit.index()]; + // if (m_freevars.contains(lit.var())) sum += h[lit.index()]; + } + unsigned sz = m_ternary_count[(~l).index()]; + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + tsum += h[b.m_u.index()] * h[b.m_v.index()]; + } + // std::cout << "sum: " << sum << " afactor " << afactor << " sqfactor " << sqfactor << " tsum " << tsum << "\n"; + sum = (double)(0.1 + afactor*sum + sqfactor*tsum); + // std::cout << "sum: " << sum << " max score " << m_config.m_max_score << "\n"; + return std::min(m_config.m_max_score, sum); + } + + // ------------------------------------ + // Implication graph + // Compute implication ordering and strongly connected components. + // sat11.w 103 - 114. + + void lookahead::get_scc() { + unsigned num_candidates = m_candidates.size(); + init_scc(); + for (unsigned i = 0; i < num_candidates && !inconsistent(); ++i) { + literal lit(m_candidates[i].m_var, false); + if (get_rank(lit) == 0) get_scc(lit); + if (get_rank(~lit) == 0) get_scc(~lit); + } + TRACE("sat", display_scc(tout);); + } + void lookahead::init_scc() { + inc_bstamp(); + for (unsigned i = 0; i < m_candidates.size(); ++i) { + literal lit(m_candidates[i].m_var, false); + init_dfs_info(lit); + init_dfs_info(~lit); + } + for (unsigned i = 0; i < m_candidates.size(); ++i) { + literal lit(m_candidates[i].m_var, false); + init_arcs(lit); + init_arcs(~lit); + } + m_rank = 0; + m_rank_max = UINT_MAX; + m_active = null_literal; + m_settled = null_literal; + TRACE("sat", display_dfs(tout);); + } + void lookahead::init_dfs_info(literal l) { + unsigned idx = l.index(); + m_dfs[idx].reset(); + set_bstamp(l); + } + // arcs are added in the opposite direction of implications. + // So for implications l => u we add arcs u -> l + void lookahead::init_arcs(literal l) { + literal_vector lits; + literal_vector const& succ = m_binary[l.index()]; + for (literal u : succ) { + SASSERT(u != l); + // l => u + // NB. u.index() > l.index() iff u.index() > (~l).index(). + // since indices for the same boolean variables occupy + // two adjacent numbers. + if (u.index() > l.index() && is_stamped(u) && ~l != u) { + add_arc(~l, ~u); + add_arc( u, l); + } + } + for (auto w : m_watches[l.index()]) { + lits.reset(); + if (w.is_ext_constraint() && m_s.m_ext->is_extended_binary(w.get_ext_constraint_idx(), lits)) { + for (literal u : lits) { + // u is positive in lits, l is negative: + if (~l != u && u.index() > l.index() && is_stamped(u)) { + add_arc(~l, ~u); + add_arc( u, l); + } + } + } + } + } + + void lookahead::add_arc(literal u, literal v) { + auto & lst = m_dfs[u.index()].m_next; + if (lst.empty() || lst.back() != v) lst.push_back(v); + } + + void lookahead::get_scc(literal v) { + TRACE("scc", tout << v << "\n";); + set_parent(v, null_literal); + activate_scc(v); + do { + literal ll = get_min(v); + if (has_arc(v)) { + literal u = pop_arc(v); + unsigned r = get_rank(u); + if (r > 0) { + // u was processed before ll + if (r < get_rank(ll)) set_min(v, u); + } + else { + // process u in dfs order, add v to dfs stack for u + set_parent(u, v); + v = u; + activate_scc(v); + } + } + else { + literal u = get_parent(v); + if (v == ll) { + found_scc(v); + } + else if (get_rank(ll) < get_rank(get_min(u))) { + set_min(u, ll); + } + // walk back in the dfs stack + v = u; + } + } + while (v != null_literal && !inconsistent()); + } + + void lookahead::activate_scc(literal l) { + SASSERT(get_rank(l) == 0); + set_rank(l, ++m_rank); + set_link(l, m_active); + set_min(l, l); + m_active = l; + } + // make v root of the scc equivalence class + // set vcomp to be the highest rated literal + void lookahead::found_scc(literal v) { + literal t = m_active; + m_active = get_link(v); + literal best = v; + double best_rating = get_rating(v); + set_rank(v, m_rank_max); + set_link(v, m_settled); m_settled = t; + while (t != v) { + if (t == ~v) { + TRACE("sat", display_scc(tout << "found contradiction during scc search\n");); + set_conflict(); + break; + } + set_rank(t, m_rank_max); + set_parent(t, v); + double t_rating = get_rating(t); + if (t_rating > best_rating) { + best = t; + best_rating = t_rating; + } + t = get_link(t); + } + set_parent(v, v); + set_vcomp(v, best); + if (maxed_rank(~v)) { + set_vcomp(v, ~get_vcomp(get_parent(~v))); + } + } + + std::ostream& lookahead::display_dfs(std::ostream& out) const { + for (unsigned i = 0; i < m_candidates.size(); ++i) { + literal l(m_candidates[i].m_var, false); + display_dfs(out, l); + display_dfs(out, ~l); + } + return out; + } + + std::ostream& lookahead::display_dfs(std::ostream& out, literal l) const { + arcs const& a1 = get_arcs(l); + if (!a1.empty()) { + out << l << " -> " << a1 << "\n"; + } + return out; + } + + std::ostream& lookahead::display_scc(std::ostream& out) const { + display_dfs(out); + for (unsigned i = 0; i < m_candidates.size(); ++i) { + literal l(m_candidates[i].m_var, false); + display_scc(out, l); + display_scc(out, ~l); + } + return out; + } + + std::ostream& lookahead::display_scc(std::ostream& out, literal l) const { + out << l << " := " << get_parent(l) + << " min: " << get_min(l) + << " rank: " << get_rank(l) + << " height: " << get_height(l) + << " link: " << get_link(l) + << " child: " << get_child(l) + << " vcomp: " << get_vcomp(l) << "\n"; + return out; + } + + + // ------------------------------------ + // lookahead forest + // sat11.w 115-121 + + literal lookahead::get_child(literal u) const { + if (u == null_literal) return m_root_child; + return m_dfs[u.index()].m_min; + } + + void lookahead::set_child(literal v, literal u) { + if (v == null_literal) m_root_child = u; + else m_dfs[v.index()].m_min = u; + } + + /* + \brief Assign heights to the nodes. + Nodes within the same strongly connected component are given the same height. + The code assumes that m_settled is topologically sorted such that + 1. nodes in the same equivalence class come together + 2. the equivalence class representative is last + + */ + void lookahead::find_heights() { + m_root_child = null_literal; + literal pp = null_literal; + unsigned h = 0; + literal w, uu; + TRACE("sat", + for (literal u = m_settled; u != null_literal; u = get_link(u)) { + tout << u << " "; + } + tout << "\n";); + for (literal u = m_settled; u != null_literal; u = uu) { + TRACE("sat", tout << "process: " << u << "\n";); + uu = get_link(u); + literal p = get_parent(u); + if (p != pp) { + // new equivalence class + h = 0; + w = null_literal; + pp = p; + } + // traverse nodes in order of implication + unsigned sz = num_next(~u); + for (unsigned j = 0; j < sz; ++j) { + literal v = ~get_next(~u, j); + TRACE("sat", tout << "child " << v << " link: " << get_link(v) << "\n";); + literal pv = get_parent(v); + // skip nodes in same equivalence, they will all be processed + if (pv == p) continue; + unsigned hh = get_height(pv); + // update the maximal height descendant + if (hh >= h) { + h = hh + 1; + w = pv; + } + } + if (p == u) { + // u is an equivalence class representative + // it is processed last + literal v = get_child(w); + set_height(u, h); + set_child(u, null_literal); + set_link(u, v); + set_child(w, u); + TRACE("sat", tout << "child(" << w << ") = " << u << " link(" << u << ") = " << v << "\n";); + } + } + TRACE("sat", + display_forest(tout << "forest: ", get_child(null_literal)); + tout << "\n"; + display_scc(tout); ); + } + std::ostream& lookahead::display_forest(std::ostream& out, literal l) { + for (literal u = l; u != null_literal; u = get_link(u)) { + out << u << " "; + l = get_child(u); + if (l != null_literal) { + out << "("; + display_forest(out, l); + out << ") "; + } + } + return out; + } + + void lookahead::display_search_string() { + printf("\r"); + uint64_t q = m_prefix; + unsigned depth = m_trail_lim.size(); + unsigned d = std::min(63u, depth); + unsigned new_prefix_length = d; + for (unsigned i = 0; i <= d; ++i) { + printf((0 != (q & (1ull << i)))? "1" : "0"); + } + if (d < depth) { + printf(" d: %d", depth); + new_prefix_length += 10; + } + for (unsigned i = new_prefix_length; i < m_last_prefix_length; ++i) { + printf(" "); + } + m_last_prefix_length = new_prefix_length; + fflush(stdout); + } + + void lookahead::construct_lookahead_table() { + literal u = get_child(null_literal), v = null_literal; + unsigned offset = 0; + SASSERT(m_lookahead.empty()); + while (u != null_literal) { + set_rank(u, m_lookahead.size()); + set_lookahead(get_vcomp(u)); + if (null_literal != get_child(u)) { + set_parent(u, v); + v = u; + u = get_child(u); + } + else { + while (true) { + set_offset(get_rank(u), offset); + offset += 2; + set_parent(u, v == null_literal ? v : get_vcomp(v)); + u = get_link(u); + if (u == null_literal && v != null_literal) { + u = v; + v = get_parent(u); + } + else { + break; + } + } + } + } + SASSERT(2*m_lookahead.size() == offset); + TRACE("sat", for (unsigned i = 0; i < m_lookahead.size(); ++i) + tout << m_lookahead[i].m_lit << " : " << m_lookahead[i].m_offset << "\n";); + } + + + + // ------------------------------------ + // initialization + + void lookahead::init_var(bool_var v) { + m_binary.push_back(literal_vector()); + m_binary.push_back(literal_vector()); + m_watches.push_back(watch_list()); + m_watches.push_back(watch_list()); + m_ternary.push_back(svector()); + m_ternary.push_back(svector()); + m_ternary_count.push_back(0); + m_ternary_count.push_back(0); + m_nary.push_back(ptr_vector()); + m_nary.push_back(ptr_vector()); + m_nary_count.push_back(0); + m_nary_count.push_back(0); + m_bstamp.push_back(0); + m_bstamp.push_back(0); + m_stamp.push_back(0); + m_dfs.push_back(dfs_info()); + m_dfs.push_back(dfs_info()); + m_lits.push_back(lit_info()); + m_lits.push_back(lit_info()); + m_rating.push_back(0); + m_vprefix.push_back(prefix()); + if (!m_s.was_eliminated(v)) + m_freevars.insert(v); + } + + void lookahead::init(bool learned) { + m_delta_trigger = 0.0; + m_delta_decrease = 0.0; + m_config.m_dl_success = 0.8; + m_inconsistent = false; + m_qhead = 0; + m_bstamp_id = 0; + + for (unsigned i = 0; i < m_num_vars; ++i) { + init_var(i); + } + + // copy binary clauses + unsigned sz = m_s.m_watches.size(); + for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { + literal l = ~to_literal(l_idx); + if (m_s.was_eliminated(l.var())) continue; + watch_list const & wlist = m_s.m_watches[l_idx]; + for (auto& w : wlist) { + if (!w.is_binary_clause()) + continue; + if (!learned && w.is_learned()) + continue; + literal l2 = w.get_literal(); + if (l.index() < l2.index() && !m_s.was_eliminated(l2.var())) + add_binary(l, l2); + } + } + + copy_clauses(m_s.m_clauses, false); + if (learned) copy_clauses(m_s.m_learned, true); + + // copy units + unsigned trail_sz = m_s.init_trail_size(); + for (unsigned i = 0; i < trail_sz; ++i) { + literal l = m_s.m_trail[i]; + if (!m_s.was_eliminated(l.var())) { + if (m_s.m_config.m_drat) m_drat.add(l, false); + assign(l); + } + } + + if (m_s.m_ext) { + // m_ext = m_s.m_ext->copy(this, learned); + } + propagate(); + m_qhead = m_trail.size(); + m_init_freevars = m_freevars.size(); + TRACE("sat", m_s.display(tout); display(tout);); + } + + void lookahead::copy_clauses(clause_vector const& clauses, bool learned) { + // copy clauses + for (clause* cp : clauses) { + clause& c = *cp; + if (c.was_removed()) continue; + // enable when there is a non-ternary reward system. + bool was_eliminated = false; + for (unsigned i = 0; !was_eliminated && i < c.size(); ++i) { + was_eliminated = m_s.was_eliminated(c[i].var()); + } + if (was_eliminated) continue; + + switch (c.size()) { + case 0: set_conflict(); break; + case 1: assign(c[0]); break; + case 2: add_binary(c[0],c[1]); break; + case 3: add_ternary(c[0],c[1],c[2]); break; + default: if (!learned) add_clause(c); break; + } + if (m_s.m_config.m_drat) m_drat.add(c, false); + } + } + + // ------------------------------------ + // search + + + void lookahead::push(literal lit, unsigned level) { + SASSERT(m_search_mode == lookahead_mode::searching); + m_binary_trail_lim.push_back(m_binary_trail.size()); + m_trail_lim.push_back(m_trail.size()); + m_num_tc1_lim.push_back(m_num_tc1); + m_qhead_lim.push_back(m_qhead); + scoped_level _sl(*this, level); + m_assumptions.push_back(~lit); + assign(lit); + propagate(); + } + + void lookahead::pop() { + SASSERT(!m_assumptions.empty()); + m_assumptions.pop_back(); + m_inconsistent = false; + SASSERT(m_search_mode == lookahead_mode::searching); + + // m_freevars only for main search + // undo assignments + unsigned old_sz = m_trail_lim.back(); + for (unsigned i = m_trail.size(); i > old_sz; ) { + --i; + literal l = m_trail[i]; + set_undef(l); + TRACE("sat", tout << "inserting free var v" << l.var() << "\n";); + m_freevars.insert(l.var()); + } + + m_num_tc1 = m_num_tc1_lim.back(); + m_num_tc1_lim.pop_back(); + + for (unsigned i = m_qhead; i > m_qhead_lim.back(); ) { + --i; + restore_ternary(m_trail[i]); + restore_clauses(m_trail[i]); + } + + m_trail.shrink(old_sz); // reset assignment. + m_trail_lim.pop_back(); + + + // remove local binary clauses + old_sz = m_binary_trail_lim.back(); + for (unsigned i = m_binary_trail.size(); i > old_sz; ) { + del_binary(m_binary_trail[--i]); + } + m_binary_trail.shrink(old_sz); + m_binary_trail_lim.pop_back(); + + // reset propagation queue + m_qhead = m_qhead_lim.back(); + m_qhead_lim.pop_back(); + } + + bool lookahead::push_lookahead2(literal lit, unsigned level) { + scoped_level _sl(*this, level); + SASSERT(m_search_mode == lookahead_mode::lookahead1); + m_search_mode = lookahead_mode::lookahead2; + lookahead_backtrack(); + assign(lit); + propagate(); + bool unsat = inconsistent(); + SASSERT(m_search_mode == lookahead_mode::lookahead2); + m_search_mode = lookahead_mode::lookahead1; + m_inconsistent = false; + return unsat; + } + + unsigned lookahead::push_lookahead1(literal lit, unsigned level) { + SASSERT(m_search_mode == lookahead_mode::searching); + m_search_mode = lookahead_mode::lookahead1; + scoped_level _sl(*this, level); + lookahead_backtrack(); + unsigned old_sz = m_trail.size(); + assign(lit); + propagate(); + return m_trail.size() - old_sz; + } + + void lookahead::pop_lookahead1(literal lit, unsigned num_units) { + bool unsat = inconsistent(); + SASSERT(m_search_mode == lookahead_mode::lookahead1); + m_inconsistent = false; + m_search_mode = lookahead_mode::searching; + // convert windfalls to binary clauses. + if (!unsat) { + literal nlit = ~lit; + + for (unsigned i = 0; i < m_wstack.size(); ++i) { + literal l2 = m_wstack[i]; + //update_prefix(~lit); + //update_prefix(m_wstack[i]); + TRACE("sat", tout << "windfall: " << nlit << " " << l2 << "\n";); + // if we use try_add_binary, then this may produce new assignments + // these assignments get put on m_trail, and they are cleared by + // lookahead_backtrack. + add_binary(nlit, l2); + } + m_stats.m_windfall_binaries += m_wstack.size(); + } + switch (m_config.m_reward_type) { + case unit_literal_reward: + m_lookahead_reward += num_units; + break; + case heule_unit_reward: + case march_cu_reward: + case heule_schur_reward: + break; + default: + break; + } + m_wstack.reset(); + } + + void lookahead::lookahead_backtrack() { + literal lit = null_literal; + while (!m_trail.empty() && is_undef((lit = m_trail.back()))) { + if (m_qhead == m_trail.size()) { + unsigned sz = m_nary_count[(~lit).index()]; + for (nary* n : m_nary[(~lit).index()]) { + if (sz-- == 0) break; + n->inc_size(); + } + --m_qhead; + } + m_trail.pop_back(); + } + SASSERT(m_trail_lim.empty() || m_trail.size() >= m_trail_lim.back()); + } + + // + // The current version is modeled after CDCL SAT solving data-structures. + // It borrows from the watch list data-structure. The cost tradeoffs are somewhat + // biased towards CDCL search overheads. + // If we walk over the positive occurrences of l, then those clauses can be retired so + // that they don't interfere with calculation of H. Instead of removing clauses from the watch + // list one can swap them to the "back" and adjust a size indicator of the watch list + // Only the size indicator needs to be updated on backtracking. + // + + class lookahead_literal_occs_fun : public literal_occs_fun { + lookahead& lh; + public: + lookahead_literal_occs_fun(lookahead& lh): lh(lh) {} + double operator()(literal l) { return lh.literal_occs(l); } + }; + + // Ternary clause managagement: + + void lookahead::add_ternary(literal u, literal v, literal w) { + SASSERT(u != w && u != v && v != w && ~u != w && ~u != v && ~w != v); + m_ternary[u.index()].push_back(binary(v, w)); + m_ternary[v.index()].push_back(binary(w, u)); + m_ternary[w.index()].push_back(binary(u, v)); + m_ternary_count[u.index()]++; + m_ternary_count[v.index()]++; + m_ternary_count[w.index()]++; + } + + lbool lookahead::propagate_ternary(literal l1, literal l2) { + if (is_fixed(l1)) { + if (is_false(l1)) { + if (is_false(l2)) { + TRACE("sat", tout << l1 << " " << l2 << " " << "\n";); + set_conflict(); + return l_false; + } + else if (is_undef(l2)) { + propagated(l2); + } + return l_true; + } + else { + return l_true; + } + } + + if (is_fixed(l2)) { + if (is_false(l2)) { + propagated(l1); + return l_false; + } + else { + return l_true; + } + } + return l_undef; + } + + void lookahead::propagate_ternary(literal l) { + unsigned sz = m_ternary_count[(~l).index()]; + + switch (m_search_mode) { + case lookahead_mode::searching: { + + // ternary clauses where l is negative become binary + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + // this could create a conflict from propagation, but we complete the transaction. + TRACE("sat", display(tout);); + literal l1 = b.m_u; + literal l2 = b.m_v; + switch (propagate_ternary(l1, l2)) { + case l_undef: + try_add_binary(l1, l2); + break; + default: + // propagated or tautology or conflict + break; + } + remove_ternary(l1, l2, ~l); + remove_ternary(l2, ~l, l1); + } + + sz = m_ternary_count[l.index()]; + // ternary clauses where l is positive are tautologies + for (binary const& b : m_ternary[l.index()]) { + if (sz-- == 0) break; + remove_ternary(b.m_u, b.m_v, l); + remove_ternary(b.m_v, l, b.m_u); + } + break; + } + case lookahead_mode::lookahead1: + // this could create a conflict from propagation, but we complete the loop. + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + literal l1 = b.m_u; + literal l2 = b.m_v; + switch (propagate_ternary(l1, l2)) { + case l_undef: + update_binary_clause_reward(l1, l2); + break; + default: + break; + } + } + break; + case lookahead2: + // this could create a conflict from propagation, but we complete the loop. + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + propagate_ternary(b.m_u, b.m_v); + } + break; + } + } + + void lookahead::remove_ternary(literal l, literal u, literal v) { + unsigned idx = l.index(); + unsigned sz = m_ternary_count[idx]--; + auto& tv = m_ternary[idx]; + for (unsigned i = sz; i-- > 0; ) { + binary const& b = tv[i]; + if (b.m_u == u && b.m_v == v) { + std::swap(tv[i], tv[sz-1]); + return; + } + } + UNREACHABLE(); + } + + void lookahead::restore_ternary(literal l) { + unsigned sz = m_ternary_count[(~l).index()]; + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + m_ternary_count[b.m_u.index()]++; + m_ternary_count[b.m_v.index()]++; + } + sz = m_ternary_count[l.index()]; + for (binary const& b : m_ternary[l.index()]) { + if (sz-- == 0) break; + m_ternary_count[b.m_u.index()]++; + m_ternary_count[b.m_v.index()]++; + } + } + + void lookahead::propagate_external(literal l) { + SASSERT(is_true(l)); + if (!m_s.m_ext || inconsistent()) return; + watch_list& wlist = m_watches[l.index()]; + watch_list::iterator it = wlist.begin(), it2 = it, end = wlist.end(); + for (; it != end && !inconsistent(); ++it) { + SASSERT(it->get_kind() == watched::EXT_CONSTRAINT); + bool keep = m_s.m_ext->propagate(l, it->get_ext_constraint_idx()); + if (m_search_mode == lookahead_mode::lookahead1 && !m_inconsistent) { + lookahead_literal_occs_fun literal_occs_fn(*this); + m_lookahead_reward += m_s.m_ext->get_reward(l, it->get_ext_constraint_idx(), literal_occs_fn); + } + if (inconsistent()) { + if (!keep) ++it; + } + else if (keep) { + *it2 = *it; + it2++; + } + } + for (; it != end; ++it, ++it2) { + *it2 = *it; + } + wlist.set_end(it2); + } + + + // new n-ary clause managment + + void lookahead::add_clause(clause const& c) { + SASSERT(c.size() > 3); + void * mem = m_allocator.allocate(nary::get_obj_size(c.size())); + nary * n = new (mem) nary(c.size(), c.begin()); + m_nary_clauses.push_back(n); + for (literal l : c) { + m_nary[l.index()].push_back(n); + m_nary_count[l.index()]++; + } + } + + + void lookahead::propagate_clauses_searching(literal l) { + // clauses where l is negative + unsigned sz = m_nary_count[(~l).index()]; + literal lit; + SASSERT(m_search_mode == lookahead_mode::searching); + for (nary* n : m_nary[(~l).index()]) { + if (sz-- == 0) break; + unsigned len = n->dec_size(); + if (is_true(n->get_head())) continue; + if (inconsistent()) continue; + if (len <= 1) continue; // already processed + // find the two unassigned literals, if any + if (len == 2) { + literal l1 = null_literal; + literal l2 = null_literal; + bool found_true = false; + for (literal lit : *n) { + if (!is_fixed(lit)) { + if (l1 == null_literal) { + l1 = lit; + } + else { + SASSERT(l2 == null_literal); + l2 = lit; + break; + } + } + else if (is_true(lit)) { + n->set_head(lit); + found_true = true; + break; + } + } + if (found_true) { + // skip, the clause will be removed when propagating on 'lit' + } + else if (l1 == null_literal) { + set_conflict(); + } + else if (l2 == null_literal) { + // clause may get revisited during propagation, when l2 is true in this clause. + // m_removed_clauses.push_back(std::make_pair(~l, idx)); + // remove_clause_at(~l, idx); + propagated(l1); + } + else { + // extract binary clause. A unary or empty clause may get revisited, + // but we skip it then because it is already handled as a binary clause. + // m_removed_clauses.push_back(std::make_pair(~l, idx)); // need to restore this clause. + // remove_clause_at(~l, idx); + try_add_binary(l1, l2); + } + } + } + // clauses where l is positive: + sz = m_nary_count[l.index()]; + for (nary* n : m_nary[l.index()]) { + if (sz-- == 0) break; + remove_clause_at(l, *n); + } + } + + void lookahead::propagate_clauses_lookahead(literal l) { + // clauses where l is negative + unsigned sz = m_nary_count[(~l).index()]; + SASSERT(m_search_mode == lookahead_mode::lookahead1 || + m_search_mode == lookahead_mode::lookahead2); + + for (nary* n : m_nary[(~l).index()]) { + if (sz-- == 0) break; + unsigned nonfixed = n->dec_size(); + // if (is_true(n->get_head())) continue; + if (inconsistent()) continue; + if (nonfixed <= 1 && !is_true(n->get_head())) { + bool found_conflict = true; + for (literal lit : *n) { + if (!is_fixed(lit)) { + propagated(lit); + found_conflict = false; + break; + } + else if (is_true(lit)) { + n->set_head(lit); + found_conflict = false; + break; + } + } + if (found_conflict) { + set_conflict(); + continue; + } + } + if (m_search_mode == lookahead_mode::lookahead1) { + //SASSERT(nonfixed >= 2); + switch (m_config.m_reward_type) { + case heule_schur_reward: { + double to_add = 0; + for (literal lit : *n) { + if (!is_fixed(lit)) { + to_add += literal_occs(lit); + } + } + m_lookahead_reward += pow(0.5, nonfixed) * to_add / nonfixed; + break; + } + case heule_unit_reward: + m_lookahead_reward += pow(0.5, nonfixed); + break; + case march_cu_reward: + m_lookahead_reward += nonfixed >= 2 ? 3.3 * pow(0.5, nonfixed - 2) : 0.0; + break; + case ternary_reward: + UNREACHABLE(); + break; + case unit_literal_reward: + break; + } + } + } + // clauses where l is positive: + sz = m_nary_count[l.index()]; + for (nary* n : m_nary[l.index()]) { + if (sz-- == 0) break; + if (get_level(l) > get_level(n->get_head())) { + n->set_head(l); + } + } + } + + void lookahead::remove_clause_at(literal l, nary& n) { + for (literal lit : n) { + if (lit != l) { + remove_clause(lit, n); + } + } + } + + void lookahead::remove_clause(literal l, nary& n) { + ptr_vector& pclauses = m_nary[l.index()]; + unsigned sz = m_nary_count[l.index()]--; + for (unsigned i = sz; i > 0; ) { + --i; + if (&n == pclauses[i]) { + std::swap(pclauses[i], pclauses[sz-1]); + return; + } + } + UNREACHABLE(); + } + + void lookahead::restore_clauses(literal l) { + SASSERT(m_search_mode == lookahead_mode::searching); + // increase the length of clauses where l is negative + unsigned sz = m_nary_count[(~l).index()]; + for (nary* n : m_nary[(~l).index()]) { + if (sz-- == 0) break; + n->inc_size(); + } + // add idx back to clause list where l is positive + // add them back in the same order as they were inserted + // in this way we can check that the clauses are the same. + sz = m_nary_count[l.index()]; + ptr_vector& pclauses = m_nary[l.index()]; + for (unsigned i = sz; i-- > 0; ) { + for (literal lit : *pclauses[i]) { + if (lit != l) { + SASSERT(m_nary[lit.index()][m_nary_count[lit.index()]] == pclauses[i]); + m_nary_count[lit.index()]++; + } + } + } + } + + void lookahead::propagate_clauses(literal l) { + SASSERT(is_true(l)); + propagate_ternary(l); + switch (m_search_mode) { + case lookahead_mode::searching: + propagate_clauses_searching(l); + break; + default: + propagate_clauses_lookahead(l); + break; + } + propagate_external(l); + } + + void lookahead::update_binary_clause_reward(literal l1, literal l2) { + SASSERT(!is_false(l1)); + SASSERT(!is_false(l2)); + switch (m_config.m_reward_type) { + case ternary_reward: + m_lookahead_reward += (*m_heur)[l1.index()] * (*m_heur)[l2.index()]; + break; + case heule_schur_reward: + m_lookahead_reward += (literal_occs(l1) + literal_occs(l2)) / 8.0; + break; + case heule_unit_reward: + m_lookahead_reward += 0.25; + break; + case march_cu_reward: + m_lookahead_reward += 3.3; + break; + case unit_literal_reward: + break; + } + } + + void lookahead::update_nary_clause_reward(clause const& c) { + if (m_config.m_reward_type == ternary_reward && m_lookahead_reward != 0) { + return; + } + literal const * l_it = c.begin() + 2, *l_end = c.end(); + unsigned sz = 0; + for (; l_it != l_end; ++l_it) { + if (is_true(*l_it)) return; + if (!is_false(*l_it)) ++sz; + } + switch (m_config.m_reward_type) { + case heule_schur_reward: { + SASSERT(sz > 0); + double to_add = 0; + for (literal l : c) { + if (!is_false(l)) { + to_add += literal_occs(l); + } + } + m_lookahead_reward += pow(0.5, sz) * to_add / sz; + break; + } + case heule_unit_reward: + m_lookahead_reward += pow(0.5, sz); + break; + case march_cu_reward: + m_lookahead_reward += 3.3 * pow(0.5, sz - 2); + break; + case ternary_reward: + m_lookahead_reward = (double)0.001; + break; + case unit_literal_reward: + break; + } + } + + // Sum_{ clause C that contains ~l } 1 + // FIXME: counts occurences of ~l; misleading + double lookahead::literal_occs(literal l) { + double result = m_binary[l.index()].size(); + result += literal_big_occs(l); + return result; + } + + // Sum_{ clause C that contains ~l such that |C| > 2} 1 + // FIXME: counts occurences of ~l; misleading + double lookahead::literal_big_occs(literal l) { + double result = m_nary_count[(~l).index()]; + result += m_ternary_count[(~l).index()]; + return result; + } + + void lookahead::propagate_binary(literal l) { + literal_vector const& lits = m_binary[l.index()]; + TRACE("sat", tout << l << " => " << lits << "\n";); + for (literal lit : lits) { + if (inconsistent()) break; + assign(lit); + } + } + + void lookahead::propagate() { + unsigned i = m_qhead; + for (; i < m_trail.size() && !inconsistent(); ++i) { + literal l = m_trail[i]; + TRACE("sat", tout << "propagate " << l << " @ " << m_level << "\n";); + propagate_binary(l); + } + while (m_qhead < m_trail.size() && !inconsistent()) { + propagate_clauses(m_trail[m_qhead++]); + } + SASSERT(m_qhead == m_trail.size() || (inconsistent() && m_qhead < m_trail.size())); + //SASSERT(!missed_conflict()); + //VERIFY(!missed_propagation()); + TRACE("sat_verbose", display(tout << scope_lvl() << " " << (inconsistent()?"unsat":"sat") << "\n");); + } + + + void lookahead::compute_lookahead_reward() { + TRACE("sat", display_lookahead(tout); ); + m_delta_decrease = pow(m_config.m_delta_rho, 1.0 / (double)m_lookahead.size()); + unsigned base = 2; + bool change = true; + literal last_changed = null_literal; + while (change && !inconsistent()) { + change = false; + for (unsigned i = 0; !inconsistent() && i < m_lookahead.size(); ++i) { + checkpoint(); + literal lit = m_lookahead[i].m_lit; + if (lit == last_changed) { + SASSERT(!change); + break; + } + if (scope_lvl() == 1) { + IF_VERBOSE(30, verbose_stream() << scope_lvl() << " " << lit << " binary: " << m_binary_trail.size() << " trail: " << m_trail_lim.back() << "\n";); + } + unsigned level = base + m_lookahead[i].m_offset; + unsigned dl_lvl = level; + if (is_fixed_at(lit, c_fixed_truth) || is_true_at(lit, level)) continue; + bool unsat = false; + if (is_false_at(lit, level)) { + unsat = true; + } + else { + TRACE("sat", tout << "lookahead: " << lit << " @ " << m_lookahead[i].m_offset << "\n";); + reset_lookahead_reward(lit); + unsigned num_units = push_lookahead1(lit, level); + update_lookahead_reward(lit, level); + num_units += do_double(lit, dl_lvl); + if (dl_lvl > level) { + base = dl_lvl; + //SASSERT(get_level(m_trail.back()) == base + m_lookahead[i].m_offset); + SASSERT(get_level(m_trail.back()) == base); + } + unsat = inconsistent(); + pop_lookahead1(lit, num_units); + } + // VERIFY(!missed_propagation()); + if (unsat) { + TRACE("sat", tout << "backtracking and settting " << ~lit << "\n";); + lookahead_backtrack(); + assign(~lit); + propagate(); + change = true; + last_changed = lit; + continue; + } + // if l was derived from lit and ~lit -> l, then l is a necessary assignment + literal_vector necessary_lits; + for (literal l : m_binary[(~lit).index()]) { + if (is_true_at(l, dl_lvl) && !is_fixed_at(l, c_fixed_truth)) { + necessary_lits.push_back(l); + } + } + if (!necessary_lits.empty()) { + change = true; + last_changed = lit; + lookahead_backtrack(); + for (literal l : necessary_lits) { + if (inconsistent()) break; + assign(l); + propagate(); + } + } + SASSERT(inconsistent() || !is_unsat()); + } + if (c_fixed_truth - 2 * m_lookahead.size() < base) { + break; + } + base += 2 * m_lookahead.size(); + } + lookahead_backtrack(); + TRACE("sat", display_lookahead(tout); ); + } + + literal lookahead::select_literal() { + literal l = null_literal; + double h = 0; + unsigned count = 1; + for (unsigned i = 0; i < m_lookahead.size(); ++i) { + literal lit = m_lookahead[i].m_lit; + if (lit.sign() || !is_undef(lit)) { + continue; + } + double diff1 = get_lookahead_reward(lit), diff2 = get_lookahead_reward(~lit); + double mixd = mix_diff(diff1, diff2); + + if (mixd == h) ++count; + if (mixd > h || (mixd == h && m_s.m_rand(count) == 0)) { + CTRACE("sat", l != null_literal, tout << lit << " mix diff: " << mixd << "\n";); + if (mixd > h) count = 1; + h = mixd; + l = diff1 < diff2 ? lit : ~lit; + } + } + TRACE("sat", tout << "selected: " << l << "\n";); + return l; + } + + + double lookahead::mix_diff(double l, double r) const { + switch (m_config.m_reward_type) { + case ternary_reward: return l + r + (1 << 10) * l * r; + case heule_schur_reward: return l * r; + case heule_unit_reward: return l * r; + case march_cu_reward: return 1024 * (1024 * l * r + l + r); + case unit_literal_reward: return l * r; + default: UNREACHABLE(); return l * r; + } + } + + void lookahead::reset_lookahead_reward(literal l) { + + m_lookahead_reward = 0; + + // inherit propagation effect from parent. + literal p = get_parent(l); + set_lookahead_reward(l, (p == null_literal || is_undef(p) || is_fixed_at(p, c_fixed_truth)) ? + 0 : get_lookahead_reward(p)); + } + + void lookahead::update_lookahead_reward(literal l, unsigned level) { + if (m_lookahead_reward != 0) { + inc_lookahead_reward(l, m_lookahead_reward); + } + } + + unsigned lookahead::do_double(literal l, unsigned& base) { + unsigned num_units = 0; + if (!inconsistent() && dl_enabled(l)) { + if (get_lookahead_reward(l) > m_delta_trigger) { + if (dl_no_overflow(base)) { + ++m_stats.m_double_lookahead_rounds; + num_units = double_look(l, base); + if (!inconsistent()) { + m_delta_trigger = get_lookahead_reward(l); + dl_disable(l); + } + } + } + else { + SASSERT(m_delta_decrease > 0.0); + m_delta_trigger *= m_delta_decrease; + } + } + return num_units; + } + + unsigned lookahead::double_look(literal l, unsigned& base) { + SASSERT(!inconsistent()); + SASSERT(dl_no_overflow(base)); + SASSERT(is_fixed_at(l, base)); + base += m_lookahead.size(); + unsigned dl_truth = base + m_lookahead.size() * m_config.m_dl_max_iterations; + scoped_level _sl(*this, dl_truth); + //SASSERT(get_level(m_trail.back()) == dl_truth); + IF_VERBOSE(3, verbose_stream() << "(sat-lookahead :double " << l << " :depth " << m_trail_lim.size() << ")\n";); + lookahead_backtrack(); + assign(l); + propagate(); + unsigned old_sz = m_trail.size(); + bool change = true; + literal last_changed = null_literal; + unsigned num_iterations = 0; + while (change && num_iterations < m_config.m_dl_max_iterations && !inconsistent()) { + num_iterations++; + for (unsigned i = 0; !inconsistent() && i < m_lookahead.size(); ++i) { + literal lit = m_lookahead[i].m_lit; + if (lit == last_changed) { + SASSERT(change == false); + break; + } + unsigned level = base + m_lookahead[i].m_offset; + if (level + m_lookahead.size() >= dl_truth) { + change = false; + break; + } + bool unsat = false; + if (is_false_at(lit, level) && !is_fixed_at(lit, dl_truth)) { + unsat = true; + } + else { + if (is_fixed_at(lit, level)) continue; + unsat = push_lookahead2(lit, level); + } + if (unsat) { + TRACE("sat", tout << "unit: " << ~lit << "\n";); + ++m_stats.m_double_lookahead_propagations; + SASSERT(m_level == dl_truth); + lookahead_backtrack(); + assign(~lit); + propagate(); + change = true; + last_changed = lit; + m_wstack.push_back(~lit); + } + } + base += 2 * m_lookahead.size(); + SASSERT(dl_truth >= base); + } + lookahead_backtrack(); + SASSERT(get_level(m_trail.back()) == dl_truth); + SASSERT(m_level == dl_truth); + base = dl_truth; + return m_trail.size() - old_sz; + } + + /** + \brief check if literal occurs in a non-tautological reduced clause. + */ + bool lookahead::in_reduced_clause(bool_var v) { + return + in_reduced_clause(literal(v, false)) || + in_reduced_clause(literal(v, true)); + } + + bool lookahead::in_reduced_clause(literal lit) { + if (lit == null_literal) return true; + if (m_trail_lim.empty()) return true; + unsigned sz = m_nary_count[lit.index()]; + for (nary* n : m_nary[lit.index()]) { + if (sz-- == 0) break; + if (!n->is_reduced()) continue; + bool has_true = false; + for (literal l : *n) { + if (is_true(l)) { + has_true = true; + break; + } + } + if (!has_true) return true; + } + + auto const& tv = m_ternary[lit.index()]; + sz = tv.size(); + unsigned i = m_ternary_count[lit.index()]; + for (; i < sz; ++i) { + binary const& b = tv[i]; + if (!is_true(b.m_u) && !is_true(b.m_v)) + return true; + } + return false; + } + + void lookahead::validate_assign(literal l) { + if (m_s.m_config.m_drat && m_search_mode == lookahead_mode::searching) { + m_assumptions.push_back(l); + m_drat.add(m_assumptions); + m_assumptions.pop_back(); + } + } + + void lookahead::assign(literal l) { + SASSERT(m_level > 0); + if (is_undef(l)) { + TRACE("sat", tout << "assign: " << l << " @ " << m_level << " " << m_trail_lim.size() << " " << m_search_mode << "\n";); + set_true(l); + SASSERT(m_trail.empty() || get_level(m_trail.back()) >= get_level(l)); + m_trail.push_back(l); + if (m_search_mode == lookahead_mode::searching) { + m_stats.m_propagations++; + TRACE("sat", tout << "removing free var v" << l.var() << "\n";); + if (l.var() > m_freevars.max_var()) IF_VERBOSE(0, verbose_stream() << "bigger than max-var: " << l << " " << " " << m_freevars.max_var() << "\n";); + if (!m_freevars.contains(l.var())) IF_VERBOSE(0, verbose_stream() << "does not contain: " << l << " eliminated: " << m_s.was_eliminated(l.var()) << "\n";); + if (m_freevars.contains(l.var())) { m_freevars.remove(l.var()); } + validate_assign(l); + } + } + else if (is_false(l)) { + TRACE("sat", tout << "conflict: " << l << " @ " << m_level << " " << m_search_mode << "\n";); + SASSERT(!is_true(l)); + validate_assign(l); + set_conflict(); + } + } + + void lookahead::propagated(literal l) { + assign(l); + for (unsigned i = m_trail.size()-1; i < m_trail.size() && !inconsistent(); ++i) { + literal l = m_trail[i]; + TRACE("sat", tout << "propagate " << l << " @ " << m_level << "\n";); + propagate_binary(l); + } + if (m_search_mode == lookahead_mode::lookahead1) { + m_wstack.push_back(l); + } + } + + bool lookahead::backtrack(literal_vector& trail) { + while (inconsistent()) { + if (trail.empty()) return false; + pop(); + flip_prefix(); + assign(~trail.back()); + trail.pop_back(); + propagate(); + } + return true; + } + + lbool lookahead::search() { + m_model.reset(); + scoped_level _sl(*this, c_fixed_truth); + literal_vector trail; + m_search_mode = lookahead_mode::searching; + while (true) { + TRACE("sat", display(tout);); + inc_istamp(); + checkpoint(); + literal l = choose(); + if (inconsistent()) { + if (!backtrack(trail)) return l_false; + continue; + } + if (l == null_literal) { + return l_true; + } + TRACE("sat", tout << "choose: " << l << " " << trail << "\n";); + ++m_stats.m_decisions; + IF_VERBOSE(1, display_search_string();); + push(l, c_fixed_truth); + trail.push_back(l); + SASSERT(inconsistent() || !is_unsat()); + } + } + + bool lookahead::backtrack(literal_vector& trail, svector & is_decision) { + while (inconsistent()) { + if (trail.empty()) return false; + if (is_decision.back()) { + pop(); + trail.back().neg(); + assign(trail.back()); + is_decision.back() = false; + propagate(); + } + else { + trail.pop_back(); + is_decision.pop_back(); + } + } + return true; + } + + void lookahead::update_cube_statistics(statistics& st) { + st.update("lh cube cutoffs", m_cube_state.m_cutoffs); + st.update("lh cube conflicts", m_cube_state.m_conflicts); + } + + double lookahead::psat_heur() { + double h = 0.0; + for (bool_var x : m_freevars) { + literal l(x, false); + for (literal lit : m_binary[l.index()]) { + h += l.index() > lit.index() ? 1.0 / m_config.m_cube_psat_clause_base : 0.0; + } + for (literal lit : m_binary[(~l).index()]) { + h += l.index() > lit.index() ? 1.0 / m_config.m_cube_psat_clause_base : 0.0; + } + for (binary b : m_ternary[l.index()]) { + h += l.index() > b.m_u.index() && l.index() > b.m_v.index() ? + 1.0 / pow(m_config.m_cube_psat_clause_base, 2) : + 0.0; + } + for (binary b : m_ternary[(~l).index()]) { + h += l.index() > b.m_u.index() && l.index() > b.m_v.index() ? + 1.0 / pow(m_config.m_cube_psat_clause_base, 2) : + 0.0; + } + } + for (nary * n : m_nary_clauses) { + h += 1.0 / pow(m_config.m_cube_psat_clause_base, n->size() - 1); + } + h /= pow(m_freevars.size(), m_config.m_cube_psat_var_exp); + IF_VERBOSE(10, verbose_stream() << "(sat-cube-psat :val " << h << ")\n";); + return h; + } + + lbool lookahead::cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level) { + scoped_ext _scoped_ext(*this); + lits.reset(); + bool is_first = m_cube_state.m_first; + if (is_first) { + m_select_lookahead_vars.reset(); + for (auto v : vars) { + m_select_lookahead_vars.insert(v); + } + init_search(); + m_model.reset(); + m_cube_state.m_first = false; + } + scoped_level _sl(*this, c_fixed_truth); + m_search_mode = lookahead_mode::searching; + unsigned depth = 0; + // unsigned init_trail = m_trail.size(); + + m_cube_state.reset_stats(); + if (!is_first) { + goto pick_up_work; + } + + while (true) { + TRACE("sat", display(tout);); + checkpoint(); + inc_istamp(); + if (inconsistent()) { + TRACE("sat", tout << "inconsistent: " << m_cube_state.m_cube << "\n";); + m_cube_state.m_freevars_threshold = m_freevars.size(); + m_cube_state.m_psat_threshold = m_config.m_cube_cutoff == adaptive_psat_cutoff ? psat_heur() : dbl_max; // MN. only compute PSAT if enabled + m_cube_state.inc_conflict(); + if (!backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision)) return l_false; + continue; + } + pick_up_work: + if (m_cube_state.m_cube.size() >= backtrack_level) { + IF_VERBOSE(10, verbose_stream() << "(sat-cube :cube: " << m_cube_state.m_cube.size() << " :backtrack_level " << backtrack_level << ")\n";); + while (m_cube_state.m_cube.size() >= backtrack_level) { + set_conflict(); + backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision); + } + } + backtrack_level = UINT_MAX; + depth = m_cube_state.m_cube.size(); + if ((m_config.m_cube_cutoff == depth_cutoff && depth == m_config.m_cube_depth) || + (m_config.m_cube_cutoff == freevars_cutoff && m_freevars.size() <= m_init_freevars * m_config.m_cube_freevars) || + (m_config.m_cube_cutoff == psat_cutoff && psat_heur() >= m_config.m_cube_psat_trigger) || + (m_config.m_cube_cutoff == adaptive_freevars_cutoff && m_freevars.size() < m_cube_state.m_freevars_threshold) || + (m_config.m_cube_cutoff == adaptive_psat_cutoff && psat_heur() >= m_cube_state.m_psat_threshold)) { + double dec = (1.0 - pow(m_config.m_cube_fraction, depth)); + m_cube_state.m_freevars_threshold *= dec; + m_cube_state.m_psat_threshold *= 2.0 - dec; + set_conflict(); + m_cube_state.inc_cutoff(); +#if 0 + // return cube of all literals, not just the ones in the main cube + lits.append(m_trail.size() - init_trail, m_trail.c_ptr() + init_trail); +#else + lits.append(m_cube_state.m_cube); +#endif + vars.reset(); + for (auto v : m_freevars) if (in_reduced_clause(v)) vars.push_back(v); + backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision); + return l_undef; + } + int prev_nfreevars = m_freevars.size(); + double prev_psat = m_config.m_cube_cutoff == adaptive_psat_cutoff ? psat_heur() : dbl_max; // MN. only compute PSAT if enabled + literal lit = choose(); + if (inconsistent()) { + TRACE("sat", tout << "inconsistent: " << m_cube_state.m_cube << "\n";); + m_cube_state.m_freevars_threshold = prev_nfreevars; + m_cube_state.m_psat_threshold = prev_psat; + m_cube_state.inc_conflict(); + if (!backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision)) return l_false; + continue; + } + if (lit == null_literal) { + vars.reset(); + for (auto v : m_freevars) if (in_reduced_clause(v)) vars.push_back(v); + return l_true; + } + TRACE("sat", tout << "choose: " << lit << " cube: " << m_cube_state.m_cube << "\n";); + ++m_stats.m_decisions; + push(lit, c_fixed_truth); + m_cube_state.m_cube.push_back(lit); + m_cube_state.m_is_decision.push_back(true); + SASSERT(inconsistent() || !is_unsat()); + } + m_cube_state.reset(); + return l_undef; + } + + void lookahead::init_model() { + m_model.reset(); + for (unsigned i = 0; i < m_num_vars; ++i) { + lbool val; + literal lit(i, false); + if (is_undef(lit)) { + val = l_undef; + } + if (is_true(lit)) { + val = l_true; + } + else { + val = l_false; + } + m_model.push_back(val); + } + } + + std::ostream& lookahead::display_cube(std::ostream& out, literal_vector const& cube) const { + out << "c"; + for (literal l : cube) { + out << " " << ~l; + } + return out << " 0\n"; + } + + std::ostream& lookahead::display_binary(std::ostream& out) const { + for (unsigned i = 0; i < m_binary.size(); ++i) { + literal_vector const& lits = m_binary[i]; + if (!lits.empty()) { + out << to_literal(i) << " -> " << lits << "\n"; + } + } + return out; + } + + std::ostream& lookahead::display_clauses(std::ostream& out) const { + for (unsigned idx = 0; idx < m_ternary.size(); ++idx) { + literal lit = to_literal(idx); + unsigned sz = m_ternary_count[idx]; + for (binary const& b : m_ternary[idx]) { + if (sz-- == 0) break; + if (idx < b.m_u.index() && idx << b.m_v.index()) { + out << lit << " " << b.m_u << " " << b.m_v << "\n"; + } + } + } + + for (nary * n : m_nary_clauses) { + for (literal l : *n) out << l << " "; + out << "\n"; + } + + return out; + } + + std::ostream& lookahead::display_values(std::ostream& out) const { + for (literal l : m_trail) { + out << l << "\n"; + } + return out; + } + + std::ostream& lookahead::display_lookahead(std::ostream& out) const { + for (unsigned i = 0; i < m_lookahead.size(); ++i) { + literal lit = m_lookahead[i].m_lit; + unsigned offset = m_lookahead[i].m_offset; + out << lit << "\toffset: " << offset; + out << (is_undef(lit)?" undef": (is_true(lit) ? " true": " false")); + out << " lookahead_reward: " << get_lookahead_reward(lit); + out << "\n"; + } + return out; + } + + void lookahead::init_search() { + m_search_mode = lookahead_mode::searching; + scoped_level _sl(*this, c_fixed_truth); + init(m_s.m_config.m_lookahead_use_learned); + } + + void lookahead::checkpoint() { + if (!m_rlimit.inc()) { + throw solver_exception(Z3_CANCELED_MSG); + } + if (memory::get_allocation_size() > m_s.m_config.m_max_memory) { + throw solver_exception(Z3_MAX_MEMORY_MSG); + } + } + + literal lookahead::choose() { + literal l = null_literal; + while (l == null_literal && !inconsistent()) { + pre_select(); + if (m_lookahead.empty()) { + break; + } + compute_lookahead_reward(); + if (inconsistent()) { + break; + } + l = select_literal(); + } + SASSERT(inconsistent() || !is_unsat()); + return l; + } + + /** + \brief simplify set of clauses by extracting units from a lookahead at base level. + */ + void lookahead::simplify(bool learned) { + scoped_ext _scoped_ext(*this); + SASSERT(m_prefix == 0); + SASSERT(m_watches.empty()); + m_search_mode = lookahead_mode::searching; + scoped_level _sl(*this, c_fixed_truth); + init(learned); + if (inconsistent()) return; + inc_istamp(); + choose(); + if (inconsistent()) return; + SASSERT(m_trail_lim.empty()); + unsigned num_units = 0; + + for (unsigned i = 0; i < m_trail.size() && !m_s.inconsistent(); ++i) { + literal lit = m_trail[i]; + if (m_s.value(lit) == l_undef && !m_s.was_eliminated(lit.var())) { + m_s.assign(lit, justification()); + ++num_units; + } + } + IF_VERBOSE(1, verbose_stream() << "(sat-lookahead :units " << num_units << " :propagations " << m_stats.m_propagations << ")\n";); + + if (m_s.inconsistent()) return; + + if (num_units > 0) { + m_s.propagate_core(false); + m_s.m_simplifier(false); + } + + if (select(0)) { + get_scc(); + if (!inconsistent()) { + normalize_parents(); + literal_vector roots; + bool_var_vector to_elim; + for (unsigned i = 0; i < m_num_vars; ++i) { + roots.push_back(literal(i, false)); + } + for (auto const& c : m_candidates) { + bool_var v = c.m_var; + literal q(v, false); + literal p = get_parent(q); + if (p != null_literal && p.var() != v && !m_s.is_external(v) && + !m_s.was_eliminated(v) && !m_s.was_eliminated(p.var())) { + to_elim.push_back(v); + roots[v] = p; + VERIFY(get_parent(p) == p); + VERIFY(get_parent(~p) == ~p); + } + } + IF_VERBOSE(1, verbose_stream() << "(sat-lookahead :equivalences " << to_elim.size() << ")\n";); + elim_eqs elim(m_s); + elim(roots, to_elim); + + if (learned && get_config().m_lookahead_simplify_bca) { + add_hyper_binary(); + } + } + } + m_lookahead.reset(); + } + + + /** + \brief reduction based on binary implication graph + */ + + void lookahead::add_hyper_binary() { + + unsigned num_lits = m_s.num_vars() * 2; + unsigned num_bins = 0; + + typedef std::pair u_pair; + hashtable, default_eq > imp; + for (unsigned idx = 0; idx < num_lits; ++idx) { + literal u = get_parent(to_literal(idx)); + if (null_literal != u) { + for (watched const& w : m_s.m_watches[idx]) { + if (!w.is_binary_clause()) continue; + literal v = get_parent(w.get_literal()); + if (null_literal != v) { + imp.insert(std::make_pair(u.index(), v.index())); + } + } + } + } + + big big(m_s.m_rand); + big.init(m_s, true); + svector> candidates; + + unsigned_vector bin_size(num_lits); + for (unsigned idx : m_binary_trail) { + bin_size[idx]++; + } + for (unsigned idx = 0; idx < num_lits; ++idx) { + literal u = to_literal(idx); + if (u != get_parent(u)) continue; + if (m_s.was_eliminated(u.var())) continue; + literal_vector const& lits = m_binary[idx]; + for (unsigned sz = bin_size[idx]; sz > 0; --sz) { + literal v = lits[lits.size() - sz]; + if (v == get_parent(v) && + !m_s.was_eliminated(v.var()) && + !imp.contains(std::make_pair(u.index(), v.index())) && + !big.connected(u, v)) { + candidates.push_back(std::make_pair(u, v)); + } + } + } + + for (unsigned count = 0; count < 5; ++count) { + big.init(m_s, true); + unsigned k = 0; + union_find_default_ctx ufctx; + union_find uf(ufctx); + for (unsigned i = 0; i < num_lits; ++i) uf.mk_var(); + for (unsigned j = 0; j < candidates.size(); ++j) { + literal u = candidates[j].first; + literal v = candidates[j].second; + if (!big.connected(u, v)) { + if (uf.find(u.index()) != uf.find(v.index())) { + ++num_bins; + uf.merge(u.index(), v.index()); + uf.merge((~u).index(), (~v).index()); + VERIFY(!m_s.was_eliminated(u.var())); + VERIFY(!m_s.was_eliminated(v.var())); + m_s.mk_clause(~u, v, true); + } + else { + candidates[k] = candidates[j]; + ++k; + } + } + } + // std::cout << candidates.size() << " -> " << k << "\n"; + if (k == candidates.size()) break; + candidates.shrink(k); + if (k == 0) break; + } + + IF_VERBOSE(10, verbose_stream() << "(sat-lookahead :bca " << num_bins << ")\n";); + m_stats.m_bca += num_bins; + } + + void lookahead::normalize_parents() { + literal_vector roots; + for (unsigned i = 0; i < m_num_vars; ++i) { + literal lit(i, false); + roots.push_back(lit); + roots.push_back(~lit); + SASSERT(roots[lit.index()] == lit); + } + for (auto const& c : m_candidates) { + bool_var v = c.m_var; + literal p(v, false); + literal q = get_parent(p); + literal r = ~get_parent(~p); + if (q != r) { + if (q.var() < r.var()) { + roots[q.index()] = r; + } + else { + roots[r.index()] = q; + } + } + } + for (auto const& c : m_candidates) { + literal p(c.m_var, false); + literal q = roots[get_parent(p).index()]; + set_parent(p, q); + set_parent(~p, ~q); + } + } + + std::ostream& lookahead::display_summary(std::ostream& out) const { + out << "Prefix: " << pp_prefix(m_prefix, m_trail_lim.size()) << "\n"; + out << "Level: " << m_level << "\n"; + out << "Free vars: " << m_freevars.size() << "\n"; + return out; + } + + std::ostream& lookahead::display(std::ostream& out) const { + display_summary(out); + display_values(out); + display_binary(out); + display_clauses(out); + out << "free vars: "; + for (bool_var b : m_freevars) out << b << " "; + out << "\n"; + clause_allocator dummy_allocator; + for (unsigned i = 0; i < m_watches.size(); ++i) { + watch_list const& wl = m_watches[i]; + if (!wl.empty()) { + sat::display_watch_list(out << to_literal(i) << " -> ", dummy_allocator, wl); + out << "\n"; + } + } + return out; + } + + model const& lookahead::get_model() { + if (m_model.empty()) { + init_model(); + } + return m_model; + } + + void lookahead::init_config() { + m_config.m_reward_type = m_s.m_config.m_lookahead_reward; + m_config.m_cube_cutoff = m_s.m_config.m_lookahead_cube_cutoff; + m_config.m_cube_fraction = m_s.m_config.m_lookahead_cube_fraction; + m_config.m_cube_depth = m_s.m_config.m_lookahead_cube_depth; + m_config.m_cube_freevars = m_s.m_config.m_lookahead_cube_freevars; + m_config.m_cube_psat_var_exp = m_s.m_config.m_lookahead_cube_psat_var_exp; + m_config.m_cube_psat_clause_base = m_s.m_config.m_lookahead_cube_psat_clause_base; + m_config.m_cube_psat_trigger = m_s.m_config.m_lookahead_cube_psat_trigger; + } + + void lookahead::collect_statistics(statistics& st) const { + st.update("lh bool var", m_vprefix.size()); + // TBD: keep count of ternary and >3-ary clauses. + st.update("lh bca", m_stats.m_bca); + st.update("lh add binary", m_stats.m_add_binary); + st.update("lh del binary", m_stats.m_del_binary); + st.update("lh propagations", m_stats.m_propagations); + st.update("lh decisions", m_stats.m_decisions); + st.update("lh windfalls", m_stats.m_windfall_binaries); + st.update("lh double lookahead propagations", m_stats.m_double_lookahead_propagations); + st.update("lh double lookahead rounds", m_stats.m_double_lookahead_rounds); + } + +} diff --git a/src/sat/sat_lookahead.h b/src/sat/sat_lookahead.h new file mode 100644 index 000000000..df954bdf1 --- /dev/null +++ b/src/sat/sat_lookahead.h @@ -0,0 +1,619 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_lookahead.h + +Abstract: + + Lookahead SAT solver in the style of March. + Thanks also to the presentation in sat11.w. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-2-11 + +Notes: + +--*/ +#ifndef _SAT_LOOKAHEAD_H_ +#define _SAT_LOOKAHEAD_H_ + +// #define OLD_NARY 0 + +#include "sat_elim_eqs.h" + +namespace sat { + + struct pp_prefix { + uint64_t m_prefix; + unsigned m_depth; + pp_prefix(uint64_t p, unsigned d) : m_prefix(p), m_depth(d) {} + }; + + inline std::ostream& operator<<(std::ostream& out, pp_prefix const& p) { + unsigned d = std::min(63u, p.m_depth); + for (unsigned i = 0; i <= d; ++i) { + if (0 != (p.m_prefix & (1ull << i))) out << "1"; else out << "0"; + } + if (d < p.m_depth) { + out << " d:" << p.m_depth; + } + return out; + } + + enum lookahead_mode { + searching, // normal search + lookahead1, // lookahead mode + lookahead2 // double lookahead + }; + + inline std::ostream& operator<<(std::ostream& out, lookahead_mode m) { + switch (m) { + case lookahead_mode::searching: return out << "searching"; + case lookahead_mode::lookahead1: return out << "lookahead1"; + case lookahead_mode::lookahead2: return out << "lookahead2"; + default: break; + } + return out; + } + + const double dbl_max = 100000000.0; + + class lookahead { + solver& m_s; + unsigned m_num_vars; + reslimit m_rlimit; + + friend class ccc; + friend class ba_solver; + + struct config { + double m_dl_success; + double m_alpha; + double m_max_score; + unsigned m_max_hlevel; + unsigned m_min_cutoff; + bool m_preselect; + unsigned m_level_cand; + double m_delta_rho; + unsigned m_dl_max_iterations; + unsigned m_tc1_limit; + reward_t m_reward_type; + cutoff_t m_cube_cutoff; + unsigned m_cube_depth; + double m_cube_fraction; + double m_cube_freevars; + double m_cube_psat_var_exp; + double m_cube_psat_clause_base; + double m_cube_psat_trigger; + + config() { + memset(this, 0, sizeof(*this)); + m_dl_success = 0.8; + m_alpha = 3.5; + m_max_score = 20.0; + m_max_hlevel = 50; + m_min_cutoff = 30; + m_preselect = false; + m_level_cand = 600; + m_delta_rho = (double)0.7; + m_dl_max_iterations = 2; + m_tc1_limit = 10000000; + m_reward_type = ternary_reward; + m_cube_cutoff = adaptive_freevars_cutoff; + m_cube_depth = 10; + m_cube_fraction = 0.4; + m_cube_freevars = 0.8; + m_cube_psat_var_exp = 1.0; + m_cube_psat_clause_base = 2.0; + m_cube_psat_trigger = 5.0; + } + }; + + struct prefix { + unsigned m_prefix; + unsigned m_length; + prefix(): m_prefix(0), m_length(0) {} + }; + + struct lit_info { + double m_lookahead_reward; + unsigned m_double_lookahead; + lit_info(): m_lookahead_reward(0), m_double_lookahead(0) {} + }; + + struct stats { + unsigned m_propagations; + unsigned m_bca; + unsigned m_add_binary; + unsigned m_del_binary; + unsigned m_decisions; + unsigned m_windfall_binaries; + unsigned m_double_lookahead_propagations; + unsigned m_double_lookahead_rounds; + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + }; + + struct binary { + binary(literal u, literal v): m_u(u), m_v(v) {} + literal m_u, m_v; + }; + + class nary { + unsigned m_size; // number of non-false literals + size_t m_obj_size; // object size (counting all literals) + literal m_head; // head literal + literal m_literals[0]; // list of literals, put any true literal in head. + size_t num_lits() const { + return (m_obj_size - sizeof(nary)) / sizeof(literal); + } + public: + static size_t get_obj_size(unsigned sz) { return sizeof(nary) + sz * sizeof(literal); } + size_t obj_size() const { return m_obj_size; } + nary(unsigned sz, literal const* lits): + m_size(sz), + m_obj_size(get_obj_size(sz)) { + for (unsigned i = 0; i < sz; ++i) m_literals[i] = lits[i]; + m_head = lits[0]; + } + unsigned size() const { return m_size; } + unsigned dec_size() { SASSERT(m_size > 0); return --m_size; } + void inc_size() { SASSERT(is_reduced()); ++m_size; } + literal get_head() const { return m_head; } + void set_head(literal l) { m_head = l; } + bool is_reduced() const { return m_size < num_lits(); } + + literal operator[](unsigned i) { SASSERT(i < num_lits()); return m_literals[i]; } + literal const* begin() const { return m_literals; } + literal const* end() const { return m_literals + num_lits(); } + // swap the true literal to the head. + // void swap(unsigned i, unsigned j) { SASSERT(i < num_lits() && j < num_lits()); std::swap(m_literals[i], m_literals[j]); } + }; + + struct cube_state { + bool m_first; + svector m_is_decision; + literal_vector m_cube; + double m_freevars_threshold; + double m_psat_threshold; + unsigned m_conflicts; + unsigned m_cutoffs; + cube_state() { reset(); } + void reset() { + m_first = true; + m_is_decision.reset(); + m_cube.reset(); + m_freevars_threshold = 0; + m_psat_threshold = dbl_max; + reset_stats(); + } + void reset_stats() { m_conflicts = 0; m_cutoffs = 0; } + void inc_conflict() { ++m_conflicts; } + void inc_cutoff() { ++m_cutoffs; } + }; + + config m_config; + double m_delta_trigger; + double m_delta_decrease; + + drat m_drat; + literal_vector m_assumptions; + + literal_vector m_trail; // trail of units + unsigned_vector m_trail_lim; + vector m_binary; // literal: binary clauses + unsigned_vector m_binary_trail; // trail of added binary clauses + unsigned_vector m_binary_trail_lim; + + // specialized clause managemet uses ternary clauses and dedicated clause data-structure. + // this replaces m_clauses below + vector> m_ternary; // lit |-> vector of ternary clauses + unsigned_vector m_ternary_count; // lit |-> current number of active ternary clauses for lit + + small_object_allocator m_allocator; + vector> m_nary; // lit |-> vector of nary clauses + ptr_vector m_nary_clauses; // vector of all nary clauses + unsigned_vector m_nary_count; // lit |-> number of valid clause_id in m_nary[lit] + + unsigned m_num_tc1; + unsigned_vector m_num_tc1_lim; + unsigned m_qhead; // propagation queue head + unsigned_vector m_qhead_lim; + bool m_inconsistent; + unsigned_vector m_bstamp; // literal: timestamp for binary implication + vector > m_H; // literal: fitness score + svector* m_heur; // current fitness + svector m_rating; // var: pre-selection rating + unsigned m_bstamp_id; // unique id for binary implication. + unsigned m_istamp_id; // unique id for managing double lookaheads + unsigned_vector m_stamp; // var: timestamp with truth value + unsigned m_level; // current level, = 2 * m_trail_lim.size() + const unsigned c_fixed_truth = UINT_MAX - 1; + vector m_watches; // literal: watch structure + svector m_lits; // literal: attributes. + double m_lookahead_reward; // metric associated with current lookahead1 literal. + literal_vector m_wstack; // windofall stack that is populated in lookahead1 mode + unsigned m_last_prefix_length; + uint64_t m_prefix; // where we are in search tree + svector m_vprefix; // var: prefix where variable participates in propagation + unsigned m_rating_throttle; // throttle to recompute rating + indexed_uint_set m_freevars; + unsigned m_init_freevars; + lookahead_mode m_search_mode; // mode of search + stats m_stats; + model m_model; + cube_state m_cube_state; + //scoped_ptr m_ext; + + // --------------------------------------- + // truth values + + + inline bool is_fixed_at(literal l, unsigned level) const { return m_stamp[l.var()] >= level; } + inline bool is_fixed(literal l) const { return is_fixed_at(l, m_level); } + inline bool is_undef(literal l) const { return !is_fixed(l); } + inline bool is_undef(bool_var v) const { return m_stamp[v] < m_level; } + inline bool is_false_at(literal l, unsigned level) const { + return is_fixed_at(l, level) && (bool)((m_stamp[l.var()] & 0x1) ^ l.sign()); + } // even iff l.sign() + inline bool is_false(literal l) const { return is_false_at(l, m_level); } + inline bool is_true_at(literal l, unsigned level) const { + return is_fixed_at(l, level) && !(bool)((m_stamp[l.var()] & 0x1) ^ l.sign()); + } + inline bool is_true(literal l) const { return is_true_at(l, m_level); } + inline void set_true(literal l) { m_stamp[l.var()] = m_level + l.sign(); } + inline void set_undef(literal l) { m_stamp[l.var()] = 0; } + inline unsigned get_level(literal l) const { return m_stamp[l.var()] & ~0x1; } + void set_level(literal d, literal s) { m_stamp[d.var()] = get_level(s) + d.sign(); } + lbool value(literal l) const { return is_undef(l) ? l_undef : is_true(l) ? l_true : l_false; } + + // set the level within a scope of the search. + class scoped_level { + lookahead& m_parent; + unsigned m_save; + public: + scoped_level(lookahead& p, unsigned l): + m_parent(p), m_save(p.m_level) { + p.m_level = l; + } + ~scoped_level() { + m_parent.m_level = m_save; + } + }; + + class scoped_ext { + lookahead& p; + public: + scoped_ext(lookahead& p); + ~scoped_ext(); + }; + + class scoped_assumptions { + lookahead& p; + literal_vector lits; + public: + scoped_assumptions(lookahead& p, literal_vector const& lits); + ~scoped_assumptions(); + }; + + // ------------------------------------- + // prefix updates. I use low order bits. + + void flip_prefix(); + void prune_prefix(); + + /** + length < trail_lim.size: + - mask m_prefix and p wrt length + - update if different. + */ + void update_prefix(literal l); + + bool active_prefix(bool_var x); + + // ---------------------------------------- + + void add_binary(literal l1, literal l2); + void del_binary(unsigned idx); + void validate_binary(literal l1, literal l2); + + // ------------------------------------- + // track consequences of binary clauses + // see also 72 - 79 in sat11.w + + void inc_bstamp(); + void inc_istamp(); + void set_bstamp(literal l) { m_bstamp[l.index()] = m_bstamp_id; } + void set_bstamps(literal l); + bool is_stamped(literal l) const { return m_bstamp[l.index()] == m_bstamp_id; } + bool add_tc1(literal u, literal v); + + /** + \brief main routine for adding a new binary clause dynamically. + */ + void try_add_binary(literal u, literal v); + + // ------------------------------------- + // pre-selection + // see also 91 - 102 sat11.w + + void pre_select(); + + struct candidate { + bool_var m_var; + double m_rating; + candidate(bool_var v, double r): m_var(v), m_rating(r) {} + }; + svector m_candidates; + uint_set m_select_lookahead_vars; + + double get_rating(bool_var v) const { return m_rating[v]; } + double get_rating(literal l) const { return get_rating(l.var()); } + bool select(unsigned level); + void heap_sort(); + void heapify(); + void sift_down(unsigned j, unsigned sz); + bool validate_heap_sort(); + double init_candidates(unsigned level, bool newbies); + std::ostream& display_candidates(std::ostream& out) const; + bool is_unsat() const; + bool is_sat() const; + bool missed_propagation() const; + bool missed_conflict() const; + void init_pre_selection(unsigned level); + void ensure_H(unsigned level); + void h_scores(svector& h, svector& hp); + void heule_schur_scores(); + double heule_schur_score(literal l); + void heule_unit_scores(); + double heule_unit_score(literal l); + void march_cu_scores(); + double march_cu_score(literal l); + double l_score(literal l, svector const& h, double factor, double sqfactor, double afactor); + + // ------------------------------------ + // Implication graph + // Compute implication ordering and strongly connected components. + // sat11.w 103 - 114. + + struct arcs : public literal_vector {}; + // Knuth uses a shared pool of fixed size for arcs. + // Should it be useful we could use this approach too + // by changing the arcs abstraction and associated functions. + + struct dfs_info { + unsigned m_rank; + unsigned m_height; + literal m_parent; + arcs m_next; + unsigned m_nextp; + literal m_link; + literal m_min; + literal m_vcomp; + dfs_info() { reset(); } + void reset() { + m_rank = 0; + m_height = 0; + m_parent = null_literal; + m_next.reset(); + m_link = null_literal; + m_min = null_literal; + m_vcomp = null_literal; + m_nextp = 0; + } + }; + + literal m_active; + unsigned m_rank; + unsigned m_rank_max; + literal m_settled; + vector m_dfs; + + void get_scc(); + void init_scc(); + void init_dfs_info(literal l); + void init_arcs(literal l); + void add_arc(literal u, literal v); + bool has_arc(literal v) const { return m_dfs[v.index()].m_next.size() > m_dfs[v.index()].m_nextp; } + arcs get_arcs(literal v) const { return m_dfs[v.index()].m_next; } + literal pop_arc(literal u) { return m_dfs[u.index()].m_next[m_dfs[u.index()].m_nextp++]; } + unsigned num_next(literal u) const { return m_dfs[u.index()].m_next.size(); } + literal get_next(literal u, unsigned i) const { return m_dfs[u.index()].m_next[i]; } + literal get_min(literal v) const { return m_dfs[v.index()].m_min; } + unsigned get_rank(literal v) const { return m_dfs[v.index()].m_rank; } + bool maxed_rank(literal v) const { return get_rank(v) >= m_rank_max; } + unsigned get_height(literal v) const { return m_dfs[v.index()].m_height; } + literal get_parent(literal u) const { return m_dfs[u.index()].m_parent; } + literal get_link(literal u) const { return m_dfs[u.index()].m_link; } + literal get_vcomp(literal u) const { return m_dfs[u.index()].m_vcomp; } + void set_link(literal v, literal u) { m_dfs[v.index()].m_link = u; } + void set_min(literal v, literal u) { m_dfs[v.index()].m_min = u; } + void set_rank(literal v, unsigned r) { m_dfs[v.index()].m_rank = r; } + void set_height(literal v, unsigned h) { m_dfs[v.index()].m_height = h; } + void set_parent(literal v, literal p) { TRACE("scc", tout << v << " <- " << p << "\n";); m_dfs[v.index()].m_parent = p; } + void set_vcomp(literal v, literal u) { m_dfs[v.index()].m_vcomp = u; } + void get_scc(literal v); + void activate_scc(literal l); + void found_scc(literal v); + std::ostream& display_dfs(std::ostream& out) const; + std::ostream& display_dfs(std::ostream& out, literal l) const; + std::ostream& display_scc(std::ostream& out) const; + std::ostream& display_scc(std::ostream& out, literal l) const; + + + // ------------------------------------ + // lookahead forest + // sat11.w 115-121 + + literal m_root_child; + + literal get_child(literal u) const; + void set_child(literal v, literal u); + void find_heights(); + std::ostream& display_forest(std::ostream& out, literal l); + + struct literal_offset { + literal m_lit; + unsigned m_offset; + literal_offset(literal l): m_lit(l), m_offset(0) {} + }; + svector m_lookahead; + void set_offset(unsigned idx, unsigned offset) { m_lookahead[idx].m_offset = offset; } + void set_lookahead(literal l) { m_lookahead.push_back(literal_offset(l)); } + void construct_lookahead_table(); + + // ------------------------------------ + // clause management + + watch_list& get_wlist(literal l) { return m_watches[l.index()]; } + watch_list const& get_wlist(literal l) const { return m_watches[l.index()]; } + + // new clause managment: + void add_ternary(literal u, literal v, literal w); + void propagate_ternary(literal l); + lbool propagate_ternary(literal l1, literal l2); + void remove_ternary(literal l, literal u, literal v); + void restore_ternary(literal l); + + void propagate_external(literal l); + void add_clause(clause const& c); + void propagate_clauses_searching(literal l); + void propagate_clauses_lookahead(literal l); + void restore_clauses(literal l); + void remove_clause(literal l, nary& n); + void remove_clause_at(literal l, nary& n); + // ------------------------------------ + // initialization + + void init_var(bool_var v); + void init(bool learned); + void copy_clauses(clause_vector const& clauses, bool learned); + nary * copy_clause(clause const& c); + + // ------------------------------------ + // search + + void push(literal lit, unsigned level); + void pop(); + bool push_lookahead2(literal lit, unsigned level); + unsigned push_lookahead1(literal lit, unsigned level); + void pop_lookahead1(literal lit, unsigned num_units); + void lookahead_backtrack(); + double mix_diff(double l, double r) const; + clause const& get_clause(watch_list::iterator it) const; + bool is_nary_propagation(clause const& c, literal l) const; + void propagate_clauses(literal l); + void propagate_binary(literal l); + void propagate(); + literal choose(); + void compute_lookahead_reward(); + literal select_literal(); + void update_binary_clause_reward(literal l1, literal l2); + void update_nary_clause_reward(clause const& c); + + void set_lookahead_reward(literal l, double f) { m_lits[l.index()].m_lookahead_reward = f; } + void inc_lookahead_reward(literal l, double f) { m_lits[l.index()].m_lookahead_reward += f; } + double get_lookahead_reward(literal l) const { return m_lits[l.index()].m_lookahead_reward; } + + void reset_lookahead_reward(literal l); + void update_lookahead_reward(literal l, unsigned level); + bool dl_enabled(literal l) const { return m_lits[l.index()].m_double_lookahead != m_istamp_id; } + void dl_disable(literal l) { m_lits[l.index()].m_double_lookahead = m_istamp_id; } + bool dl_no_overflow(unsigned base) const { return base + 2 * m_lookahead.size() * static_cast(m_config.m_dl_max_iterations + 1) < c_fixed_truth; } + + unsigned do_double(literal l, unsigned& base); + unsigned double_look(literal l, unsigned& base); + void set_conflict() { TRACE("sat", tout << "conflict\n";); m_inconsistent = true; } + bool inconsistent() const { return m_inconsistent; } + + unsigned scope_lvl() const { return m_trail_lim.size(); } + + bool in_reduced_clause(literal l); + bool in_reduced_clause(bool_var v); + void validate_assign(literal l); + void assign(literal l); + void propagated(literal l); + bool backtrack(literal_vector& trail); + bool backtrack(literal_vector& trail, svector & is_decision); + lbool search(); + void init_model(); + std::ostream& display_binary(std::ostream& out) const; + std::ostream& display_clauses(std::ostream& out) const; + std::ostream& display_values(std::ostream& out) const; + std::ostream& display_lookahead(std::ostream& out) const; + std::ostream& display_cube(std::ostream& out, literal_vector const& cube) const; + void display_search_string(); + + void init_search(); + void checkpoint(); + + void init_config(); + + void normalize_parents(); + + void add_hyper_binary(); + + double psat_heur(); + + public: + lookahead(solver& s) : + m_s(s), + m_num_vars(s.num_vars()), + m_drat(s), + m_num_tc1(0), + m_level(2), + m_last_prefix_length(0), + m_prefix(0), + m_rating_throttle(0) { + m_s.rlimit().push_child(&m_rlimit); + init_config(); + } + + ~lookahead() { + m_s.rlimit().pop_child(); + for (nary* n : m_nary_clauses) { + m_allocator.deallocate(n->obj_size(), n); + } + } + + + lbool check() { + init_search(); + return search(); + } + + /** + \brief create cubes to std-out in DIMACS form. + The cubes are controlled using cut-depth and cut-fraction parameters. + If cut-depth != 0, then it is used to control thedepth of cuts. + Otherwise, cut-fraction gives an adaptive threshold for creating cuts. + */ + + lbool cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level); + + void update_cube_statistics(statistics& st); + + /** + \brief simplify set of clauses by extracting units from a lookahead at base level. + */ + void simplify(bool learned); + + std::ostream& display(std::ostream& out) const; + std::ostream& display_summary(std::ostream& out) const; + model const& get_model(); + + void collect_statistics(statistics& st) const; + + double literal_occs(literal l); + double literal_big_occs(literal l); + + sat::config const& get_config() const { return m_s.get_config(); } + + }; +} + +#endif + diff --git a/src/sat/sat_model_converter.cpp b/src/sat/sat_model_converter.cpp index 4882018bb..d132f1cd4 100644 --- a/src/sat/sat_model_converter.cpp +++ b/src/sat/sat_model_converter.cpp @@ -18,11 +18,12 @@ Revision History: --*/ #include "sat/sat_model_converter.h" #include "sat/sat_clause.h" +#include "sat/sat_solver.h" #include "util/trace.h" namespace sat { - model_converter::model_converter() { + model_converter::model_converter(): m_solver(nullptr) { } model_converter::~model_converter() { @@ -33,39 +34,111 @@ namespace sat { m_entries.finalize(); } + model_converter& model_converter::operator=(model_converter const& other) { + copy(other); + return *this; + } + + bool model_converter::legal_to_flip(bool_var v) const { + // std::cout << "check " << v << " " << m_solver << "\n"; + if (m_solver && m_solver->is_assumption(v)) { + std::cout << "flipping assumption v" << v << "\n"; + UNREACHABLE(); + throw solver_exception("flipping assumption"); + } + if (m_solver && m_solver->is_external(v) && m_solver->is_incremental()) { + std::cout << "flipping external v" << v << "\n"; + UNREACHABLE(); + throw solver_exception("flipping external"); + } + return !m_solver || !m_solver->is_assumption(v); + } + + + void model_converter::process_stack(model & m, literal_vector const& c, elim_stackv const& stack) const { + SASSERT(!stack.empty()); + unsigned sz = stack.size(); + for (unsigned i = sz; i-- > 0; ) { + unsigned csz = stack[i].first; + literal lit = stack[i].second; + bool sat = false; + for (unsigned j = 0; !sat && j < csz; ++j) { + sat = value_at(c[j], m) == l_true; + } + if (!sat) { + VERIFY(legal_to_flip(lit.var())); + m[lit.var()] = lit.sign() ? l_false : l_true; + } + } + } + void model_converter::operator()(model & m) const { vector::const_iterator begin = m_entries.begin(); vector::const_iterator it = m_entries.end(); + bool first = false; // true; // false; // // true; + //SASSERT(!m_solver || m_solver->check_clauses(m)); while (it != begin) { --it; - SASSERT(it->get_kind() != ELIM_VAR || m[it->var()] == l_undef); - // if it->get_kind() == BLOCK_LIT, then it might be the case that m[it->var()] != l_undef, + bool_var v0 = it->var(); + SASSERT(it->get_kind() != ELIM_VAR || v0 == null_bool_var || m[v0] == l_undef); + // if it->get_kind() == BCE, then it might be the case that m[v] != l_undef, // and the following procedure flips its value. bool sat = false; bool var_sign = false; + unsigned index = 0; + literal_vector clause; + VERIFY(v0 == null_bool_var || legal_to_flip(v0)); for (literal l : it->m_clauses) { if (l == null_literal) { // end of clause - if (!sat) { - m[it->var()] = var_sign ? l_false : l_true; - break; + if (!sat && it->get_kind() == ATE) { + IF_VERBOSE(0, display(verbose_stream() << "violated ate\n", *it) << "\n"); + IF_VERBOSE(0, for (unsigned v = 0; v < m.size(); ++v) verbose_stream() << v << " := " << m[v] << "\n";); + IF_VERBOSE(0, display(verbose_stream())); + exit(0); + first = false; + } + if (!sat && it->get_kind() != ATE && v0 != null_bool_var) { + VERIFY(legal_to_flip(v0)); + m[v0] = var_sign ? l_false : l_true; + //IF_VERBOSE(0, verbose_stream() << "assign " << v0 << " "<< m[v0] << "\n"); + } + elim_stack* st = it->m_elim_stack[index]; + if (st) { + process_stack(m, clause, st->stack()); } sat = false; - continue; + if (first && m_solver && !m_solver->check_clauses(m)) { + IF_VERBOSE(0, display(verbose_stream() << "after processing stack\n", *it) << "\n"); + IF_VERBOSE(0, display(verbose_stream())); + first = false; + } + ++index; + clause.reset(); + continue; } + clause.push_back(l); if (sat) continue; bool sign = l.sign(); bool_var v = l.var(); - if (v == it->var()) + if (v >= m.size()) std::cout << v << " model size: " << m.size() << "\n"; + VERIFY(v < m.size()); + if (v == v0) var_sign = sign; if (value_at(l, m) == l_true) sat = true; - else if (!sat && v != it->var() && m[v] == l_undef) { + else if (!sat && v != v0 && m[v] == l_undef) { + VERIFY(legal_to_flip(v)); // clause can be satisfied by assigning v. m[v] = sign ? l_false : l_true; + // if (first) std::cout << "set: " << l << "\n"; sat = true; + if (first && m_solver && !m_solver->check_clauses(m)) { + IF_VERBOSE(0, display(verbose_stream() << "after flipping undef\n", *it) << "\n"); + first = false; + } } } DEBUG_CODE({ @@ -127,18 +200,43 @@ namespace sat { entry & e = m_entries.back(); SASSERT(e.var() == v); SASSERT(e.get_kind() == k); + VERIFY(v == null_bool_var || legal_to_flip(v)); return e; } + void model_converter::add_ate(clause const& c) { + if (stackv().empty()) return; + insert(mk(ATE, null_bool_var), c); + } + + void model_converter::add_ate(literal_vector const& lits) { + if (stackv().empty()) return; + insert(mk(ATE, null_bool_var), lits); + } + + void model_converter::add_ate(literal l1, literal l2) { + if (stackv().empty()) return; + insert(mk(ATE, null_bool_var), l1, l2); + } + + void model_converter::add_elim_stack(entry & e) { + e.m_elim_stack.push_back(stackv().empty() ? nullptr : alloc(elim_stack, stackv())); +#if 0 + if (!stackv().empty() && e.get_kind() == ATE) { + IF_VERBOSE(0, display(verbose_stream(), e) << "\n"); + } +#endif + for (auto const& s : stackv()) VERIFY(legal_to_flip(s.second.var())); + stackv().reset(); + } + void model_converter::insert(entry & e, clause const & c) { SASSERT(c.contains(e.var())); SASSERT(m_entries.begin() <= &e); SASSERT(&e < m_entries.end()); - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - e.m_clauses.push_back(c[i]); - } + for (literal l : c) e.m_clauses.push_back(l); e.m_clauses.push_back(null_literal); + add_elim_stack(e); TRACE("sat_mc_bug", tout << "adding: " << c << "\n";); } @@ -149,6 +247,7 @@ namespace sat { e.m_clauses.push_back(l1); e.m_clauses.push_back(l2); e.m_clauses.push_back(null_literal); + add_elim_stack(e); TRACE("sat_mc_bug", tout << "adding (binary): " << l1 << " " << l2 << "\n";); } @@ -157,30 +256,42 @@ namespace sat { SASSERT(m_entries.begin() <= &e); SASSERT(&e < m_entries.end()); unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) + for (unsigned i = 0; i < sz; ++i) e.m_clauses.push_back(c[i]); e.m_clauses.push_back(null_literal); - TRACE("sat_mc_bug", tout << "adding (wrapper): "; for (unsigned i = 0; i < c.size(); i++) tout << c[i] << " "; tout << "\n";); + add_elim_stack(e); + // TRACE("sat_mc_bug", tout << "adding (wrapper): "; for (literal l : c) tout << l << " "; tout << "\n";); } + void model_converter::insert(entry & e, literal_vector const& c) { + SASSERT(c.contains(literal(e.var(), false)) || c.contains(literal(e.var(), true))); + SASSERT(m_entries.begin() <= &e); + SASSERT(&e < m_entries.end()); + for (literal l : c) e.m_clauses.push_back(l); + e.m_clauses.push_back(null_literal); + add_elim_stack(e); + TRACE("sat_mc_bug", tout << "adding: " << c << "\n";); + } + + bool model_converter::check_invariant(unsigned num_vars) const { // After a variable v occurs in an entry n and the entry has kind ELIM_VAR, // then the variable must not occur in any other entry occurring after it. vector::const_iterator it = m_entries.begin(); vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { - SASSERT(it->var() < num_vars); + SASSERT(it->var() == null_bool_var || it->var() < num_vars); if (it->get_kind() == ELIM_VAR) { svector::const_iterator it2 = it; it2++; for (; it2 != end; ++it2) { SASSERT(it2->var() != it->var()); - literal_vector::const_iterator it3 = it2->m_clauses.begin(); - literal_vector::const_iterator end3 = it2->m_clauses.end(); - for (; it3 != end3; ++it3) { - CTRACE("sat_model_converter", it3->var() == it->var(), tout << "var: " << it->var() << "\n"; display(tout);); - SASSERT(it3->var() != it->var()); - SASSERT(*it3 == null_literal || it3->var() < num_vars); + if (it2->var() == it->var()) return false; + for (literal l : it2->m_clauses) { + CTRACE("sat_model_converter", l.var() == it->var(), tout << "var: " << it->var() << "\n"; display(tout);); + SASSERT(l.var() != it->var()); + VERIFY(l == null_literal || l.var() < num_vars); + if (it2->var() == it->var()) return false; } } } @@ -189,61 +300,75 @@ namespace sat { } void model_converter::display(std::ostream & out) const { - out << "(sat::model-converter"; - vector::const_iterator it = m_entries.begin(); - vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - out << "\n (" << (it->get_kind() == ELIM_VAR ? "elim" : "blocked") << " " << it->var(); - bool start = true; - literal_vector::const_iterator it2 = it->m_clauses.begin(); - literal_vector::const_iterator end2 = it->m_clauses.end(); - for (; it2 != end2; ++it2) { - if (start) { - out << "\n ("; - start = false; - } - else { - if (*it2 != null_literal) - out << " "; - } - if (*it2 == null_literal) { - out << ")"; - start = true; - continue; - } - out << *it2; - } - out << ")"; + out << "(sat::model-converter\n"; + bool first = true; + for (auto & entry : m_entries) { + if (first) first = false; else out << "\n"; + display(out, entry); } out << ")\n"; } + std::ostream& model_converter::display(std::ostream& out, entry const& entry) const { + out << " (" << entry.get_kind() << " "; + if (entry.var() != null_bool_var) out << entry.var(); + bool start = true; + unsigned index = 0; + for (literal l : entry.m_clauses) { + if (start) { + out << "\n ("; + start = false; + } + else { + if (l != null_literal) + out << " "; + } + if (l == null_literal) { + out << ")"; + start = true; + elim_stack* st = entry.m_elim_stack[index]; + if (st) { + elim_stackv const& stack = st->stack(); + unsigned sz = stack.size(); + for (unsigned i = sz; i-- > 0; ) { + out << "\n " << stack[i].first << " " << stack[i].second; + } + } + ++index; + continue; + } + out << l; + } + out << ")"; + for (literal l : entry.m_clauses) { + if (l != null_literal && l.var() != null_bool_var) { + if (false && m_solver && m_solver->was_eliminated(l.var())) out << "\neliminated: " << l; + } + } + return out; + } + void model_converter::copy(model_converter const & src) { - vector::const_iterator it = src.m_entries.begin(); - vector::const_iterator end = src.m_entries.end(); - for (; it != end; ++it) - m_entries.push_back(*it); + reset(); + m_entries.append(src.m_entries); + } + + void model_converter::flush(model_converter & src) { + VERIFY(this != &src); + m_entries.append(src.m_entries); + src.m_entries.reset(); } void model_converter::collect_vars(bool_var_set & s) const { - vector::const_iterator it = m_entries.begin(); - vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - s.insert(it->m_var); - } + for (entry const & e : m_entries) s.insert(e.m_var); } unsigned model_converter::max_var(unsigned min) const { unsigned result = min; - vector::const_iterator it = m_entries.begin(); - vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - literal_vector::const_iterator lvit = it->m_clauses.begin(); - literal_vector::const_iterator lvend = it->m_clauses.end(); - for (; lvit != lvend; ++lvit) { - literal l = *lvit; + for (entry const& e : m_entries) { + for (literal l : e.m_clauses) { if (l != null_literal) { - if (l.var() > result) + if (l.var() != null_bool_var && l.var() > result) result = l.var(); } } @@ -251,4 +376,48 @@ namespace sat { return result; } + void model_converter::swap(bool_var v, unsigned sz, literal_vector& clause) { + for (unsigned j = 0; j < sz; ++j) { + if (v == clause[j].var()) { + std::swap(clause[0], clause[j]); + return; + } + } + IF_VERBOSE(0, verbose_stream() << "not found: v" << v << " " << clause << "\n";); + UNREACHABLE(); + } + + void model_converter::expand(literal_vector& update_stack) { + sat::literal_vector clause; + for (entry const& e : m_entries) { + unsigned index = 0; + clause.reset(); + for (literal l : e.m_clauses) { + if (l == null_literal) { + elim_stack* st = e.m_elim_stack[index]; + if (st) { + // clause sizes increase, so we can always swap + // the blocked literal to the front from the prefix. + for (auto const& p : st->stack()) { + unsigned csz = p.first; + literal lit = p.second; + swap(lit.var(), csz, clause); + update_stack.append(csz, clause.c_ptr()); + update_stack.push_back(null_literal); + } + } + if (e.var() != null_bool_var) { + swap(e.var(), clause.size(), clause); + update_stack.append(clause); + update_stack.push_back(null_literal); + } + clause.reset(); + } + else { + clause.push_back(l); + } + } + } + } + }; diff --git a/src/sat/sat_model_converter.h b/src/sat/sat_model_converter.h index 9a5ebc0ff..65e132729 100644 --- a/src/sat/sat_model_converter.h +++ b/src/sat/sat_model_converter.h @@ -20,6 +20,7 @@ Revision History: #define SAT_MODEL_CONVERTER_H_ #include "sat/sat_types.h" +#include "util/ref_vector.h" namespace sat { /** @@ -35,35 +36,87 @@ namespace sat { This is a low-level model_converter. Given a mapping from bool_var to expr, it can be converted into a general Z3 model_converter */ + + class solver; + + static unsigned counter = 0; + class model_converter { + public: - enum kind { ELIM_VAR = 0, BLOCK_LIT }; + typedef svector> elim_stackv; + + + class elim_stack { + unsigned m_counter; + unsigned m_refcount; + elim_stackv m_stack; + elim_stack(elim_stack const& ); + public: + elim_stack(elim_stackv const& stack): + m_counter(0), + m_refcount(0), + m_stack(stack) { + m_counter = ++counter; + } + ~elim_stack() { } + void inc_ref() { ++m_refcount; } + void dec_ref() { if (0 == --m_refcount) { dealloc(this); } } + elim_stackv const& stack() const { return m_stack; } + unsigned ref_count() const { return m_refcount; } + }; + + enum kind { ELIM_VAR = 0, BCE, CCE, ACCE, ABCE, ATE }; class entry { friend class model_converter; - unsigned m_var:31; - unsigned m_kind:1; - literal_vector m_clauses; // the different clauses are separated by null_literal - entry(kind k, bool_var v):m_var(v), m_kind(k) {} + bool_var m_var; + kind m_kind; + literal_vector m_clauses; // the different clauses are separated by null_literal + sref_vector m_elim_stack; + entry(kind k, bool_var v): m_var(v), m_kind(k) {} public: entry(entry const & src): m_var(src.m_var), m_kind(src.m_kind), m_clauses(src.m_clauses) { + m_elim_stack.append(src.m_elim_stack); } bool_var var() const { return m_var; } - kind get_kind() const { return static_cast(m_kind); } + kind get_kind() const { return m_kind; } }; private: vector m_entries; + solver const* m_solver; + elim_stackv m_elim_stack; + + void process_stack(model & m, literal_vector const& clause, elim_stackv const& stack) const; + + std::ostream& display(std::ostream & out, entry const& entry) const; + + bool legal_to_flip(bool_var v) const; + + void swap(bool_var v, unsigned sz, literal_vector& clause); + + void add_elim_stack(entry & e); + public: model_converter(); ~model_converter(); + void set_solver(solver const* s) { m_solver = s; } void operator()(model & m) const; + model_converter& operator=(model_converter const& other); + + elim_stackv& stackv() { return m_elim_stack; } entry & mk(kind k, bool_var v); void insert(entry & e, clause const & c); void insert(entry & e, literal l1, literal l2); void insert(entry & e, clause_wrapper const & c); + void insert(entry & c, literal_vector const& covered_clause); + + void add_ate(literal_vector const& lits); + void add_ate(literal l1, literal l2); + void add_ate(clause const& c); bool empty() const { return m_entries.empty(); } @@ -71,12 +124,37 @@ namespace sat { bool check_invariant(unsigned num_vars) const; void display(std::ostream & out) const; bool check_model(model const & m) const; - void copy(model_converter const & src); + + /* + \brief Append entries from src, then remove entries in src. + */ + void flush(model_converter& src); void collect_vars(bool_var_set & s) const; unsigned max_var(unsigned min) const; + /* + * \brief expand entries to a list of clauses, such that + * the first literal in each clause is the literal whose + * truth value is updated as follows: + * + * lit0 := lit0 or (~lit1 & ~lit2 & ... & ~lit_k) + * + */ + void expand(literal_vector& update_stack); }; + inline std::ostream& operator<<(std::ostream& out, model_converter::kind k) { + switch (k) { + case model_converter::ELIM_VAR: out << "elim"; break; + case model_converter::BCE: out << "bce"; break; + case model_converter::CCE: out << "cce"; break; + case model_converter::ACCE: out << "acce"; break; + case model_converter::ABCE: out << "abce"; break; + case model_converter::ATE: out << "ate"; break; + } + return out; + } + }; #endif diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp new file mode 100644 index 000000000..c6e29f64c --- /dev/null +++ b/src/sat/sat_parallel.cpp @@ -0,0 +1,315 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_parallel.cpp + + Abstract: + + Utilities for parallel SAT solving. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-1-29. + +Revision History: + +--*/ +#include "sat_parallel.h" +#include "sat_clause.h" +#include "sat_solver.h" + + +namespace sat { + + void parallel::vector_pool::next(unsigned& index) { + SASSERT(index < m_size); + unsigned n = index + 2 + get_length(index); + if (n >= m_size) { + index = 0; + } + else { + index = n; + } + } + + void parallel::vector_pool::reserve(unsigned num_threads, unsigned sz) { + m_vectors.reset(); + m_vectors.resize(sz, 0); + m_heads.reset(); + m_heads.resize(num_threads, 0); + m_at_end.reset(); + m_at_end.resize(num_threads, true); + m_tail = 0; + m_size = sz; + } + + void parallel::vector_pool::begin_add_vector(unsigned owner, unsigned n) { + SASSERT(m_tail < m_size); + unsigned capacity = n + 2; + m_vectors.reserve(m_size + capacity, 0); + IF_VERBOSE(3, verbose_stream() << owner << ": begin-add " << n << " tail: " << m_tail << " size: " << m_size << "\n";); + for (unsigned i = 0; i < m_heads.size(); ++i) { + while (m_tail < m_heads[i] && m_heads[i] < m_tail + capacity) { + next(m_heads[i]); + } + m_at_end[i] = false; + } + m_vectors[m_tail++] = owner; + m_vectors[m_tail++] = n; + } + + void parallel::vector_pool::add_vector_elem(unsigned e) { + m_vectors[m_tail++] = e; + } + + void parallel::vector_pool::end_add_vector() { + if (m_tail >= m_size) { + m_tail = 0; + } + } + + + bool parallel::vector_pool::get_vector(unsigned owner, unsigned& n, unsigned const*& ptr) { + unsigned head = m_heads[owner]; + unsigned iterations = 0; + while (head != m_tail || !m_at_end[owner]) { + ++iterations; + SASSERT(head < m_size && m_tail < m_size); + bool is_self = owner == get_owner(head); + next(m_heads[owner]); + IF_VERBOSE(static_cast(iterations > m_size ? 0 : 3), verbose_stream() << owner << ": [" << head << ":" << m_heads[owner] << "] tail: " << m_tail << "\n";); + m_at_end[owner] = (m_heads[owner] == m_tail); + if (!is_self) { + n = get_length(head); + ptr = get_ptr(head); + return true; + } + head = m_heads[owner]; + } + return false; + } + + parallel::parallel(solver& s): m_num_clauses(0), m_consumer_ready(false), m_scoped_rlimit(s.rlimit()) {} + + parallel::~parallel() { + for (unsigned i = 0; i < m_solvers.size(); ++i) { + dealloc(m_solvers[i]); + } + } + + void parallel::init_solvers(solver& s, unsigned num_extra_solvers) { + unsigned num_threads = num_extra_solvers + 1; + m_solvers.resize(num_extra_solvers); + symbol saved_phase = s.m_params.get_sym("phase", symbol("caching")); + for (unsigned i = 0; i < num_extra_solvers; ++i) { + m_limits.push_back(reslimit()); + } + + for (unsigned i = 0; i < num_extra_solvers; ++i) { + s.m_params.set_uint("random_seed", s.m_rand()); + if (i == 1 + num_threads/2) { + s.m_params.set_sym("phase", symbol("random")); + } + m_solvers[i] = alloc(sat::solver, s.m_params, m_limits[i]); + m_solvers[i]->copy(s); + m_solvers[i]->set_par(this, i); + push_child(m_solvers[i]->rlimit()); + } + s.set_par(this, num_extra_solvers); + s.m_params.set_sym("phase", saved_phase); + } + + void parallel::push_child(reslimit& rl) { + m_scoped_rlimit.push_child(&rl); + } + + + void parallel::exchange(solver& s, literal_vector const& in, unsigned& limit, literal_vector& out) { + if (s.get_config().m_num_threads == 1 || s.m_par_syncing_clauses) return; + flet _disable_sync_clause(s.m_par_syncing_clauses, true); + #pragma omp critical (par_solver) + { + if (limit < m_units.size()) { + // this might repeat some literals. + out.append(m_units.size() - limit, m_units.c_ptr() + limit); + } + for (unsigned i = 0; i < in.size(); ++i) { + literal lit = in[i]; + if (!m_unit_set.contains(lit.index())) { + m_unit_set.insert(lit.index()); + m_units.push_back(lit); + } + } + limit = m_units.size(); + } + } + + void parallel::share_clause(solver& s, literal l1, literal l2) { + if (s.get_config().m_num_threads == 1 || s.m_par_syncing_clauses) return; + flet _disable_sync_clause(s.m_par_syncing_clauses, true); + IF_VERBOSE(3, verbose_stream() << s.m_par_id << ": share " << l1 << " " << l2 << "\n";); + #pragma omp critical (par_solver) + { + m_pool.begin_add_vector(s.m_par_id, 2); + m_pool.add_vector_elem(l1.index()); + m_pool.add_vector_elem(l2.index()); + m_pool.end_add_vector(); + } + } + + void parallel::share_clause(solver& s, clause const& c) { + if (s.get_config().m_num_threads == 1 || !enable_add(c) || s.m_par_syncing_clauses) return; + flet _disable_sync_clause(s.m_par_syncing_clauses, true); + unsigned n = c.size(); + unsigned owner = s.m_par_id; + IF_VERBOSE(3, verbose_stream() << owner << ": share " << c << "\n";); + #pragma omp critical (par_solver) + { + m_pool.begin_add_vector(owner, n); + for (unsigned i = 0; i < n; ++i) { + m_pool.add_vector_elem(c[i].index()); + } + m_pool.end_add_vector(); + } + } + + void parallel::get_clauses(solver& s) { + if (s.m_par_syncing_clauses) return; + flet _disable_sync_clause(s.m_par_syncing_clauses, true); + #pragma omp critical (par_solver) + { + _get_clauses(s); + } + } + + void parallel::_get_clauses(solver& s) { + unsigned n; + unsigned const* ptr; + unsigned owner = s.m_par_id; + while (m_pool.get_vector(owner, n, ptr)) { + m_lits.reset(); + bool usable_clause = true; + for (unsigned i = 0; usable_clause && i < n; ++i) { + literal lit(to_literal(ptr[i])); + m_lits.push_back(lit); + usable_clause = lit.var() <= s.m_par_num_vars && !s.was_eliminated(lit.var()); + } + IF_VERBOSE(3, verbose_stream() << s.m_par_id << ": retrieve " << m_lits << "\n";); + SASSERT(n >= 2); + if (usable_clause) { + s.mk_clause_core(m_lits.size(), m_lits.c_ptr(), true); + } + } + } + + bool parallel::enable_add(clause const& c) const { + // plingeling, glucose heuristic: + return (c.size() <= 40 && c.glue() <= 8) || c.glue() <= 2; + } + + void parallel::_set_phase(solver& s) { + if (!m_phase.empty()) { + m_phase.reserve(s.num_vars(), l_undef); + for (unsigned i = 0; i < s.num_vars(); ++i) { + if (s.value(i) != l_undef) { + m_phase[i] = s.value(i); + continue; + } + switch (s.m_phase[i]) { + case POS_PHASE: + m_phase[i] = l_true; + break; + case NEG_PHASE: + m_phase[i] = l_false; + break; + default: + m_phase[i] = l_undef; + break; + } + } + } + if (m_consumer_ready && (m_num_clauses == 0 || (m_num_clauses > s.m_clauses.size()))) { + // time to update local search with new clauses. + // there could be multiple local search engines runing at the same time. + IF_VERBOSE(1, verbose_stream() << "(sat-parallel refresh :from " << m_num_clauses << " :to " << s.m_clauses.size() << ")\n";); + m_solver_copy = alloc(solver, s.m_params, s.rlimit()); + m_solver_copy->copy(s); + m_num_clauses = s.m_clauses.size(); + } + } + + void parallel::set_phase(solver& s) { + #pragma omp critical (par_solver) + { + _set_phase(s); + } + } + + void parallel::get_phase(solver& s) { + #pragma omp critical (par_solver) + { + _get_phase(s); + } + } + + void parallel::_get_phase(solver& s) { + if (!m_phase.empty()) { + m_phase.reserve(s.num_vars(), l_undef); + for (unsigned i = 0; i < s.num_vars(); ++i) { + switch (m_phase[i]) { + case l_false: s.m_phase[i] = NEG_PHASE; break; + case l_true: s.m_phase[i] = POS_PHASE; break; + default: break; + } + } + } + } + + bool parallel::get_phase(local_search& s) { + bool copied = false; + #pragma omp critical (par_solver) + { + m_consumer_ready = true; + if (m_solver_copy && s.num_non_binary_clauses() > m_solver_copy->m_clauses.size()) { + copied = true; + s.import(*m_solver_copy.get(), true); + } + for (unsigned i = 0; i < m_phase.size(); ++i) { + s.set_phase(i, m_phase[i]); + m_phase[i] = l_undef; + } + m_phase.reserve(s.num_vars(), l_undef); + } + return copied; + } + + void parallel::set_phase(local_search& s) { + #pragma omp critical (par_solver) + { + m_consumer_ready = true; + m_phase.reserve(s.num_vars(), l_undef); + for (unsigned i = 0; i < s.num_vars(); ++i) { + m_phase[i] = s.get_phase(i) ? l_true : l_false; + } + m_num_clauses = s.num_non_binary_clauses(); + } + } + + bool parallel::copy_solver(solver& s) { + bool copied = false; + #pragma omp critical (par_solver) + { + m_consumer_ready = true; + if (m_solver_copy && s.m_clauses.size() > m_solver_copy->m_clauses.size()) { + s.copy(*m_solver_copy); + copied = true; + m_num_clauses = s.m_clauses.size(); + } + } + return copied; + } + +}; + diff --git a/src/sat/sat_parallel.h b/src/sat/sat_parallel.h new file mode 100644 index 000000000..256623380 --- /dev/null +++ b/src/sat/sat_parallel.h @@ -0,0 +1,116 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_parallel.h + +Abstract: + + Utilities for parallel SAT solving. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-1-29. + +Revision History: + +--*/ +#ifndef SAT_PARALLEL_H_ +#define SAT_PARALLEL_H_ + +#include "sat/sat_types.h" +#include "util/hashtable.h" +#include "util/map.h" +#include "util/rlimit.h" + +namespace sat { + + class local_search; + + class parallel { + + // shared pool of learned clauses. + class vector_pool { + unsigned_vector m_vectors; + unsigned m_size; + unsigned m_tail; + unsigned_vector m_heads; + svector m_at_end; + void next(unsigned& index); + unsigned get_owner(unsigned index) const { return m_vectors[index]; } + unsigned get_length(unsigned index) const { return m_vectors[index+1]; } + unsigned const* get_ptr(unsigned index) const { return m_vectors.c_ptr() + index + 2; } + public: + vector_pool() {} + void reserve(unsigned num_owners, unsigned sz); + void begin_add_vector(unsigned owner, unsigned n); + void end_add_vector(); + void add_vector_elem(unsigned e); + bool get_vector(unsigned owner, unsigned& n, unsigned const*& ptr); + }; + + bool enable_add(clause const& c) const; + void _get_clauses(solver& s); + void _get_phase(solver& s); + void _set_phase(solver& s); + + typedef hashtable index_set; + literal_vector m_units; + index_set m_unit_set; + literal_vector m_lits; + vector_pool m_pool; + + // for exchange with local search: + svector m_phase; + unsigned m_num_clauses; + scoped_ptr m_solver_copy; + bool m_consumer_ready; + + scoped_limits m_scoped_rlimit; + vector m_limits; + ptr_vector m_solvers; + + public: + + parallel(solver& s); + + ~parallel(); + + void init_solvers(solver& s, unsigned num_extra_solvers); + + void push_child(reslimit& rl); + + // reserve space + void reserve(unsigned num_owners, unsigned sz) { m_pool.reserve(num_owners, sz); } + + solver& get_solver(unsigned i) { return *m_solvers[i]; } + + void cancel_solver(unsigned i) { m_limits[i].cancel(); } + + // exchange unit literals + void exchange(solver& s, literal_vector const& in, unsigned& limit, literal_vector& out); + + // add clause to shared clause pool + void share_clause(solver& s, clause const& c); + + void share_clause(solver& s, literal l1, literal l2); + + // receive clauses from shared clause pool + void get_clauses(solver& s); + + // exchange phase of variables. + void set_phase(solver& s); + + void get_phase(solver& s); + + void set_phase(local_search& s); + + bool get_phase(local_search& s); + + bool copy_solver(solver& s); + }; + +}; + +#endif diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 2b1dc6646..dd840468e 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -5,23 +5,62 @@ def_module_params('sat', ('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, caching, random'), ('phase.caching.on', UINT, 400, 'phase caching on period (in number of conflicts)'), ('phase.caching.off', UINT, 100, 'phase caching off period (in number of conflicts)'), - ('restart', SYMBOL, 'luby', 'restart strategy: luby or geometric'), - ('restart.initial', UINT, 100, 'initial restart (number of conflicts)'), - ('restart.max', UINT, UINT_MAX, 'maximal number of restarts.'), + ('phase.sticky', BOOL, False, 'use sticky phase caching for local search'), + ('propagate.prefetch', BOOL, True, 'prefetch watch lists for assigned literals'), + ('restart', SYMBOL, 'ema', 'restart strategy: static, luby, ema or geometric'), + ('restart.initial', UINT, 2, 'initial restart (number of conflicts)'), + ('restart.max', UINT, UINT_MAX, 'maximal number of restarts.'), + ('restart.fast', BOOL, True, 'use fast restart approach only removing less active literals.'), ('restart.factor', DOUBLE, 1.5, 'restart increment factor for geometric strategy'), + ('restart.margin', DOUBLE, 1.1, 'margin between fast and slow restart factors. For ema'), + ('restart.emafastglue', DOUBLE, 3e-2, 'ema alpha factor for fast moving average'), + ('restart.emaslowglue', DOUBLE, 1e-5, 'ema alpha factor for slow moving average'), + ('variable_decay', UINT, 110, 'multiplier (divided by 100) for the VSIDS activity increement'), + ('inprocess.max', UINT, UINT_MAX, 'maximal number of inprocessing passes'), + ('branching.heuristic', SYMBOL, 'vsids', 'branching heuristic vsids, lrb or chb'), + ('branching.anti_exploration', BOOL, False, 'apply anti-exploration heuristic for branch selection'), ('random_freq', DOUBLE, 0.01, 'frequency of random case splits'), ('random_seed', UINT, 0, 'random seed'), ('burst_search', UINT, 100, 'number of conflicts before first global simplification'), ('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts'), ('gc', SYMBOL, 'glue_psm', 'garbage collection strategy: psm, glue, glue_psm, dyn_psm'), - ('gc.initial', UINT, 20000, 'learned clauses garbage collection frequence'), + ('gc.initial', UINT, 20000, 'learned clauses garbage collection frequency'), ('gc.increment', UINT, 500, 'increment to the garbage collection threshold'), ('gc.small_lbd', UINT, 3, 'learned clauses with small LBD are never deleted (only used in dyn_psm)'), ('gc.k', UINT, 7, 'learned clauses that are inactive for k gc rounds are permanently deleted (only used in dyn_psm)'), + ('gc.burst', BOOL, False, 'perform eager garbage collection during initialization'), + ('gc.defrag', BOOL, True, 'defragment clauses when garbage collecting'), + ('simplify.delay', UINT, 0, 'set initial delay of simplification by a conflict count'), ('minimize_lemmas', BOOL, True, 'minimize learned clauses'), ('dyn_sub_res', BOOL, True, 'dynamic subsumption resolution for minimizing learned clauses'), ('core.minimize', BOOL, False, 'minimize computed core'), ('core.minimize_partial', BOOL, False, 'apply partial (cheap) core minimization'), - ('parallel_threads', UINT, 1, 'number of parallel threads to use'), + ('threads', UINT, 1, 'number of parallel threads to use'), ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'), - ('dimacs.display', BOOL, False, 'display SAT instance in DIMACS format and return unknown instead of solving'))) + ('drat.file', SYMBOL, '', 'file to dump DRAT proofs'), + ('drat.check_unsat', BOOL, False, 'build up internal proof and check'), + ('drat.check_sat', BOOL, False, 'build up internal trace, check satisfying model'), + ('cardinality.solver', BOOL, True, 'use cardinality solver'), + ('pb.solver', SYMBOL, 'solver', 'method for handling Pseudo-Boolean constraints: circuit (arithmetical circuit), sorting (sorting circuit), totalizer (use totalizer encoding), solver (use native solver)'), + ('xor.solver', BOOL, False, 'use xor solver'), + ('atmost1_encoding', SYMBOL, 'grouped', 'encoding used for at-most-1 constraints grouped, bimander, ordered'), + ('local_search', BOOL, False, 'use local search instead of CDCL'), + ('local_search_threads', UINT, 0, 'number of local search threads to find satisfiable solution'), + ('local_search_mode', SYMBOL, 'wsat', 'local search algorithm, either default wsat or qsat'), + ('unit_walk', BOOL, False, 'use unit-walk search instead of CDCL'), + ('unit_walk_threads', UINT, 0, 'number of unit-walk search threads to find satisfiable solution'), + ('lookahead.cube.cutoff', SYMBOL, 'depth', 'cutoff type used to create lookahead cubes: depth, freevars, psat, adaptive_freevars, adaptive_psat'), + ('lookahead.cube.fraction', DOUBLE, 0.4, 'adaptive fraction to create lookahead cubes. Used when lookahead.cube.cutoff is adaptive_freevars or adaptive_psat'), + ('lookahead.cube.depth', UINT, 1, 'cut-off depth to create cubes. Used when lookahead.cube.cutoff is depth.'), + ('lookahead.cube.freevars', DOUBLE, 0.8, 'cube free fariable fraction. Used when lookahead.cube.cutoff is freevars'), + ('lookahead.cube.psat.var_exp', DOUBLE, 1, 'free variable exponent for PSAT cutoff'), + ('lookahead.cube.psat.clause_base', DOUBLE, 2, 'clause base for PSAT cutoff'), + ('lookahead.cube.psat.trigger', DOUBLE, 5, 'trigger value to create lookahead cubes for PSAT cutoff. Used when lookahead.cube.cutoff is psat'), + ('lookahead_search', BOOL, False, 'use lookahead solver'), + ('lookahead.preselect', BOOL, False, 'use pre-selection of subset of variables for branching'), + ('lookahead_simplify', BOOL, False, 'use lookahead solver during simplification'), + ('lookahead.use_learned', BOOL, False, 'use learned clauses when selecting lookahead literal'), + ('lookahead_simplify.bca', BOOL, True, 'add learned binary clauses as part of lookahead simplification'), + ('lookahead.global_autarky', BOOL, False, 'prefer to branch on variables that occur in clauses that are reduced'), + ('lookahead.reward', SYMBOL, 'march_cu', 'select lookahead heuristic: ternary, heule_schur (Heule Schur), heuleu (Heule Unit), unit, or march_cu'))) + diff --git a/src/sat/sat_probing.cpp b/src/sat/sat_probing.cpp index 57b09c7f7..52ab9f9f7 100644 --- a/src/sat/sat_probing.cpp +++ b/src/sat/sat_probing.cpp @@ -19,6 +19,7 @@ Revision History: --*/ #include "sat/sat_probing.h" #include "sat/sat_solver.h" +#include "sat/sat_simplifier_params.hpp" namespace sat { probing::probing(solver & _s, params_ref const & p): @@ -62,11 +63,9 @@ namespace sat { SASSERT(s.value(l.var()) == l_undef); literal_vector * implied_lits = updt_cache ? nullptr : cached_implied_lits(l); if (implied_lits) { - literal_vector::iterator it = implied_lits->begin(); - literal_vector::iterator end = implied_lits->end(); - for (; it != end; ++it) { - if (m_assigned.contains(*it)) { - s.assign(*it, justification()); + for (literal lit : *implied_lits) { + if (m_assigned.contains(lit)) { + s.assign(lit, justification()); m_num_assigned++; } } @@ -96,10 +95,8 @@ namespace sat { cache_bins(l, old_tr_sz); s.pop(1); - literal_vector::iterator it = m_to_assert.begin(); - literal_vector::iterator end = m_to_assert.end(); - for (; it != end; ++it) { - s.assign(*it, justification()); + for (literal l : m_to_assert) { + s.assign(l, justification()); m_num_assigned++; } } @@ -139,10 +136,9 @@ namespace sat { if (m_probing_binary) { watch_list & wlist = s.get_wlist(~l); - for (unsigned i = 0; i < wlist.size(); i++) { - watched & w = wlist[i]; + for (watched & w : wlist) { if (!w.is_binary_clause()) - break; + continue; literal l2 = w.get_literal(); if (l.index() > l2.index()) continue; @@ -243,12 +239,13 @@ namespace sat { return r; } - void probing::updt_params(params_ref const & p) { - m_probing = p.get_bool("probing", true); - m_probing_limit = p.get_uint("probing_limit", 5000000); - m_probing_cache = p.get_bool("probing_cache", true); - m_probing_binary = p.get_bool("probing_binary", true); - m_probing_cache_limit = megabytes_to_bytes(p.get_uint("probing_chache_limit", 1024)); + void probing::updt_params(params_ref const & _p) { + sat_simplifier_params p(_p); + m_probing = p.probing(); + m_probing_limit = p.probing_limit(); + m_probing_cache = p.probing_cache(); + m_probing_binary = p.probing_binary(); + m_probing_cache_limit = p.probing_cache_limit(); } void probing::collect_param_descrs(param_descrs & d) { diff --git a/src/sat/sat_scc.cpp b/src/sat/sat_scc.cpp index 9682da4e8..e430bcb47 100644 --- a/src/sat/sat_scc.cpp +++ b/src/sat/sat_scc.cpp @@ -26,17 +26,19 @@ Revision History: namespace sat { scc::scc(solver & s, params_ref const & p): - m_solver(s) { + m_solver(s), + m_big(s.m_rand) { reset_statistics(); updt_params(p); } struct frame { unsigned m_lidx; + unsigned m_succ_idx; bool m_first; watched * m_it; watched * m_end; - frame(unsigned lidx, watched * it, watched * end):m_lidx(lidx), m_first(true), m_it(it), m_end(end) {} + frame(unsigned lidx, watched * it, watched * end, unsigned sidx = 0):m_lidx(lidx), m_succ_idx(sidx), m_first(true), m_it(it), m_end(end) {} }; typedef svector frames; @@ -44,16 +46,20 @@ namespace sat { scc & m_scc; stopwatch m_watch; unsigned m_num_elim; + unsigned m_num_elim_bin; report(scc & c): m_scc(c), - m_num_elim(c.m_num_elim) { + m_num_elim(c.m_num_elim), + m_num_elim_bin(c.m_num_elim_bin) { m_watch.start(); } ~report() { m_watch.stop(); + unsigned elim_bin = m_scc.m_num_elim_bin - m_num_elim_bin; IF_VERBOSE(SAT_VB_LVL, - verbose_stream() << " (sat-scc :elim-vars " << (m_scc.m_num_elim - m_num_elim) - << mk_stat(m_scc.m_solver) + verbose_stream() << " (sat-scc :elim-vars " << (m_scc.m_num_elim - m_num_elim); + if (elim_bin > 0) verbose_stream() << " :elim-bin " << elim_bin; + verbose_stream() << mk_stat(m_scc.m_solver) << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; @@ -75,7 +81,7 @@ namespace sat { index.resize(num_lits, UINT_MAX); lowlink.resize(num_lits, UINT_MAX); in_s.resize(num_lits, false); - literal_vector roots; + literal_vector roots, lits; roots.resize(m_solver.num_vars(), null_literal); unsigned next_index = 0; svector frames; @@ -98,14 +104,15 @@ namespace sat { watch_list & wlist = m_solver.get_wlist(LIDX); \ frames.push_back(frame(LIDX, wlist.begin(), wlist.end())); \ } - + NEW_NODE(l_idx); - + while (!frames.empty()) { loop: - frame & fr = frames.back(); - unsigned l_idx = fr.m_lidx; + frame & fr = frames.back(); + unsigned l_idx = fr.m_lidx; if (!fr.m_first) { + SASSERT(fr.m_it->is_binary_clause()); // after visiting child literal l2 = fr.m_it->get_literal(); unsigned l2_idx = l2.index(); @@ -136,20 +143,19 @@ namespace sat { if (lowlink[l_idx] == index[l_idx]) { // found new SCC CTRACE("scc_cycle", s.back() != l_idx, { - tout << "cycle: "; - unsigned j = s.size() - 1; - unsigned l2_idx; - do { - l2_idx = s[j]; - j--; - tout << to_literal(l2_idx) << " "; - } - while (l2_idx != l_idx); - tout << "\n"; - }); + tout << "cycle: "; + unsigned j = s.size() - 1; + unsigned l2_idx; + do { + l2_idx = s[j]; + j--; + tout << to_literal(l2_idx) << " "; + } while (l2_idx != l_idx); + tout << "\n"; + }); SASSERT(!s.empty()); - literal l = to_literal(l_idx); + literal l = to_literal(l_idx); bool_var v = l.var(); if (roots[v] != null_literal) { // variable was already assigned... just consume stack @@ -158,10 +164,9 @@ namespace sat { do { l2_idx = s.back(); s.pop_back(); - in_s[l2_idx] = false; + in_s[l2_idx] = false; SASSERT(roots[to_literal(l2_idx).var()].var() == roots[v].var()); - } - while (l2_idx != l_idx); + } while (l2_idx != l_idx); } else { // check if the SCC has an external variable, and check for conflicts @@ -180,20 +185,19 @@ namespace sat { r = to_literal(l2_idx); break; } - } - while (l2_idx != l_idx); - + } while (l2_idx != l_idx); + if (r == null_literal) { // SCC does not contain external variable r = to_literal(l_idx); } - + TRACE("scc_detail", tout << "r: " << r << "\n";); do { l2_idx = s.back(); s.pop_back(); - in_s[l2_idx] = false; + in_s[l2_idx] = false; literal l2 = to_literal(l2_idx); bool_var v2 = l2.var(); if (roots[v2] == null_literal) { @@ -203,37 +207,63 @@ namespace sat { else { roots[v2] = r; } - if (v2 != r.var()) + if (v2 != r.var()) to_elim.push_back(v2); } - } - while (l2_idx != l_idx); + } while (l2_idx != l_idx); } } frames.pop_back(); } } + for (unsigned i = 0; i < m_solver.num_vars(); ++i) { + if (roots[i] == null_literal) { + roots[i] = literal(i, false); + } + } TRACE("scc", for (unsigned i = 0; i < roots.size(); i++) { tout << i << " -> " << roots[i] << "\n"; } - tout << "to_elim: "; for (unsigned i = 0; i < to_elim.size(); i++) tout << to_elim[i] << " "; tout << "\n";); + tout << "to_elim: "; for (unsigned v : to_elim) tout << v << " "; tout << "\n";); m_num_elim += to_elim.size(); elim_eqs eliminator(m_solver); eliminator(roots, to_elim); TRACE("scc_detail", m_solver.display(tout);); CASSERT("scc_bug", m_solver.check_invariant()); + + if (m_scc_tr) { + reduce_tr(); + } + TRACE("scc_detail", m_solver.display(tout);); return to_elim.size(); } + unsigned scc::reduce_tr(bool learned) { + init_big(learned); + unsigned num_elim = m_big.reduce_tr(m_solver); + m_num_elim_bin += num_elim; + return num_elim; + } + + void scc::reduce_tr() { + unsigned quota = 0, num_reduced = 0; + while ((num_reduced = reduce_tr(false)) > quota) { quota = std::max(100u, num_reduced / 2); } + quota = 0; + while ((num_reduced = reduce_tr(true)) > quota) { quota = std::max(100u, num_reduced / 2); } + } + void scc::collect_statistics(statistics & st) const { - st.update("elim bool vars", m_num_elim); + st.update("elim bool vars scc", m_num_elim); + st.update("elim binary", m_num_elim_bin); } void scc::reset_statistics() { m_num_elim = 0; + m_num_elim_bin = 0; } void scc::updt_params(params_ref const & _p) { sat_scc_params p(_p); m_scc = p.scc(); + m_scc_tr = p.scc_tr(); } void scc::collect_param_descrs(param_descrs & d) { diff --git a/src/sat/sat_scc.h b/src/sat/sat_scc.h index c8392685e..146bd2366 100644 --- a/src/sat/sat_scc.h +++ b/src/sat/sat_scc.h @@ -19,9 +19,10 @@ Revision History: #ifndef SAT_SCC_H_ #define SAT_SCC_H_ -#include "sat/sat_types.h" #include "util/statistics.h" #include "util/params.h" +#include "sat/sat_types.h" +#include "sat/sat_big.h" namespace sat { class solver; @@ -31,9 +32,18 @@ namespace sat { solver & m_solver; // config bool m_scc; + bool m_scc_tr; // stats unsigned m_num_elim; + unsigned m_num_elim_bin; + + big m_big; + + void reduce_tr(); + unsigned reduce_tr(bool learned); + public: + scc(solver & s, params_ref const & p); unsigned operator()(); @@ -42,6 +52,16 @@ namespace sat { void collect_statistics(statistics & st) const; void reset_statistics(); + + /* + \brief create binary implication graph and associated data-structures to check transitivity. + */ + void init_big(bool learned) { m_big.init(m_solver, learned); } + void ensure_big(bool learned) { m_big.ensure_big(m_solver, learned); } + int get_left(literal l) const { return m_big.get_left(l); } + int get_right(literal l) const { return m_big.get_right(l); } + literal get_root(literal l) const { return m_big.get_root(l); } + bool connected(literal u, literal v) const { return m_big.connected(u, v); } }; }; diff --git a/src/sat/sat_scc_params.pyg b/src/sat/sat_scc_params.pyg index b88de4de8..ead4eeb96 100644 --- a/src/sat/sat_scc_params.pyg +++ b/src/sat/sat_scc_params.pyg @@ -1,5 +1,6 @@ def_module_params(module_name='sat', class_name='sat_scc_params', export=True, - params=(('scc', BOOL, True, 'eliminate Boolean variables by computing strongly connected components'),)) + params=(('scc', BOOL, True, 'eliminate Boolean variables by computing strongly connected components'), + ('scc.tr', BOOL, True, 'apply transitive reduction, eliminate redundant binary clauses'), )) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 432903b4c..4fd9b08be 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -21,6 +21,8 @@ Revision History: #include "sat/sat_simplifier.h" #include "sat/sat_simplifier_params.hpp" #include "sat/sat_solver.h" +#include "sat/sat_elim_vars.h" +#include "sat/sat_integrity_checker.h" #include "util/stopwatch.h" #include "util/trace.h" @@ -33,26 +35,29 @@ namespace sat { } void use_list::insert(clause & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - m_use_list[c[i].index()].insert(c); - } + for (literal l : c) + m_use_list[l.index()].insert(c); } void use_list::erase(clause & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - m_use_list[c[i].index()].erase(c); - } + for (literal l : c) + m_use_list[l.index()].erase(c); } void use_list::erase(clause & c, literal l) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - literal l2 = c[i]; + for (literal l2 : c) if (l2 != l) m_use_list[l2.index()].erase(c); - } + } + + void use_list::block(clause& c) { + for (literal l : c) + m_use_list[l.index()].block(c); + } + + void use_list::unblock(clause& c) { + for (literal l : c) + m_use_list[l.index()].unblock(c); } simplifier::simplifier(solver & _s, params_ref const & p): @@ -66,11 +71,18 @@ namespace sat { finalize(); } - inline watch_list & simplifier::get_wlist(literal l) { return s.get_wlist(l); } + watch_list & simplifier::get_wlist(literal l) { return s.get_wlist(l); } - inline watch_list const & simplifier::get_wlist(literal l) const { return s.get_wlist(l); } + watch_list const & simplifier::get_wlist(literal l) const { return s.get_wlist(l); } - inline bool simplifier::is_external(bool_var v) const { return s.is_external(v); } + bool simplifier::is_external(bool_var v) const { + return + s.is_assumption(v) || + (s.is_external(v) && s.is_incremental()) || + (s.is_external(v) && s.m_ext && + (!m_ext_use_list.get(literal(v, false)).empty() || + !m_ext_use_list.get(literal(v, true)).empty())); + } inline bool simplifier::was_eliminated(bool_var v) const { return s.was_eliminated(v); } @@ -80,24 +92,41 @@ namespace sat { inline void simplifier::checkpoint() { s.checkpoint(); } + bool simplifier::single_threaded() const { return s.m_config.m_num_threads == 1; } + + bool simplifier::bce_enabled_base() const { + return + !m_incremental_mode && !s.tracking_assumptions() && + !m_learned_in_use_lists && m_num_calls >= m_bce_delay && single_threaded(); + } + + bool simplifier::ate_enabled() const { return m_num_calls >= m_bce_delay && m_ate; } + bool simplifier::bce_enabled() const { return bce_enabled_base() && (m_bce || m_bce_at == m_num_calls || m_acce || m_abce || m_cce); } + bool simplifier::acce_enabled() const { return bce_enabled_base() && m_acce; } + bool simplifier::cce_enabled() const { return bce_enabled_base() && (m_cce || m_acce); } + bool simplifier::abce_enabled() const { return bce_enabled_base() && m_abce; } + bool simplifier::bca_enabled() const { return bce_enabled_base() && m_bca; } + bool simplifier::elim_vars_bdd_enabled() const { + return !m_incremental_mode && !s.tracking_assumptions() && m_elim_vars_bdd && m_num_calls >= m_elim_vars_bdd_delay && single_threaded(); + } + bool simplifier::elim_vars_enabled() const { + return !m_incremental_mode && !s.tracking_assumptions() && m_elim_vars && single_threaded(); + } + void simplifier::register_clauses(clause_vector & cs) { std::stable_sort(cs.begin(), cs.end(), size_lt()); - clause_vector::iterator it = cs.begin(); - clause_vector::iterator end = cs.end(); - for (; it != end; ++it) { - clause & c = *(*it); - if (!c.frozen()) { - m_use_list.insert(c); - if (c.strengthened()) - m_sub_todo.insert(c); + for (clause* c : cs) { + if (!c->frozen()) { + m_use_list.insert(*c); + if (c->strengthened()) + m_sub_todo.insert(*c); } } } inline void simplifier::remove_clause_core(clause & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) - insert_elim_todo(c[i].var()); + for (literal l : c) + insert_elim_todo(l.var()); m_sub_todo.erase(c); c.set_removed(true); TRACE("resolution_bug", tout << "del_clause: " << c << "\n";); @@ -114,10 +143,17 @@ namespace sat { m_use_list.erase(c, l); } - inline void simplifier::remove_bin_clause_half(literal l1, literal l2, bool learned) { - SASSERT(s.get_wlist(~l1).contains(watched(l2, learned))); - s.get_wlist(~l1).erase(watched(l2, learned)); - m_sub_bin_todo.erase(bin_clause(l1, l2, learned)); + inline void simplifier::set_learned(clause & c) { + m_need_cleanup = true; + s.set_learned(c, true); + m_use_list.block(c); + } + + inline void simplifier::set_learned(literal l1, literal l2) { + m_sub_bin_todo.erase(bin_clause(l1, l2, false)); + m_sub_bin_todo.erase(bin_clause(l2, l1, false)); + m_sub_bin_todo.push_back(bin_clause(l1, l2, true)); + m_sub_bin_todo.push_back(bin_clause(l2, l1, true)); } void simplifier::init_visited() { @@ -133,6 +169,7 @@ namespace sat { m_visited.finalize(); m_bs_cs.finalize(); m_bs_ls.finalize(); + m_ext_use_list.finalize(); } void simplifier::initialize() { @@ -140,6 +177,7 @@ namespace sat { s.m_cleaner(true); m_last_sub_trail_sz = s.m_trail.size(); m_use_list.init(s.num_vars()); + if (s.m_ext) s.m_ext->init_use_list(m_ext_use_list); m_sub_todo.reset(); m_sub_bin_todo.reset(); m_elim_todo.reset(); @@ -149,78 +187,73 @@ namespace sat { } void simplifier::operator()(bool learned) { + if (s.inconsistent()) return; - if (!m_subsumption && !m_elim_blocked_clauses && !m_resolution) + if (!m_subsumption && !bce_enabled() && !bca_enabled() && !elim_vars_enabled()) return; - - // solver::scoped_disable_checkpoint _scoped_disable_checkpoint(s); initialize(); CASSERT("sat_solver", s.check_invariant()); - TRACE("before_simplifier", s.display(tout);); + TRACE("sat_simplifier", s.display(tout);); - m_sub_todo.reset(); - m_sub_bin_todo.reset(); s.m_cleaner(true); - m_last_sub_trail_sz = s.m_trail.size(); TRACE("after_cleanup", s.display(tout);); CASSERT("sat_solver", s.check_invariant()); m_need_cleanup = false; m_use_list.init(s.num_vars()); - m_learned_in_use_lists = false; + m_learned_in_use_lists = learned; if (learned) { register_clauses(s.m_learned); - m_learned_in_use_lists = true; } register_clauses(s.m_clauses); - if (!learned && (m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls)) + if (!learned && (bce_enabled() || bca_enabled() || ate_enabled())) { elim_blocked_clauses(); + } - if (!learned) + if (!learned) { m_num_calls++; + } m_sub_counter = m_subsumption_limit; m_elim_counter = m_res_limit; m_old_num_elim_vars = m_num_elim_vars; - scoped_finalize _scoped_finalize(*this); + for (bool_var v = 0; v < s.num_vars(); ++v) { + if (!s.m_eliminated[v] && !is_external(v)) { + insert_elim_todo(v); + } + } do { if (m_subsumption) subsume(); if (s.inconsistent()) return; - if (!learned && m_resolution) - elim_vars(); + if (!learned && elim_vars_enabled()) + elim_vars(); if (s.inconsistent()) return; if (!m_subsumption || m_sub_counter < 0) break; } while (!m_sub_todo.empty()); - } - - void simplifier::scoped_finalize_fn() { bool vars_eliminated = m_num_elim_vars > m_old_num_elim_vars; - if (m_need_cleanup) { + if (m_need_cleanup || vars_eliminated) { TRACE("after_simplifier", tout << "cleanning watches...\n";); cleanup_watches(); + move_clauses(s.m_learned, true); + move_clauses(s.m_clauses, false); cleanup_clauses(s.m_learned, true, vars_eliminated, m_learned_in_use_lists); cleanup_clauses(s.m_clauses, false, vars_eliminated, true); } - else { - TRACE("after_simplifier", tout << "skipping cleanup...\n";); - if (vars_eliminated) { - // must remove learned clauses with eliminated variables - cleanup_clauses(s.m_learned, true, true, m_learned_in_use_lists); - } - } + CASSERT("sat_solver", s.check_invariant()); - TRACE("after_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); + TRACE("sat_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); + finalize(); } @@ -228,10 +261,7 @@ namespace sat { \brief Eliminate all ternary and clause watches. */ void simplifier::cleanup_watches() { - vector::iterator it = s.m_watches.begin(); - vector::iterator end = s.m_watches.end(); - for (; it != end; ++it) { - watch_list & wlist = *it; + for (watch_list& wlist : s.m_watches) { watch_list::iterator it2 = wlist.begin(); watch_list::iterator itprev = it2; watch_list::iterator end2 = wlist.end(); @@ -247,10 +277,33 @@ namespace sat { break; } } - wlist.set_end(itprev); + wlist.set_end(itprev); } } + void simplifier::move_clauses(clause_vector& cs, bool learned) { + clause_vector::iterator it = cs.begin(); + clause_vector::iterator it2 = it; + clause_vector::iterator end = cs.end(); + unsigned nm = 0; + for (; it != end; ++it) { + clause & c = *(*it); + if (learned && !c.is_learned()) { + s.m_clauses.push_back(&c); + ++nm; + } + else if (!learned && c.is_learned()) { + s.m_learned.push_back(&c); + ++nm; + } + else { + *it2 = *it; + ++it2; + } + } + cs.set_end(it2); + } + void simplifier::cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists) { TRACE("sat", tout << "cleanup_clauses\n";); clause_vector::iterator it = cs.begin(); @@ -258,6 +311,7 @@ namespace sat { clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); + VERIFY(learned == c.is_learned()); if (c.was_removed()) { s.del_clause(c); continue; @@ -285,6 +339,7 @@ namespace sat { s.set_conflict(justification()); for (; it != end; ++it, ++it2) { *it2 = *it; + ++it2; } break; } @@ -298,48 +353,36 @@ namespace sat { s.del_clause(c); continue; } - // clause became a problem clause - if (learned && !c.is_learned()) { - SASSERT(!c.frozen()); - s.m_clauses.push_back(&c); - continue; - } *it2 = *it; it2++; if (!c.frozen()) { - if (sz == 3) - s.attach_ter_clause(c); - else - s.attach_nary_clause(c); + s.attach_clause(c); + if (s.m_config.m_drat) { + s.m_drat.add(c, true); + } } } cs.set_end(it2); } - void simplifier::mark_all_but(clause const & c, literal l) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - if (c[i] == l) - continue; - mark_visited(c[i]); - } + void simplifier::mark_all_but(clause const & c, literal l1) { + for (literal l2 : c) + if (l2 != l1) + mark_visited(l2); } void simplifier::unmark_all(clause const & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) - unmark_visited(c[i]); + for (literal l : c) + unmark_visited(l); } /** \brief Return the variable in c with the minimal number positive+negative occurrences. */ bool_var simplifier::get_min_occ_var(clause const & c) const { - literal l_best = c[0]; - unsigned best = m_use_list.get(l_best).size() + m_use_list.get(~l_best).size(); - unsigned sz = c.size(); - for (unsigned i = 1; i < sz; i++) { - literal l = c[i]; + literal l_best = null_literal; + unsigned best = UINT_MAX; + for (literal l : c) { unsigned num = m_use_list.get(l).size() + m_use_list.get(~l).size(); if (num < best) { l_best = l; @@ -355,17 +398,15 @@ namespace sat { Otherwise return false */ bool simplifier::subsumes1(clause const & c1, clause const & c2, literal & l) { - unsigned sz2 = c2.size(); - for (unsigned i = 0; i < sz2; i++) - mark_visited(c2[i]); + for (literal lit : c2) + mark_visited(lit); bool r = true; l = null_literal; - unsigned sz1 = c1.size(); - for (unsigned i = 0; i < sz1; i++) { - if (!is_marked(c1[i])) { - if (l == null_literal && is_marked(~c1[i])) { - l = ~c1[i]; + for (literal lit : c1) { + if (!is_marked(lit)) { + if (l == null_literal && is_marked(~lit)) { + l = ~lit; } else { l = null_literal; @@ -375,8 +416,8 @@ namespace sat { } } - for (unsigned i = 0; i < sz2; i++) - unmark_visited(c2[i]); + for (literal lit : c2) + unmark_visited(lit); return r; } @@ -387,8 +428,7 @@ namespace sat { void simplifier::collect_subsumed1_core(clause const & c1, clause_vector & out, literal_vector & out_lits, literal target) { clause_use_list const & cs = m_use_list.get(target); - clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { + for (auto it = cs.mk_iterator(); !it.at_end(); it.next()) { clause & c2 = it.curr(); CTRACE("resolution_bug", c2.was_removed(), tout << "clause has been removed:\n" << c2 << "\n";); SASSERT(!c2.was_removed()); @@ -402,7 +442,6 @@ namespace sat { out_lits.push_back(l); } } - it.next(); } } @@ -416,7 +455,7 @@ namespace sat { } /** - \brief Perform backward subsumption and self-subsumption resolution using c. + \brief Perform backward subsumption and self-subsumption resolution using c1. */ void simplifier::back_subsumption1(clause & c1) { m_bs_cs.reset(); @@ -431,7 +470,7 @@ namespace sat { if (!c2.was_removed() && *l_it == null_literal) { // c2 was subsumed if (c1.is_learned() && !c2.is_learned()) - c1.unset_learned(); + s.set_learned(c1, false); TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";); remove_clause(c2); m_num_subsumed++; @@ -458,11 +497,9 @@ namespace sat { \brief Return the literal in c with the minimal number of occurrences. */ literal simplifier::get_min_occ_var0(clause const & c) const { - literal l_best = c[0]; - unsigned best = m_use_list.get(l_best).size(); - unsigned sz = c.size(); - for (unsigned i = 1; i < sz; i++) { - literal l = c[i]; + literal l_best = null_literal; + unsigned best = UINT_MAX; + for (literal l : c) { unsigned num = m_use_list.get(l).size(); if (num < best) { l_best = l; @@ -477,21 +514,19 @@ namespace sat { Otherwise return false */ bool simplifier::subsumes0(clause const & c1, clause const & c2) { - unsigned sz2 = c2.size(); - for (unsigned i = 0; i < sz2; i++) - mark_visited(c2[i]); + for (literal l : c2) + mark_visited(l); bool r = true; - unsigned sz1 = c1.size(); - for (unsigned i = 0; i < sz1; i++) { - if (!is_marked(c1[i])) { + for (literal l : c1) { + if (!is_marked(l)) { r = false; break; } } - for (unsigned i = 0; i < sz2; i++) - unmark_visited(c2[i]); + for (literal l : c2) + unmark_visited(l); return r; } @@ -502,7 +537,7 @@ namespace sat { void simplifier::collect_subsumed0_core(clause const & c1, clause_vector & out, literal target) { clause_use_list const & cs = m_use_list.get(target); clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { + for (; !it.at_end(); it.next()) { clause & c2 = it.curr(); SASSERT(!c2.was_removed()); if (&c2 != &c1 && @@ -513,7 +548,6 @@ namespace sat { out.push_back(&c2); } } - it.next(); } } @@ -532,13 +566,11 @@ namespace sat { void simplifier::back_subsumption0(clause & c1) { m_bs_cs.reset(); collect_subsumed0(c1, m_bs_cs); - clause_vector::iterator it = m_bs_cs.begin(); - clause_vector::iterator end = m_bs_cs.end(); - for (; it != end; ++it) { - clause & c2 = *(*it); + for (clause* cp : m_bs_cs) { + clause & c2 = *cp; // c2 was subsumed if (c1.is_learned() && !c2.is_learned()) - c1.unset_learned(); + s.set_learned(c1, false); TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";); remove_clause(c2); m_num_subsumed++; @@ -558,7 +590,9 @@ namespace sat { literal l = c[i]; switch (value(l)) { case l_undef: - c[j] = l; + if (i != j) { + std::swap(c[j], c[i]); + } j++; break; case l_false: @@ -571,12 +605,18 @@ namespace sat { break; case l_true: r = true; - c[j] = l; + if (i != j) { + std::swap(c[j], c[i]); + } j++; break; } } - c.shrink(j); + if (j < sz) { + if (s.m_config.m_drat) s.m_drat.del(c); + c.shrink(j); + if (s.m_config.m_drat) s.m_drat.add(c, true); + } return r; } @@ -592,7 +632,9 @@ namespace sat { literal l = c[i]; switch (value(l)) { case l_undef: - c[j] = l; + if (i != j) { + std::swap(c[j], c[i]); + } j++; break; case l_false: @@ -601,7 +643,7 @@ namespace sat { return true; } } - c.shrink(j); + c.shrink(j); return false; } @@ -614,38 +656,28 @@ namespace sat { unsigned new_trail_sz = s.m_trail.size(); for (unsigned i = old_trail_sz; i < new_trail_sz; i++) { literal l = s.m_trail[i]; - { - // put clauses with literals assigned to false back into todo-list - clause_use_list & cs = m_use_list.get(~l); - clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { - clause & c = it.curr(); - it.next(); - m_sub_todo.insert(c); - } + // put clauses with literals assigned to false back into todo-list + for (auto it = m_use_list.get(~l).mk_iterator(); !it.at_end(); it.next()) { + m_sub_todo.insert(it.curr()); } - { - // erase satisfied clauses - clause_use_list & cs = m_use_list.get(l); - { - clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { - clause & c = it.curr(); - it.next(); - remove_clause(c, l); - } - } - cs.reset(); + clause_use_list& cs = m_use_list.get(l); + for (auto it = cs.mk_iterator(); !it.at_end(); ) { + clause & c = it.curr(); + it.next(); + remove_clause(c, l); } + cs.reset(); } } void simplifier::elim_lit(clause & c, literal l) { - TRACE("elim_lit", tout << "processing: " << c << "\n";); + TRACE("elim_lit", tout << "processing: " << l << " @ " << c << "\n";); m_need_cleanup = true; m_num_elim_lits++; insert_elim_todo(l.var()); c.elim(l); + if (s.m_config.m_drat) s.m_drat.add(c, true); + // if (s.m_config.m_drat) s.m_drat.del(c0); // can delete previous version clause_use_list & occurs = m_use_list.get(l); occurs.erase_not_removed(c); m_sub_counter -= occurs.size()/2; @@ -689,7 +721,7 @@ namespace sat { // should not traverse wlist using iterators, since back_subsumption1 may add new binary clauses there for (unsigned j = 0; j < wlist.size(); j++) { watched w = wlist[j]; - if (w.is_binary_clause()) { + if (w.is_binary_non_learned_clause()) { literal l2 = w.get_literal(); if (l.index() < l2.index()) { m_dummy.set(l, l2, w.is_learned()); @@ -697,9 +729,9 @@ namespace sat { back_subsumption1(c); if (w.is_learned() && !c.is_learned()) { SASSERT(wlist[j] == w); - TRACE("mark_not_learned_bug", + TRACE("set_not_learned_bug", tout << "marking as not learned: " << l2 << " " << wlist[j].is_learned() << "\n";); - wlist[j].mark_not_learned(); + wlist[j].set_learned(false); mark_as_not_learned_core(get_wlist(~l2), l); } if (s.inconsistent()) @@ -714,11 +746,9 @@ namespace sat { } void simplifier::mark_as_not_learned_core(watch_list & wlist, literal l2) { - watch_list::iterator it = wlist.begin(); - watch_list::iterator end = wlist.end(); - for (; it != end; ++it) { - if (it->is_binary_clause() && it->get_literal() == l2 && it->is_learned()) { - it->mark_not_learned(); + for (watched & w : wlist) { + if (w.is_binary_clause() && w.get_literal() == l2 && w.is_learned()) { + w.set_learned(false); return; } } @@ -745,34 +775,31 @@ namespace sat { \brief Eliminate duplicated binary clauses. */ void simplifier::elim_dup_bins() { - vector::iterator it = s.m_watches.begin(); - vector::iterator end = s.m_watches.end(); #ifdef _TRACE unsigned l_idx = 0; #endif unsigned elim = 0; - for (; it != end; ++it) { + for (watch_list & wlist : s.m_watches) { checkpoint(); - watch_list & wlist = *it; std::stable_sort(wlist.begin(), wlist.end(), bin_lt()); literal last_lit = null_literal; - watch_list::iterator it2 = wlist.begin(); - watch_list::iterator itprev = it2; - watch_list::iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (!it2->is_binary_clause()) { - *itprev = *it2; + watch_list::iterator it = wlist.begin(); + watch_list::iterator itprev = it; + watch_list::iterator end = wlist.end(); + for (; it != end; ++it) { + if (!it->is_binary_clause()) { + *itprev = *it; itprev++; continue; } - if (it2->get_literal() == last_lit) { + if (it->get_literal() == last_lit) { TRACE("subsumption", tout << "eliminating: " << ~to_literal(l_idx) - << " " << it2->get_literal() << "\n";); + << " " << it->get_literal() << "\n";); elim++; } else { - last_lit = it2->get_literal(); - *itprev = *it2; + last_lit = it->get_literal(); + *itprev = *it; itprev++; } } @@ -840,6 +867,7 @@ namespace sat { break; clause & c = m_sub_todo.erase(); + c.unmark_strengthened(); m_sub_counter--; TRACE("subsumption", tout << "next: " << c << "\n";); @@ -888,6 +916,47 @@ namespace sat { } }; + class clause_ante { + bool m_from_ri; + literal m_lit1; + literal m_lit2; + clause* m_clause; + public: + clause_ante(): + m_from_ri(false), m_lit1(null_literal), m_lit2(null_literal), m_clause(nullptr) {} + clause_ante(literal l1, bool from_ri): + m_from_ri(from_ri), m_lit1(l1), m_lit2(null_literal), m_clause(nullptr) {} + clause_ante(literal l1, literal l2): + m_from_ri(false), m_lit1(l1), m_lit2(l2), m_clause(nullptr) {} + clause_ante(clause& c): + m_from_ri(false), m_lit1(null_literal), m_lit2(null_literal), m_clause(&c) {} + literal lit1() const { return m_lit1; } + literal lit2() const { return m_lit2; } + clause* cls() const { return m_clause; } + bool from_ri() const { return m_from_ri; } + bool operator==(clause_ante const& a) const { + return a.m_lit1 == m_lit1 && a.m_lit2 == m_lit2 && a.m_clause == m_clause; + } + std::ostream& display(std::ostream& out, literal lit) const { + if (cls()) { + out << *cls() << " "; + } + else { + out << "(" << ~lit; + } + if (lit1() != null_literal) { + out << " " << lit1(); + } + if (lit2() != null_literal) { + out << " " << lit2(); + } + if (!cls()) out << ")"; + if (from_ri()) out << "ri"; + out << "\n"; + return out; + } + }; + class queue { heap m_queue; public: @@ -907,148 +976,698 @@ namespace sat { } literal next() { SASSERT(!empty()); return to_literal(m_queue.erase_min()); } bool empty() const { return m_queue.empty(); } + void reset() { m_queue.reset(); } }; simplifier & s; int m_counter; - model_converter & mc; + model_converter & m_mc; queue m_queue; - clause_vector m_to_remove; + + literal_vector m_covered_clause; // covered clause + svector m_covered_antecedent; // explainations for literals in covered clause + literal_vector m_intersection; // current resolution intersection + literal_vector m_tautology; // literals that are used in blocking tautology + literal_vector m_new_intersection; + svector m_in_intersection; + unsigned m_ala_qhead; + clause_wrapper m_clause; blocked_clause_elim(simplifier & _s, unsigned limit, model_converter & _mc, use_list & l, vector & wlist): s(_s), m_counter(limit), - mc(_mc), - m_queue(l, wlist) { + m_mc(_mc), + m_queue(l, wlist), + m_clause(null_literal, null_literal) { + m_in_intersection.resize(s.s.num_vars() * 2, false); } void insert(literal l) { + VERIFY(process_var(l.var())); m_queue.insert(l); } bool process_var(bool_var v) { - return !s.is_external(v) && !s.was_eliminated(v); + return !s.s.is_assumption(v) && !s.was_eliminated(v) && !s.is_external(v) && s.value(v) == l_undef; } - void operator()(unsigned num_vars) { + enum elim_type { + bce_t, + cce_t, + acce_t, + abce_t, + ate_t, + no_t + }; + + void operator()() { + if (s.acce_enabled()) { + cce(); + } + if (s.ate_enabled() && !s.abce_enabled() && !s.acce_enabled()) { + cce(); + } + if (s.cce_enabled() && !s.acce_enabled()) { + cce(); + } + if (s.abce_enabled() && !s.acce_enabled()) { + cce(); + } + if (s.bce_enabled() && !s.cce_enabled() && !s.abce_enabled()) { + cce(); + } + if (s.bca_enabled()) { + bca(); + } + } + + void insert_queue() { + unsigned num_vars = s.s.num_vars(); for (bool_var v = 0; v < num_vars; v++) { if (process_var(v)) { insert(literal(v, false)); insert(literal(v, true)); } } - while (!m_queue.empty()) { + } + + void reset_intersection() { + for (literal l : m_intersection) m_in_intersection[l.index()] = false; + m_intersection.reset(); + } + + void add_intersection(literal lit) { + m_intersection.push_back(lit); + m_in_intersection[lit.index()] = true; + } + + // + // Resolve intersection + // Find literals that are in the intersection of all resolvents with l. + // + bool resolution_intersection(literal l, bool adding) { + unsigned tsz = m_tautology.size(); + reset_intersection(); + if (!process_var(l.var())) return false; + bool first = true; + VERIFY(s.value(l) == l_undef); + for (watched & w : s.get_wlist(l)) { + // when adding a blocked clause, then all non-learned clauses have to be considered for the + // resolution intersection. + bool process_bin = adding ? w.is_binary_clause() : w.is_binary_non_learned_clause(); + if (process_bin) { + literal lit = w.get_literal(); + if (s.is_marked(~lit) && lit != ~l) { + m_tautology.push_back(~lit); + continue; // tautology + } + if (!first || s.is_marked(lit)) { + reset_intersection(); + m_tautology.shrink(tsz); + return false; // intersection is empty or does not add anything new. + } + first = false; + SASSERT(m_intersection.empty()); + add_intersection(lit); + } + } + clause_use_list & neg_occs = s.m_use_list.get(~l); + for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { + bool tautology = false; + clause & c = it.curr(); + if (c.is_learned() && !adding) continue; + if (c.was_removed()) continue; + for (literal lit : c) { + if (s.is_marked(~lit) && lit != ~l) { + m_tautology.push_back(~lit); + tautology = true; + break; + } + } + if (!tautology) { + if (first) { + for (literal lit : c) + if (lit != ~l && !s.is_marked(lit)) + add_intersection(lit); + first = false; + } + else { + m_new_intersection.reset(); + for (literal lit : c) + if (m_in_intersection[lit.index()]) + m_new_intersection.push_back(lit); + reset_intersection(); + for (literal lit : m_new_intersection) + add_intersection(lit); + } + if (m_intersection.empty()) { + m_tautology.shrink(tsz); + return false; + } + } + } + // remove tautology literals if literal has no resolution intersection + if (m_intersection.empty() && !first) { + m_tautology.shrink(tsz); + } + // if (first) IF_VERBOSE(0, verbose_stream() << "taut: " << m_tautology << "\n";); + return first; + } + + bool check_abce_tautology(literal l) { + unsigned tsz = m_tautology.size(); + if (!process_var(l.var())) return false; + for (watched & w : s.get_wlist(l)) { + if (w.is_binary_non_learned_clause()) { + literal lit = w.get_literal(); + VERIFY(lit != ~l); + if (!s.is_marked(~lit)) { + m_tautology.shrink(tsz); + return false; + } + m_tautology.push_back(~lit); + } + } + clause_use_list & neg_occs = s.m_use_list.get(~l); + for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { + clause & c = it.curr(); + if (c.is_learned() || c.was_removed()) continue; + bool tautology = false; + for (literal lit : c) { + if (s.is_marked(~lit) && lit != ~l) { + tautology = true; + m_tautology.push_back(~lit); + break; + } + } + if (!tautology) { + m_tautology.shrink(tsz); + return false; + } + } + return true; + } + + bool revisit_binary(literal l1, literal l2) const { + return m_clause.is_binary() && + ((m_clause[0] == l1 && m_clause[1] == l2) || + (m_clause[0] == l2 && m_clause[1] == l1)); + } + + bool revisit_clause(clause const& c) const { + return !m_clause.is_binary() && (m_clause.get_clause() == &c); + } + + /** + \brief idx is the index of the blocked literal. + m_tautology contains literals that were used to establish that the current members of m_covered_clause is blocked. + This routine removes literals that were not relevant to establishing it was blocked. + + It has a bug: literals that are used to prune tautologies during resolution intersection should be included + in the dependencies. They may get used in some RI prunings and then they have to be included to avoid flipping + RI literals. + */ + void minimize_covered_clause(unsigned idx) { + // IF_VERBOSE(0, verbose_stream() << "minimize: " << m_covered_clause + // << " @ " << idx << "\n" << "tautology: " << m_tautology << "\n";); + literal _blocked = m_covered_clause[idx]; + for (literal l : m_tautology) VERIFY(s.is_marked(l)); + for (literal l : m_covered_clause) s.unmark_visited(l); + for (literal l : m_tautology) s.mark_visited(l); + s.mark_visited(m_covered_clause[idx]); + for (unsigned i = 0; i < m_covered_clause.size(); ++i) { + literal lit = m_covered_clause[i]; + if (m_covered_antecedent[i] == clause_ante()) s.mark_visited(lit); + if (s.is_marked(lit)) idx = i; + } + if (false && _blocked.var() == 8074) { + IF_VERBOSE(0, verbose_stream() << "covered: " << m_covered_clause << "\n"; + verbose_stream() << "tautology: " << m_tautology << "\n"; + verbose_stream() << "index: " << idx << "\n"; + for (unsigned i = idx; i > 0; --i) { + m_covered_antecedent[i].display(verbose_stream(), m_covered_clause[i]); + }); + } + for (unsigned i = idx; i > 0; --i) { + literal lit = m_covered_clause[i]; + //s.mark_visited(lit); + //continue; + if (!s.is_marked(lit)) continue; + clause_ante const& ante = m_covered_antecedent[i]; + if (ante.cls()) { + for (literal l : *ante.cls()) { + if (l != ~lit) s.mark_visited(l); + } + } + if (ante.lit1() != null_literal) { + s.mark_visited(ante.lit1()); + } + if (ante.lit2() != null_literal) { + s.mark_visited(ante.lit2()); + } + } + unsigned j = 0; + literal blocked = null_literal; + for (unsigned i = 0; i <= idx; ++i) { + literal lit = m_covered_clause[i]; + if (s.is_marked(lit)) { + // + // Record that the resolving literal for + // resolution intersection can be flipped. + // + clause_ante const& ante = m_covered_antecedent[i]; + if (ante.from_ri() && blocked != ante.lit1()) { + blocked = ante.lit1(); + VERIFY(s.value(blocked) == l_undef); + m_mc.stackv().push_back(std::make_pair(j, blocked)); + } + m_covered_clause[j++] = lit; + s.unmark_visited(lit); + } + } + for (literal l : m_covered_clause) VERIFY(!s.is_marked(l)); + for (bool_var v = 0; v < s.s.num_vars(); ++v) VERIFY(!s.is_marked(literal(v, true)) && !s.is_marked(literal(v, false))); + + // unsigned sz0 = m_covered_clause.size(); + m_covered_clause.resize(j); + VERIFY(j >= m_clause.size()); + if (false && _blocked.var() == 16774) { + IF_VERBOSE(0, verbose_stream() << "covered: " << m_covered_clause << "\n"); + } + // IF_VERBOSE(0, verbose_stream() << "reduced from size " << sz0 << " to " << m_covered_clause << "\n" << m_clause << "\n";); + } + + /* + * C \/ l l \/ lit + * ------------------- + * C \/ l \/ ~lit + * + * C \/ lit \/ l l \/ lit + * ------------------------ + * l \/ lit C \/ lit \/ l can be removed + * + * C \/ l1 \/ ... \/ lk l1 \/ ... \/ lk \/ lit + * ----------------------------------------------- + * C \/ l1 \/ ... \/ lk \/ ~lit + * unless C contains lit, and it is a tautology. + */ + bool add_ala() { + for (; m_ala_qhead < m_covered_clause.size(); ++m_ala_qhead) { + literal l = m_covered_clause[m_ala_qhead]; + for (watched & w : s.get_wlist(~l)) { + if (w.is_binary_non_learned_clause()) { + literal lit = w.get_literal(); + if (revisit_binary(l, lit)) continue; + if (s.is_marked(lit)) { + return true; + } + if (!s.is_marked(~lit)) { + // if (m_covered_clause[0].var() == 10219) IF_VERBOSE(0, verbose_stream() << "ala: " << l << " " << lit << "\n"); + m_covered_clause.push_back(~lit); + m_covered_antecedent.push_back(clause_ante(l, false)); + s.mark_visited(~lit); + } + } + } + clause_use_list & pos_occs = s.m_use_list.get(l); + clause_use_list::iterator it = pos_occs.mk_iterator(); + for (; !it.at_end(); it.next()) { + clause & c = it.curr(); + if (c.is_learned() || c.was_removed()) continue; + if (revisit_clause(c)) continue; + literal lit1 = null_literal; + bool ok = true; + for (literal lit : c) { + if (lit == l) continue; + if (s.is_marked(lit)) continue; + if (!s.is_marked(~lit) && lit1 == null_literal) { + lit1 = lit; + } + else { + ok = false; + break; + } + } + if (!ok) continue; + if (lit1 == null_literal) { + return true; + } + // if (m_covered_clause[0].var() == 10219) IF_VERBOSE(0, verbose_stream() << "ala: " << c << " " << lit1 << "\n"); + m_covered_clause.push_back(~lit1); + m_covered_antecedent.push_back(clause_ante(c)); + s.mark_visited(~lit1); + } + } + return false; + } + + + /* + * C \/ l ~l \/ lit \/ D_i for i = 1...N all the clauses that have ~l + * ------------------------- + * C \/ l \/ lit + * + * + */ + bool add_cla(literal& blocked) { + for (unsigned i = 0; i < m_covered_clause.size(); ++i) { + literal lit = m_covered_clause[i]; + if (resolution_intersection(lit, false)) { + blocked = m_covered_clause[i]; + minimize_covered_clause(i); + return true; + } + for (literal l : m_intersection) { + if (!s.is_marked(l)) { + s.mark_visited(l); + m_covered_clause.push_back(l); + m_covered_antecedent.push_back(clause_ante(lit, true)); + } + } + } + return false; + } + + bool above_threshold(unsigned sz0) const { + // if (sz0 * 400 < m_covered_clause.size()) IF_VERBOSE(0, verbose_stream() << "above threshold " << sz0 << " " << m_covered_clause.size() << "\n";); + return sz0 * 400 < m_covered_clause.size(); + } + + void reset_mark() { + for (literal l : m_covered_clause) s.unmark_visited(l); + } + + template + elim_type cce(literal& blocked, model_converter::kind& k) { + bool first = true; + unsigned sz = 0, sz0 = m_covered_clause.size(); + for (literal l : m_covered_clause) s.mark_visited(l); + shuffle(m_covered_clause.size(), m_covered_clause.c_ptr(), s.s.m_rand); + m_tautology.reset(); + m_mc.stackv().reset(); + m_ala_qhead = 0; + + switch (et) { + case cce_t: + k = model_converter::CCE; + break; + case acce_t: + k = model_converter::ACCE; + break; + default: + k = model_converter::BCE; + break; + } + + /* + * For blocked clause elimination with asymmetric literal addition (ABCE) + * it suffices to check if one of the original + * literals in the clause is blocked modulo the additional literals added to the clause. + * So we record sz0, the original set of literals in the clause, mark additional literals, + * and then check if any of the first sz0 literals are blocked. + */ + + if (et == ate_t) { + bool ala = add_ala(); + reset_mark(); + m_covered_clause.shrink(sz0); + return ala ? ate_t : no_t; + } + + while (m_covered_clause.size() > sz && !above_threshold(sz0)) { + + if ((et == abce_t || et == acce_t) && add_ala()) { + reset_mark(); + if (first) { + m_covered_clause.shrink(sz0); + } + else { + /* + * tautology depends on resolution intersection. + * literals used for resolution intersection may have to be flipped. + */ + for (literal l : m_covered_clause) { + m_tautology.push_back(l); + s.mark_visited(l); + } + minimize_covered_clause(m_covered_clause.size()-1); + } + return ate_t; + } + + if (first) { + for (unsigned i = 0; i < sz0; ++i) { + if (check_abce_tautology(m_covered_clause[i])) { + blocked = m_covered_clause[i]; + reset_mark(); +#if 0 + if (sz0 == 3 && blocked.var() == 10219) { + IF_VERBOSE(0, verbose_stream() << "abce: " << m_covered_clause << "\n"; + for (literal l : m_covered_clause) verbose_stream() << s.value(l) << "\n"; + ); + literal l = blocked; + clause_use_list & neg_occs = s.m_use_list.get(~l); + for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { + clause & c = it.curr(); + IF_VERBOSE(0, verbose_stream() << c << "\n"); + } + } +#endif + m_covered_clause.shrink(sz0); + if (et == bce_t) return bce_t; + k = model_converter::ABCE; + return abce_t; + } + } + } + first = false; + + if (et == abce_t || et == bce_t) { + break; + } + + /* + * Add resolution intersection while checking if the clause becomes a tautology. + */ + sz = m_covered_clause.size(); + if ((et == cce_t || et == acce_t) && add_cla(blocked)) { + reset_mark(); + return et; + } + } + reset_mark(); + return no_t; + } + + // perform covered clause elimination. + // first extract the covered literal addition (CLA). + // then check whether the CLA is blocked. + template + elim_type cce(clause& c, literal& blocked, model_converter::kind& k) { + m_clause = clause_wrapper(c); + m_covered_clause.reset(); + m_covered_antecedent.reset(); + for (literal l : c) { + m_covered_clause.push_back(l); + m_covered_antecedent.push_back(clause_ante()); + } + return cce(blocked, k); + } + + template + elim_type cce(literal l1, literal l2, literal& blocked, model_converter::kind& k) { + m_clause = clause_wrapper(l1, l2); + m_covered_clause.reset(); + m_covered_antecedent.reset(); + m_covered_clause.push_back(l1); + m_covered_clause.push_back(l2); + m_covered_antecedent.push_back(clause_ante()); + m_covered_antecedent.push_back(clause_ante()); + return cce(blocked, k); + } + + template + void cce() { + insert_queue(); + cce_clauses(); + cce_binary(); + } + + template + void cce_binary() { + while (!m_queue.empty() && m_counter >= 0) { s.checkpoint(); - if (m_counter < 0) - return; - literal l = m_queue.next(); - process(l); + process_cce_binary(m_queue.next()); } } - void process(literal l) { - TRACE("blocked_clause", tout << "processing: " << l << "\n";); - model_converter::entry * new_entry = nullptr; - if (!process_var(l.var())) { + template + void process_cce_binary(literal l) { + literal blocked = null_literal; + watch_list & wlist = s.get_wlist(~l); + m_counter -= wlist.size(); + model_converter::kind k; + for (watched & w : wlist) { + if (!w.is_binary_non_learned_clause()) continue; + if (!select_clause(2)) continue; + literal l2 = w.get_literal(); + elim_type r = cce(l, l2, blocked, k); + inc_bc(r); + switch (r) { + case ate_t: + w.set_learned(true); + s.s.set_learned1(l2, l, true); + m_mc.add_ate(m_covered_clause); + break; + case no_t: + break; + default: + w.set_learned(true); + s.s.set_learned1(l2, l, true); + block_covered_binary(w, l, blocked, k); + break; + } + } + } + + template + void cce_clauses() { + literal blocked; + model_converter::kind k; + for (clause* cp : s.s.m_clauses) { + clause& c = *cp; + if (c.was_removed() || c.is_learned()) continue; + if (!select_clause(c.size())) continue; + elim_type r = cce(c, blocked, k); + inc_bc(r); + switch (r) { + case ate_t: + m_mc.add_ate(m_covered_clause); + s.set_learned(c); + break; + case no_t: + break; + default: + block_covered_clause(c, blocked, k); + s.set_learned(c); + break; + } + } + } + + void inc_bc(elim_type et) { + switch (et) { + case cce_t: s.m_num_cce++; break; + case acce_t: s.m_num_acce++; break; + case abce_t: s.m_num_abce++; break; + case ate_t: s.m_num_ate++; break; + case bce_t: s.m_num_bce++; break; + default: break; + } + } + + // select 25% of clauses size 2, 3 + // always try to remove larger clauses. + template + bool select_clause(unsigned sz) { + return (sz > 3) || s.s.m_rand(4) == 0; + } + + void block_covered_clause(clause& c, literal l, model_converter::kind k) { + if (false) { + IF_VERBOSE(0, verbose_stream() << "blocked: " << l << " @ " << c << " :covered " << m_covered_clause << "\n"; + s.m_use_list.display(verbose_stream() << "use " << l << ":", l); + s.m_use_list.display(verbose_stream() << "use " << ~l << ":", ~l); + s.s.display_watch_list(verbose_stream() << ~l << ": ", s.get_wlist(l)) << "\n"; + s.s.display_watch_list(verbose_stream() << l << ": ", s.get_wlist(~l)) << "\n"; + ); + + } + TRACE("blocked_clause", tout << "new blocked clause: " << c << "\n";); + SASSERT(!s.is_external(l)); + model_converter::entry& new_entry = m_mc.mk(k, l.var()); + for (literal lit : c) { + if (lit != l && process_var(lit.var())) { + m_queue.decreased(~lit); + } + } + m_mc.insert(new_entry, m_covered_clause); + } + + void block_covered_binary(watched const& w, literal l1, literal blocked, model_converter::kind k) { + SASSERT(!s.is_external(blocked)); + model_converter::entry& new_entry = m_mc.mk(k, blocked.var()); + literal l2 = w.get_literal(); + TRACE("blocked_clause", tout << "new blocked clause: " << l2 << " " << l1 << "\n";); + s.set_learned(l1, l2); + m_mc.insert(new_entry, m_covered_clause); + m_queue.decreased(~l2); + } + + void bca() { + m_queue.reset(); + insert_queue(); + while (!m_queue.empty() && m_counter >= 0) { + s.checkpoint(); + bca(m_queue.next()); + } + } + + /* + \brief blocked binary clause addition for literal l + Let RI be the resolution intersection with l, e.g, RI are the literals + that are in all clauses of the form ~l \/ C. + If RI is non-empty, then let l' be a literal in RI. + Then the following binary clause is blocked: l \/ ~l' + */ + void bca(literal l) { + m_tautology.reset(); + if (resolution_intersection(l, true)) { + // this literal is pure. return; } - { - m_to_remove.reset(); - { - clause_use_list & occs = s.m_use_list.get(l); - clause_use_list::iterator it = occs.mk_iterator(); - while (!it.at_end()) { - clause & c = it.curr(); - m_counter -= c.size(); - SASSERT(c.contains(l)); - s.mark_all_but(c, l); - if (all_tautology(l)) { - TRACE("blocked_clause", tout << "new blocked clause: " << c << "\n";); - if (new_entry == nullptr) - new_entry = &(mc.mk(model_converter::BLOCK_LIT, l.var())); - m_to_remove.push_back(&c); - s.m_num_blocked_clauses++; - mc.insert(*new_entry, c); - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - literal lit = c[i]; - if (lit != l && process_var(lit.var())) { - m_queue.decreased(~lit); - } - } - } - s.unmark_all(c); - it.next(); - } + for (literal l2 : m_intersection) { + watched* w = find_binary_watch(s.get_wlist(~l), ~l2); + if (!w) { + s.s.mk_bin_clause(l, ~l2, true); + ++s.m_num_bca; } - clause_vector::iterator it = m_to_remove.begin(); - clause_vector::iterator end = m_to_remove.end(); - for (; it != end; ++it) { - s.remove_clause(*(*it)); - } - } - { - watch_list & wlist = s.get_wlist(~l); - m_counter -= wlist.size(); - watch_list::iterator it = wlist.begin(); - watch_list::iterator it2 = it; - watch_list::iterator end = wlist.end(); - for (; it != end; ++it) { - if (!it->is_binary_clause()) { - *it2 = *it; - it2++; - continue; - } - literal l2 = it->get_literal(); - s.mark_visited(l2); - if (all_tautology(l)) { - if (new_entry == nullptr) - new_entry = &(mc.mk(model_converter::BLOCK_LIT, l.var())); - TRACE("blocked_clause", tout << "new blocked clause: " << l2 << " " << l << "\n";); - s.remove_bin_clause_half(l2, l, it->is_learned()); - s.m_num_blocked_clauses++; - m_queue.decreased(~l2); - mc.insert(*new_entry, l, l2); - } - else { - *it2 = *it; - it2++; - } - s.unmark_visited(l2); - } - wlist.set_end(it2); } } bool all_tautology(literal l) { - { - watch_list & wlist = s.get_wlist(l); - m_counter -= wlist.size(); - watch_list::iterator it = wlist.begin(); - watch_list::iterator end = wlist.end(); - for (; it != end; ++it) { - if (!it->is_binary_non_learned_clause()) - continue; - if (!s.is_marked(~it->get_literal())) - return false; - } + watch_list & wlist = s.get_wlist(l); + m_counter -= wlist.size(); + for (auto const& w : wlist) { + if (w.is_binary_non_learned_clause() && + !s.is_marked(~w.get_literal())) + return false; } - { - clause_use_list & neg_occs = s.m_use_list.get(~l); - clause_use_list::iterator it = neg_occs.mk_iterator(); - while (!it.at_end()) { - clause & c = it.curr(); - m_counter -= c.size(); - unsigned sz = c.size(); - unsigned i; - for (i = 0; i < sz; i++) { - if (s.is_marked(~c[i])) - break; - } - if (i == sz) + + clause_use_list & neg_occs = s.m_use_list.get(~l); + clause_use_list::iterator it = neg_occs.mk_iterator(); + for (; !it.at_end(); it.next()) { + clause & c = it.curr(); + if (c.is_learned()) continue; + if (c.was_removed()) continue; + m_counter -= c.size(); + unsigned sz = c.size(); + unsigned i; + for (i = 0; i < sz; i++) { + if (s.is_marked(~c[i])) + break; + } + if (i == sz) + return false; + } + + if (s.s.m_ext) { + ext_constraint_list const& ext_list = s.m_ext_use_list.get(~l); + for (ext_constraint_idx idx : ext_list) { + if (!s.s.m_ext->is_blocked(l, idx)) { return false; - it.next(); + } } } return true; @@ -1058,37 +1677,54 @@ namespace sat { struct simplifier::blocked_cls_report { simplifier & m_simplifier; stopwatch m_watch; - unsigned m_num_blocked_clauses; + unsigned m_num_bce; + unsigned m_num_cce; + unsigned m_num_acce; + unsigned m_num_abce; + unsigned m_num_ate; + unsigned m_num_bca; blocked_cls_report(simplifier & s): m_simplifier(s), - m_num_blocked_clauses(s.m_num_blocked_clauses) { + m_num_bce(s.m_num_bce), + m_num_cce(s.m_num_cce), + m_num_acce(s.m_num_acce), + m_num_abce(s.m_num_abce), + m_num_ate(s.m_num_ate), + m_num_bca(s.m_num_bca) { m_watch.start(); } ~blocked_cls_report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, - verbose_stream() << " (sat-blocked-clauses :elim-blocked-clauses " - << (m_simplifier.m_num_blocked_clauses - m_num_blocked_clauses) - << mem_stat() + verbose_stream() << " (sat-blocked-clauses"; + report(m_simplifier.m_num_ate, m_num_ate, " :ate "); + report(m_simplifier.m_num_bce, m_num_bce, " :bce "); + report(m_simplifier.m_num_abce, m_num_abce, " :abce "); + report(m_simplifier.m_num_cce, m_num_cce, " :cce "); + report(m_simplifier.m_num_bca, m_num_bca, " :bca "); + report(m_simplifier.m_num_acce, m_num_acce, " :acce "); + verbose_stream() << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } + + void report(unsigned n, unsigned m, char const* s) { + if (n > m) verbose_stream() << s << (n - m); + } }; void simplifier::elim_blocked_clauses() { TRACE("blocked_clause_bug", tout << "trail: " << s.m_trail.size() << "\n"; s.display_watches(tout); s.display(tout);); blocked_cls_report rpt(*this); blocked_clause_elim elim(*this, m_blocked_clause_limit, s.m_mc, m_use_list, s.m_watches); - elim(s.num_vars()); + elim(); } - unsigned simplifier::get_num_non_learned_bin(literal l) const { + unsigned simplifier::num_nonlearned_bin(literal l) const { unsigned r = 0; - watch_list const & wlist = get_wlist(~l); - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { - if (it->is_binary_non_learned_clause()) + watch_list const & wlist = get_wlist(~l); + for (auto & w : wlist) { + if (w.is_binary_non_learned_clause()) r++; } return r; @@ -1099,8 +1735,8 @@ namespace sat { literal neg_l(v, true); unsigned num_pos = m_use_list.get(pos_l).size(); unsigned num_neg = m_use_list.get(neg_l).size(); - unsigned num_bin_pos = get_num_non_learned_bin(pos_l); - unsigned num_bin_neg = get_num_non_learned_bin(neg_l); + unsigned num_bin_pos = num_nonlearned_bin(pos_l); + unsigned num_bin_neg = num_nonlearned_bin(neg_l); unsigned cost = 2 * num_pos * num_neg + num_pos * num_bin_neg + num_neg * num_bin_pos; CTRACE("elim_vars_detail", cost == 0, tout << v << " num_pos: " << num_pos << " num_neg: " << num_neg << " num_bin_pos: " << num_bin_pos << " num_bin_neg: " << num_bin_neg << " cost: " << cost << "\n";); @@ -1115,10 +1751,7 @@ namespace sat { void simplifier::order_vars_for_elim(bool_var_vector & r) { svector tmp; - bool_var_set::iterator it = m_elim_todo.begin(); - bool_var_set::iterator end = m_elim_todo.end(); - for (; it != end; ++it) { - bool_var v = *it; + for (bool_var v : m_elim_todo) { if (is_external(v)) continue; if (was_eliminated(v)) @@ -1131,17 +1764,10 @@ namespace sat { m_elim_todo.reset(); std::stable_sort(tmp.begin(), tmp.end(), bool_var_and_cost_lt()); TRACE("elim_vars", - svector::iterator it = tmp.begin(); - svector::iterator end = tmp.end(); - for (; it != end; ++it) { - tout << "(" << it->first << ", " << it->second << ") "; - } + for (auto& p : tmp) tout << "(" << p.first << ", " << p.second << ") "; tout << "\n";); - svector::iterator it2 = tmp.begin(); - svector::iterator end2 = tmp.end(); - for (; it2 != end2; ++it2) { - r.push_back(it2->first); - } + for (auto& p : tmp) + r.push_back(p.first); } /** @@ -1149,19 +1775,18 @@ namespace sat { */ void simplifier::collect_clauses(literal l, clause_wrapper_vector & r) { clause_use_list const & cs = m_use_list.get(l); - clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { - r.push_back(clause_wrapper(it.curr())); - SASSERT(r.back().size() == it.curr().size()); - it.next(); + for (auto it = cs.mk_iterator(); !it.at_end(); it.next()) { + clause& c = it.curr(); + if (!c.is_learned() && !c.was_removed()) { + r.push_back(clause_wrapper(c)); + SASSERT(r.back().size() == c.size()); + } } watch_list & wlist = get_wlist(~l); - watch_list::iterator it2 = wlist.begin(); - watch_list::iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_non_learned_clause()) { - r.push_back(clause_wrapper(l, it2->get_literal())); + for (auto & w : wlist) { + if (w.is_binary_non_learned_clause()) { + r.push_back(clause_wrapper(l, w.get_literal())); SASSERT(r.back().size() == 2); } } @@ -1179,20 +1804,19 @@ namespace sat { SASSERT(c1.contains(l)); SASSERT(c2.contains(~l)); bool res = true; - unsigned sz = c1.size(); - m_elim_counter -= sz; - for (unsigned i = 0; i < sz; i++) { - literal l2 = c1[i]; - if (l == l2) + m_elim_counter -= c1.size() + c2.size(); + unsigned sz1 = c1.size(); + for (unsigned i = 0; i < sz1; ++i) { + literal l1 = c1[i]; + if (l == l1) continue; - m_visited[l2.index()] = true; - r.push_back(l2); + m_visited[l1.index()] = true; + r.push_back(l1); } literal not_l = ~l; - sz = c2.size(); - m_elim_counter -= sz; - for (unsigned i = 0; i < sz; i++) { + unsigned sz2 = c2.size(); + for (unsigned i = 0; i < sz2; ++i) { literal l2 = c2[i]; if (not_l == l2) continue; @@ -1204,50 +1828,50 @@ namespace sat { r.push_back(l2); } - sz = c1.size(); - for (unsigned i = 0; i < sz; i++) { - literal l2 = c1[i]; - if (l == l2) - continue; - m_visited[l2.index()] = false; + for (unsigned i = 0; i < sz1; ++i) { + literal l1 = c1[i]; + m_visited[l1.index()] = false; } return res; } void simplifier::save_clauses(model_converter::entry & mc_entry, clause_wrapper_vector const & cs) { - model_converter & mc = s.m_mc; - clause_wrapper_vector::const_iterator it = cs.begin(); - clause_wrapper_vector::const_iterator end = cs.end(); - for (; it != end; ++it) - mc.insert(mc_entry, *it); + for (auto & e : cs) { + s.m_mc.insert(mc_entry, e); + } } void simplifier::add_non_learned_binary_clause(literal l1, literal l2) { - watch_list & wlist1 = s.m_watches[(~l1).index()]; - watch_list & wlist2 = s.m_watches[(~l2).index()]; - watch_list::iterator it1 = wlist1.begin(); - watch_list::iterator end1 = wlist1.end(); - for (; it1 != end1; ++it1) { - if (it1->is_binary_clause() && it1->get_literal() == l2) { - *it1 = watched(l2, false); - watch_list::iterator it2 = wlist2.begin(); - watch_list::iterator end2 = wlist2.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_clause() && it2->get_literal() == l1) { - *it2 = watched(l1, false); - break; - } - } - CTRACE("resolve_bug", it2 == end2, - tout << ~l1 << " -> "; - display(tout, s.m_cls_allocator, wlist1); tout << "\n" << ~l2 << " -> "; - display(tout, s.m_cls_allocator, wlist2); tout << "\n";); - SASSERT(it2 != end2); - return; - } +#if 0 + if ((l1.var() == 2039 || l2.var() == 2039) && + (l1.var() == 27042 || l2.var() == 27042)) { + IF_VERBOSE(1, verbose_stream() << "add_bin: " << l1 << " " << l2 << "\n"); } - wlist1.push_back(watched(l2, false)); - wlist2.push_back(watched(l1, false)); +#endif +#if 0 + watched* w; + watch_list & wlist1 = get_wlist(~l1); + watch_list & wlist2 = get_wlist(~l2); + w = find_binary_watch(wlist1, l2); + if (w) { + if (w->is_learned()) + w->set_learned(false); + } + else { + wlist1.push_back(watched(l2, false)); + } + + w = find_binary_watch(wlist2, l1); + if (w) { + if (w->is_learned()) + w->set_learned(false); + } + else { + wlist2.push_back(watched(l1, false)); + } +#else + s.mk_bin_clause(l1, l2, false); +#endif } /** @@ -1255,17 +1879,20 @@ namespace sat { */ void simplifier::remove_bin_clauses(literal l) { watch_list & wlist = get_wlist(~l); - watch_list::iterator it = wlist.begin(); - watch_list::iterator end = wlist.end(); - for (; it != end; ++it) { - if (it->is_binary_clause()) { - literal l2 = it->get_literal(); + for (auto & w : wlist) { + if (w.is_binary_clause()) { + literal l2 = w.get_literal(); watch_list & wlist2 = get_wlist(~l2); watch_list::iterator it2 = wlist2.begin(); watch_list::iterator itprev = it2; watch_list::iterator end2 = wlist2.end(); for (; it2 != end2; ++it2) { if (it2->is_binary_clause() && it2->get_literal() == l) { + if ((l.var() == 2039 || l2.var() == 2039) && + (l.var() == 27042 || l2.var() == 27042)) { + IF_VERBOSE(1, verbose_stream() << "remove_bin: " << l << " " << l2 << "\n"); + } + TRACE("bin_clause_bug", tout << "removing: " << l << " " << it2->get_literal() << "\n";); m_sub_bin_todo.erase(bin_clause(l2, l, it2->is_learned())); continue; @@ -1274,7 +1901,7 @@ namespace sat { itprev++; } wlist2.set_end(itprev); - m_sub_bin_todo.erase(bin_clause(l, l2, it->is_learned())); + m_sub_bin_todo.erase(bin_clause(l, l2, w.is_learned())); } } TRACE("bin_clause_bug", tout << "collapsing watch_list of: " << l << "\n";); @@ -1285,8 +1912,7 @@ namespace sat { \brief Eliminate the clauses where the variable being eliminated occur. */ void simplifier::remove_clauses(clause_use_list const & cs, literal l) { - clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { + for (auto it = cs.mk_iterator(); !it.at_end(); ) { clause & c = it.curr(); it.next(); SASSERT(c.contains(l)); @@ -1305,12 +1931,12 @@ namespace sat { literal pos_l(v, false); literal neg_l(v, true); - unsigned num_bin_pos = get_num_non_learned_bin(pos_l); - unsigned num_bin_neg = get_num_non_learned_bin(neg_l); + unsigned num_bin_pos = num_nonlearned_bin(pos_l); + unsigned num_bin_neg = num_nonlearned_bin(neg_l); clause_use_list & pos_occs = m_use_list.get(pos_l); clause_use_list & neg_occs = m_use_list.get(neg_l); - unsigned num_pos = pos_occs.size() + num_bin_pos; - unsigned num_neg = neg_occs.size() + num_bin_neg; + unsigned num_pos = pos_occs.num_irredundant() + num_bin_pos; + unsigned num_neg = neg_occs.num_irredundant() + num_bin_neg; TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << "\n";); @@ -1319,20 +1945,14 @@ namespace sat { unsigned before_lits = num_bin_pos*2 + num_bin_neg*2; - { - clause_use_list::iterator it = pos_occs.mk_iterator(); - while (!it.at_end()) { + for (auto it = pos_occs.mk_iterator(); !it.at_end(); it.next()) { + if (!it.curr().is_learned()) before_lits += it.curr().size(); - it.next(); - } } - { - clause_use_list::iterator it2 = neg_occs.mk_iterator(); - while (!it2.at_end()) { - before_lits += it2.curr().size(); - it2.next(); - } + for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { + if (!it.curr().is_learned()) + before_lits += it.curr().size(); } TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << " before_lits: " << before_lits << "\n";); @@ -1354,16 +1974,12 @@ namespace sat { TRACE("resolution_detail", tout << "collecting number of after_clauses\n";); unsigned before_clauses = num_pos + num_neg; unsigned after_clauses = 0; - clause_wrapper_vector::iterator it1 = m_pos_cls.begin(); - clause_wrapper_vector::iterator end1 = m_pos_cls.end(); - for (; it1 != end1; ++it1) { - clause_wrapper_vector::iterator it2 = m_neg_cls.begin(); - clause_wrapper_vector::iterator end2 = m_neg_cls.end(); - for (; it2 != end2; ++it2) { + for (clause_wrapper& c1 : m_pos_cls) { + for (clause_wrapper& c2 : m_neg_cls) { m_new_cls.reset(); - if (resolve(*it1, *it2, pos_l, m_new_cls)) { - TRACE("resolution_detail", tout << *it1 << "\n" << *it2 << "\n-->\n"; - for (unsigned i = 0; i < m_new_cls.size(); i++) tout << m_new_cls[i] << " "; tout << "\n";); + if (resolve(c1, c2, pos_l, m_new_cls)) { + TRACE("resolution_detail", tout << c1 << "\n" << c2 << "\n-->\n"; + for (literal l : m_new_cls) tout << l << " "; tout << "\n";); after_clauses++; if (after_clauses > before_clauses) { TRACE("resolution", tout << "too many after clauses: " << after_clauses << "\n";); @@ -1375,7 +1991,18 @@ namespace sat { TRACE("resolution", tout << "found var to eliminate, before: " << before_clauses << " after: " << after_clauses << "\n";); m_elim_counter -= num_pos * num_neg + before_lits; + m_elim_counter -= num_pos * num_neg + before_lits; + + if (false) { + literal l(v, false); + IF_VERBOSE(0, + verbose_stream() << "elim: " << l << "\n"; + s.display_watch_list(verbose_stream() << ~l << ": ", get_wlist(l)) << "\n"; + s.display_watch_list(verbose_stream() << l << ": ", get_wlist(~l)) << "\n";); + } // eliminate variable + ++s.m_stats.m_elim_var_res; + VERIFY(!is_external(v)); model_converter::entry & mc_entry = s.m_mc.mk(model_converter::ELIM_VAR, v); save_clauses(mc_entry, m_pos_cls); save_clauses(mc_entry, m_neg_cls); @@ -1389,16 +2016,13 @@ namespace sat { m_elim_counter -= num_pos * num_neg + before_lits; - it1 = m_pos_cls.begin(); - end1 = m_pos_cls.end(); - for (; it1 != end1; ++it1) { - clause_wrapper_vector::iterator it2 = m_neg_cls.begin(); - clause_wrapper_vector::iterator end2 = m_neg_cls.end(); - for (; it2 != end2; ++it2) { + for (auto & c1 : m_pos_cls) { + for (auto & c2 : m_neg_cls) { m_new_cls.reset(); - if (!resolve(*it1, *it2, pos_l, m_new_cls)) + if (!resolve(c1, c2, pos_l, m_new_cls)) continue; - TRACE("resolution_new_cls", tout << *it1 << "\n" << *it2 << "\n-->\n" << m_new_cls << "\n";); + if (false && v == 767) IF_VERBOSE(0, verbose_stream() << "elim: " << c1 << " + " << c2 << " -> " << m_new_cls << "\n"); + TRACE("resolution_new_cls", tout << c1 << "\n" << c2 << "\n-->\n" << m_new_cls << "\n";); if (cleanup_clause(m_new_cls)) continue; // clause is already satisfied. switch (m_new_cls.size()) { @@ -1418,8 +2042,11 @@ namespace sat { s.m_stats.m_mk_ter_clause++; else s.m_stats.m_mk_clause++; - clause * new_c = s.m_cls_allocator.mk_clause(m_new_cls.size(), m_new_cls.c_ptr(), false); + clause * new_c = s.alloc_clause(m_new_cls.size(), m_new_cls.c_ptr(), false); + + if (s.m_config.m_drat) s.m_drat.add(*new_c, true); s.m_clauses.push_back(new_c); + m_use_list.insert(*new_c); if (m_sub_counter > 0) back_subsumption1(*new_c); @@ -1431,7 +2058,6 @@ namespace sat { return true; } } - return true; } @@ -1448,7 +2074,7 @@ namespace sat { ~elim_var_report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, - verbose_stream() << " (sat-resolution :elim-bool-vars " + verbose_stream() << " (sat-resolution :elim-vars " << (m_simplifier.m_num_elim_vars - m_num_elim_vars) << " :threshold " << m_simplifier.m_elim_counter << mem_stat() @@ -1457,19 +2083,22 @@ namespace sat { }; void simplifier::elim_vars() { - if (!m_elim_vars) return; + if (!elim_vars_enabled()) return; elim_var_report rpt(*this); bool_var_vector vars; order_vars_for_elim(vars); - - bool_var_vector::iterator it = vars.begin(); - bool_var_vector::iterator end = vars.end(); - for (; it != end; ++it) { + sat::elim_vars elim_bdd(*this); + for (bool_var v : vars) { checkpoint(); - if (m_elim_counter < 0) - return; - bool_var v = *it; - if (try_eliminate(v)) { + if (m_elim_counter < 0) + break; + if (is_external(v)) { + // skip + } + else if (try_eliminate(v)) { + m_num_elim_vars++; + } + else if (elim_vars_bdd_enabled() && elim_bdd(v)) { m_num_elim_vars++; } } @@ -1481,10 +2110,16 @@ namespace sat { void simplifier::updt_params(params_ref const & _p) { sat_simplifier_params p(_p); - m_elim_blocked_clauses = p.elim_blocked_clauses(); - m_elim_blocked_clauses_at = p.elim_blocked_clauses_at(); + m_cce = p.cce(); + m_acce = p.acce(); + m_bca = false && p.bca(); // disabled + m_abce = p.abce(); + m_ate = p.ate(); + m_bce_delay = p.bce_delay(); + m_bce = p.bce(); + m_bce_at = p.bce_at(); + m_retain_blocked_clauses = p.retain_blocked_clauses(); m_blocked_clause_limit = p.blocked_clause_limit(); - m_resolution = p.resolution(); m_res_limit = p.resolution_limit(); m_res_occ_cutoff = p.resolution_occ_cutoff(); m_res_occ_cutoff1 = p.resolution_occ_cutoff_range1(); @@ -1498,6 +2133,9 @@ namespace sat { m_subsumption = p.subsumption(); m_subsumption_limit = p.subsumption_limit(); m_elim_vars = p.elim_vars(); + m_elim_vars_bdd = false && p.elim_vars_bdd(); // buggy? + m_elim_vars_bdd_delay = p.elim_vars_bdd_delay(); + m_incremental_mode = s.get_config().m_incremental && !p.override_incremental(); } void simplifier::collect_param_descrs(param_descrs & r) { @@ -1508,15 +2146,24 @@ namespace sat { st.update("subsumed", m_num_subsumed); st.update("subsumption resolution", m_num_sub_res); st.update("elim literals", m_num_elim_lits); - st.update("elim bool vars", m_num_elim_vars); - st.update("elim blocked clauses", m_num_blocked_clauses); + st.update("bce", m_num_bce); + st.update("cce", m_num_cce); + st.update("acce", m_num_acce); + st.update("abce", m_num_abce); + st.update("bca", m_num_bca); + st.update("ate", m_num_ate); } void simplifier::reset_statistics() { - m_num_blocked_clauses = 0; + m_num_bce = 0; + m_num_cce = 0; + m_num_acce = 0; + m_num_abce = 0; m_num_subsumed = 0; m_num_sub_res = 0; m_num_elim_lits = 0; m_num_elim_vars = 0; + m_num_bca = 0; + m_num_ate = 0; } }; diff --git a/src/sat/sat_simplifier.h b/src/sat/sat_simplifier.h index 44e4276e0..3787b5894 100644 --- a/src/sat/sat_simplifier.h +++ b/src/sat/sat_simplifier.h @@ -25,6 +25,7 @@ Revision History: #include "sat/sat_clause.h" #include "sat/sat_clause_set.h" #include "sat/sat_clause_use_list.h" +#include "sat/sat_extension.h" #include "sat/sat_watched.h" #include "sat/sat_model_converter.h" #include "util/heap.h" @@ -39,17 +40,23 @@ namespace sat { public: void init(unsigned num_vars); void insert(clause & c); + void block(clause & c); + void unblock(clause & c); void erase(clause & c); void erase(clause & c, literal l); clause_use_list & get(literal l) { return m_use_list[l.index()]; } clause_use_list const & get(literal l) const { return m_use_list[l.index()]; } void finalize() { m_use_list.finalize(); } + std::ostream& display(std::ostream& out, literal l) const { return m_use_list[l.index()].display(out); } }; class simplifier { + friend class ba_solver; + friend class elim_vars; solver & s; unsigned m_num_calls; use_list m_use_list; + ext_use_list m_ext_use_list; clause_set m_sub_todo; svector m_sub_bin_todo; unsigned m_last_sub_trail_sz; // size of the trail since last cleanup @@ -65,9 +72,17 @@ namespace sat { int m_elim_counter; // config - bool m_elim_blocked_clauses; - unsigned m_elim_blocked_clauses_at; + bool m_abce; // block clauses using asymmetric added literals + bool m_cce; // covered clause elimination + bool m_acce; // cce with asymetric literal addition + bool m_bca; // blocked (binary) clause addition. + unsigned m_bce_delay; + bool m_bce; // blocked clause elimination + bool m_ate; // asymmetric tautology elimination + unsigned m_bce_at; + bool m_retain_blocked_clauses; unsigned m_blocked_clause_limit; + bool m_incremental_mode; bool m_resolution; unsigned m_res_limit; unsigned m_res_occ_cutoff; @@ -83,9 +98,16 @@ namespace sat { bool m_subsumption; unsigned m_subsumption_limit; bool m_elim_vars; + bool m_elim_vars_bdd; + unsigned m_elim_vars_bdd_delay; // stats - unsigned m_num_blocked_clauses; + unsigned m_num_bce; + unsigned m_num_cce; + unsigned m_num_acce; + unsigned m_num_abce; + unsigned m_num_bca; + unsigned m_num_ate; unsigned m_num_subsumed; unsigned m_num_elim_vars; unsigned m_num_sub_res; @@ -114,7 +136,8 @@ namespace sat { void remove_clause_core(clause & c); void remove_clause(clause & c); void remove_clause(clause & c, literal l); - void remove_bin_clause_half(literal l1, literal l2, bool learned); + void set_learned(clause & c); + void set_learned(literal l1, literal l2); bool_var get_min_occ_var(clause const & c) const; bool subsumes1(clause const & c1, clause const & c2, literal & l); @@ -133,18 +156,18 @@ namespace sat { bool cleanup_clause(clause & c, bool in_use_list); bool cleanup_clause(literal_vector & c); - void propagate_unit(literal l); void elim_lit(clause & c, literal l); void elim_dup_bins(); bool subsume_with_binaries(); void mark_as_not_learned_core(watch_list & wlist, literal l2); void mark_as_not_learned(literal l1, literal l2); - void subsume(); void cleanup_watches(); + void move_clauses(clause_vector & cs, bool learned); void cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists); bool is_external(bool_var v) const; + bool is_external(literal l) const { return is_external(l.var()); } bool was_eliminated(bool_var v) const; lbool value(bool_var v) const; lbool value(literal l) const; @@ -154,7 +177,18 @@ namespace sat { struct blocked_clause_elim; void elim_blocked_clauses(); - unsigned get_num_non_learned_bin(literal l) const; + bool single_threaded() const; // { return s.m_config.m_num_threads == 1; } + bool bce_enabled_base() const; + bool ate_enabled() const; + bool bce_enabled() const; + bool acce_enabled() const; + bool cce_enabled() const; + bool abce_enabled() const; + bool bca_enabled() const; + bool elim_vars_bdd_enabled() const; + bool elim_vars_enabled() const; + + unsigned num_nonlearned_bin(literal l) const; unsigned get_to_elim_cost(bool_var v) const; void order_vars_for_elim(bool_var_vector & r); void collect_clauses(literal l, clause_wrapper_vector & r); @@ -185,6 +219,8 @@ namespace sat { simplifier(solver & s, params_ref const & p); ~simplifier(); + void init_search() { m_num_calls = 0; } + void insert_elim_todo(bool_var v) { m_elim_todo.insert(v); } void reset_todos() { @@ -202,6 +238,10 @@ namespace sat { void collect_statistics(statistics & st) const; void reset_statistics(); + + void propagate_unit(literal l); + void subsume(); + }; }; diff --git a/src/sat/sat_simplifier_params.pyg b/src/sat/sat_simplifier_params.pyg index ff2944987..3757aad2d 100644 --- a/src/sat/sat_simplifier_params.pyg +++ b/src/sat/sat_simplifier_params.pyg @@ -1,10 +1,17 @@ def_module_params(module_name='sat', class_name='sat_simplifier_params', export=True, - params=(('elim_blocked_clauses', BOOL, False, 'eliminate blocked clauses'), - ('elim_blocked_clauses_at', UINT, 2, 'eliminate blocked clauses only once at the given simplification round'), + params=(('bce', BOOL, False, 'eliminate blocked clauses'), + ('abce', BOOL, False, 'eliminate blocked clauses using asymmmetric literals'), + ('cce', BOOL, False, 'eliminate covered clauses'), + ('ate', BOOL, True, 'asymmetric tautology elimination'), + ('acce', BOOL, False, 'eliminate covered clauses using asymmetric added literals'), + ('bce_at', UINT, 2, 'eliminate blocked clauses only once at the given simplification round'), + ('bca', BOOL, False, 'blocked clause addition - add blocked binary clauses'), + ('bce_delay', UINT, 2, 'delay eliminate blocked clauses until simplification round'), + ('retain_blocked_clauses', BOOL, True, 'retain blocked clauses as lemmas'), ('blocked_clause_limit', UINT, 100000000, 'maximum number of literals visited during blocked clause elimination'), - ('resolution', BOOL, True, 'eliminate boolean variables using resolution'), + ('override_incremental', BOOL, False, 'override incemental safety gaps. Enable elimination of blocked clauses and variables even if solver is reused'), ('resolution.limit', UINT, 500000000, 'approx. maximum number of literals visited during variable elimination'), ('resolution.occ_cutoff', UINT, 10, 'first cutoff (on number of positive/negative occurrences) for Boolean variable elimination'), ('resolution.occ_cutoff_range1', UINT, 8, 'second cutoff (number of positive/negative occurrences) for Boolean variable elimination, for problems containing less than res_cls_cutoff1 clauses'), @@ -15,6 +22,13 @@ def_module_params(module_name='sat', ('resolution.lit_cutoff_range3', UINT, 300, 'second cutoff (total number of literals) for Boolean variable elimination, for problems containing more than res_cls_cutoff2'), ('resolution.cls_cutoff1', UINT, 100000000, 'limit1 - total number of problems clauses for the second cutoff of Boolean variable elimination'), ('resolution.cls_cutoff2', UINT, 700000000, 'limit2 - total number of problems clauses for the second cutoff of Boolean variable elimination'), - ('elim_vars', BOOL, True, 'enable variable elimination during simplification'), + ('elim_vars', BOOL, True, 'enable variable elimination using resolution during simplification'), + ('elim_vars_bdd', BOOL, True, 'enable variable elimination using BDD recompilation during simplification'), + ('elim_vars_bdd_delay', UINT, 3, 'delay elimination of variables using BDDs until after simplification round'), + ('probing', BOOL, True, 'apply failed literal detection during simplification'), + ('probing_limit', UINT, 5000000, 'limit to the number of probe calls'), + ('probing_cache', BOOL, True, 'add binary literals as lemmas'), + ('probing_cache_limit', UINT, 1024, 'cache binaries unless overall memory usage exceeds cache limit'), + ('probing_binary', BOOL, True, 'probe binary clauses'), ('subsumption', BOOL, True, 'eliminate subsumed clauses'), ('subsumption.limit', UINT, 100000000, 'approx. maximum number of literals visited during subsumption (and subsumption resolution)'))) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 135a7417d..a59dd2b46 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -16,114 +16,192 @@ Author: Revision History: --*/ + +#include #include "sat/sat_solver.h" #include "sat/sat_integrity_checker.h" +#include "sat/sat_lookahead.h" +#include "sat/sat_unit_walk.h" #include "util/luby.h" #include "util/trace.h" #include "util/max_cliques.h" +#include "util/gparams.h" // define to update glue during propagation #define UPDATE_GLUE -// define to create a copy of the solver before starting the search -// useful for checking models -// #define CLONE_BEFORE_SOLVING - namespace sat { - solver::solver(params_ref const & p, reslimit& l, extension * ext): + solver::solver(params_ref const & p, reslimit& l): m_rlimit(l), m_checkpoint_enabled(true), m_config(p), - m_ext(ext), m_par(nullptr), + m_cls_allocator_idx(false), m_cleaner(*this), m_simplifier(*this, p), m_scc(*this, p), m_asymm_branch(*this, p), m_probing(*this, p), m_mus(*this), + m_drat(*this), m_inconsistent(false), + m_searching(false), m_num_frozen(0), m_activity_inc(128), m_case_split_queue(m_activity), m_qhead(0), m_scope_lvl(0), - m_params(p) { + m_search_lvl(0), + m_fast_glue_avg(), + m_slow_glue_avg(), + m_params(p), + m_par_id(0), + m_par_syncing_clauses(false) { + init_reason_unknown(); updt_params(p); m_conflicts_since_gc = 0; - m_conflicts = 0; + m_conflicts_since_init = 0; m_next_simplify = 0; m_num_checkpoints = 0; + m_simplifications = 0; + m_ext = 0; + m_cuber = nullptr; + m_mc.set_solver(this); } solver::~solver() { + m_ext = 0; SASSERT(check_invariant()); TRACE("sat", tout << "Delete clauses\n";); - del_clauses(m_clauses.begin(), m_clauses.end()); + del_clauses(m_clauses); TRACE("sat", tout << "Delete learned\n";); - del_clauses(m_learned.begin(), m_learned.end()); + del_clauses(m_learned); } - void solver::del_clauses(clause * const * begin, clause * const * end) { - for (clause * const * it = begin; it != end; ++it) { - m_cls_allocator.del_clause(*it); - } + void solver::del_clauses(clause_vector& clauses) { + for (clause * cp : clauses) + dealloc_clause(cp); + clauses.reset(); ++m_stats.m_non_learned_generation; } + void solver::set_extension(extension* ext) { + m_ext = ext; + if (ext) ext->set_solver(this); + } + void solver::copy(solver const & src) { pop_to_base_level(); - SASSERT(m_mc.empty() && src.m_mc.empty()); - SASSERT(scope_lvl() == 0); + del_clauses(m_clauses); + del_clauses(m_learned); + m_watches.reset(); + m_assignment.reset(); + m_justification.reset(); + m_decision.reset(); + m_eliminated.reset(); + m_activity.reset(); + m_level.reset(); + m_mark.reset(); + m_lit_mark.reset(); + m_phase.reset(); + m_prev_phase.reset(); + m_assigned_since_gc.reset(); + m_last_conflict.reset(); + m_last_propagation.reset(); + m_participated.reset(); + m_canceled.reset(); + m_reasoned.reset(); + m_simplifier.reset_todos(); + m_qhead = 0; + m_trail.reset(); + m_scopes.reset(); + // create new vars - if (num_vars() < src.num_vars()) { - for (bool_var v = num_vars(); v < src.num_vars(); v++) { - SASSERT(!src.was_eliminated(v)); - bool ext = src.m_external[v] != 0; - bool dvar = src.m_decision[v] != 0; - VERIFY(v == mk_var(ext, dvar)); + for (bool_var v = num_vars(); v < src.num_vars(); v++) { + bool ext = src.m_external[v] != 0; + bool dvar = src.m_decision[v] != 0; + VERIFY(v == mk_var(ext, dvar)); + if (src.was_eliminated(v)) { + m_eliminated[v] = true; } + m_phase[v] = src.m_phase[v]; + m_prev_phase[v] = src.m_prev_phase[v]; + +#if 1 + // inherit activity: + m_activity[v] = src.m_activity[v]; + m_case_split_queue.activity_changed_eh(v, false); +#endif } - unsigned sz = src.init_trail_size(); - for (unsigned i = 0; i < sz; ++i) { + + // + // register the extension before performing assignments. + // the assignments may call back into the extension. + // + if (src.get_extension()) { + m_ext = src.get_extension()->copy(this); + } + + unsigned trail_sz = src.init_trail_size(); + for (unsigned i = 0; i < trail_sz; ++i) { assign(src.m_trail[i], justification()); } + // copy binary clauses { - // copy binary clauses unsigned sz = src.m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { literal l = ~to_literal(l_idx); + if (src.was_eliminated(l.var())) continue; watch_list const & wlist = src.m_watches[l_idx]; - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { - if (!it->is_binary_non_learned_clause()) + for (auto & wi : wlist) { + if (!wi.is_binary_clause()) continue; - literal l2 = it->get_literal(); - if (l.index() > l2.index()) + literal l2 = wi.get_literal(); + if (l.index() > l2.index() || + src.was_eliminated(l2.var())) continue; - mk_clause_core(l, l2); + + watched w1(l2, wi.is_learned()); + watched w2(l, wi.is_learned()); + m_watches[(~l).index()].push_back(w1); + m_watches[(~l2).index()].push_back(w2); } } } + { literal_vector buffer; - // copy clause - clause_vector::const_iterator it = src.m_clauses.begin(); - clause_vector::const_iterator end = src.m_clauses.end(); - for (; it != end; ++it) { - clause const & c = *(*it); + // copy clauses + for (clause* c : src.m_clauses) { buffer.reset(); - for (unsigned i = 0; i < c.size(); i++) - buffer.push_back(c[i]); + for (literal l : *c) buffer.push_back(l); mk_clause_core(buffer); } + + // copy high quality lemmas + unsigned num_learned = 0; + for (clause* c : src.m_learned) { + if (c->glue() <= 2 || (c->size() <= 40 && c->glue() <= 8)) { + buffer.reset(); + for (literal l : *c) buffer.push_back(l); + clause* c1 = mk_clause_core(buffer.size(), buffer.c_ptr(), true); + if (c1) { + ++num_learned; + c1->set_glue(c->glue()); + c1->set_psm(c->psm()); + } + } + } + IF_VERBOSE(1, verbose_stream() << "(sat.copy :learned " << num_learned << ")\n";); } m_user_scope_literals.reset(); m_user_scope_literals.append(src.m_user_scope_literals); + + m_mc = src.m_mc; + m_stats.m_units = init_trail_size(); } // ----------------------- @@ -152,13 +230,43 @@ namespace sat { m_phase.push_back(PHASE_NOT_AVAILABLE); m_prev_phase.push_back(PHASE_NOT_AVAILABLE); m_assigned_since_gc.push_back(false); + m_last_conflict.push_back(0); + m_last_propagation.push_back(0); + m_participated.push_back(0); + m_canceled.push_back(0); + m_reasoned.push_back(0); m_case_split_queue.mk_var_eh(v); m_simplifier.insert_elim_todo(v); SASSERT(!was_eliminated(v)); return v; } - void solver::mk_clause(unsigned num_lits, literal * lits) { + void solver::set_non_external(bool_var v) { + m_external[v] = 0; + } + + void solver::set_external(bool_var v) { + if (m_external[v] != 0) return; + m_external[v] = 1; + if (!m_ext) return; + + lbool val = value(v); + + switch (val) { + case l_true: { + m_ext->asserted(literal(v, false)); + break; + } + case l_false: { + m_ext->asserted(literal(v, true)); + break; + } + default: + break; + } + } + + void solver::mk_clause(unsigned num_lits, literal * lits, bool learned) { m_model_is_current = false; DEBUG_CODE({ for (unsigned i = 0; i < num_lits; i++) @@ -166,29 +274,34 @@ namespace sat { }); if (m_user_scope_literals.empty()) { - mk_clause_core(num_lits, lits, false); + mk_clause_core(num_lits, lits, learned); } else { m_aux_literals.reset(); m_aux_literals.append(num_lits, lits); m_aux_literals.append(m_user_scope_literals); - mk_clause_core(m_aux_literals.size(), m_aux_literals.c_ptr(), false); + mk_clause_core(m_aux_literals.size(), m_aux_literals.c_ptr(), learned); } } - void solver::mk_clause(literal l1, literal l2) { + void solver::mk_clause(literal l1, literal l2, bool learned) { literal ls[2] = { l1, l2 }; - mk_clause(2, ls); + mk_clause(2, ls, learned); } - void solver::mk_clause(literal l1, literal l2, literal l3) { + void solver::mk_clause(literal l1, literal l2, literal l3, bool learned) { literal ls[3] = { l1, l2, l3 }; - mk_clause(3, ls); + mk_clause(3, ls, learned); } void solver::del_clause(clause& c) { - if (!c.is_learned()) m_stats.m_non_learned_generation++; - m_cls_allocator.del_clause(&c); + if (!c.is_learned()) { + m_stats.m_non_learned_generation++; + } + if (m_config.m_drat && !m_drat.is_cleaned(c)) { + m_drat.del(c); + } + dealloc_clause(&c); m_stats.m_del_clause++; } @@ -202,9 +315,10 @@ namespace sat { } ++m_stats.m_non_learned_generation; } - + switch (num_lits) { case 0: + if (m_config.m_drat) m_drat.add(); set_conflict(justification()); return nullptr; case 1: @@ -212,6 +326,7 @@ namespace sat { return nullptr; case 2: mk_bin_clause(lits[0], lits[1], learned); + if (learned && m_par) m_par->share_clause(*this, lits[0], lits[1]); return nullptr; case 3: return mk_ter_clause(lits, learned); @@ -221,15 +336,44 @@ namespace sat { } void solver::mk_bin_clause(literal l1, literal l2, bool learned) { - if (propagate_bin_clause(l1, l2)) { - if (scope_lvl() == 0) +#if 0 + if ((l1.var() == 2039 || l2.var() == 2039) && + (l1.var() == 27042 || l2.var() == 27042)) { + IF_VERBOSE(1, verbose_stream() << "mk_bin: " << l1 << " " << l2 << " " << learned << "\n"); + } +#endif + if (find_binary_watch(get_wlist(~l1), ~l2)) { + assign(l1, justification()); + return; + } + if (find_binary_watch(get_wlist(~l2), ~l1)) { + assign(l2, justification()); + return; + } + watched* w0 = find_binary_watch(get_wlist(~l1), l2); + if (w0) { + if (w0->is_learned() && !learned) { + w0->set_learned(false); + } + else { return; - if (!learned) + } + w0 = find_binary_watch(get_wlist(~l2), l1); + VERIFY(w0); + w0->set_learned(false); + return; + } + if (m_config.m_drat) + m_drat.add(l1, l2, learned); + if (propagate_bin_clause(l1, l2)) { + if (at_base_lvl()) + return; + if (!learned && !at_search_lvl()) m_clauses_to_reinit.push_back(clause_wrapper(l1, l2)); } m_stats.m_mk_bin_clause++; - m_watches[(~l1).index()].push_back(watched(l2, learned)); - m_watches[(~l2).index()].push_back(watched(l1, learned)); + get_wlist(~l1).push_back(watched(l2, learned)); + get_wlist(~l2).push_back(watched(l1, learned)); } bool solver::propagate_bin_clause(literal l1, literal l2) { @@ -255,9 +399,10 @@ namespace sat { clause * solver::mk_ter_clause(literal * lits, bool learned) { m_stats.m_mk_ter_clause++; - clause * r = m_cls_allocator.mk_clause(3, lits, learned); + clause * r = alloc_clause(3, lits, learned); bool reinit = attach_ter_clause(*r); if (reinit && !learned) push_reinit_stack(*r); + if (m_config.m_drat) m_drat.add(*r, learned); if (learned) m_learned.push_back(r); @@ -271,7 +416,7 @@ namespace sat { m_watches[(~c[0]).index()].push_back(watched(c[1], c[2])); m_watches[(~c[1]).index()].push_back(watched(c[0], c[2])); m_watches[(~c[2]).index()].push_back(watched(c[0], c[1])); - if (scope_lvl() > 0) { + if (!at_base_lvl()) { if (value(c[1]) == l_false && value(c[2]) == l_false) { m_stats.m_ter_propagate++; assign(c[0], justification(c[1], c[2])); @@ -293,21 +438,25 @@ namespace sat { clause * solver::mk_nary_clause(unsigned num_lits, literal * lits, bool learned) { m_stats.m_mk_clause++; - clause * r = m_cls_allocator.mk_clause(num_lits, lits, learned); + clause * r = alloc_clause(num_lits, lits, learned); SASSERT(!learned || r->is_learned()); bool reinit = attach_nary_clause(*r); if (reinit && !learned) push_reinit_stack(*r); - if (learned) + if (learned) { m_learned.push_back(r); - else + } + else { m_clauses.push_back(r); + } + if (m_config.m_drat) + m_drat.add(*r, learned); return r; } bool solver::attach_nary_clause(clause & c) { bool reinit = false; - clause_offset cls_off = m_cls_allocator.get_offset(&c); - if (scope_lvl() > 0) { + clause_offset cls_off = cls_allocator().get_offset(&c); + if (!at_base_lvl()) { if (c.is_learned()) { unsigned w2_idx = select_learned_watch_lit(c); std::swap(c[1], c[w2_idx]); @@ -332,6 +481,9 @@ namespace sat { } unsigned some_idx = c.size() >> 1; literal block_lit = c[some_idx]; + DEBUG_CODE(for (auto const& w : m_watches[(~c[0]).index()]) VERIFY(!w.is_clause() || w.get_clause_offset() != cls_off);); + DEBUG_CODE(for (auto const& w : m_watches[(~c[1]).index()]) VERIFY(!w.is_clause() || w.get_clause_offset() != cls_off);); + VERIFY(c[0] != c[1]); m_watches[(~c[0]).index()].push_back(watched(block_lit, cls_off)); m_watches[(~c[1]).index()].push_back(watched(block_lit, cls_off)); return reinit; @@ -346,6 +498,109 @@ namespace sat { reinit = attach_nary_clause(c); } + void solver::set_learned(clause& c, bool learned) { + if (c.is_learned() != learned) + c.set_learned(learned); + } + + void solver::set_learned1(literal l1, literal l2, bool learned) { + for (watched& w : get_wlist(~l1)) { + if (w.is_binary_clause() && l2 == w.get_literal() && !w.is_learned()) { + w.set_learned(learned); + break; + } + } + } + + bool solver::memory_pressure() { + return 3*cls_allocator().get_allocation_size()/2 + memory::get_allocation_size() > memory::get_max_memory_size(); + } + + struct solver::cmp_activity { + solver& s; + cmp_activity(solver& s):s(s) {} + bool operator()(bool_var v1, bool_var v2) const { + return s.m_activity[v1] > s.m_activity[v2]; + } + }; + + bool solver::should_defrag() { + if (m_defrag_threshold > 0) --m_defrag_threshold; + return m_defrag_threshold == 0 && m_config.m_gc_defrag; + } + + void solver::defrag_clauses() { + if (memory_pressure()) return; + pop(scope_lvl()); + IF_VERBOSE(1, verbose_stream() << "(sat-defrag)\n"); + clause_allocator& alloc = m_cls_allocator[!m_cls_allocator_idx]; + ptr_vector new_clauses, new_learned; + for (clause* c : m_clauses) c->unmark_used(); + for (clause* c : m_learned) c->unmark_used(); + + svector vars; + for (unsigned i = 0; i < num_vars(); ++i) vars.push_back(i); + std::stable_sort(vars.begin(), vars.end(), cmp_activity(*this)); + literal_vector lits; + for (bool_var v : vars) lits.push_back(literal(v, false)), lits.push_back(literal(v, true)); + // walk clauses, reallocate them in an order that defragments memory and creates locality. + for (literal lit : lits) { + watch_list& wlist = m_watches[lit.index()]; + for (watched& w : wlist) { + if (w.is_clause()) { + clause& c1 = get_clause(w); + clause_offset offset; + if (c1.was_used()) { + offset = c1.get_new_offset(); + } + else { + clause* c2 = alloc.copy_clause(c1); + c1.mark_used(); + if (c1.is_learned()) { + new_learned.push_back(c2); + } + else { + new_clauses.push_back(c2); + } + offset = get_offset(*c2); + c1.set_new_offset(offset); + } + w = watched(w.get_blocked_literal(), offset); + } + } + } + + // reallocate ternary clauses. + for (clause* c : m_clauses) { + if (!c->was_used()) { + SASSERT(c->size() == 3); + new_clauses.push_back(alloc.copy_clause(*c)); + } + dealloc_clause(c); + } + + for (clause* c : m_learned) { + if (!c->was_used()) { + SASSERT(c->size() == 3); + new_learned.push_back(alloc.copy_clause(*c)); + } + dealloc_clause(c); + } + m_clauses.swap(new_clauses); + m_learned.swap(new_learned); + + cls_allocator().finalize(); + m_cls_allocator_idx = !m_cls_allocator_idx; + + reinit_assumptions(); + } + + + void solver::set_learned(literal l1, literal l2, bool learned) { + set_learned1(l1, l2, learned); + set_learned1(l2, l1, learned); + } + /** \brief Select a watch literal starting the search at the given position. This method is only used for clauses created during the search. @@ -457,7 +712,7 @@ namespace sat { } bool solver::simplify_clause(unsigned & num_lits, literal * lits) const { - if (scope_lvl() == 0) + if (at_base_lvl()) return simplify_clause_core(num_lits, lits); else return simplify_clause_core(num_lits, lits); @@ -466,6 +721,7 @@ namespace sat { void solver::detach_bin_clause(literal l1, literal l2, bool learned) { get_wlist(~l1).erase(watched(l2, learned)); get_wlist(~l2).erase(watched(l1, learned)); + if (m_config.m_drat) m_drat.del(l1, l2); } void solver::detach_clause(clause & c) { @@ -504,8 +760,10 @@ namespace sat { void solver::assign_core(literal l, justification j) { SASSERT(value(l) == l_undef); TRACE("sat_assign_core", tout << l << " " << j << " level: " << scope_lvl() << "\n";); - if (scope_lvl() == 0) + if (at_base_lvl()) { + if (m_config.m_drat) m_drat.add(l, !j.is_none()); j = justification(); // erase justification for level 0 + } m_assignment[l.index()] = l_true; m_assignment[(~l).index()] = l_false; bool_var v = l.var(); @@ -518,9 +776,39 @@ namespace sat { if (m_ext && m_external[v]) m_ext->asserted(l); + switch (m_config.m_branching_heuristic) { + case BH_VSIDS: + break; + case BH_CHB: + m_last_propagation[v] = m_stats.m_conflict; + break; + case BH_LRB: + m_participated[v] = 0; + m_reasoned[v] = 0; + break; + } + + if (m_config.m_anti_exploration) { + uint64_t age = m_stats.m_conflict - m_canceled[v]; + if (age > 0) { + double decay = pow(0.95, age); + m_activity[v] = static_cast(m_activity[v] * decay); + // NB. MapleSAT does not update canceled. + m_canceled[v] = m_stats.m_conflict; + m_case_split_queue.activity_changed_eh(v, false); + } + } + + if (m_config.m_propagate_prefetch) { +#if defined(__GNUC__) || defined(__clang__) + __builtin_prefetch((const char*)((m_watches[l.index()].c_ptr()))); +#else + _mm_prefetch((const char*)((m_watches[l.index()].c_ptr())), _MM_HINT_T1); +#endif + } + SASSERT(!l.sign() || m_phase[v] == NEG_PHASE); SASSERT(l.sign() || m_phase[v] == POS_PHASE); - SASSERT(!l.sign() || value(v) == l_false); SASSERT(l.sign() || value(v) == l_true); SASSERT(value(l) == l_true); @@ -529,9 +817,8 @@ namespace sat { lbool solver::status(clause const & c) const { bool found_undef = false; - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - switch (value(c[i])) { + for (literal lit : c) { + switch (value(lit)) { case l_true: return l_true; case l_undef: @@ -559,7 +846,7 @@ namespace sat { while (m_qhead < m_trail.size()) { checkpoint(); m_cleaner.dec(); - SASSERT(!m_inconsistent); + if (m_inconsistent) return false; l = m_trail[m_qhead]; TRACE("sat_propagate", tout << "propagating: " << l << " " << m_justification[l.var()] << "\n";); m_qhead++; @@ -620,15 +907,13 @@ namespace sat { case watched::CLAUSE: { if (value(it->get_blocked_literal()) == l_true) { TRACE("propagate_clause_bug", tout << "blocked literal " << it->get_blocked_literal() << "\n"; - clause_offset cls_off = it->get_clause_offset(); - clause & c = *(m_cls_allocator.get_clause(cls_off)); - tout << c << "\n";); + tout << get_clause(it) << "\n";); *it2 = *it; it2++; break; } clause_offset cls_off = it->get_clause_offset(); - clause & c = *(m_cls_allocator.get_clause(cls_off)); + clause & c = get_clause(cls_off); TRACE("propagate_clause_bug", tout << "processing... " << c << "\nwas_removed: " << c.was_removed() << "\n";); if (c[0] == not_l) std::swap(c[0], c[1]); @@ -643,18 +928,19 @@ namespace sat { it2++; break; } - SASSERT(c[1] == not_l); if (value(c[0]) == l_true) { it2->set_clause(c[0], cls_off); it2++; break; } + VERIFY(c[1] == not_l); literal * l_it = c.begin() + 2; literal * l_end = c.end(); for (; l_it != l_end; ++l_it) { if (value(*l_it) != l_false) { c[1] = *l_it; *l_it = not_l; + DEBUG_CODE(for (auto const& w : m_watches[(~c[1]).index()]) VERIFY(!w.is_clause() || w.get_clause_offset() != cls_off);); m_watches[(~c[1]).index()].push_back(watched(c[0], cls_off)); goto end_clause_case; } @@ -674,8 +960,8 @@ namespace sat { assign_core(c[0], justification(cls_off)); #ifdef UPDATE_GLUE if (update && c.is_learned() && c.glue() > 2) { - unsigned glue = num_diff_levels(c.size(), c.begin()); - if (glue+1 < c.glue()) { + unsigned glue; + if (num_diff_levels_below(c.size(), c.begin(), c.glue()-1, glue)) { c.set_glue(glue); } } @@ -686,15 +972,18 @@ namespace sat { } case watched::EXT_CONSTRAINT: SASSERT(m_ext); - m_ext->propagate(l, it->get_ext_constraint_idx(), keep); + keep = m_ext->propagate(l, it->get_ext_constraint_idx()); + if (m_inconsistent) { + if (!keep) { + ++it; + } + CONFLICT_CLEANUP(); + return false; + } if (keep) { *it2 = *it; it2++; } - if (m_inconsistent) { - CONFLICT_CLEANUP(); - return false; - } break; default: UNREACHABLE(); @@ -709,46 +998,72 @@ namespace sat { } bool solver::propagate(bool update) { + unsigned qhead = m_qhead; bool r = propagate_core(update); + if (m_config.m_branching_heuristic == BH_CHB) { + update_chb_activity(r, qhead); + } CASSERT("sat_propagate", check_invariant()); CASSERT("sat_missed_prop", check_missed_propagation()); return r; } + lbool solver::cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level) { + if (!m_cuber) { + m_cuber = alloc(lookahead, *this); + } + lbool result = m_cuber->cube(vars, lits, backtrack_level); + m_cuber->update_cube_statistics(m_aux_stats); + if (result == l_false) { + dealloc(m_cuber); + m_cuber = nullptr; + } + return result; + } + + // ----------------------- // // Search // // ----------------------- lbool solver::check(unsigned num_lits, literal const* lits) { + init_reason_unknown(); pop_to_base_level(); + m_stats.m_units = init_trail_size(); IF_VERBOSE(2, verbose_stream() << "(sat.sat-solver)\n";); - SASSERT(scope_lvl() == 0); - if (m_config.m_dimacs_display) { - display_dimacs(std::cout); - for (unsigned i = 0; i < num_lits; ++i) { - std::cout << dimacs_lit(lits[i]) << " 0\n"; - } - return l_undef; + SASSERT(at_base_lvl()); + + if (m_config.m_local_search) { + return do_local_search(num_lits, lits); } - if (m_config.m_num_parallel > 1 && !m_par) { + if ((m_config.m_num_threads > 1 || m_config.m_local_search_threads > 0 || m_config.m_unit_walk_threads > 0) && !m_par) { + SASSERT(scope_lvl() == 0); return check_par(num_lits, lits); } -#ifdef CLONE_BEFORE_SOLVING - if (m_mc.empty()) { - m_clone = alloc(solver, m_params, 0 /* do not clone extension */); - SASSERT(m_clone); + flet _searching(m_searching, true); + if (m_mc.empty() && gparams::get_ref().get_bool("model_validate", false)) { + m_clone = alloc(solver, m_params, m_rlimit); + m_clone->copy(*this); } -#endif try { - if (inconsistent()) return l_false; init_search(); + if (inconsistent()) return l_false; propagate(false); if (inconsistent()) return l_false; init_assumptions(num_lits, lits); propagate(false); if (check_inconsistent()) return l_false; cleanup(); + + if (m_config.m_unit_walk) { + return do_unit_walk(); + } + if (m_config.m_gc_burst) { + // force gc + m_conflicts_since_gc = m_gc_threshold + 1; + gc(); + } if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; lbool r = bounded_search(); @@ -756,15 +1071,14 @@ namespace sat { return r; pop_reinit(scope_lvl()); m_conflicts_since_restart = 0; - m_restart_threshold = m_config.m_restart_initial; + m_restart_threshold = m_config.m_restart_initial; } // iff3_finder(*this)(); simplify_problem(); if (check_inconsistent()) return l_false; - if (m_config.m_max_conflicts == 0) { - IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = 0\")\n";); + if (reached_max_conflicts()) { return l_undef; } @@ -775,24 +1089,30 @@ namespace sat { if (r != l_undef) return r; - if (m_conflicts > m_config.m_max_conflicts) { - IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = " << m_conflicts << "\")\n";); + if (reached_max_conflicts()) { return l_undef; } - restart(); + restart(!m_config.m_restart_fast); simplify_problem(); if (check_inconsistent()) return l_false; gc(); if (m_config.m_restart_max <= m_restarts) { + m_reason_unknown = "sat.max.restarts"; IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-restarts\")\n";); return l_undef; } + if (m_config.m_inprocess_max <= m_simplifications) { + m_reason_unknown = "sat.max.inprocess"; + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-inprocess\")\n";); + return l_undef; + } } } catch (abort_solver) { + m_reason_unknown = "sat.giveup"; return l_undef; } } @@ -802,37 +1122,87 @@ namespace sat { ERROR_EX }; + lbool solver::do_local_search(unsigned num_lits, literal const* lits) { + scoped_limits scoped_rl(rlimit()); + local_search srch; + srch.config().set_config(m_config); + srch.import(*this, false); + scoped_rl.push_child(&srch.rlimit()); + lbool r = srch.check(num_lits, lits, 0); + m_model = srch.get_model(); + // srch.collect_statistics(m_aux_stats); + return r; + } + + lbool solver::do_unit_walk() { + unit_walk srch(*this); + lbool r = srch(); + return r; + } + lbool solver::check_par(unsigned num_lits, literal const* lits) { - int num_threads = static_cast(m_config.m_num_parallel); - int num_extra_solvers = num_threads - 1; - scoped_limits scoped_rlimit(rlimit()); - vector rlims(num_extra_solvers); - ptr_vector solvers(num_extra_solvers); - sat::par par; - symbol saved_phase = m_params.get_sym("phase", symbol("caching")); - for (int i = 0; i < num_extra_solvers; ++i) { - m_params.set_uint("random_seed", m_rand()); - if (i == 1 + num_threads/2) { - m_params.set_sym("phase", symbol("random")); - } - solvers[i] = alloc(sat::solver, m_params, rlims[i], nullptr); - solvers[i]->copy(*this); - solvers[i]->set_par(&par); - scoped_rlimit.push_child(&solvers[i]->rlimit()); + scoped_ptr_vector ls; + scoped_ptr_vector uw; + int num_extra_solvers = m_config.m_num_threads - 1; + int num_local_search = static_cast(m_config.m_local_search_threads); + int num_unit_walk = static_cast(m_config.m_unit_walk_threads); + int num_threads = num_extra_solvers + 1 + num_local_search + num_unit_walk; + for (int i = 0; i < num_local_search; ++i) { + local_search* l = alloc(local_search); + l->config().set_config(m_config); + l->config().set_random_seed(m_config.m_random_seed + i); + l->import(*this, false); + ls.push_back(l); + } + + // set up unit walk + vector lims(num_unit_walk); + for (int i = 0; i < num_unit_walk; ++i) { + solver* s = alloc(solver, m_params, lims[i]); + s->copy(*this); + s->m_config.m_unit_walk = true; + uw.push_back(s); + } + + int local_search_offset = num_extra_solvers; + int unit_walk_offset = num_extra_solvers + num_local_search; + int main_solver_offset = unit_walk_offset + num_unit_walk; + +#define IS_AUX_SOLVER(i) (0 <= i && i < num_extra_solvers) +#define IS_LOCAL_SEARCH(i) (local_search_offset <= i && i < unit_walk_offset) +#define IS_UNIT_WALK(i) (unit_walk_offset <= i && i < main_solver_offset) +#define IS_MAIN_SOLVER(i) (i == main_solver_offset) + + sat::parallel par(*this); + par.reserve(num_threads, 1 << 12); + par.init_solvers(*this, num_extra_solvers); + for (unsigned i = 0; i < ls.size(); ++i) { + par.push_child(ls[i]->rlimit()); + } + for (reslimit& rl : lims) { + par.push_child(rl); + } + for (unsigned i = 0; i < uw.size(); ++i) { + uw[i]->set_par(&par, 0); } - set_par(&par); - m_params.set_sym("phase", saved_phase); int finished_id = -1; std::string ex_msg; par_exception_kind ex_kind = DEFAULT_EX; unsigned error_code = 0; lbool result = l_undef; + bool canceled = false; #pragma omp parallel for for (int i = 0; i < num_threads; ++i) { try { lbool r = l_undef; - if (i < num_extra_solvers) { - r = solvers[i]->check(num_lits, lits); + if (IS_AUX_SOLVER(i)) { + r = par.get_solver(i).check(num_lits, lits); + } + else if (IS_LOCAL_SEARCH(i)) { + r = ls[i-local_search_offset]->check(num_lits, lits, &par); + } + else if (IS_UNIT_WALK(i)) { + r = uw[i-unit_walk_offset]->check(num_lits, lits); } else { r = check(num_lits, lits); @@ -847,41 +1217,57 @@ namespace sat { } } if (first) { - if (r == l_true && i < num_extra_solvers) { - set_model(solvers[i]->get_model()); + for (unsigned j = 0; j < ls.size(); ++j) { + ls[j]->rlimit().cancel(); } - else if (r == l_false && i < num_extra_solvers) { - m_core.reset(); - m_core.append(solvers[i]->get_core()); + for (auto& rl : lims) { + rl.cancel(); } for (int j = 0; j < num_extra_solvers; ++j) { if (i != j) { - rlims[j].cancel(); + par.cancel_solver(j); + } + } + if (!IS_MAIN_SOLVER(i)) { + canceled = !rlimit().inc(); + if (!canceled) { + rlimit().cancel(); } } } } catch (z3_error & err) { - if (i == 0) { - error_code = err.error_code(); - ex_kind = ERROR_EX; - } + error_code = err.error_code(); + ex_kind = ERROR_EX; } catch (z3_exception & ex) { - if (i == 0) { - ex_msg = ex.msg(); - ex_kind = DEFAULT_EX; - } + ex_msg = ex.msg(); + ex_kind = DEFAULT_EX; } } - set_par(nullptr); - if (finished_id != -1 && finished_id < num_extra_solvers) { - m_stats = solvers[finished_id]->m_stats; + + if (IS_AUX_SOLVER(finished_id)) { + m_stats = par.get_solver(finished_id).m_stats; } - - for (int i = 0; i < num_extra_solvers; ++i) { - dealloc(solvers[i]); + if (result == l_true && IS_AUX_SOLVER(finished_id)) { + set_model(par.get_solver(finished_id).get_model()); } + else if (result == l_false && IS_AUX_SOLVER(finished_id)) { + m_core.reset(); + m_core.append(par.get_solver(finished_id).get_core()); + } + if (result == l_true && IS_LOCAL_SEARCH(finished_id)) { + set_model(ls[finished_id - local_search_offset]->get_model()); + } + if (result == l_true && IS_UNIT_WALK(finished_id)) { + set_model(uw[finished_id - unit_walk_offset]->get_model()); + } + if (!canceled) { + rlimit().reset_cancel(); + } + set_par(0, 0); + ls.reset(); + uw.reset(); if (finished_id == -1) { switch (ex_kind) { case ERROR_EX: throw z3_error(error_code); @@ -896,7 +1282,10 @@ namespace sat { \brief import lemmas/units from parallel sat solvers. */ void solver::exchange_par() { - if (m_par && scope_lvl() == 0) { + if (m_par && at_base_lvl() && m_config.m_num_threads > 1) m_par->get_clauses(*this); + if (m_par && at_base_lvl() && m_config.m_num_threads > 1) { + // SASSERT(scope_lvl() == search_lvl()); + // TBD: import also dependencies of assumptions. unsigned sz = init_trail_size(); unsigned num_in = 0, num_out = 0; literal_vector in, out; @@ -908,7 +1297,7 @@ namespace sat { } } m_par_limit_out = sz; - m_par->exchange(out, m_par_limit_in, in); + m_par->exchange(*this, out, m_par_limit_in, in); for (unsigned i = 0; !inconsistent() && i < in.size(); ++i) { literal lit = in[i]; SASSERT(lit.var() < m_par_num_vars); @@ -918,16 +1307,18 @@ namespace sat { } } if (num_in > 0 || num_out > 0) { - IF_VERBOSE(1, verbose_stream() << "(sat-sync out: " << num_out << " in: " << num_in << ")\n";); + IF_VERBOSE(2, verbose_stream() << "(sat-sync out: " << num_out << " in: " << num_in << ")\n";); } } } - void solver::set_par(par* p) { + void solver::set_par(parallel* p, unsigned id) { m_par = p; m_par_num_vars = num_vars(); m_par_limit_in = 0; m_par_limit_out = 0; + m_par_id = id; + m_par_syncing_clauses = false; } bool_var solver::next_var() { @@ -943,6 +1334,17 @@ namespace sat { } while (!m_case_split_queue.empty()) { + if (m_config.m_anti_exploration) { + next = m_case_split_queue.min_var(); + auto age = m_stats.m_conflict - m_canceled[next]; + while (age > 0) { + m_activity[next] = static_cast(m_activity[next] * pow(0.95, age)); + m_case_split_queue.activity_changed_eh(next, false); + m_canceled[next] = m_stats.m_conflict; + next = m_case_split_queue.min_var(); + age = m_stats.m_conflict - m_canceled[next]; + } + } next = m_case_split_queue.next_var(); if (value(next) == l_undef && !was_eliminated(next)) return next; @@ -968,10 +1370,12 @@ namespace sat { phase = l_false; break; case PS_CACHING: - if (m_phase_cache_on && m_phase[next] != PHASE_NOT_AVAILABLE) + if ((m_phase_cache_on || m_config.m_phase_sticky) && m_phase[next] != PHASE_NOT_AVAILABLE) { phase = m_phase[next] == POS_PHASE ? l_true : l_false; - else + } + else { phase = l_false; + } break; case PS_RANDOM: phase = to_lbool((m_rand() % 2) == 0); @@ -1017,11 +1421,11 @@ namespace sat { return l_true; if (!resolve_conflict()) return l_false; - if (m_conflicts > m_config.m_max_conflicts) + if (reached_max_conflicts()) return l_undef; - if (m_conflicts_since_restart > m_restart_threshold) + if (should_restart()) return l_undef; - if (scope_lvl() == 0) { + if (at_base_lvl()) { cleanup(); // cleaner may propagate frozen clauses if (inconsistent()) { TRACE("sat", tout << "conflict at level 0\n";); @@ -1069,6 +1473,7 @@ namespace sat { return; } + SASSERT(at_base_lvl()); reset_assumptions(); push(); @@ -1096,9 +1501,10 @@ namespace sat { add_assumption(lit); assign(lit, justification()); } + m_search_lvl = scope_lvl(); + SASSERT(m_search_lvl == 1); } - void solver::update_min_core() { if (!m_min_core_valid || m_core.size() < m_min_core.size()) { m_min_core.reset(); @@ -1115,6 +1521,7 @@ namespace sat { void solver::add_assumption(literal lit) { m_assumption_set.insert(lit); m_assumptions.push_back(lit); + set_external(lit.var()); } void solver::pop_assumption() { @@ -1128,8 +1535,7 @@ namespace sat { push(); reset_assumptions(); TRACE("sat", tout << "reassert: " << m_min_core << "\n";); - for (unsigned i = 0; i < m_min_core.size(); ++i) { - literal lit = m_min_core[i]; + for (literal lit : m_min_core) { SASSERT(is_external(lit.var())); add_assumption(lit); assign(lit, justification()); @@ -1139,7 +1545,7 @@ namespace sat { } void solver::reinit_assumptions() { - if (tracking_assumptions() && scope_lvl() == 0 && !inconsistent()) { + if (tracking_assumptions() && at_base_lvl() && !inconsistent()) { TRACE("sat", tout << m_assumptions << "\n";); push(); for (unsigned i = 0; !inconsistent() && i < m_user_scope_literals.size(); ++i) { @@ -1149,12 +1555,12 @@ namespace sat { assign(m_assumptions[i], justification()); } TRACE("sat", - for (unsigned i = 0; i < m_assumptions.size(); ++i) { - index_set s; - if (m_antecedents.find(m_assumptions[i].var(), s)) { - tout << m_assumptions[i] << ": "; display_index_set(tout, s) << "\n"; - } - }); + for (literal a : m_assumptions) { + index_set s; + if (m_antecedents.find(a.var(), s)) { + tout << a << ": "; display_index_set(tout, s) << "\n"; + } + }); } } @@ -1166,86 +1572,134 @@ namespace sat { return tracking_assumptions() && m_assumption_set.contains(l); } + bool solver::is_assumption(bool_var v) const { + return is_assumption(literal(v, false)) || is_assumption(literal(v, true)); + } + void solver::init_search() { m_model_is_current = false; m_phase_counter = 0; - m_phase_cache_on = false; + m_phase_cache_on = m_config.m_phase_sticky; m_conflicts_since_restart = 0; m_restart_threshold = m_config.m_restart_initial; m_luby_idx = 1; m_gc_threshold = m_config.m_gc_initial; + m_defrag_threshold = 2; m_restarts = 0; + m_simplifications = 0; + m_conflicts_since_init = 0; + m_next_simplify = m_config.m_simplify_delay; m_min_d_tk = 1.0; + m_search_lvl = 0; + m_conflicts_since_gc = 0; + m_restart_next_out = 0; + m_asymm_branch.init_search(); m_stopwatch.reset(); m_stopwatch.start(); m_core.reset(); m_min_core_valid = false; m_min_core.reset(); + m_simplifier.init_search(); TRACE("sat", display(tout);); } /** \brief Apply all simplifications. - */ void solver::simplify_problem() { - if (m_conflicts < m_next_simplify) { + if (m_conflicts_since_init < m_next_simplify) { return; } - IF_VERBOSE(2, verbose_stream() << "(sat.simplify)\n";); + m_simplifications++; + IF_VERBOSE(2, verbose_stream() << "(sat.simplify :simplifications " << m_simplifications << ")\n";); - // Disable simplification during MUS computation. - // if (m_mus.is_active()) return; TRACE("sat", tout << "simplify\n";); pop(scope_lvl()); - SASSERT(scope_lvl() == 0); + SASSERT(at_base_lvl()); m_cleaner(); CASSERT("sat_simplify_bug", check_invariant()); m_scc(); CASSERT("sat_simplify_bug", check_invariant()); - + m_simplifier(false); + CASSERT("sat_simplify_bug", check_invariant()); CASSERT("sat_missed_prop", check_missed_propagation()); - if (!m_learned.empty()) { m_simplifier(true); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); } - sort_watch_lits(); CASSERT("sat_simplify_bug", check_invariant()); m_probing(); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); + m_asymm_branch(false); - m_asymm_branch(); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); - if (m_ext) { m_ext->clauses_modifed(); m_ext->simplify(); } + if (m_config.m_lookahead_simplify && !m_ext) { + lookahead lh(*this); + lh.simplify(true); + lh.collect_statistics(m_aux_stats); + } TRACE("sat", display(tout << "consistent: " << (!inconsistent()) << "\n");); reinit_assumptions(); if (m_next_simplify == 0) { - m_next_simplify = m_config.m_restart_initial * m_config.m_simplify_mult1; + m_next_simplify = m_config.m_next_simplify1; } else { - m_next_simplify = static_cast(m_conflicts * m_config.m_simplify_mult2); - if (m_next_simplify > m_conflicts + m_config.m_simplify_max) - m_next_simplify = m_conflicts + m_config.m_simplify_max; + m_next_simplify = static_cast(m_conflicts_since_init * m_config.m_simplify_mult2); + if (m_next_simplify > m_conflicts_since_init + m_config.m_simplify_max) + m_next_simplify = m_conflicts_since_init + m_config.m_simplify_max; } + + + if (m_par) m_par->set_phase(*this); + +#if 0 + static unsigned file_no = 0; + #pragma omp critical (print_sat) + { + ++file_no; + std::ostringstream ostrm; + ostrm << "s" << file_no << ".txt"; + std::ofstream ous(ostrm.str()); + display(ous); + } +#endif + } + + unsigned solver::get_hash() const { + unsigned result = 0; + for (clause* cp : m_clauses) { + result = combine_hash(cp->size(), combine_hash(result, cp->id())); + } + for (clause* cp : m_learned) { + result = combine_hash(cp->size(), combine_hash(result, cp->id())); + } + return result; + } + + bool solver::set_root(literal l, literal r) { + return !m_ext || m_ext->set_root(l, r); + } + + void solver::flush_roots() { + if (m_ext) m_ext->flush_roots(); } void solver::sort_watch_lits() { @@ -1266,70 +1720,101 @@ namespace sat { unsigned num = num_vars(); m_model.resize(num, l_undef); for (bool_var v = 0; v < num; v++) { - if (!was_eliminated(v)) + if (!was_eliminated(v)) { m_model[v] = value(v); + m_phase[v] = (value(v) == l_true) ? POS_PHASE : NEG_PHASE; + } } TRACE("sat_mc_bug", m_mc.display(tout);); - m_mc(m_model); - TRACE("sat", for (bool_var v = 0; v < num; v++) tout << v << ": " << m_model[v] << "\n";); -#ifndef _EXTERNAL_RELEASE - IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"checking model\"\n";); - if (!check_model(m_model)) +#if 0 + IF_VERBOSE(0, for (bool_var v = 0; v < num; v++) verbose_stream() << v << ": " << m_model[v] << "\n";); + for (auto p : big::s_del_bin) { + if (value(p.first) != l_true && value(p.second) != l_true) { + IF_VERBOSE(0, verbose_stream() << "binary violation: " << p.first << " " << p.second << "\n"); + } + } +#endif + + IF_VERBOSE(10, verbose_stream() << "\"checking model\"\n";); + if (!check_clauses(m_model)) { throw solver_exception("check model failed"); + } + + if (m_config.m_drat) m_drat.check_model(m_model); + + // m_mc.set_solver(nullptr); + m_mc(m_model); + + if (!check_clauses(m_model)) { + IF_VERBOSE(0, verbose_stream() << "failure checking clauses on transformed model\n";); + IF_VERBOSE(10, m_mc.display(verbose_stream())); + //IF_VERBOSE(0, display_units(verbose_stream())); + //IF_VERBOSE(0, display(verbose_stream())); + IF_VERBOSE(0, for (bool_var v = 0; v < num; v++) verbose_stream() << v << ": " << m_model[v] << "\n";); + + throw solver_exception("check model failed"); + } + + TRACE("sat", for (bool_var v = 0; v < num; v++) tout << v << ": " << m_model[v] << "\n";); if (m_clone) { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"checking model (on original set of clauses)\"\n";); - if (!m_clone->check_model(m_model)) + if (!m_clone->check_model(m_model)) { + //IF_VERBOSE(0, display(verbose_stream())); + //IF_VERBOSE(0, display_watches(verbose_stream())); + IF_VERBOSE(0, m_mc.display(verbose_stream())); + IF_VERBOSE(0, display_units(verbose_stream())); + //IF_VERBOSE(0, m_clone->display(verbose_stream() << "clone\n")); throw solver_exception("check model failed (for cloned solver)"); - } -#endif - } - - bool solver::check_model(model const & m) const { - bool ok = true; - clause_vector const * vs[2] = { &m_clauses, &m_learned }; - for (unsigned i = 0; i < 2; i++) { - clause_vector const & cs = *(vs[i]); - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - clause const & c = *(*it); - if (!c.satisfied_by(m)) { - TRACE("sat", tout << "failed: " << c << "\n"; - tout << "assumptions: " << m_assumptions << "\n"; - tout << "trail: " << m_trail << "\n"; - tout << "model: " << m << "\n"; - m_mc.display(tout); - ); - ok = false; - } } } - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { + } + + bool solver::check_clauses(model const& m) const { + bool ok = true; + for (clause const* cp : m_clauses) { + clause const & c = *cp; + if (!c.satisfied_by(m)) { + IF_VERBOSE(0, verbose_stream() << "failed clause " << c.id() << ": " << c << "\n";); + TRACE("sat", tout << "failed: " << c << "\n"; + tout << "assumptions: " << m_assumptions << "\n"; + tout << "trail: " << m_trail << "\n"; + tout << "model: " << m << "\n"; + m_mc.display(tout); + ); + for (literal l : c) { + if (was_eliminated(l.var())) IF_VERBOSE(0, verbose_stream() << "eliminated: " << l << "\n";); + } + ok = false; + } + } + unsigned l_idx = 0; + for (watch_list const& wlist : m_watches) { literal l = ~to_literal(l_idx); if (value_at(l, m) != l_true) { - watch_list const & wlist = *it; - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (!it2->is_binary_clause()) + for (watched const& w : wlist) { + if (!w.is_binary_non_learned_clause()) + continue; + literal l2 = w.get_literal(); + if (l.index() > l2.index()) continue; - literal l2 = it2->get_literal(); if (value_at(l2, m) != l_true) { - TRACE("sat", tout << "failed binary: " << l << " " << l2 << " learned: " << it2->is_learned() << "\n"; - m_mc.display(tout);); + IF_VERBOSE(0, verbose_stream() << "failed binary: " << l << " := " << value_at(l, m) << " " << l2 << " := " << value_at(l2, m) << "\n"); + IF_VERBOSE(0, verbose_stream() << "elim l1: " << was_eliminated(l.var()) << " elim l2: " << was_eliminated(l2) << "\n"); + TRACE("sat", m_mc.display(tout << "failed binary: " << l << " " << l2 << "\n");); ok = false; } } } + ++l_idx; } - for (unsigned i = 0; i < m_assumptions.size(); ++i) { - if (value_at(m_assumptions[i], m) != l_true) { + for (literal l : m_assumptions) { + if (value_at(l, m) != l_true) { + VERIFY(is_external(l.var())); + IF_VERBOSE(0, verbose_stream() << "assumption: " << l << " does not model check " << value_at(l, m) << "\n";); TRACE("sat", - tout << m_assumptions[i] << " does not model check\n"; + tout << l << " does not model check\n"; tout << "trail: " << m_trail << "\n"; tout << "model: " << m << "\n"; m_mc.display(tout); @@ -1337,22 +1822,75 @@ namespace sat { ok = false; } } - if (ok && !m_mc.check_model(m)) { + if (m_ext && !m_ext->check_model(m)) { ok = false; - TRACE("sat", tout << "model: " << m << "\n"; m_mc.display(tout);); } return ok; } - void solver::restart() { + bool solver::check_model(model const & m) const { + bool ok = check_clauses(m); + if (ok && !m_mc.check_model(m)) { + ok = false; + TRACE("sat", tout << "model: " << m << "\n"; m_mc.display(tout);); + } + IF_VERBOSE(1, verbose_stream() << "model check " << (ok?"OK":"failed") << "\n";); + return ok; + } + + bool solver::should_restart() const { + if (m_conflicts_since_restart <= m_restart_threshold) return false; + if (scope_lvl() < 2 + search_lvl()) return false; + if (m_config.m_restart != RS_EMA) return true; + return + m_fast_glue_avg + search_lvl() <= scope_lvl() && + m_config.m_restart_margin * m_slow_glue_avg <= m_fast_glue_avg; + } + + void solver::restart(bool to_base) { m_stats.m_restart++; m_restarts++; - IF_VERBOSE(1, - verbose_stream() << "(sat-restart :conflicts " << m_stats.m_conflict << " :decisions " << m_stats.m_decision - << " :restarts " << m_stats.m_restart << mk_stat(*this) - << " :time " << std::fixed << std::setprecision(2) << m_stopwatch.get_current_seconds() << ")\n";); + if (m_conflicts_since_init > m_restart_next_out + 500) { + m_restart_next_out = m_conflicts_since_init; + IF_VERBOSE(1, + verbose_stream() << "(sat-restart :conflicts " << m_stats.m_conflict << " :decisions " << m_stats.m_decision + << " :restarts " << m_stats.m_restart << mk_stat(*this) + << " :time " << std::fixed << std::setprecision(2) << m_stopwatch.get_current_seconds() << ")\n";); + } IF_VERBOSE(30, display_status(verbose_stream());); - pop_reinit(scope_lvl()); + pop_reinit(restart_level(to_base)); + set_next_restart(); + } + + unsigned solver::restart_level(bool to_base) { + if (to_base || scope_lvl() == search_lvl()) { + return scope_lvl() - search_lvl(); + } + else { + bool_var next = m_case_split_queue.min_var(); + + // Implementations of Marijn's idea of reusing the + // trail when the next decision literal has lower precedence. + // pop trail from top +#if 0 + unsigned n = 0; + do { + bool_var prev = scope_literal(scope_lvl() - n - 1).var(); + if (m_case_split_queue.more_active(prev, next)) break; + ++n; + } + while (n < scope_lvl() - search_lvl()); + return n; +#endif + // pop trail from bottom + unsigned n = search_lvl(); + for (; n < scope_lvl() && m_case_split_queue.more_active(scope_literal(n).var(), next); ++n) { + } + return n - search_lvl(); + } + } + + void solver::set_next_restart() { m_conflicts_since_restart = 0; switch (m_config.m_restart) { case RS_GEOMETRIC: @@ -1362,6 +1900,11 @@ namespace sat { m_luby_idx++; m_restart_threshold = m_config.m_restart_initial * get_luby(m_luby_idx); break; + case RS_EMA: + m_restart_threshold = m_config.m_restart_initial; + break; + case RS_STATIC: + break; default: UNREACHABLE(); break; @@ -1378,6 +1921,10 @@ namespace sat { void solver::gc() { if (m_conflicts_since_gc <= m_gc_threshold) return; + if (m_config.m_gc_strategy == GC_DYN_PSM && !at_base_lvl()) + return; + unsigned gc = m_stats.m_gc_clause; + IF_VERBOSE(10, verbose_stream() << "(sat.gc)\n";); CASSERT("sat_gc_bug", check_invariant()); switch (m_config.m_gc_strategy) { case GC_GLUE: @@ -1393,7 +1940,7 @@ namespace sat { gc_psm_glue(); break; case GC_DYN_PSM: - if (m_scope_lvl != 0) + if (!at_base_lvl()) return; gc_dyn_psm(); break; @@ -1401,6 +1948,10 @@ namespace sat { UNREACHABLE(); break; } + if (m_ext) m_ext->gc(); + if (gc > 0 && should_defrag()) { + defrag_clauses(); + } m_conflicts_since_gc = 0; m_gc_threshold += m_config.m_gc_increment; CASSERT("sat_gc_bug", check_invariant()); @@ -1479,11 +2030,8 @@ namespace sat { \brief Compute the psm of all learned clauses. */ void solver::save_psm() { - clause_vector::iterator it = m_learned.begin(); - clause_vector::iterator end = m_learned.end(); - for (; it != end; ++it) { - clause & c = *(*it); - c.set_psm(psm(c)); + for (clause* cp : m_learned) { + cp->set_psm(psm(*cp)); } } @@ -1493,7 +2041,7 @@ namespace sat { void solver::gc_half(char const * st_name) { TRACE("sat", tout << "gc\n";); unsigned sz = m_learned.size(); - unsigned new_sz = sz/2; + unsigned new_sz = sz/2; // std::min(sz/2, m_clauses.size()*2); unsigned j = new_sz; for (unsigned i = new_sz; i < sz; i++) { clause & c = *(m_learned[i]); @@ -1519,7 +2067,7 @@ namespace sat { TRACE("sat", tout << "gc\n";); // To do gc at scope_lvl() > 0, I will need to use the reinitialization stack, or live with the fact // that I may miss some propagations for reactivated clauses. - SASSERT(scope_lvl() == 0); + SASSERT(at_base_lvl()); // compute // d_tk unsigned h = 0; @@ -1610,7 +2158,7 @@ namespace sat { // return true if should keep the clause, and false if we should delete it. bool solver::activate_frozen_clause(clause & c) { TRACE("sat_gc", tout << "reactivating:\n" << c << "\n";); - SASSERT(scope_lvl() == 0); + SASSERT(at_base_lvl()); // do some cleanup unsigned sz = c.size(); unsigned j = 0; @@ -1622,7 +2170,9 @@ namespace sat { case l_false: break; case l_undef: - c[j] = c[i]; + if (i != j) { + std::swap(c[i], c[j]); + } j++; break; } @@ -1640,7 +2190,11 @@ namespace sat { mk_bin_clause(c[0], c[1], true); return false; default: - c.shrink(new_sz); + if (new_sz != sz) { + if (m_config.m_drat) m_drat.del(c); + c.shrink(new_sz); + if (m_config.m_drat) m_drat.add(c, true); + } attach_clause(c); return true; } @@ -1651,9 +2205,7 @@ namespace sat { */ unsigned solver::psm(clause const & c) const { unsigned r = 0; - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - literal l = c[i]; + for (literal l : c) { if (l.sign()) { if (m_phase[l.var()] == NEG_PHASE) r++; @@ -1686,10 +2238,13 @@ namespace sat { bool solver::resolve_conflict_core() { - m_stats.m_conflict++; - m_conflicts++; + m_conflicts_since_init++; m_conflicts_since_restart++; m_conflicts_since_gc++; + m_stats.m_conflict++; + if (m_step_size > m_config.m_step_size_min) { + m_step_size -= m_config.m_step_size_dec; + } m_conflict_lvl = get_max_lvl(m_not_l, m_conflict); TRACE("sat", tout << "conflict detected at level " << m_conflict_lvl << " for "; @@ -1705,21 +2260,36 @@ namespace sat { return false; } - m_lemma.reset(); - forget_phase_of_vars(m_conflict_lvl); + if (m_ext) { + switch (m_ext->resolve_conflict()) { + case l_true: + learn_lemma_and_backjump(); + return true; + case l_undef: + break; + case l_false: + // backjumping was taken care of internally. + return true; + } + } + + m_lemma.reset(); + unsigned idx = skip_literals_above_conflict_level(); + // save space for first uip m_lemma.push_back(null_literal); unsigned num_marks = 0; + literal consequent = null_literal; if (m_not_l != null_literal) { TRACE("sat_conflict", tout << "not_l: " << m_not_l << "\n";); process_antecedent(m_not_l, num_marks); + consequent = ~m_not_l; } - literal consequent = m_not_l; justification js = m_conflict; do { @@ -1736,7 +2306,7 @@ namespace sat { process_antecedent(~(js.get_literal2()), num_marks); break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); + clause & c = get_clause(js); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); @@ -1755,10 +2325,8 @@ namespace sat { } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(consequent, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) - process_antecedent(*it, num_marks); + for (literal l : m_ext_antecedents) + process_antecedent(l, num_marks); break; } default: @@ -1785,38 +2353,48 @@ namespace sat { while (num_marks > 0); m_lemma[0] = ~consequent; + learn_lemma_and_backjump(); + return true; + } + + void solver::learn_lemma_and_backjump() { TRACE("sat_lemma", tout << "new lemma size: " << m_lemma.size() << "\n" << m_lemma << "\n";); - if (m_config.m_minimize_lemmas) { - minimize_lemma(); - reset_lemma_var_marks(); - if (m_config.m_dyn_sub_res) - dyn_sub_res(); - TRACE("sat_lemma", tout << "new lemma (after minimization) size: " << m_lemma.size() << "\n" << m_lemma << "\n";); - } - else - reset_lemma_var_marks(); - - literal_vector::iterator it = m_lemma.begin(); - literal_vector::iterator end = m_lemma.end(); unsigned new_scope_lvl = 0; - ++it; - for(; it != end; ++it) { - bool_var var = (*it).var(); - new_scope_lvl = std::max(new_scope_lvl, lvl(var)); + if (!m_lemma.empty()) { + if (m_config.m_minimize_lemmas) { + minimize_lemma(); + reset_lemma_var_marks(); + if (m_config.m_dyn_sub_res) + dyn_sub_res(); + TRACE("sat_lemma", tout << "new lemma (after minimization) size: " << m_lemma.size() << "\n" << m_lemma << "\n";); + } + else + reset_lemma_var_marks(); + + literal_vector::iterator it = m_lemma.begin(); + literal_vector::iterator end = m_lemma.end(); + ++it; + for(; it != end; ++it) { + bool_var var = (*it).var(); + new_scope_lvl = std::max(new_scope_lvl, lvl(var)); + } } unsigned glue = num_diff_levels(m_lemma.size(), m_lemma.c_ptr()); + m_fast_glue_avg.update(glue); + m_slow_glue_avg.update(glue); pop_reinit(m_scope_lvl - new_scope_lvl); - TRACE("sat_conflict_detail", display(tout); tout << "assignment:\n"; display_assignment(tout);); + TRACE("sat_conflict_detail", tout << new_scope_lvl << "\n"; display(tout);); + // unsound: m_asymm_branch.minimize(m_scc, m_lemma); clause * lemma = mk_clause_core(m_lemma.size(), m_lemma.c_ptr(), true); if (lemma) { lemma->set_glue(glue); + if (m_par) m_par->share_clause(*this, *lemma); } decay_activity(); updt_phase_counters(); - return true; } void solver::process_antecedent_for_unsat_core(literal antecedent) { @@ -1836,8 +2414,7 @@ namespace sat { TRACE("sat", tout << "processing consequent: "; if (consequent == null_literal) tout << "null\n"; else tout << consequent << "\n"; - display_justification(tout << "js kind: ", js); - tout << "\n";); + display_justification(tout << "js kind: ", js) << "\n";); switch (js.get_kind()) { case justification::NONE: break; @@ -1851,7 +2428,7 @@ namespace sat { process_antecedent_for_unsat_core(~(js.get_literal2())); break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); + clause & c = get_clause(js); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); @@ -1870,10 +2447,9 @@ namespace sat { } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(consequent, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) - process_antecedent_for_unsat_core(*it); + for (literal l : m_ext_antecedents) { + process_antecedent_for_unsat_core(l); + } break; } default: @@ -1882,125 +2458,10 @@ namespace sat { } } - bool solver::resolve_conflict_for_init() { - if (m_conflict_lvl == 0) { - return false; - } - m_lemma.reset(); - m_lemma.push_back(null_literal); // asserted literal - if (m_not_l != null_literal) { - TRACE("sat", tout << "not_l: " << m_not_l << "\n";); - process_antecedent_for_init(m_not_l); - } - literal consequent = m_not_l; - justification js = m_conflict; - - SASSERT(m_trail.size() > 0); - unsigned idx = m_trail.size(); - while (process_consequent_for_init(consequent, js)) { - while (true) { - --idx; - literal l = m_trail[idx]; - if (is_marked(l.var())) - break; - SASSERT(idx > 0); - } - consequent = m_trail[idx]; - bool_var c_var = consequent.var(); - if (lvl(consequent) == 0) { - return false; - } - SASSERT(m_conflict_lvl == 1); - js = m_justification[c_var]; - reset_mark(c_var); - } - - unsigned new_scope_lvl = 0; - m_lemma[0] = ~consequent; - for (unsigned i = 1; i < m_lemma.size(); ++i) { - bool_var var = m_lemma[i].var(); - if (is_marked(var)) { - reset_mark(var); - new_scope_lvl = std::max(new_scope_lvl, lvl(var)); - } - else { - m_lemma[i] = m_lemma.back(); - m_lemma.pop_back(); - --i; - } - } - TRACE("sat", tout << "lemma: " << m_lemma << "\n"; display(tout); tout << "assignment:\n"; display_assignment(tout);); - if (new_scope_lvl == 0) { - pop_reinit(m_scope_lvl); - } - else { - unassign_vars(idx); - } - mk_clause_core(m_lemma.size(), m_lemma.c_ptr(), true); - TRACE("sat", tout << "Trail: " << m_trail << "\n";); - m_inconsistent = false; - return true; - } - - bool solver::process_consequent_for_init(literal consequent, justification const& js) { - switch (js.get_kind()) { - case justification::NONE: - return false; - case justification::BINARY: - process_antecedent_for_init(~(js.get_literal())); - break; - case justification::TERNARY: - process_antecedent_for_init(~(js.get_literal1())); - process_antecedent_for_init(~(js.get_literal2())); - break; - case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); - unsigned i = 0; - if (consequent != null_literal) { - SASSERT(c[0] == consequent || c[1] == consequent); - if (c[0] == consequent) { - i = 1; - } - else { - process_antecedent_for_init(~c[0]); - i = 2; - } - } - unsigned sz = c.size(); - for (; i < sz; i++) - process_antecedent_for_init(~c[i]); - break; - } - case justification::EXT_JUSTIFICATION: { - fill_ext_antecedents(consequent, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) - process_antecedent_for_init(*it); - break; - } - default: - UNREACHABLE(); - break; - } - return true; - } - - void solver::process_antecedent_for_init(literal antecedent) { - bool_var var = antecedent.var(); - SASSERT(var < num_vars()); - if (!is_marked(var) && lvl(var) > 0) { - mark(var); - m_lemma.push_back(~antecedent); - } - } - - void solver::resolve_conflict_for_unsat_core() { TRACE("sat", display(tout); unsigned level = 0; - for (unsigned i = 0; i < m_trail.size(); ++i) { - literal l = m_trail[i]; + for (literal l : m_trail) { if (level != m_level[l.var()]) { level = m_level[l.var()]; tout << level << ": "; @@ -2020,18 +2481,18 @@ namespace sat { } SASSERT(m_unmark.empty()); DEBUG_CODE({ - for (unsigned i = 0; i < m_trail.size(); ++i) { - SASSERT(!is_marked(m_trail[i].var())); + for (literal lit : m_trail) { + SASSERT(!is_marked(lit.var())); }}); unsigned old_size = m_unmark.size(); int idx = skip_literals_above_conflict_level(); + literal consequent = m_not_l; if (m_not_l != null_literal) { justification js = m_justification[m_not_l.var()]; TRACE("sat", tout << "not_l: " << m_not_l << "\n"; - display_justification(tout, js); - tout << "\n";); + display_justification(tout, js) << "\n";); process_antecedent_for_unsat_core(m_not_l); if (is_assumption(~m_not_l)) { @@ -2040,11 +2501,10 @@ namespace sat { else { process_consequent_for_unsat_core(m_not_l, js); } + consequent = ~m_not_l; } - literal consequent = m_not_l; - justification js = m_conflict; - + justification js = m_conflict; while (true) { process_consequent_for_unsat_core(consequent, js); @@ -2069,6 +2529,14 @@ namespace sat { idx--; } reset_unmark(old_size); + if (m_core.size() > 1) { + unsigned j = 0; + for (unsigned i = 0; i < m_core.size(); ++i) { + if (lvl(m_core[i]) > 0) m_core[j++] = m_core[i]; + } + m_core.shrink(j); + } + if (m_config.m_core_minimize) { if (m_min_core_valid && m_min_core.size() < m_core.size()) { IF_VERBOSE(1, verbose_stream() << "(sat.updating core " << m_min_core.size() << " " << m_core.size() << ")\n";); @@ -2082,62 +2550,35 @@ namespace sat { set_model(m_mus.get_model()); IF_VERBOSE(2, verbose_stream() << "(sat.core: " << m_core << ")\n";); } + } - unsigned solver::get_max_lvl(literal consequent, justification js) { - if (!m_ext) + unsigned solver::get_max_lvl(literal not_l, justification js) { + if (!m_ext || at_base_lvl()) return scope_lvl(); - if (scope_lvl() == 0) - return 0; - - unsigned r = 0; - - if (consequent != null_literal) - r = lvl(consequent); - switch (js.get_kind()) { case justification::NONE: - break; case justification::BINARY: - r = std::max(r, lvl(js.get_literal())); - break; case justification::TERNARY: - r = std::max(r, lvl(js.get_literal1())); - r = std::max(r, lvl(js.get_literal2())); - break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); - unsigned i = 0; - if (consequent != null_literal) { - SASSERT(c[0] == consequent || c[1] == consequent); - if (c[0] == consequent) { - i = 1; - } - else { - r = std::max(r, lvl(c[0])); - i = 2; - } - } - unsigned sz = c.size(); - for (; i < sz; i++) - r = std::max(r, lvl(c[i])); - break; + return scope_lvl(); } case justification::EXT_JUSTIFICATION: { - fill_ext_antecedents(consequent, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) - r = std::max(r, lvl(*it)); - break; + unsigned r = 0; + SASSERT(not_l != null_literal); + r = lvl(not_l); + fill_ext_antecedents(~not_l, js); + for (literal l : m_ext_antecedents) { + r = std::max(r, lvl(l)); + } + return r; } default: UNREACHABLE(); - break; + return 0; } - return r; } /** @@ -2165,7 +2606,16 @@ namespace sat { SASSERT(var < num_vars()); if (!is_marked(var) && var_lvl > 0) { mark(var); - inc_activity(var); + switch (m_config.m_branching_heuristic) { + case BH_VSIDS: + inc_activity(var); + break; + case BH_CHB: + m_last_conflict[var] = m_stats.m_conflict; + break; + default: + break; + } if (var_lvl == m_conflict_lvl) num_marks++; else @@ -2232,6 +2682,51 @@ namespace sat { return r; } + bool solver::num_diff_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue) { + m_diff_levels.reserve(scope_lvl() + 1, false); + glue = 0; + unsigned i = 0; + for (; i < num && glue < max_glue; i++) { + SASSERT(value(lits[i]) != l_undef); + unsigned lit_lvl = lvl(lits[i]); + if (m_diff_levels[lit_lvl] == false) { + m_diff_levels[lit_lvl] = true; + glue++; + } + } + num = i; + // reset m_diff_levels. + for (i = 0; i < num; i++) + m_diff_levels[lvl(lits[i])] = false; + return glue < max_glue; + } + + bool solver::num_diff_false_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue) { + m_diff_levels.reserve(scope_lvl() + 1, false); + glue = 0; + unsigned i = 0; + for (; i < num && glue < max_glue; i++) { + if (value(lits[i]) == l_false) { + unsigned lit_lvl = lvl(lits[i]); + if (m_diff_levels[lit_lvl] == false) { + m_diff_levels[lit_lvl] = true; + glue++; + } + } + } + num = i; + // reset m_diff_levels. + for (i = 0; i < num; i++) { + literal lit = lits[i]; + if (value(lit) == l_false) { + VERIFY(lvl(lit) < m_diff_levels.size()); + m_diff_levels[lvl(lit)] = false; + } + } + return glue < max_glue; + } + + /** \brief Process an antecedent for lemma minimization. */ @@ -2266,7 +2761,7 @@ namespace sat { while (!m_lemma_min_stack.empty()) { bool_var var = m_lemma_min_stack.back(); m_lemma_min_stack.pop_back(); - justification js = m_justification[var]; + justification const& js = m_justification[var]; switch(js.get_kind()) { case justification::NONE: // it is a decision variable from a previous scope level @@ -2289,7 +2784,7 @@ namespace sat { } break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); + clause & c = get_clause(js); unsigned i = 0; if (c[0].var() == var) { i = 1; @@ -2314,10 +2809,8 @@ namespace sat { case justification::EXT_JUSTIFICATION: { literal consequent(var, value(var) == l_false); fill_ext_antecedents(consequent, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) { - if (!process_antecedent_for_minimization(*it)) { + for (literal l : m_ext_antecedents) { + if (!process_antecedent_for_minimization(l)) { reset_unmark(old_size); return false; } @@ -2349,10 +2842,8 @@ namespace sat { */ void solver::updt_lemma_lvl_set() { m_lvl_set.reset(); - literal_vector::const_iterator it = m_lemma.begin(); - literal_vector::const_iterator end = m_lemma.end(); - for(; it != end; ++it) - m_lvl_set.insert(lvl(*it)); + for (literal l : m_lemma) + m_lvl_set.insert(lvl(l)); } /** @@ -2361,6 +2852,7 @@ namespace sat { assigned at level 0. */ void solver::minimize_lemma() { + SASSERT(!m_lemma.empty()); SASSERT(m_unmark.empty()); //m_unmark.reset(); updt_lemma_lvl_set(); @@ -2368,11 +2860,14 @@ namespace sat { unsigned sz = m_lemma.size(); unsigned i = 1; // the first literal is the FUIP unsigned j = 1; + //bool drop = false; + //unsigned bound = sz/5+10; for (; i < sz; i++) { literal l = m_lemma[i]; if (implied_by_marked(l)) { TRACE("sat", tout << "drop: " << l << "\n";); m_unmark.push_back(l.var()); + //drop = true; } else { if (j != i) { @@ -2380,6 +2875,12 @@ namespace sat { } j++; } +#if 0 + if (!drop && i >= bound) { + j = sz; + break; + } +#endif } reset_unmark(0); @@ -2391,6 +2892,10 @@ namespace sat { \brief Reset the mark of the variables in the current lemma. */ void solver::reset_lemma_var_marks() { + if (m_config.m_branching_heuristic == BH_LRB || + m_config.m_branching_heuristic == BH_VSIDS) { + update_lrb_reasoned(); + } literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); SASSERT(!is_marked((*it).var())); @@ -2401,6 +2906,55 @@ namespace sat { } } + void solver::update_lrb_reasoned() { + unsigned sz = m_lemma.size(); + SASSERT(!is_marked(m_lemma[0].var())); + mark(m_lemma[0].var()); + for (unsigned i = m_lemma.size(); i-- > 0; ) { + justification js = m_justification[m_lemma[i].var()]; + switch (js.get_kind()) { + case justification::NONE: + break; + case justification::BINARY: + update_lrb_reasoned(js.get_literal()); + break; + case justification::TERNARY: + update_lrb_reasoned(js.get_literal1()); + update_lrb_reasoned(js.get_literal2()); + break; + case justification::CLAUSE: { + clause & c = get_clause(js); + for (literal l : c) { + update_lrb_reasoned(l); + } + break; + } + case justification::EXT_JUSTIFICATION: { + fill_ext_antecedents(m_lemma[i], js); + for (literal l : m_ext_antecedents) { + update_lrb_reasoned(l); + } + break; + } + } + } + reset_mark(m_lemma[0].var()); + for (unsigned i = m_lemma.size(); i-- > sz; ) { + reset_mark(m_lemma[i].var()); + } + m_lemma.shrink(sz); + } + + void solver::update_lrb_reasoned(literal lit) { + bool_var v = lit.var(); + if (!is_marked(v)) { + mark(v); + m_reasoned[v]++; + inc_activity(v); + m_lemma.push_back(lit); + } + } + /** \brief Apply dynamic subsumption resolution to new lemma. Only binary and ternary clauses are used. @@ -2423,25 +2977,23 @@ namespace sat { continue; // literal was eliminated // first use watch lists watch_list const & wlist = get_wlist(~l); - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { + for (watched const& w : wlist) { // 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 (w.is_binary_clause()) { + literal l2 = w.get_literal(); if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } } - else if (it->is_ternary_clause()) { - literal l2 = it->get_literal1(); - literal l3 = it->get_literal2(); + else if (w.is_ternary_clause()) { + literal l2 = w.get_literal1(); + literal l3 = w.get_literal2(); 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); @@ -2546,6 +3098,7 @@ namespace sat { pop(num_scopes); exchange_par(); reinit_assumptions(); + m_stats.m_units = init_trail_size(); } void solver::pop(unsigned num_scopes) { @@ -2561,6 +3114,8 @@ namespace sat { m_scope_lvl -= num_scopes; m_scopes.shrink(new_lvl); reinit_clauses(s.m_clauses_to_reinit_lim); + if (m_ext) + m_ext->pop_reinit(); } void solver::unassign_vars(unsigned old_sz) { @@ -2574,6 +3129,18 @@ namespace sat { bool_var v = l.var(); SASSERT(value(v) == l_undef); m_case_split_queue.unassign_var_eh(v); + if (m_config.m_branching_heuristic == BH_LRB) { + uint64_t interval = m_stats.m_conflict - m_last_propagation[v]; + if (interval > 0) { + auto activity = m_activity[v]; + auto reward = (m_config.m_reward_offset * (m_participated[v] + m_reasoned[v])) / interval; + m_activity[v] = static_cast(m_step_size * reward + ((1 - m_step_size) * activity)); + m_case_split_queue.activity_changed_eh(v, m_activity[v] > activity); + } + } + if (m_config.m_anti_exploration) { + m_canceled[v] = m_stats.m_conflict; + } } m_trail.shrink(old_sz); m_qhead = old_sz; @@ -2589,7 +3156,7 @@ namespace sat { bool reinit = false; if (cw.is_binary()) { if (propagate_bin_clause(cw[0], cw[1])) { - if (scope_lvl() > 0) { + if (!at_base_lvl()) { m_clauses_to_reinit[j] = cw; j++; } @@ -2599,7 +3166,7 @@ namespace sat { clause & c = *(cw.get_clause()); detach_clause(c); attach_clause(c, reinit); - if (scope_lvl() > 0 && reinit) { + if (!at_base_lvl() && reinit) { // clause propagated literal, must keep it in the reinit stack. m_clauses_to_reinit[j] = cw; j++; @@ -2673,16 +3240,11 @@ namespace sat { } bool_var solver::max_var(clause_vector& clauses, bool_var v) { - for (unsigned i = 0; i < clauses.size(); ++i) { - clause & c = *(clauses[i]); - literal* it = c.begin(); - literal * end = c.end(); - for (; it != end; ++it) { - if (it->var() > v) { + for (clause* cp : clauses) + for (auto it = cp->begin(), end = cp->end(); it != end; ++it) { + if (it->var() > v) v = it->var(); - } } - } return v; } @@ -2693,8 +3255,8 @@ namespace sat { w = max_var(true, w); w = max_var(false, w); v = m_mc.max_var(w); - for (unsigned i = 0; i < m_trail.size(); ++i) { - if (m_trail[i].var() > w) w = m_trail[i].var(); + for (literal lit : m_trail) { + if (lit.var() > w) w = lit.var(); } v = std::max(v, w + 1); } @@ -2768,6 +3330,10 @@ namespace sat { m_probing.updt_params(p); m_scc.updt_params(p); m_rand.set_seed(m_config.m_random_seed); + m_step_size = m_config.m_step_size_init; + m_drat.updt_config(); + m_fast_glue_avg.set_alpha(m_config.m_fast_glue_avg); + m_slow_glue_avg.set_alpha(m_config.m_slow_glue_avg); } void solver::collect_param_descrs(param_descrs & d) { @@ -2785,6 +3351,8 @@ namespace sat { m_scc.collect_statistics(st); m_asymm_branch.collect_statistics(st); m_probing.collect_statistics(st); + if (m_ext) m_ext->collect_statistics(st); + st.copy(m_aux_stats); } void solver::reset_statistics() { @@ -2793,6 +3361,7 @@ namespace sat { m_simplifier.reset_statistics(); m_asymm_branch.reset_statistics(); m_probing.reset_statistics(); + m_aux_stats.reset(); } // ----------------------- @@ -2802,33 +3371,44 @@ namespace sat { // ----------------------- void solver::rescale_activity() { - svector::iterator it = m_activity.begin(); - svector::iterator end = m_activity.end(); - for (; it != end; ++it) { - *it >>= 14; + SASSERT(m_config.m_branching_heuristic == BH_VSIDS); + for (unsigned& act : m_activity) { + act >>= 14; } m_activity_inc >>= 14; } + void solver::update_chb_activity(bool is_sat, unsigned qhead) { + SASSERT(m_config.m_branching_heuristic == BH_CHB); + double multiplier = m_config.m_reward_offset * (is_sat ? m_config.m_reward_multiplier : 1.0); + for (unsigned i = qhead; i < m_trail.size(); ++i) { + auto v = m_trail[i].var(); + auto reward = multiplier / (m_stats.m_conflict - m_last_conflict[v] + 1); + auto activity = m_activity[v]; + m_activity[v] = static_cast(m_step_size * reward + ((1.0 - m_step_size) * activity)); + m_case_split_queue.activity_changed_eh(v, m_activity[v] > activity); + } + } + // ----------------------- // // Iterators // // ----------------------- - void solver::collect_bin_clauses(svector & r, bool learned) const { + void solver::collect_bin_clauses(svector & r, bool learned, bool learned_only) const { + SASSERT(learned || !learned_only); unsigned sz = m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; l_idx++) { literal l = to_literal(l_idx); l.neg(); - watch_list const & wlist = m_watches[l_idx]; - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { - if (!it->is_binary_clause()) + for (watched const& w : m_watches[l_idx]) { + if (!w.is_binary_clause()) continue; - if (!learned && it->is_learned()) + if (!learned && w.is_learned()) continue; - literal l2 = it->get_literal(); + else if (learned && learned_only && !w.is_learned()) + continue; + literal l2 = w.get_literal(); if (l.index() > l2.index()) continue; TRACE("cleanup_bug", tout << "collected: " << l << " " << l2 << "\n";); @@ -2845,7 +3425,8 @@ namespace sat { bool solver::check_invariant() const { if (!m_rlimit.inc()) return true; integrity_checker checker(*this); - SASSERT(checker()); + VERIFY(checker()); + VERIFY(!m_ext || m_ext->validate()); return true; } @@ -2861,27 +3442,32 @@ namespace sat { for (unsigned l_idx = 0; l_idx < sz; l_idx++) { literal l = to_literal(l_idx); l.neg(); - watch_list const & wlist = m_watches[l_idx]; - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { - if (!it->is_binary_clause()) + for (watched const& w : m_watches[l_idx]) { + if (!w.is_binary_clause()) continue; - literal l2 = it->get_literal(); + literal l2 = w.get_literal(); if (l.index() > l2.index()) continue; - out << "(" << l << " " << l2 << ")\n"; + out << "(" << l << " " << l2 << ")"; + if (w.is_learned()) out << "*"; + out << "\n"; } } } void solver::display_units(std::ostream & out) const { - unsigned end = init_trail_size(); - for (unsigned i = 0; i < end; i++) { - out << m_trail[i] << " "; + unsigned level = 0; + for (literal lit : m_trail) { + if (lvl(lit) > level) { + level = lvl(lit); + out << level << ": "; + } + else { + out << " "; + } + out << lit << " "; + display_justification(out, m_justification[lit.var()]) << "\n"; } - if (end != 0) - out << "\n"; } void solver::display(std::ostream & out) const { @@ -2889,66 +3475,69 @@ namespace sat { display_units(out); display_binary(out); out << m_clauses << m_learned; + if (m_ext) { + m_ext->display(out); + } out << ")\n"; } - void solver::display_justification(std::ostream & out, justification const& js) const { + std::ostream& solver::display_justification(std::ostream & out, justification const& js) const { out << js; if (js.is_clause()) { - out << *(m_cls_allocator.get_clause(js.get_clause_offset())); + out << get_clause(js); } + else if (js.is_ext_justification() && m_ext) { + m_ext->display_justification(out << " ", js.get_ext_justification_idx()); + } + return out; } unsigned solver::num_clauses() const { - unsigned num_cls = 0; - num_cls += m_trail.size(); // units; - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - literal l = ~to_literal(l_idx); - watch_list const & wlist = *it; - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_clause() && l.index() < it2->get_literal().index()) + unsigned num_cls = m_trail.size(); // units; + unsigned l_idx = 0; + for (auto const& wl : m_watches) { + literal l = ~to_literal(l_idx++); + for (auto const& w : wl) { + if (w.is_binary_clause() && l.index() < w.get_literal().index()) num_cls++; } } - clause_vector const * vs[2] = { &m_clauses, &m_learned }; - for (unsigned i = 0; i < 2; i++) { - clause_vector const & cs = *(vs[i]); - num_cls += cs.size(); + return num_cls + m_clauses.size() + m_learned.size(); + } + + void solver::num_binary(unsigned& given, unsigned& learned) const { + given = learned = 0; + unsigned l_idx = 0; + for (auto const& wl : m_watches) { + literal l = ~to_literal(l_idx++); + for (auto const& w : wl) { + if (w.is_binary_clause() && l.index() < w.get_literal().index()) { + if (w.is_learned()) ++learned; else ++given; + } + } } - return num_cls; } void solver::display_dimacs(std::ostream & out) const { out << "p cnf " << num_vars() << " " << num_clauses() << "\n"; - for (unsigned i = 0; i < m_trail.size(); i++) { - out << dimacs_lit(m_trail[i]) << " 0\n"; + for (literal lit : m_trail) { + out << dimacs_lit(lit) << " 0\n"; } - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - literal l = ~to_literal(l_idx); - watch_list const & wlist = *it; - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_clause() && l.index() < it2->get_literal().index()) - out << dimacs_lit(l) << " " << dimacs_lit(it2->get_literal()) << " 0\n"; + unsigned l_idx = 0; + for (auto const& wlist : m_watches) { + literal l = ~to_literal(l_idx++); + for (auto const& w : wlist) { + if (w.is_binary_clause() && l.index() < w.get_literal().index()) + out << dimacs_lit(l) << " " << dimacs_lit(w.get_literal()) << " 0\n"; } } clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - clause const & c = *(*it); - unsigned sz = c.size(); - for (unsigned j = 0; j < sz; j++) - out << dimacs_lit(c[j]) << " "; + for (auto cp : cs) { + for (literal l : *cp) { + out << dimacs_lit(l) << " "; + } out << "0\n"; } } @@ -2964,32 +3553,26 @@ namespace sat { out << "p wcnf " << num_vars() << " " << num_clauses() + sz << " " << max_weight << "\n"; out << "c soft " << sz << "\n"; - for (unsigned i = 0; i < m_trail.size(); i++) { - out << max_weight << " " << dimacs_lit(m_trail[i]) << " 0\n"; + for (literal lit : m_trail) { + out << max_weight << " " << dimacs_lit(lit) << " 0\n"; } - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { + unsigned l_idx = 0; + for (watch_list const& wlist : m_watches) { literal l = ~to_literal(l_idx); - watch_list const & wlist = *it; - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_clause() && l.index() < it2->get_literal().index()) - out << max_weight << " " << dimacs_lit(l) << " " << dimacs_lit(it2->get_literal()) << " 0\n"; + for (watched const& w : wlist) { + if (w.is_binary_clause() && l.index() < w.get_literal().index()) + out << max_weight << " " << dimacs_lit(l) << " " << dimacs_lit(w.get_literal()) << " 0\n"; } + ++l_idx; } clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - clause const & c = *(*it); - unsigned clsz = c.size(); + for (clause const* cp : cs) { + clause const & c = *cp; out << max_weight << " "; - for (unsigned j = 0; j < clsz; j++) - out << dimacs_lit(c[j]) << " "; + for (literal l : c) + out << dimacs_lit(l) << " "; out << "0\n"; } } @@ -2999,19 +3582,23 @@ namespace sat { out.flush(); } + void solver::display_watches(std::ostream & out, literal lit) const { + display_watch_list(out << lit << ": ", get_wlist(lit)) << "\n"; + } void solver::display_watches(std::ostream & out) const { - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - watch_list const & wlist = *it; - literal l = to_literal(l_idx); - out << l << ": "; - sat::display(out, m_cls_allocator, wlist); - out << "\n"; + unsigned l_idx = 0; + for (watch_list const& wlist : m_watches) { + literal l = to_literal(l_idx++); + if (!wlist.empty()) + display_watch_list(out << l << ": ", wlist) << "\n"; } } + std::ostream& solver::display_watch_list(std::ostream& out, watch_list const& wl) const { + return sat::display_watch_list(out, cls_allocator(), wl); + } + void solver::display_assignment(std::ostream & out) const { out << m_trail << "\n"; } @@ -3021,9 +3608,8 @@ namespace sat { */ bool solver::is_unit(clause const & c) const { bool found_undef = false; - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - switch (value(c[i])) { + for (literal l : c) { + switch (value(l)) { case l_undef: if (found_undef) return false; @@ -3042,24 +3628,20 @@ namespace sat { \brief Return true, if all literals in c are assigned to false. */ bool solver::is_empty(clause const & c) const { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - if (value(c[i]) != l_false) + for (literal lit : c) + if (value(lit) != l_false) return false; - } return true; } bool solver::check_missed_propagation(clause_vector const & cs) const { - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - clause const & c = *(*it); + for (clause* cp : cs) { + clause const & c = *cp; if (c.frozen()) continue; if (is_empty(c) || is_unit(c)) { TRACE("sat_missed_prop", tout << "missed_propagation: " << c << "\n"; - for (unsigned i = 0; i < c.size(); i++) tout << c[i] << ": " << value(c[i]) << "\n";); + for (literal l : c) tout << l << ": " << value(l) << "\n";); UNREACHABLE(); } SASSERT(!is_empty(c)); @@ -3080,14 +3662,14 @@ namespace sat { // // ----------------------- void solver::cleanup() { - if (scope_lvl() > 0 || inconsistent()) + if (!at_base_lvl() || inconsistent()) return; if (m_cleaner() && m_ext) m_ext->clauses_modifed(); } void solver::simplify(bool learned) { - if (scope_lvl() > 0 || inconsistent()) + if (!at_base_lvl() || inconsistent()) return; m_simplifier(learned); m_simplifier.finalize(); @@ -3096,7 +3678,7 @@ namespace sat { } unsigned solver::scc_bin() { - if (scope_lvl() > 0 || inconsistent()) + if (!at_base_lvl() || inconsistent()) return 0; unsigned r = m_scc(); if (r > 0 && m_ext) @@ -3121,11 +3703,10 @@ namespace sat { m_user_bin_clauses.reset(); m_binary_clause_graph.reset(); collect_bin_clauses(m_user_bin_clauses, true); - collect_bin_clauses(m_user_bin_clauses, false); hashtable, default_eq > seen_bc; - for (unsigned i = 0; i < m_user_bin_clauses.size(); ++i) { - literal l1 = m_user_bin_clauses[i].first; - literal l2 = m_user_bin_clauses[i].second; + for (auto const& b : m_user_bin_clauses) { + literal l1 = b.first; + literal l2 = b.second; literal_pair p(l1, l2); if (!seen_bc.contains(p)) { seen_bc.insert(p); @@ -3133,17 +3714,21 @@ namespace sat { } } vector _mutexes; + literal_vector _lits(lits); + if (m_ext) { + // m_ext->find_mutexes(_lits, mutexes); + } unsigned_vector ps; - for (unsigned i = 0; i < lits.size(); ++i) { - ps.push_back(lits[i].index()); + for (literal lit : _lits) { + ps.push_back(lit.index()); } mc.cliques(ps, _mutexes); - for (unsigned i = 0; i < _mutexes.size(); ++i) { - literal_vector lits; - for (unsigned j = 0; j < _mutexes[i].size(); ++j) { - lits.push_back(to_literal(_mutexes[i][j])); + for (auto const& mux : _mutexes) { + literal_vector clique; + for (auto const& idx : mux) { + clique.push_back(to_literal(idx)); } - mutexes.push_back(lits); + mutexes.push_back(clique); } return l_true; } @@ -3179,10 +3764,9 @@ namespace sat { } static void brute_force_consequences(sat::solver& s, sat::literal_vector const& asms, sat::literal_vector const& gamma, vector& conseq) { - for (unsigned i = 0; i < gamma.size(); ++i) { - sat::literal nlit = ~gamma[i]; + for (literal lit : gamma) { sat::literal_vector asms1(asms); - asms1.push_back(nlit); + asms1.push_back(~lit); lbool r = s.check(asms1.size(), asms1.c_ptr()); if (r == l_false) { conseq.push_back(s.get_core()); @@ -3192,8 +3776,8 @@ namespace sat { static lbool core_chunking(sat::solver& s, model const& m, sat::bool_var_vector const& vars, sat::literal_vector const& asms, vector& conseq, unsigned K) { sat::literal_vector lambda; - for (unsigned i = 0; i < vars.size(); i++) { - lambda.push_back(sat::literal(vars[i], m[vars[i]] == l_false)); + for (bool_var v : vars) { + lambda.push_back(sat::literal(v, m[v] == l_false)); } while (!lambda.empty()) { IF_VERBOSE(1, verbose_stream() << "(sat-backbone-core " << lambda.size() << " " << conseq.size() << ")\n";); @@ -3290,13 +3874,23 @@ namespace sat { s |= m_antecedents.find(m_core[i].var()); } m_core.reset(); - index_set::iterator it = s.begin(), end = s.end(); - for (; it != end; ++it) { - m_core.push_back(to_literal(*it)); + for (unsigned idx : s) { + m_core.push_back(to_literal(idx)); } TRACE("sat", tout << m_core << "\n";); } + bool solver::reached_max_conflicts() { + if (m_config.m_max_conflicts == 0 || m_conflicts_since_init > m_config.m_max_conflicts) { + if (m_reason_unknown != "sat.max.conflicts") { + m_reason_unknown = "sat.max.conflicts"; + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = " << m_conflicts_since_init << "\")\n";); + } + return true; + } + return false; + } + lbool solver::get_bounded_consequences(literal_vector const& asms, bool_var_vector const& vars, vector& conseq) { bool_var_set unfixed_vars; @@ -3342,12 +3936,11 @@ namespace sat { extract_fixed_consequences(num_units, asms, unfixed_vars, conseq); - if (m_conflicts > m_config.m_max_conflicts) { - IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = " << m_conflicts << "\")\n";); + if (reached_max_conflicts()) { return l_undef; } - restart(); + restart(true); simplify_problem(); if (check_inconsistent()) { fixup_consequence_core(); @@ -3386,24 +3979,23 @@ namespace sat { } propagate(false); if (check_inconsistent()) return l_false; + SASSERT(search_lvl() == 1); unsigned num_iterations = 0; extract_fixed_consequences(unfixed_lits, assumptions, unfixed_vars, conseq); update_unfixed_literals(unfixed_lits, unfixed_vars); while (!unfixed_lits.empty()) { - if (scope_lvl() > 1) { - pop(scope_lvl() - 1); + if (scope_lvl() > search_lvl()) { + pop(scope_lvl() - search_lvl()); } propagate(false); ++num_iterations; checkpoint(); - literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); unsigned num_resolves = 0; unsigned num_fixed = 0; unsigned num_assigned = 0; lbool is_sat = l_true; - for (; it != end; ++it) { - literal lit = *it; + for (literal lit : unfixed_lits) { if (value(lit) != l_undef) { ++num_fixed; if (lvl(lit) <= 1 && value(lit) == l_true) { @@ -3425,7 +4017,7 @@ namespace sat { propagate(false); ++num_resolves; } - if (false && scope_lvl() == 1) { + if (false && scope_lvl() == search_lvl()) { is_sat = l_undef; break; } @@ -3434,14 +4026,14 @@ namespace sat { extract_fixed_consequences(unfixed_lits, assumptions, unfixed_vars, conseq); if (is_sat == l_true) { - if (scope_lvl() == 1 && num_resolves > 0) { + if (scope_lvl() == search_lvl() && num_resolves > 0) { IF_VERBOSE(1, verbose_stream() << "(sat.get-consequences backjump)\n";); is_sat = l_undef; } else { is_sat = bounded_search(); if (is_sat == l_undef) { - restart(); + restart(true); } extract_fixed_consequences(unfixed_lits, assumptions, unfixed_vars, conseq); } @@ -3472,9 +4064,7 @@ namespace sat { void solver::delete_unfixed(literal_set& unfixed_lits, bool_var_set& unfixed_vars) { literal_set to_keep; - literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); - for (; it != end; ++it) { - literal lit = *it; + for (literal lit : unfixed_lits) { if (value(lit) == l_true) { to_keep.insert(lit); } @@ -3487,9 +4077,7 @@ namespace sat { void solver::update_unfixed_literals(literal_set& unfixed_lits, bool_var_set& unfixed_vars) { literal_vector to_delete; - literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); - for (; it != end; ++it) { - literal lit = *it; + for (literal lit : unfixed_lits) { if (!unfixed_vars.contains(lit.var())) { to_delete.push_back(lit); } @@ -3510,9 +4098,7 @@ namespace sat { } void solver::extract_fixed_consequences(literal_set const& unfixed_lits, literal_set const& assumptions, bool_var_set& unfixed_vars, vector& conseq) { - literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); - for (; it != end; ++it) { - literal lit = *it; + for (literal lit: unfixed_lits) { TRACE("sat", tout << "extract: " << lit << " " << value(lit) << " " << lvl(lit) << "\n";); if (lvl(lit) <= 1 && value(lit) == l_true) { @@ -3551,11 +4137,11 @@ namespace sat { s |= m_antecedents.find(js.get_literal2().var()); break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); - for (unsigned i = 0; i < c.size(); ++i) { - if (c[i] != lit) { - if (check_domain(lit, ~c[i]) && all_found) { - s |= m_antecedents.find(c[i].var()); + clause & c = get_clause(js); + for (literal l : c) { + if (l != lit) { + if (check_domain(lit, ~l) && all_found) { + s |= m_antecedents.find(l.var()); } else { all_found = false; @@ -3566,11 +4152,9 @@ namespace sat { } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(lit, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) { - if (check_domain(lit, *it) && all_found) { - s |= m_antecedents.find(it->var()); + for (literal l : m_ext_antecedents) { + if (check_domain(lit, l) && all_found) { + s |= m_antecedents.find(l.var()); } else { all_found = false; @@ -3587,10 +4171,8 @@ namespace sat { } std::ostream& solver::display_index_set(std::ostream& out, index_set const& s) const { - index_set::iterator it = s.begin(); - index_set::iterator end = s.end(); - for (; it != end; ++it) { - out << to_literal(*it) << " "; + for (unsigned idx : s) { + out << to_literal(idx) << " "; } return out; } @@ -3615,9 +4197,8 @@ namespace sat { if (unfixed.contains(lit.var())) { literal_vector cons; cons.push_back(lit); - index_set::iterator it = s.begin(), end = s.end(); - for (; it != end; ++it) { - cons.push_back(to_literal(*it)); + for (unsigned idx : s) { + cons.push_back(to_literal(idx)); } unfixed.remove(lit.var()); conseq.push_back(cons); @@ -3635,14 +4216,6 @@ namespace sat { } } - void solver::asymmetric_branching() { - if (scope_lvl() > 0 || inconsistent()) - return; - m_asymm_branch(); - if (m_ext) - m_ext->clauses_modifed(); - } - // ----------------------- // // Statistics @@ -3653,17 +4226,13 @@ namespace sat { unsigned num_bin = 0; unsigned num_ext = 0; unsigned num_lits = 0; - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - literal l = ~to_literal(l_idx); - watch_list const & wlist = *it; - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - switch (it2->get_kind()) { + unsigned l_idx = 0; + for (watch_list const& wlist : m_watches) { + literal l = ~to_literal(l_idx++); + for (watched const& w : wlist) { + switch (w.get_kind()) { case watched::BINARY: - if (l.index() < it2->get_literal().index()) { + if (l.index() < w.get_literal().index()) { num_lits += 2; num_bin++; } @@ -3686,10 +4255,8 @@ namespace sat { clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - clause & c = *(*it); + for (clause* cp : cs) { + clause & c = *cp; if (c.size() == 3) num_ter++; else @@ -3729,35 +4296,26 @@ namespace sat { st.update("minimized lits", m_minimized_lits); st.update("dyn subsumption resolution", m_dyn_sub_res); st.update("blocked correction sets", m_blocked_corr_sets); + st.update("units", m_units); + st.update("elim bool vars res", m_elim_var_res); + st.update("elim bool vars bdd", m_elim_var_bdd); } void stats::reset() { - m_mk_var = 0; - m_mk_bin_clause = 0; - m_mk_ter_clause = 0; - m_mk_clause = 0; - m_conflict = 0; - m_propagate = 0; - m_bin_propagate = 0; - m_ter_propagate = 0; - m_decision = 0; - m_restart = 0; - m_gc_clause = 0; - m_del_clause = 0; - m_minimized_lits = 0; - m_dyn_sub_res = 0; - m_non_learned_generation = 0; - m_blocked_corr_sets = 0; + memset(this, 0, sizeof(*this)); } void mk_stat::display(std::ostream & out) const { + unsigned given, learned; + m_solver.num_binary(given, learned); if (!m_solver.m_clauses.empty()) - out << " :clauses " << m_solver.m_clauses.size(); + out << " :clauses " << m_solver.m_clauses.size() + given << "/" << given; if (!m_solver.m_learned.empty()) { - out << " :learned " << (m_solver.m_learned.size() - m_solver.m_num_frozen); + out << " :learned " << (m_solver.m_learned.size() + learned - m_solver.m_num_frozen) << "/" << learned; if (m_solver.m_num_frozen > 0) out << " :frozen " << m_solver.m_num_frozen; } + out << " :units " << m_solver.init_trail_size(); out << " :gc-clause " << m_solver.m_stats.m_gc_clause; out << mem_stat(); } diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 7f5a02ab3..b44c04604 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -19,6 +19,7 @@ Revision History: #ifndef SAT_SOLVER_H_ #define SAT_SOLVER_H_ + #include "sat/sat_types.h" #include "sat/sat_clause.h" #include "sat/sat_watched.h" @@ -33,12 +34,17 @@ Revision History: #include "sat/sat_iff3_finder.h" #include "sat/sat_probing.h" #include "sat/sat_mus.h" +#include "sat/sat_drat.h" +#include "sat/sat_parallel.h" +#include "sat/sat_local_search.h" #include "sat/sat_par.h" #include "util/params.h" #include "util/statistics.h" #include "util/stopwatch.h" +#include "util/ema.h" #include "util/trace.h" #include "util/rlimit.h" +#include "util/scoped_ptr_vector.h" namespace sat { @@ -62,6 +68,9 @@ namespace sat { unsigned m_dyn_sub_res; unsigned m_non_learned_generation; unsigned m_blocked_corr_sets; + unsigned m_elim_var_res; + unsigned m_elim_var_bdd; + unsigned m_units; stats() { reset(); } void reset(); void collect_statistics(statistics & st) const; @@ -75,10 +84,11 @@ namespace sat { bool m_checkpoint_enabled; config m_config; stats m_stats; - extension * m_ext; - par* m_par; + scoped_ptr m_ext; + parallel* m_par; random_gen m_rand; - clause_allocator m_cls_allocator; + clause_allocator m_cls_allocator[2]; + bool m_cls_allocator_idx; cleaner m_cleaner; model m_model; model_converter m_mc; @@ -88,7 +98,9 @@ namespace sat { asymm_branch m_asymm_branch; probing m_probing; mus m_mus; // MUS for minimal core extraction + drat m_drat; // DRAT for generating proofs bool m_inconsistent; + bool m_searching; // A conflict is usually a single justification. That is, a justification // for false. If m_not_l is not null_literal, then m_conflict is a // justification for l, and the conflict is union of m_no_l and m_conflict; @@ -106,8 +118,17 @@ namespace sat { svector m_eliminated; svector m_external; svector m_level; + // branch variable selection: svector m_activity; unsigned m_activity_inc; + svector m_last_conflict; + svector m_last_propagation; + svector m_participated; + svector m_canceled; + svector m_reasoned; + int m_action; + double m_step_size; + // phase svector m_phase; svector m_prev_phase; svector m_assigned_since_gc; @@ -116,8 +137,13 @@ namespace sat { var_queue m_case_split_queue; unsigned m_qhead; unsigned m_scope_lvl; + unsigned m_search_lvl; + ema m_fast_glue_avg; + ema m_slow_glue_avg; literal_vector m_trail; clause_wrapper_vector m_clauses_to_reinit; + std::string m_reason_unknown; + struct scope { unsigned m_trail_lim; unsigned m_clauses_to_reinit_lim; @@ -131,24 +157,39 @@ namespace sat { literal_set m_assumption_set; // set of enabled assumptions literal_vector m_core; // unsat core + unsigned m_par_id; unsigned m_par_limit_in; unsigned m_par_limit_out; unsigned m_par_num_vars; + bool m_par_syncing_clauses; - void del_clauses(clause * const * begin, clause * const * end); + class lookahead* m_cuber; + + statistics m_aux_stats; + + void del_clauses(clause_vector& clauses); friend class integrity_checker; friend class cleaner; friend class simplifier; friend class scc; + friend class big; friend class elim_eqs; friend class asymm_branch; friend class probing; friend class iff3_finder; friend class mus; + friend class drat; + friend class ba_solver; + friend class parallel; + friend class lookahead; + friend class local_search; + friend class unit_walk; friend struct mk_stat; + friend class elim_vars; + friend class scoped_detach; public: - solver(params_ref const & p, reslimit& l, extension * ext); + solver(params_ref const & p, reslimit& l); ~solver(); // ----------------------- @@ -156,7 +197,7 @@ namespace sat { // Misc // // ----------------------- - void updt_params(params_ref const & p); + void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void collect_statistics(statistics & st) const; @@ -177,16 +218,24 @@ namespace sat { // // ----------------------- bool_var mk_var(bool ext = false, bool dvar = true); - void mk_clause(literal_vector const& lits) { mk_clause(lits.size(), lits.c_ptr()); } - void mk_clause(unsigned num_lits, literal * lits); - void mk_clause(literal l1, literal l2); - void mk_clause(literal l1, literal l2, literal l3); + void mk_clause(literal_vector const& lits, bool learned = false) { mk_clause(lits.size(), lits.c_ptr(), learned); } + void mk_clause(unsigned num_lits, literal * lits, bool learned = false); + void mk_clause(literal l1, literal l2, bool learned = false); + void mk_clause(literal l1, literal l2, literal l3, bool learned = false); protected: + inline clause_allocator& cls_allocator() { return m_cls_allocator[m_cls_allocator_idx]; } + inline clause_allocator const& cls_allocator() const { return m_cls_allocator[m_cls_allocator_idx]; } + inline clause * alloc_clause(unsigned num_lits, literal const * lits, bool learned) { return cls_allocator().mk_clause(num_lits, lits, learned); } + inline void dealloc_clause(clause* c) { cls_allocator().del_clause(c); } + struct cmp_activity; + void defrag_clauses(); + bool should_defrag(); + bool memory_pressure(); void del_clause(clause & c); clause * mk_clause_core(unsigned num_lits, literal * lits, bool learned); - void mk_clause_core(literal_vector const& lits) { mk_clause_core(lits.size(), lits.c_ptr()); } - void mk_clause_core(unsigned num_lits, literal * lits) { mk_clause_core(num_lits, lits, false); } + clause * mk_clause_core(literal_vector const& lits) { return mk_clause_core(lits.size(), lits.c_ptr()); } + clause * mk_clause_core(unsigned num_lits, literal * lits) { return mk_clause_core(num_lits, lits, false); } void mk_clause_core(literal l1, literal l2) { literal lits[2] = { l1, l2 }; mk_clause_core(2, lits); } void mk_bin_clause(literal l1, literal l2, bool learned); bool propagate_bin_clause(literal l1, literal l2); @@ -196,25 +245,13 @@ namespace sat { bool attach_nary_clause(clause & c); void attach_clause(clause & c, bool & reinit); void attach_clause(clause & c) { bool reinit; attach_clause(c, reinit); } - class scoped_detach { - solver& s; - clause& c; - bool m_deleted; - public: - scoped_detach(solver& s, clause& c): s(s), c(c), m_deleted(false) { - s.detach_clause(c); - } - ~scoped_detach() { - if (!m_deleted) s.attach_clause(c); - } + void set_learned(clause& c, bool learned); + void set_learned(literal l1, literal l2, bool learned); + void set_learned1(literal l1, literal l2, bool learned); + void add_ate(clause& c) { m_mc.add_ate(c); } + void add_ate(literal l1, literal l2) { m_mc.add_ate(l1, l2); } + void add_ate(literal_vector const& lits) { m_mc.add_ate(lits); } - void del_clause() { - if (!m_deleted) { - s.del_clause(c); - m_deleted = true; - } - } - }; class scoped_disable_checkpoint { solver& s; public: @@ -245,16 +282,26 @@ namespace sat { bool inconsistent() const { return m_inconsistent; } unsigned num_vars() const { return m_level.size(); } unsigned num_clauses() const; + void num_binary(unsigned& given, unsigned& learned) const; unsigned num_restarts() const { return m_restarts; } bool is_external(bool_var v) const { return m_external[v] != 0; } - void set_external(bool_var v) { m_external[v] = true; } + bool is_external(literal l) const { return is_external(l.var()); } + void set_external(bool_var v); + void set_non_external(bool_var v); bool was_eliminated(bool_var v) const { return m_eliminated[v] != 0; } + void set_eliminated(bool_var v, bool f) { m_eliminated[v] = f; } + bool was_eliminated(literal l) const { return was_eliminated(l.var()); } unsigned scope_lvl() const { return m_scope_lvl; } + unsigned search_lvl() const { return m_search_lvl; } + bool at_search_lvl() const { return m_scope_lvl == m_search_lvl; } + bool at_base_lvl() const { return m_scope_lvl == 0; } lbool value(literal l) const { return static_cast(m_assignment[l.index()]); } lbool value(bool_var v) const { return static_cast(m_assignment[literal(v, false).index()]); } unsigned lvl(bool_var v) const { return m_level[v]; } unsigned lvl(literal l) const { return m_level[l.var()]; } - unsigned init_trail_size() const { return scope_lvl() == 0 ? m_trail.size() : m_scopes[0].m_trail_lim; } + unsigned init_trail_size() const { return at_base_lvl() ? m_trail.size() : m_scopes[0].m_trail_lim; } + literal trail_literal(unsigned i) const { return m_trail[i]; } + literal scope_literal(unsigned n) const { return m_trail[m_scopes[n].m_trail_lim]; } void assign(literal l, justification j) { TRACE("sat_assign", tout << l << " previous value: " << value(l) << "\n";); switch (value(l)) { @@ -267,7 +314,7 @@ namespace sat { void set_conflict(justification c, literal not_l); void set_conflict(justification c) { set_conflict(c, null_literal); } lbool status(clause const & c) const; - clause_offset get_offset(clause const & c) const { return m_cls_allocator.get_offset(&c); } + clause_offset get_offset(clause const & c) const { return cls_allocator().get_offset(&c); } void checkpoint() { if (!m_checkpoint_enabled) return; if (!m_rlimit.inc()) { @@ -280,9 +327,15 @@ namespace sat { m_num_checkpoints = 0; if (memory::get_allocation_size() > m_config.m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); } - void set_par(par* p); + void set_par(parallel* p, unsigned id); bool canceled() { return !m_rlimit.inc(); } - config const& get_config() { return m_config; } + config const& get_config() const { return m_config; } + void set_incremental(bool b) { m_config.m_incremental = b; } + bool is_incremental() const { return m_config.m_incremental; } + extension* get_extension() const { return m_ext.get(); } + void set_extension(extension* e); + bool set_root(literal l, literal r); + void flush_roots(); typedef std::pair bin_clause; protected: watch_list & get_wlist(literal l) { return m_watches[l.index()]; } @@ -321,16 +374,26 @@ namespace sat { bool model_is_current() const { return m_model_is_current; } literal_vector const& get_core() const { return m_core; } model_converter const & get_model_converter() const { return m_mc; } + void flush(model_converter& mc) { mc.flush(m_mc); } void set_model(model const& mdl); + char const* get_reason_unknown() const { return m_reason_unknown.c_str(); } + bool check_clauses(model const& m) const; + bool is_assumption(bool_var v) const; + + lbool cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level); protected: - unsigned m_conflicts; + + unsigned m_conflicts_since_init; unsigned m_restarts; + unsigned m_restart_next_out; unsigned m_conflicts_since_restart; + unsigned m_simplifications; unsigned m_restart_threshold; unsigned m_luby_idx; unsigned m_conflicts_since_gc; unsigned m_gc_threshold; + unsigned m_defrag_threshold; unsigned m_num_checkpoints; double m_min_d_tk; unsigned m_next_simplify; @@ -343,6 +406,7 @@ namespace sat { literal_vector m_min_core; bool m_min_core_valid; + void init_reason_unknown() { m_reason_unknown = "no reason given"; } void init_assumptions(unsigned num_lits, literal const* lits); void reassert_min_core(); void update_min_core(); @@ -356,10 +420,16 @@ namespace sat { void simplify_problem(); void mk_model(); bool check_model(model const & m) const; - void restart(); + void restart(bool to_base); + unsigned restart_level(bool to_base); + bool should_restart() const; + void set_next_restart(); + bool reached_max_conflicts(); void sort_watch_lits(); void exchange_par(); lbool check_par(unsigned num_lits, literal const* lits); + lbool do_local_search(unsigned num_lits, literal const* lits); + lbool do_unit_walk(); // ----------------------- // @@ -386,7 +456,26 @@ namespace sat { if (value(l0) != l_true) return true; justification const & jst = m_justification[l0.var()]; - return !jst.is_clause() || m_cls_allocator.get_clause(jst.get_clause_offset()) != &c; + return !jst.is_clause() || cls_allocator().get_clause(jst.get_clause_offset()) != &c; + } + + clause& get_clause(watch_list::iterator it) const { + SASSERT(it->get_kind() == watched::CLAUSE); + return get_clause(it->get_clause_offset()); + } + + clause& get_clause(watched const& w) const { + SASSERT(w.get_kind() == watched::CLAUSE); + return get_clause(w.get_clause_offset()); + } + + clause& get_clause(justification const& j) const { + SASSERT(j.is_clause()); + return get_clause(j.get_clause_offset()); + } + + clause& get_clause(clause_offset cls_off) const { + return *(cls_allocator().get_clause(cls_off)); } // ----------------------- @@ -400,20 +489,20 @@ namespace sat { literal_vector m_ext_antecedents; bool resolve_conflict(); bool resolve_conflict_core(); + void learn_lemma_and_backjump(); unsigned get_max_lvl(literal consequent, justification js); void process_antecedent(literal antecedent, unsigned & num_marks); void resolve_conflict_for_unsat_core(); void process_antecedent_for_unsat_core(literal antecedent); void process_consequent_for_unsat_core(literal consequent, justification const& js); - bool resolve_conflict_for_init(); - void process_antecedent_for_init(literal antecedent); - bool process_consequent_for_init(literal consequent, justification const& js); void fill_ext_antecedents(literal consequent, justification js); unsigned skip_literals_above_conflict_level(); void forget_phase_of_vars(unsigned from_lvl); void updt_phase_counters(); svector m_diff_levels; unsigned num_diff_levels(unsigned num, literal const * lits); + bool num_diff_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue); + bool num_diff_false_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue); // lemma minimization typedef approx_set_tpl level_approx_set; @@ -476,8 +565,13 @@ namespace sat { lbool get_consequences(literal_vector const& assms, bool_var_vector const& vars, vector& conseq); + // initialize and retrieve local search. + // local_search& init_local_search(); + private: + unsigned get_hash() const; + typedef hashtable index_set; u_map m_antecedents; @@ -523,13 +617,19 @@ namespace sat { } void decay_activity() { - m_activity_inc *= 11; - m_activity_inc /= 10; + m_activity_inc *= m_config.m_variable_decay; + m_activity_inc /= 100; } private: void rescale_activity(); + void update_chb_activity(bool is_sat, unsigned qhead); + + void update_lrb_reasoned(); + + void update_lrb_reasoned(literal lit); + // ----------------------- // // Iterators @@ -540,7 +640,9 @@ namespace sat { clause * const * end_clauses() const { return m_clauses.end(); } clause * const * begin_learned() const { return m_learned.begin(); } clause * const * end_learned() const { return m_learned.end(); } - void collect_bin_clauses(svector & r, bool learned) const; + clause_vector const& learned() const { return m_learned; } + clause_vector const& clauses() const { return m_clauses; } + void collect_bin_clauses(svector & r, bool learned, bool learned_only = false) const; // ----------------------- // @@ -551,10 +653,12 @@ namespace sat { bool check_invariant() const; void display(std::ostream & out) const; void display_watches(std::ostream & out) const; + void display_watches(std::ostream & out, literal lit) const; void display_dimacs(std::ostream & out) const; void display_wcnf(std::ostream & out, unsigned sz, literal const* lits, unsigned const* weights) const; void display_assignment(std::ostream & out) const; - void display_justification(std::ostream & out, justification const& j) const; + std::ostream& display_justification(std::ostream & out, justification const& j) const; + std::ostream& display_watch_list(std::ostream& out, watch_list const& wl) const; protected: void display_binary(std::ostream & out) const; @@ -572,6 +676,27 @@ namespace sat { void display(std::ostream & out) const; }; + class scoped_detach { + solver& s; + clause& c; + bool m_deleted; + public: + scoped_detach(solver& s, clause& c): s(s), c(c), m_deleted(false) { + s.detach_clause(c); + } + ~scoped_detach() { + if (!m_deleted) s.attach_clause(c); + } + + void del_clause() { + if (!m_deleted) { + s.del_clause(c); + m_deleted = true; + } + } + }; + + std::ostream & operator<<(std::ostream & out, mk_stat const & stat); }; diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 453fff417..e1e0e0b0a 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -17,32 +17,39 @@ Notes: --*/ + +#include "util/gparams.h" +#include "ast/ast_pp.h" +#include "ast/ast_translation.h" +#include "ast/ast_util.h" #include "solver/solver.h" -#include "tactic/tactical.h" -#include "sat/sat_solver.h" #include "solver/tactic2solver.h" +#include "solver/parallel_params.hpp" +#include "solver/parallel_tactic.h" +#include "tactic/tactical.h" #include "tactic/aig/aig_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/arith/card2bv_tactic.h" #include "tactic/bv/bit_blaster_tactic.h" #include "tactic/core/simplify_tactic.h" -#include "sat/tactic/goal2sat.h" -#include "ast/ast_pp.h" -#include "model/model_smt2_pp.h" -#include "tactic/filter_model_converter.h" +#include "tactic/core/solve_eqs_tactic.h" #include "tactic/bv/bit_blaster_model_converter.h" -#include "ast/ast_translation.h" -#include "ast/ast_util.h" -#include "tactic/core/propagate_values_tactic.h" +#include "model/model_smt2_pp.h" +#include "model/model_v2_pp.h" +#include "model/model_evaluator.h" +#include "sat/sat_solver.h" +#include "sat/sat_params.hpp" +#include "sat/tactic/goal2sat.h" +#include "sat/tactic/sat_tactic.h" +#include "sat/sat_simplifier_params.hpp" // incremental SAT solver. class inc_sat_solver : public solver { ast_manager& m; - sat::solver m_solver; + mutable sat::solver m_solver; goal2sat m_goal2sat; params_ref m_params; - bool m_optimize_model; // parameter expr_ref_vector m_fmls; expr_ref_vector m_asmsf; unsigned_vector m_fmls_lim; @@ -51,55 +58,77 @@ class inc_sat_solver : public solver { unsigned m_fmls_head; expr_ref_vector m_core; atom2bool_var m_map; - model_ref m_model; scoped_ptr m_bb_rewriter; tactic_ref m_preprocess; unsigned m_num_scopes; sat::literal_vector m_asms; goal_ref_buffer m_subgoals; proof_converter_ref m_pc; - model_converter_ref m_mc; - model_converter_ref m_mc0; - expr_dependency_ref m_dep_core; + sref_vector m_mcs; + mutable model_converter_ref m_mc0; // TBD: should be saved/retained under push/pop + mutable obj_hashtable m_inserted_const2bits; + mutable ref m_sat_mc; + mutable model_converter_ref m_cached_mc; svector m_weights; std::string m_unknown; - + // access formulas after they have been pre-processed and handled by the sat solver. + // this allows to access the internal state of the SAT solver and carry on partial results. + bool m_internalized_converted; // have internalized formulas been converted back + expr_ref_vector m_internalized_fmls; // formulas in internalized format typedef obj_map dep2asm_t; + + bool is_internalized() const { return m_fmls_head == m_fmls.size(); } public: - inc_sat_solver(ast_manager& m, params_ref const& p): - m(m), m_solver(p, m.limit(), nullptr), - m_optimize_model(false), + inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_mode): + m(m), + m_solver(p, m.limit()), m_fmls(m), m_asmsf(m), m_fmls_head(0), m_core(m), m_map(m), m_num_scopes(0), - m_dep_core(m), - m_unknown("no reason given") { - m_params.set_bool("elim_vars", false); + m_unknown("no reason given"), + m_internalized_converted(false), + m_internalized_fmls(m) { updt_params(p); + m_mcs.push_back(nullptr); init_preprocess(); + m_solver.set_incremental(incremental_mode && !override_incremental()); + } + + bool override_incremental() const { + sat_simplifier_params p(m_params); + return p.override_incremental(); + } + + bool is_incremental() const { + return m_solver.get_config().m_incremental; } ~inc_sat_solver() override {} solver* translate(ast_manager& dst_m, params_ref const& p) override { - ast_translation tr(m, dst_m); if (m_num_scopes > 0) { throw default_exception("Cannot translate sat solver at non-base level"); } - inc_sat_solver* result = alloc(inc_sat_solver, dst_m, p); - expr_ref fml(dst_m); - for (unsigned i = 0; i < m_fmls.size(); ++i) { - fml = tr(m_fmls[i].get()); - result->m_fmls.push_back(fml); - } - for (unsigned i = 0; i < m_asmsf.size(); ++i) { - fml = tr(m_asmsf[i].get()); - result->m_asmsf.push_back(fml); - } + ast_translation tr(m, dst_m); + m_solver.pop_to_base_level(); + inc_sat_solver* result = alloc(inc_sat_solver, dst_m, p, is_incremental()); + result->m_solver.copy(m_solver); + result->m_fmls_head = m_fmls_head; + for (expr* f : m_fmls) result->m_fmls.push_back(tr(f)); + for (expr* f : m_asmsf) result->m_asmsf.push_back(tr(f)); + for (auto & kv : m_map) result->m_map.insert(tr(kv.m_key), kv.m_value); + for (unsigned l : m_fmls_lim) result->m_fmls_lim.push_back(l); + for (unsigned a : m_asms_lim) result->m_asms_lim.push_back(a); + for (unsigned h : m_fmls_head_lim) result->m_fmls_head_lim.push_back(h); + for (expr* f : m_internalized_fmls) result->m_internalized_fmls.push_back(tr(f)); + if (m_mcs.back()) result->m_mcs.push_back(m_mcs.back()->translate(tr)); + if (m_sat_mc) result->m_sat_mc = dynamic_cast(m_sat_mc->translate(tr)); + // copy m_bb_rewriter? + result->m_internalized_converted = m_internalized_converted; return result; } @@ -137,6 +166,8 @@ public: lbool check_sat(unsigned sz, expr * const * assumptions) override { m_solver.pop_to_base_level(); + m_core.reset(); + if (m_solver.inconsistent()) return l_false; expr_ref_vector _assumptions(m); obj_map asm2fml; for (unsigned i = 0; i < sz; ++i) { @@ -155,19 +186,21 @@ public: TRACE("sat", tout << _assumptions << "\n";); dep2asm_t dep2asm; - m_model = nullptr; lbool r = internalize_formulas(); if (r != l_true) return r; r = internalize_assumptions(sz, _assumptions.c_ptr(), dep2asm); if (r != l_true) return r; - r = m_solver.check(m_asms.size(), m_asms.c_ptr()); - if (r == l_undef && m_solver.get_config().m_dimacs_display) { - for (auto const& kv : m_map) { - std::cout << "c " << kv.m_value << " " << mk_pp(kv.m_key, m) << "\n"; - } + init_reason_unknown(); + m_internalized_converted = false; + try { + // IF_VERBOSE(0, m_solver.display(verbose_stream())); + r = m_solver.check(m_asms.size(), m_asms.c_ptr()); + } + catch (z3_exception& ex) { + IF_VERBOSE(10, verbose_stream() << "exception: " << ex.msg() << "\n";); + r = l_undef; } - switch (r) { case l_true: if (sz > 0) { @@ -181,30 +214,37 @@ public: } break; default: + set_reason_unknown(m_solver.get_reason_unknown()); break; } return r; } + void push() override { internalize_formulas(); m_solver.user_push(); ++m_num_scopes; + m_mcs.push_back(m_mcs.back()); m_fmls_lim.push_back(m_fmls.size()); m_asms_lim.push_back(m_asmsf.size()); m_fmls_head_lim.push_back(m_fmls_head); if (m_bb_rewriter) m_bb_rewriter->push(); m_map.push(); } + void pop(unsigned n) override { if (n > m_num_scopes) { // allow inc_sat_solver to n = m_num_scopes; // take over for another solver. } if (m_bb_rewriter) m_bb_rewriter->pop(n); + m_inserted_const2bits.reset(); m_map.pop(n); SASSERT(n <= m_num_scopes); m_solver.user_pop(n); m_num_scopes -= n; + // ? m_internalized_converted = false; while (n > 0) { + m_mcs.pop_back(); m_fmls_head = m_fmls_head_lim.back(); m_fmls.resize(m_fmls_lim.back()); m_fmls_lim.pop_back(); @@ -214,33 +254,46 @@ public: --n; } } + unsigned get_scope_level() const override { return m_num_scopes; } - void assert_expr(expr * t, expr * a) override { + + void assert_expr_core2(expr * t, expr * a) override { if (a) { m_asmsf.push_back(a); - assert_expr(m.mk_implies(a, t)); + assert_expr_core(m.mk_implies(a, t)); } else { - assert_expr(t); + assert_expr_core(t); } } + ast_manager& get_manager() const override { return m; } - void assert_expr(expr * t) override { - TRACE("sat", tout << mk_pp(t, m) << "\n";); + void assert_expr_core(expr * t) override { + TRACE("goal2sat", tout << mk_pp(t, m) << "\n";); m_fmls.push_back(t); } void set_produce_models(bool f) override {} void collect_param_descrs(param_descrs & r) override { + solver::collect_param_descrs(r); goal2sat::collect_param_descrs(r); sat::solver::collect_param_descrs(r); } void updt_params(params_ref const & p) override { - solver::updt_params(p); - m_params.set_bool("elim_vars", false); + m_params.append(p); + sat_params p1(p); + m_params.set_bool("keep_cardinality_constraints", p1.cardinality_solver()); + m_params.set_sym("pb.solver", p1.pb_solver()); + + m_params.set_bool("keep_pb_constraints", m_solver.get_config().m_pb_solver == sat::PB_SOLVER); + m_params.set_bool("pb_num_system", m_solver.get_config().m_pb_solver == sat::PB_SORTING); + m_params.set_bool("pb_totalizer", m_solver.get_config().m_pb_solver == sat::PB_TOTALIZER); + + m_params.set_bool("xor_solver", p1.xor_solver()); m_solver.updt_params(m_params); - m_optimize_model = m_params.get_bool("optimize_model", false); + m_solver.set_incremental(is_incremental() && !override_incremental()); + } void collect_statistics(statistics & st) const override { if (m_preprocess) m_preprocess->collect_statistics(st); @@ -250,17 +303,51 @@ public: r.reset(); r.append(m_core.size(), m_core.c_ptr()); } - void get_model(model_ref & mdl) override { - if (!m_model.get()) { - extract_model(); - } - mdl = m_model; - } proof * get_proof() override { UNREACHABLE(); return nullptr; } + expr_ref_vector cube(expr_ref_vector& vs, unsigned backtrack_level) override { + if (!is_internalized()) { + lbool r = internalize_formulas(); + if (r != l_true) return expr_ref_vector(m); + } + convert_internalized(); + obj_hashtable _vs; + for (expr* v : vs) _vs.insert(v); + sat::bool_var_vector vars; + for (auto& kv : m_map) { + if (_vs.empty() || _vs.contains(kv.m_key)) + vars.push_back(kv.m_value); + } + sat::literal_vector lits; + lbool result = m_solver.cube(vars, lits, backtrack_level); + if (result == l_false || lits.empty()) { + expr_ref_vector result(m); + result.push_back(m.mk_false()); + return result; + } + if (result == l_true) { + return expr_ref_vector(m); + } + expr_ref_vector fmls(m); + expr_ref_vector lit2expr(m); + lit2expr.resize(m_solver.num_vars() * 2); + m_map.mk_inv(lit2expr); + for (sat::literal l : lits) { + fmls.push_back(lit2expr[l.index()].get()); + } + vs.reset(); + for (sat::bool_var v : vars) { + expr* x = lit2expr[sat::literal(v, false).index()].get(); + if (x) { + vs.push_back(x); + } + } + return fmls; + } + lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) override { init_preprocess(); TRACE("sat", tout << assumptions << "\n" << vars << "\n";); @@ -295,9 +382,9 @@ public: // extract original fixed variables u_map asm2dep; extract_asm2dep(dep2asm, asm2dep); - for (unsigned i = 0; i < vars.size(); ++i) { + for (auto v : vars) { expr_ref cons(m); - if (extract_fixed_variable(dep2asm, asm2dep, vars[i], bool_var2conseq, lconseq, cons)) { + if (extract_fixed_variable(dep2asm, asm2dep, v, bool_var2conseq, lconseq, cons)) { conseq.push_back(cons); } } @@ -319,38 +406,86 @@ public: } vector ls_mutexes; m_solver.find_mutexes(ls, ls_mutexes); - for (unsigned i = 0; i < ls_mutexes.size(); ++i) { - sat::literal_vector const ls_mutex = ls_mutexes[i]; + for (sat::literal_vector const& ls_mutex : ls_mutexes) { expr_ref_vector mutex(m); - for (unsigned j = 0; j < ls_mutex.size(); ++j) { - mutex.push_back(lit2var.find(ls_mutex[j].index())); + for (sat::literal l : ls_mutex) { + mutex.push_back(lit2var.find(l.index())); } mutexes.push_back(mutex); } return l_true; } + void init_reason_unknown() { + m_unknown = "no reason given"; + } + std::string reason_unknown() const override { return m_unknown; } + void set_reason_unknown(char const* msg) override { m_unknown = msg; } + void get_labels(svector & r) override { } + unsigned get_num_assertions() const override { - return m_fmls.size(); + const_cast(this)->convert_internalized(); + if (is_internalized() && m_internalized_converted) { + return m_internalized_fmls.size(); + } + else { + return m_fmls.size(); + } } + expr * get_assertion(unsigned idx) const override { + if (is_internalized() && m_internalized_converted) { + return m_internalized_fmls[idx]; + } return m_fmls[idx]; } + unsigned get_num_assumptions() const override { return m_asmsf.size(); } + expr * get_assumption(unsigned idx) const override { return m_asmsf[idx]; } + model_converter_ref get_model_converter() const override { + const_cast(this)->convert_internalized(); + if (m_cached_mc) + return m_cached_mc; + if (is_internalized() && m_internalized_converted) { + m_sat_mc->flush_smc(m_solver, m_map); + m_cached_mc = m_mcs.back(); + m_cached_mc = concat(solver::get_model_converter().get(), m_cached_mc.get()); + m_cached_mc = concat(m_cached_mc.get(), m_sat_mc.get()); + return m_cached_mc; + } + else { + return solver::get_model_converter(); + } + } + + void convert_internalized() { + if (!is_internalized() && m_fmls_head > 0) { + internalize_formulas(); + } + if (!is_internalized() || m_internalized_converted) return; + sat2goal s2g; + m_cached_mc = nullptr; + goal g(m, false, true, false); + s2g(m_solver, m_map, m_params, g, m_sat_mc); + m_internalized_fmls.reset(); + g.get_formulas(m_internalized_fmls); + m_internalized_converted = true; + } + void init_preprocess() { if (m_preprocess) { m_preprocess->reset(); @@ -369,12 +504,13 @@ public: simp2_p.set_bool("elim_and", true); simp2_p.set_bool("blast_distinct", true); m_preprocess = - and_then(mk_card2bv_tactic(m, m_params), + and_then(mk_simplify_tactic(m), + mk_propagate_values_tactic(m), + //time consuming if done in inner loop: mk_solve_eqs_tactic(m, simp2_p), + mk_card2bv_tactic(m, m_params), // updates model converter using_params(mk_simplify_tactic(m), simp2_p), mk_max_bv_sharing_tactic(m), - mk_bit_blaster_tactic(m, m_bb_rewriter.get()), - //mk_aig_tactic(), - //mk_propagate_values_tactic(m, simp2_p), + mk_bit_blaster_tactic(m, m_bb_rewriter.get()), // updates model converter using_params(mk_simplify_tactic(m), simp2_p)); while (m_bb_rewriter->get_num_scopes() < m_num_scopes) { m_bb_rewriter->push(); @@ -384,11 +520,8 @@ public: private: - - lbool internalize_goal(goal_ref& g, dep2asm_t& dep2asm) { - m_mc.reset(); + lbool internalize_goal(goal_ref& g, dep2asm_t& dep2asm, bool is_lemma) { m_pc.reset(); - m_dep_core.reset(); m_subgoals.reset(); init_preprocess(); SASSERT(g->models_enabled()); @@ -398,7 +531,7 @@ private: SASSERT(!g->proofs_enabled()); TRACE("sat", g->display(tout);); try { - (*m_preprocess)(g, m_subgoals, m_mc, m_pc, m_dep_core); + (*m_preprocess)(g, m_subgoals); } catch (tactic_exception & ex) { IF_VERBOSE(0, verbose_stream() << "exception in tactic " << ex.msg() << "\n";); @@ -406,16 +539,23 @@ private: m_preprocess = nullptr; m_bb_rewriter = nullptr; return l_undef; - } + } if (m_subgoals.size() != 1) { - IF_VERBOSE(0, verbose_stream() << "size of subgoals is not 1, it is: " << m_subgoals.size() << "\n";); + IF_VERBOSE(0, verbose_stream() << "size of subgoals is not 1, it is: " << m_subgoals.size() << "\n"); return l_undef; } g = m_subgoals[0]; expr_ref_vector atoms(m); + m_pc = g->pc(); + m_mcs.set(m_mcs.size()-1, concat(m_mcs.back(), g->mc())); TRACE("sat", g->display_with_dependencies(tout);); - m_goal2sat(*g, m_params, m_solver, m_map, dep2asm, true); + + // ensure that if goal is already internalized, then import mc from m_solver. + + m_goal2sat(*g, m_params, m_solver, m_map, dep2asm, is_incremental(), is_lemma); m_goal2sat.get_interpreted_atoms(atoms); + if (!m_sat_mc) m_sat_mc = alloc(sat2goal::mc, m); + m_sat_mc->flush_smc(m_solver, m_map); if (!atoms.empty()) { std::stringstream strm; strm << "interpreted atoms sent to SAT solver " << atoms; @@ -439,7 +579,7 @@ private: for (unsigned i = 0; i < get_num_assumptions(); ++i) { g->assert_expr(get_assumption(i), m.mk_leaf(get_assumption(i))); } - lbool res = internalize_goal(g, dep2asm); + lbool res = internalize_goal(g, dep2asm, false); if (res == l_true) { extract_assumptions(sz, asms, dep2asm); } @@ -447,20 +587,21 @@ private: } lbool internalize_vars(expr_ref_vector const& vars, sat::bool_var_vector& bvars) { - for (unsigned i = 0; i < vars.size(); ++i) { - internalize_var(vars[i], bvars); + for (expr* v : vars) { + internalize_var(v, bvars); } return l_true; } bool internalize_var(expr* v, sat::bool_var_vector& bvars) { - obj_map const& const2bits = m_bb_rewriter->const2bits(); + obj_map const2bits; + ptr_vector newbits; + m_bb_rewriter->end_rewrite(const2bits, newbits); expr* bv; bv_util bvutil(m); bool internalized = false; if (is_uninterp_const(v) && m.is_bool(v)) { sat::bool_var b = m_map.to_bool_var(v); - if (b != sat::null_bool_var) { bvars.push_back(b); internalized = true; @@ -470,10 +611,9 @@ private: SASSERT(bvutil.is_bv(bv)); app* abv = to_app(bv); internalized = true; - unsigned sz = abv->get_num_args(); - for (unsigned j = 0; j < sz; ++j) { - SASSERT(is_uninterp_const(abv->get_arg(j))); - sat::bool_var b = m_map.to_bool_var(abv->get_arg(j)); + for (expr* arg : *abv) { + SASSERT(is_uninterp_const(arg)); + sat::bool_var b = m_map.to_bool_var(arg); if (b == sat::null_bool_var) { internalized = false; } @@ -481,7 +621,7 @@ private: bvars.push_back(b); } } - CTRACE("sat", internalized, tout << "var: "; for (unsigned j = 0; j < sz; ++j) tout << bvars[bvars.size()-sz+j] << " "; tout << "\n";); + CTRACE("sat", internalized, tout << "var: " << bvars << "\n";); } else if (is_uninterp_const(v) && bvutil.is_bv(v)) { // variable does not occur in assertions, so is unconstrained. @@ -498,9 +638,9 @@ private: } sat::literal_vector value; sat::literal_set premises; - for (unsigned i = 0; i < bvars.size(); ++i) { + for (sat::bool_var bv : bvars) { unsigned index; - if (bool_var2conseq.find(bvars[i], index)) { + if (bool_var2conseq.find(bv, index)) { value.push_back(lconseq[index][0]); for (unsigned j = 1; j < lconseq[index].size(); ++j) { premises.insert(lconseq[index][j]); @@ -560,10 +700,11 @@ private: for (unsigned i = m_fmls_head ; i < m_fmls.size(); ++i) { g->assert_expr(m_fmls[i].get()); } - lbool res = internalize_goal(g, dep2asm); + lbool res = internalize_goal(g, dep2asm, false); if (res != l_undef) { m_fmls_head = m_fmls.size(); } + m_internalized_converted = false; return res; } @@ -592,10 +733,8 @@ private: } void extract_asm2dep(dep2asm_t const& dep2asm, u_map& asm2dep) { - dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); - for (; it != end; ++it) { - expr* e = it->m_key; - asm2dep.insert(it->m_value.index(), e); + for (auto const& kv : dep2asm) { + asm2dep.insert(kv.m_value.index(), kv.m_key); } } @@ -615,23 +754,22 @@ private: ); m_core.reset(); - for (unsigned i = 0; i < core.size(); ++i) { + for (sat::literal c : core) { expr* e = nullptr; - VERIFY(asm2dep.find(core[i].index(), e)); + VERIFY(asm2dep.find(c.index(), e)); if (asm2fml.contains(e)) { e = asm2fml.find(e); } - m_core.push_back(e); + m_core.push_back(e); } } void check_assumptions(dep2asm_t& dep2asm) { sat::model const & ll_m = m_solver.get_model(); - dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); - for (; it != end; ++it) { - sat::literal lit = it->m_value; + for (auto const& kv : dep2asm) { + sat::literal lit = kv.m_value; if (sat::value_at(lit, ll_m) != l_true) { - IF_VERBOSE(0, verbose_stream() << mk_pp(it->m_key, m) << " does not evaluate to true\n"; + IF_VERBOSE(0, verbose_stream() << mk_pp(kv.m_key, m) << " does not evaluate to true\n"; verbose_stream() << m_asms << "\n"; m_solver.display_assignment(verbose_stream()); m_solver.display(verbose_stream());); @@ -640,14 +778,14 @@ private: } } - void extract_model() { + void get_model_core(model_ref & mdl) override { TRACE("sat", tout << "retrieve model " << (m_solver.model_is_current()?"present":"absent") << "\n";); if (!m_solver.model_is_current()) { - m_model = nullptr; + mdl = nullptr; return; } sat::model const & ll_m = m_solver.get_model(); - model_ref md = alloc(model, m); + mdl = alloc(model, m); for (auto const& kv : m_map) { expr * n = kv.m_key; if (is_app(n) && to_app(n)->get_num_args() > 0) { @@ -656,42 +794,65 @@ private: sat::bool_var v = kv.m_value; switch (sat::value_at(v, ll_m)) { case l_true: - md->register_decl(to_app(n)->get_decl(), m.mk_true()); + mdl->register_decl(to_app(n)->get_decl(), m.mk_true()); break; case l_false: - md->register_decl(to_app(n)->get_decl(), m.mk_false()); + mdl->register_decl(to_app(n)->get_decl(), m.mk_false()); break; default: break; } } - m_model = md; + //IF_VERBOSE(0, model_v2_pp(verbose_stream(), *mdl, true);); - if (m_bb_rewriter.get() && !m_bb_rewriter->const2bits().empty()) { - m_mc0 = concat(m_mc0.get(), mk_bit_blaster_model_converter(m, m_bb_rewriter->const2bits())); + if (m_sat_mc) { + //IF_VERBOSE(0, m_sat_mc->display(verbose_stream() << "satmc\n");); + (*m_sat_mc)(mdl); } - if (m_mc0) { - (*m_mc0)(m_model); + if (m_mcs.back()) { + //IF_VERBOSE(0, m_mc0->display(verbose_stream() << "mc0\n");); + (*m_mcs.back())(mdl); } - SASSERT(m_model); + TRACE("sat", model_smt2_pp(tout, m, *mdl, 0);); + - DEBUG_CODE( - for (unsigned i = 0; i < m_fmls.size(); ++i) { - expr_ref tmp(m); - if (m_model->eval(m_fmls[i].get(), tmp, true)) { - CTRACE("sat", !m.is_true(tmp), - tout << "Evaluation failed: " << mk_pp(m_fmls[i].get(), m) - << " to " << tmp << "\n"; - model_smt2_pp(tout, m, *(m_model.get()), 0);); - SASSERT(m.is_true(tmp)); - } - }); + if (!gparams::get_ref().get_bool("model_validate", false)) return; + IF_VERBOSE(1, verbose_stream() << "Verifying solution\n";); + model_evaluator eval(*mdl); + eval.set_model_completion(false); + bool all_true = true; + //unsigned i = 0; + for (expr * f : m_fmls) { + expr_ref tmp(m); + eval(f, tmp); + CTRACE("sat", !m.is_true(tmp), + tout << "Evaluation failed: " << mk_pp(f, m) << " to " << mk_pp(f, m) << "\n"; + model_smt2_pp(tout, m, *(mdl.get()), 0);); + if (!m.is_true(tmp)) { + IF_VERBOSE(0, verbose_stream() << "failed to verify: " << mk_pp(f, m) << "\n";); + all_true = false; + } + //IF_VERBOSE(0, verbose_stream() << (i++) << ": " << mk_pp(f, m) << "\n"); + } + if (!all_true) { + IF_VERBOSE(0, verbose_stream() << m_params << "\n"); + IF_VERBOSE(0, m_sat_mc->display(verbose_stream() << "sat mc\n")); + IF_VERBOSE(0, if (m_mcs.back()) m_mcs.back()->display(verbose_stream() << "mc0\n")); + //IF_VERBOSE(0, m_solver.display(verbose_stream())); + IF_VERBOSE(0, for (auto const& kv : m_map) verbose_stream() << mk_pp(kv.m_key, m) << " |-> " << kv.m_value << "\n"); + } + else { + IF_VERBOSE(1, verbose_stream() << "solution verified\n"); +// IF_VERBOSE(0, if (m_mcs.back()) m_mcs.back()->display(verbose_stream() << "mcs\n")); +// IF_VERBOSE(0, if (m_sat_mc) m_sat_mc->display(verbose_stream() << "sat_mc\n")); +// IF_VERBOSE(0, model_smt2_pp(verbose_stream() << "after\n", m, *mdl, 0);); + } } }; -solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p) { - return alloc(inc_sat_solver, m, p); +solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_mode) { + return alloc(inc_sat_solver, m, p, incremental_mode); } @@ -707,3 +868,8 @@ void inc_sat_display(std::ostream& out, solver& _s, unsigned sz, expr*const* sof s.display_weighted(out, sz, soft, weights.c_ptr()); } + +tactic * mk_psat_tactic(ast_manager& m, params_ref const& p) { + parallel_params pp(p); + return pp.enable() ? mk_parallel_tactic(mk_inc_sat_solver(m, p, false), p) : mk_sat_tactic(m); +} diff --git a/src/sat/sat_solver/inc_sat_solver.h b/src/sat/sat_solver/inc_sat_solver.h index 658c0583d..71ec48b99 100644 --- a/src/sat/sat_solver/inc_sat_solver.h +++ b/src/sat/sat_solver/inc_sat_solver.h @@ -22,7 +22,11 @@ Notes: #include "solver/solver.h" -solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p); +class tactic; + +solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_mode = true); + +tactic* mk_psat_tactic(ast_manager& m, params_ref const& p); void inc_sat_display(std::ostream& out, solver& s, unsigned sz, expr*const* soft, rational const* _weights); diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index 6652fb3b0..002e49006 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -25,6 +25,7 @@ Revision History: #include "util/z3_exception.h" #include "util/common_msgs.h" #include "util/vector.h" +#include "util/uint_set.h" #include namespace sat { @@ -103,14 +104,14 @@ namespace sat { inline bool operator==(literal const & l1, literal const & l2) { return l1.m_val == l2.m_val; } inline bool operator!=(literal const & l1, literal const & l2) { return l1.m_val != l2.m_val; } - inline std::ostream & operator<<(std::ostream & out, literal l) { out << (l.sign() ? "-" : "") << l.var(); return out; } + inline std::ostream & operator<<(std::ostream & out, literal l) { if (l == null_literal) out << "null"; else out << (l.sign() ? "-" : "") << l.var(); return out; } typedef svector literal_vector; typedef std::pair literal_pair; - typedef unsigned clause_offset; - typedef unsigned ext_constraint_idx; - typedef unsigned ext_justification_idx; + typedef size_t clause_offset; + typedef size_t ext_constraint_idx; + typedef size_t ext_justification_idx; struct literal2unsigned { unsigned operator()(literal l) const { return l.to_uint(); } }; @@ -123,6 +124,8 @@ namespace sat { }; class solver; + class lookahead; + class unit_walk; class clause; class clause_wrapper; class integrity_checker; @@ -137,6 +140,7 @@ namespace sat { typedef svector model; + inline void negate(literal_vector& ls) { for (unsigned i = 0; i < ls.size(); ++i) ls[i].neg(); } inline lbool value_at(bool_var v, model const & m) { return m[v]; } inline lbool value_at(literal l, model const & m) { lbool r = value_at(l.var(), m); return l.sign() ? ~r : r; } @@ -150,79 +154,7 @@ namespace sat { return out; } - class uint_set { - svector m_in_set; - svector m_set; - public: - typedef svector::const_iterator iterator; - void insert(unsigned v) { - m_in_set.reserve(v+1, false); - if (m_in_set[v]) - return; - m_in_set[v] = true; - m_set.push_back(v); - } - - void remove(unsigned v) { - if (contains(v)) { - m_in_set[v] = false; - unsigned i = 0; - for (i = 0; i < m_set.size() && m_set[i] != v; ++i) - ; - SASSERT(i < m_set.size()); - m_set[i] = m_set.back(); - m_set.pop_back(); - } - } - - uint_set& operator=(uint_set const& other) { - m_in_set = other.m_in_set; - m_set = other.m_set; - return *this; - } - - bool contains(unsigned v) const { - return v < m_in_set.size() && m_in_set[v] != 0; - } - - bool empty() const { - return m_set.empty(); - } - - // erase some variable from the set - unsigned erase() { - SASSERT(!empty()); - unsigned v = m_set.back(); - m_set.pop_back(); - m_in_set[v] = false; - return v; - } - unsigned size() const { return m_set.size(); } - iterator begin() const { return m_set.begin(); } - iterator end() const { return m_set.end(); } - void reset() { m_set.reset(); m_in_set.reset(); } - void finalize() { m_set.finalize(); m_in_set.finalize(); } - uint_set& operator&=(uint_set const& other) { - unsigned j = 0; - for (unsigned i = 0; i < m_set.size(); ++i) { - if (other.contains(m_set[i])) { - m_set[j] = m_set[i]; - ++j; - } - else { - m_in_set[m_set[i]] = false; - } - } - m_set.resize(j); - return *this; - } - uint_set& operator|=(uint_set const& other) { - for (unsigned i = 0; i < other.m_set.size(); ++i) { - insert(other.m_set[i]); - } - return *this; - } - }; + typedef tracked_uint_set uint_set; typedef uint_set bool_var_set; diff --git a/src/sat/sat_unit_walk.cpp b/src/sat/sat_unit_walk.cpp new file mode 100644 index 000000000..4a3cda47c --- /dev/null +++ b/src/sat/sat_unit_walk.cpp @@ -0,0 +1,370 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_unit_walk.cpp + +Abstract: + + unit walk local search procedure. + A variant of UnitWalk. Hirsch and Kojevinkov, SAT 2001. + This version uses a trail to reset assignments and integrates directly with the + watch list structure. Thus, assignments are not delayed and we avoid treating + pending units as a multi-set. + + It uses standard DPLL approach for backracking, flipping the last decision literal that + lead to a conflict. It restarts after evern 100 conflicts. + + It does not attempt to add conflict clauses or alternate with + walksat. + + It can receive conflict clauses from a concurrent CDCL solver and does not + create its own conflict clauses. + + The phase of variables is optionally sticky between rounds. We use a decay rate + to compute stickiness of a variable. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-12-15. + +Revision History: + +--*/ + +#include "sat_unit_walk.h" + +namespace sat { + + unit_walk::unit_walk(solver& s): + s(s) + { + m_runs = 0; + m_periods = 0; + m_max_runs = UINT_MAX; + m_max_periods = 5000; // UINT_MAX; // TBD configure + m_max_conflicts = 100; + m_sticky_phase = s.get_config().m_phase_sticky; + m_flips = 0; + } + + class scoped_set_unit_walk { + solver& s; + public: + scoped_set_unit_walk(unit_walk* u, solver& s): s(s) { + if (s.get_extension()) s.get_extension()->set_unit_walk(u); + } + ~scoped_set_unit_walk() { + if (s.get_extension()) s.get_extension()->set_unit_walk(nullptr); + } + }; + + lbool unit_walk::operator()() { + scoped_set_unit_walk _scoped_set(this, s); + init_runs(); + for (m_runs = 0; m_runs < m_max_runs || m_max_runs == UINT_MAX; ++m_runs) { + init_propagation(); + init_phase(); + for (m_periods = 0; m_periods < m_max_periods || m_max_periods == UINT_MAX; ++m_periods) { + if (!s.rlimit().inc()) return l_undef; + lbool r = unit_propagation(); + if (r != l_undef) return r; + } + } + return l_undef; + } + + lbool unit_walk::unit_propagation() { + init_propagation(); + while (!m_freevars.empty() && !inconsistent()) { + bool_var v = m_freevars.begin()[m_rand(m_freevars.size())]; + literal lit(v, !m_phase[v]); + ++s.m_stats.m_decision; + m_decisions.push_back(lit); + assign(lit); + propagate(); + while (inconsistent() && !m_decisions.empty()) { + ++m_conflicts; + backtrack(); + propagate(); + } + if (m_conflicts >= m_max_conflicts && !m_freevars.empty()) { + set_conflict(); + break; + } + } + if (!inconsistent()) { + log_status(); + IF_VERBOSE(1, verbose_stream() << "(sat-unit-walk sat)\n";); + s.mk_model(); + return l_true; + } + return l_undef; + } + + void unit_walk::init_runs() { + m_freevars.reset(); + m_trail.reset(); + m_decisions.reset(); + m_phase.resize(s.num_vars()); + double2 d2; + d2.t = 1.0; + d2.f = 1.0; + m_phase_tf.resize(s.num_vars(), d2); + for (unsigned i = 0; i < s.num_vars(); ++i) { + literal l(i, false); + if (!s.was_eliminated(l.var()) && s.m_assignment[l.index()] == l_undef) + m_freevars.insert(l.var()); + } + IF_VERBOSE(1, verbose_stream() << "num vars: " << s.num_vars() << " free vars: " << m_freevars.size() << "\n";); + } + + void unit_walk::init_phase() { + m_max_trail = 0; + if (m_sticky_phase) { + for (bool_var v : m_freevars) { + if (s.m_phase[v] == POS_PHASE) { + m_phase[v] = true; + } + else if (s.m_phase[v] == NEG_PHASE) { + m_phase[v] = false; + } + else { + m_phase[v] = m_rand(100 * static_cast(m_phase_tf[v].t + m_phase_tf[v].f)) <= 100 * m_phase_tf[v].t; + } + } + } + else { + for (bool_var v : m_freevars) + m_phase[v] = (m_rand(2) == 0); + } + } + + void unit_walk::init_propagation() { + if (s.m_par && s.m_par->copy_solver(s)) { + IF_VERBOSE(1, verbose_stream() << "(sat-unit-walk fresh copy)\n";); + if (s.get_extension()) s.get_extension()->set_unit_walk(this); + init_runs(); + init_phase(); + } + if (m_max_trail == 0 || m_trail.size() > m_max_trail) { + m_max_trail = m_trail.size(); + log_status(); + } + for (literal lit : m_trail) { + s.m_assignment[lit.index()] = l_undef; + s.m_assignment[(~lit).index()] = l_undef; + m_freevars.insert(lit.var()); + } + m_flips = 0; + m_trail.reset(); + m_conflicts = 0; + m_decisions.reset(); + m_qhead = 0; + m_inconsistent = false; + } + + void unit_walk::propagate() { + while (m_qhead < m_trail.size() && !inconsistent()) + propagate(choose_literal()); + // IF_VERBOSE(1, verbose_stream() << m_trail.size() << " " << inconsistent() << "\n";); + } + + void unit_walk::propagate(literal l) { + ++s.m_stats.m_propagate; + literal not_l = ~l; + literal l1, l2; + lbool val1, val2; + bool keep; + watch_list & wlist = s.get_wlist(l); + watch_list::iterator it = wlist.begin(); + watch_list::iterator it2 = it; + watch_list::iterator end = wlist.end(); + for (; it != end; ++it) { + switch (it->get_kind()) { + case watched::BINARY: + l1 = it->get_literal(); + switch (value(l1)) { + case l_false: + conflict_cleanup(it, it2, wlist); + set_conflict(l,l1); + return; + case l_undef: + assign(l1); + break; + case l_true: + break; // skip + } + *it2 = *it; + it2++; + break; + case watched::TERNARY: + l1 = it->get_literal1(); + l2 = it->get_literal2(); + val1 = value(l1); + val2 = value(l2); + if (val1 == l_false && val2 == l_undef) { + assign(l2); + } + else if (val1 == l_undef && val2 == l_false) { + assign(l1); + } + else if (val1 == l_false && val2 == l_false) { + conflict_cleanup(it, it2, wlist); + set_conflict(l,l1,l2); + return; + } + *it2 = *it; + it2++; + break; + case watched::CLAUSE: { + if (value(it->get_blocked_literal()) == l_true) { + *it2 = *it; + it2++; + break; + } + clause_offset cls_off = it->get_clause_offset(); + clause & c = s.get_clause(cls_off); + if (c[0] == not_l) + std::swap(c[0], c[1]); + if (c[1] != not_l) { + *it2 = *it; + it2++; + break; + } + if (value(c[0]) == l_true) { + it2->set_clause(c[0], cls_off); + it2++; + break; + } + SASSERT(c[1] == not_l); + literal * l_it = c.begin() + 2; + literal * l_end = c.end(); + for (; l_it != l_end; ++l_it) { + if (value(*l_it) != l_false) { + c[1] = *l_it; + *l_it = not_l; + s.get_wlist((~c[1]).index()).push_back(watched(c[0], cls_off)); + goto end_clause_case; + } + } + SASSERT(value(c[0]) == l_false || value(c[0]) == l_undef); + if (value(c[0]) == l_false) { + c.mark_used(); + conflict_cleanup(it, it2, wlist); + set_conflict(c); + return; + } + else { + *it2 = *it; + it2++; + assign(c[0]); + } + end_clause_case: + break; + } + case watched::EXT_CONSTRAINT: + SASSERT(s.get_extension()); + keep = s.get_extension()->propagate(l, it->get_ext_constraint_idx()); + if (inconsistent()) { + if (!keep) { + ++it; + } + set_conflict(l, l); + conflict_cleanup(it, it2, wlist); + return; + } + if (keep) { + *it2 = *it; + it2++; + } + break; + default: + UNREACHABLE(); + break; + } + } + wlist.set_end(it2); + } + + void unit_walk::assign(literal lit) { + SASSERT(value(lit) == l_undef); + s.m_assignment[lit.index()] = l_true; + s.m_assignment[(~lit).index()] = l_false; + m_trail.push_back(lit); + m_freevars.remove(lit.var()); + if (s.get_extension() && s.is_external(lit.var())) { + s.get_extension()->asserted(lit); + } + if (m_phase[lit.var()] == lit.sign()) { + ++m_flips; + flip_phase(lit); + } + } + + void unit_walk::flip_phase(literal l) { + bool_var v = l.var(); + m_phase[v] = !m_phase[v]; + if (m_sticky_phase) { + m_phase_tf[v].f *= 0.98; + m_phase_tf[v].t *= 0.98; + if (m_phase[v]) m_phase_tf[v].t += 1; else m_phase_tf[v].f += 1; + } + } + + void unit_walk::log_status() { + IF_VERBOSE(1, verbose_stream() << "(sat-unit-walk :trail " << m_max_trail + << " :branches " << m_decisions.size() + << " :free " << m_freevars.size() + << " :periods " << m_periods + << " :decisions " << s.m_stats.m_decision + << " :propagations " << s.m_stats.m_propagate + << ")\n";); + } + + literal unit_walk::choose_literal() { + SASSERT(m_qhead < m_trail.size()); + unsigned idx = m_rand(m_trail.size() - m_qhead); + std::swap(m_trail[m_qhead], m_trail[m_qhead + idx]); + literal lit = m_trail[m_qhead++]; + return lit; + } + + void unit_walk::set_conflict(literal l1, literal l2) { + set_conflict(); + } + + void unit_walk::set_conflict(literal l1, literal l2, literal l3) { + set_conflict(); + } + + void unit_walk::set_conflict(clause const& c) { + set_conflict(); + } + + void unit_walk::set_conflict() { + m_inconsistent = true; + } + + void unit_walk::backtrack() { + if (m_decisions.empty()) return; + literal dlit = m_decisions.back(); + literal lit; + do { + SASSERT(!m_trail.empty()); + lit = m_trail.back(); + s.m_assignment[lit.index()] = l_undef; + s.m_assignment[(~lit).index()] = l_undef; + m_freevars.insert(lit.var()); + m_trail.pop_back(); + } + while (lit != dlit); + m_inconsistent = false; + m_decisions.pop_back(); + m_qhead = m_trail.size(); + assign(~dlit); + } + +}; + diff --git a/src/sat/sat_unit_walk.h b/src/sat/sat_unit_walk.h new file mode 100644 index 000000000..8ab9bab70 --- /dev/null +++ b/src/sat/sat_unit_walk.h @@ -0,0 +1,79 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_unit_walk.h + +Abstract: + + unit walk local search procedure. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-12-15. + +Revision History: + +--*/ +#ifndef SAT_UNIT_WALK_H_ +#define SAT_UNIT_WALK_H_ + +#include "sat/sat_solver.h" + +namespace sat { + + class unit_walk { + struct double2 { + double t, f; + }; + solver& s; + random_gen m_rand; + svector m_phase; + svector m_phase_tf; + indexed_uint_set m_freevars; + unsigned m_runs; + unsigned m_periods; + + // settings + unsigned m_max_runs; + unsigned m_max_periods; + unsigned m_max_conflicts; + bool m_sticky_phase; + + unsigned m_propagations; + unsigned m_flips; + unsigned m_max_trail; + unsigned m_qhead; + literal_vector m_trail; + bool m_inconsistent; + literal_vector m_decisions; + unsigned m_conflicts; + + void push(); + void backtrack(); + void init_runs(); + void init_phase(); + void init_propagation(); + void flip_phase(literal l); + lbool unit_propagation(); + void propagate(); + void propagate(literal lit); + literal choose_literal(); + void set_conflict(literal l1, literal l2); + void set_conflict(literal l1, literal l2, literal l3); + void set_conflict(clause const& c); + inline lbool value(literal lit) { return s.value(lit); } + void log_status(); + public: + + unit_walk(solver& s); + lbool operator()(); + std::ostream& display(std::ostream& out) const; + bool inconsistent() const { return m_inconsistent; } + void set_conflict(); + void assign(literal lit); + }; +}; + +#endif diff --git a/src/sat/sat_var_queue.h b/src/sat/sat_var_queue.h index a14eb4cff..0c22766ba 100644 --- a/src/sat/sat_var_queue.h +++ b/src/sat/sat_var_queue.h @@ -39,6 +39,15 @@ namespace sat { m_queue.decreased(v); } + void activity_changed_eh(bool_var v, bool up) { + if (m_queue.contains(v)) { + if (up) + m_queue.decreased(v); + else + m_queue.increased(v); + } + } + void mk_var_eh(bool_var v) { m_queue.reserve(v+1); m_queue.insert(v); @@ -61,6 +70,10 @@ namespace sat { bool empty() const { return m_queue.empty(); } bool_var next_var() { SASSERT(!empty()); return m_queue.erase_min(); } + + bool_var min_var() { SASSERT(!empty()); return m_queue.min_value(); } + + bool more_active(bool_var v1, bool_var v2) const { return m_queue.less_than(v1, v2); } }; }; diff --git a/src/sat/sat_watched.cpp b/src/sat/sat_watched.cpp index 6335d37fc..199d12797 100644 --- a/src/sat/sat_watched.cpp +++ b/src/sat/sat_watched.cpp @@ -27,10 +27,10 @@ namespace sat { for (; it != end; ++it) { if (it->is_clause() && it->get_clause_offset() == c) { watch_list::iterator it2 = it; - ++it; - for (; it != end; ++it) { + ++it; + for (; it != end; ++it, ++it2) { + SASSERT(!((it->is_clause() && it->get_clause_offset() == c))); *it2 = *it; - ++it2; } wlist.set_end(it2); return true; @@ -39,33 +39,90 @@ namespace sat { return false; } - void display(std::ostream & out, clause_allocator const & ca, watch_list const & wlist) { - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (bool first = true; it != end; ++it) { + watched* find_binary_watch(watch_list & wlist, literal l) { + for (watched& w : wlist) { + if (w.is_binary_clause() && w.get_literal() == l) return &w; + } + return nullptr; + } + + watched const* find_binary_watch(watch_list const& wlist, literal l) { + for (watched const& w : wlist) { + if (w.is_binary_clause() && w.get_literal() == l) return &w; + } + return nullptr; + } + + void erase_binary_watch(watch_list& wlist, literal l) { + watch_list::iterator it = wlist.begin(), end = wlist.end(); + watch_list::iterator it2 = it; + bool found = false; + for (; it != end; ++it) { + if (it->is_binary_clause() && it->get_literal() == l && !found) { + found = true; + } + else { + *it2 = *it; + ++it2; + } + } + wlist.set_end(it2); + VERIFY(found); + } + + void erase_ternary_watch(watch_list& wlist, literal l1, literal l2) { + watched w(l1, l2); + watch_list::iterator it = wlist.begin(), end = wlist.end(); + watch_list::iterator it2 = it; + bool found = false; + for (; it != end; ++it) { + if (!found && w == *it) { + found = true; + } + else { + *it2 = *it; + ++it2; + } + } + wlist.set_end(it2); + //VERIFY(found); + } + + void conflict_cleanup(watch_list::iterator it, watch_list::iterator it2, watch_list& wlist) { + watch_list::iterator end = wlist.end(); + for (; it != end; ++it, ++it2) + *it2 = *it; + wlist.set_end(it2); + } + + + std::ostream& display_watch_list(std::ostream & out, clause_allocator const & ca, watch_list const & wlist) { + bool first = true; + for (watched const& w : wlist) { if (first) first = false; else out << " "; - switch (it->get_kind()) { + switch (w.get_kind()) { case watched::BINARY: - out << it->get_literal(); - if (it->is_learned()) + out << w.get_literal(); + if (w.is_learned()) out << "*"; break; case watched::TERNARY: - out << "(" << it->get_literal1() << " " << it->get_literal2() << ")"; + out << "(" << w.get_literal1() << " " << w.get_literal2() << ")"; break; case watched::CLAUSE: - out << "(" << it->get_blocked_literal() << " " << *(ca.get_clause(it->get_clause_offset())) << ")"; + out << "(" << w.get_blocked_literal() << " " << *(ca.get_clause(w.get_clause_offset())) << ")"; break; case watched::EXT_CONSTRAINT: - out << it->get_ext_constraint_idx(); + out << "ext: " << w.get_ext_constraint_idx(); break; default: UNREACHABLE(); } } + return out; } }; diff --git a/src/sat/sat_watched.h b/src/sat/sat_watched.h index 639d3e6a8..09ad22ffd 100644 --- a/src/sat/sat_watched.h +++ b/src/sat/sat_watched.h @@ -33,7 +33,7 @@ namespace sat { For binary clauses: we use a bit to store whether the binary clause was learned or not. - Remark: there is not Clause object for binary clauses. + Remark: there are no clause objects for binary clauses. */ class watched { public: @@ -41,7 +41,7 @@ namespace sat { BINARY = 0, TERNARY, CLAUSE, EXT_CONSTRAINT }; private: - unsigned m_val1; + size_t m_val1; unsigned m_val2; public: watched(literal l, bool learned): @@ -64,6 +64,8 @@ namespace sat { SASSERT(get_literal2() == l2); } + unsigned val2() const { return m_val2; } + watched(literal blocked_lit, clause_offset cls_off): m_val1(cls_off), m_val2(static_cast(CLAUSE) + (blocked_lit.to_uint() << 2)) { @@ -72,7 +74,7 @@ namespace sat { SASSERT(get_clause_offset() == cls_off); } - watched(ext_constraint_idx cnstr_idx): + explicit watched(ext_constraint_idx cnstr_idx): m_val1(cnstr_idx), m_val2(static_cast(EXT_CONSTRAINT)) { SASSERT(is_ext_constraint()); @@ -82,18 +84,21 @@ namespace sat { kind get_kind() const { return static_cast(m_val2 & 3); } bool is_binary_clause() const { return get_kind() == BINARY; } - literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(m_val1); } + literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(static_cast(m_val1)); } void set_literal(literal l) { SASSERT(is_binary_clause()); m_val1 = l.to_uint(); } - bool is_learned() const { SASSERT(is_binary_clause()); return (m_val2 >> 2) == 1; } - bool is_binary_non_learned_clause() const { return m_val2 == 0; } - void mark_not_learned() { SASSERT(is_learned()); m_val2 = static_cast(BINARY); SASSERT(!is_learned()); } - + bool is_learned() const { SASSERT(is_binary_clause()); return ((m_val2 >> 2) & 1) == 1; } + + bool is_binary_learned_clause() const { return is_binary_clause() && is_learned(); } + bool is_binary_non_learned_clause() const { return is_binary_clause() && !is_learned(); } + + void set_learned(bool l) { if (l) m_val2 |= 4u; else m_val2 &= ~4u; SASSERT(is_learned() == l); } + bool is_ternary_clause() const { return get_kind() == TERNARY; } - literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(m_val1); } + literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(static_cast(m_val1)); } literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 2); } bool is_clause() const { return get_kind() == CLAUSE; } - clause_offset get_clause_offset() const { SASSERT(is_clause()); return m_val1; } + clause_offset get_clause_offset() const { SASSERT(is_clause()); return static_cast(m_val1); } literal get_blocked_literal() const { SASSERT(is_clause()); return to_literal(m_val2 >> 2); } void set_clause_offset(clause_offset c) { SASSERT(is_clause()); m_val1 = c; } void set_blocked_literal(literal l) { SASSERT(is_clause()); m_val2 = static_cast(CLAUSE) + (l.to_uint() << 2); } @@ -103,7 +108,7 @@ namespace sat { } bool is_ext_constraint() const { return get_kind() == EXT_CONSTRAINT; } - ext_constraint_idx get_ext_constraint_idx() const { SASSERT(is_ext_constraint()); return m_val2; } + ext_constraint_idx get_ext_constraint_idx() const { SASSERT(is_ext_constraint()); return m_val1; } bool operator==(watched const & w) const { return m_val1 == w.m_val1 && m_val2 == w.m_val2; } bool operator!=(watched const & w) const { return !operator==(w); } @@ -126,11 +131,16 @@ namespace sat { typedef vector watch_list; + watched* find_binary_watch(watch_list & wlist, literal l); + watched const* find_binary_watch(watch_list const & wlist, literal l); bool erase_clause_watch(watch_list & wlist, clause_offset c); - inline void erase_ternary_watch(watch_list & wlist, literal l1, literal l2) { wlist.erase(watched(l1, l2)); } + void erase_ternary_watch(watch_list & wlist, literal l1, literal l2); + void set_ternary_learned(watch_list& wlist, literal l1, literal l2, bool learned); class clause_allocator; - void display(std::ostream & out, clause_allocator const & ca, watch_list const & wlist); + std::ostream& display_watch_list(std::ostream & out, clause_allocator const & ca, watch_list const & wlist); + + void conflict_cleanup(watch_list::iterator it, watch_list::iterator it2, watch_list& wlist); }; #endif diff --git a/src/sat/tactic/CMakeLists.txt b/src/sat/tactic/CMakeLists.txt index fed6a89c8..dbbfbe753 100644 --- a/src/sat/tactic/CMakeLists.txt +++ b/src/sat/tactic/CMakeLists.txt @@ -6,6 +6,7 @@ z3_add_component(sat_tactic COMPONENT_DEPENDENCIES sat tactic + solver TACTIC_HEADERS sat_tactic.h ) diff --git a/src/sat/tactic/atom2bool_var.cpp b/src/sat/tactic/atom2bool_var.cpp index 26f3448d3..e3c9b6767 100644 --- a/src/sat/tactic/atom2bool_var.cpp +++ b/src/sat/tactic/atom2bool_var.cpp @@ -16,19 +16,24 @@ Author: Notes: --*/ -#include "sat/tactic/atom2bool_var.h" -#include "ast/ast_smt2_pp.h" + #include "util/ref_util.h" +#include "ast/ast_smt2_pp.h" #include "tactic/goal.h" +#include "sat/tactic/atom2bool_var.h" void atom2bool_var::mk_inv(expr_ref_vector & lit2expr) const { - obj_map::iterator it = m_mapping.begin(); - obj_map::iterator end = m_mapping.end(); - for (; it != end; ++it) { - sat::literal l(static_cast(it->m_value), false); - lit2expr.set(l.index(), it->m_key); + for (auto const& kv : m_mapping) { + sat::literal l(static_cast(kv.m_value), false); + lit2expr.set(l.index(), kv.m_key); l.neg(); - lit2expr.set(l.index(), m().mk_not(it->m_key)); + lit2expr.set(l.index(), m().mk_not(kv.m_key)); + } +} + +void atom2bool_var::mk_var_inv(app_ref_vector & var2expr) const { + for (auto const& kv : m_mapping) { + var2expr.set(kv.m_value, to_app(kv.m_key)); } } diff --git a/src/sat/tactic/atom2bool_var.h b/src/sat/tactic/atom2bool_var.h index d360d3fe0..b7f533537 100644 --- a/src/sat/tactic/atom2bool_var.h +++ b/src/sat/tactic/atom2bool_var.h @@ -31,6 +31,7 @@ public: void insert(expr * n, sat::bool_var v) { expr2var::insert(n, v); } sat::bool_var to_bool_var(expr * n) const; void mk_inv(expr_ref_vector & lit2expr) const; + void mk_var_inv(app_ref_vector & var2expr) const; // return true if the mapping contains uninterpreted atoms. bool interpreted_atoms() const { return expr2var::interpreted_vars(); } }; diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 5a16d5e8c..ea9c5b4ea 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -26,16 +26,19 @@ Author: Notes: --*/ -#include "sat/tactic/goal2sat.h" -#include "ast/ast_smt2_pp.h" #include "util/ref_util.h" #include "util/cooperate.h" -#include "tactic/filter_model_converter.h" -#include "model/model_evaluator.h" +#include "ast/ast_smt2_pp.h" +#include "ast/ast_pp.h" +#include "ast/pb_decl_plugin.h" +#include "ast/ast_util.h" #include "ast/for_each_expr.h" +#include "sat/tactic/goal2sat.h" +#include "sat/ba_solver.h" +#include "model/model_evaluator.h" #include "model/model_v2_pp.h" #include "tactic/tactic.h" -#include "ast/ast_pp.h" +#include "tactic/generic_model_converter.h" #include struct goal2sat::imp { @@ -48,6 +51,8 @@ struct goal2sat::imp { m_t(t), m_root(r), m_sign(s), m_idx(idx) {} }; ast_manager & m; + pb_util pb; + sat::ba_solver* m_ext; svector m_frame_stack; svector m_result_stack; obj_map m_cache; @@ -61,22 +66,28 @@ struct goal2sat::imp { expr_ref_vector m_trail; expr_ref_vector m_interpreted_atoms; bool m_default_external; + bool m_xor_solver; + bool m_is_lemma; imp(ast_manager & _m, params_ref const & p, sat::solver & s, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external): m(_m), + pb(m), + m_ext(0), m_solver(s), m_map(map), m_dep2asm(dep2asm), m_trail(m), m_interpreted_atoms(m), - m_default_external(default_external) { + m_default_external(default_external), + m_is_lemma(false) { updt_params(p); m_true = sat::null_bool_var; } void updt_params(params_ref const & p) { - m_ite_extra = p.get_bool("ite_extra", true); - m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_ite_extra = p.get_bool("ite_extra", true); + m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_xor_solver = p.get_bool("xor_solver", false); } void throw_op_not_handled(std::string const& s) { @@ -89,31 +100,33 @@ struct goal2sat::imp { m_solver.mk_clause(1, &l); } + void set_lemma_mode(bool f) { m_is_lemma = f; } + void mk_clause(sat::literal l1, sat::literal l2) { TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << "\n";); - m_solver.mk_clause(l1, l2); + m_solver.mk_clause(l1, l2, m_is_lemma); } void mk_clause(sat::literal l1, sat::literal l2, sat::literal l3) { TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << " " << l3 << "\n";); - m_solver.mk_clause(l1, l2, l3); + m_solver.mk_clause(l1, l2, l3, m_is_lemma); } void mk_clause(unsigned num, sat::literal * lits) { TRACE("goal2sat", tout << "mk_clause: "; for (unsigned i = 0; i < num; i++) tout << lits[i] << " "; tout << "\n";); - m_solver.mk_clause(num, lits); + m_solver.mk_clause(num, lits, m_is_lemma); } sat::bool_var mk_true() { if (m_true == sat::null_bool_var) { // create fake variable to represent true; - m_true = m_solver.mk_var(); + m_true = m_solver.mk_var(false); mk_clause(sat::literal(m_true, false)); // v is true } return m_true; } - void convert_atom(expr * t, bool root, bool sign) { + void convert_atom(expr * t, bool root, bool sign) { SASSERT(m.is_bool(t)); sat::literal l; sat::bool_var v = m_map.to_bool_var(t); @@ -129,7 +142,7 @@ struct goal2sat::imp { sat::bool_var v = m_solver.mk_var(ext); m_map.insert(t, v); l = sat::literal(v, sign); - TRACE("goal2sat", tout << "new_var: " << v << "\n" << mk_ismt2_pp(t, m) << "\n";); + TRACE("sat", tout << "new_var: " << v << ": " << mk_ismt2_pp(t, m) << "\n";); if (ext && !is_uninterp_const(t)) { m_interpreted_atoms.push_back(t); } @@ -138,6 +151,7 @@ struct goal2sat::imp { else { SASSERT(v != sat::null_bool_var); l = sat::literal(v, sign); + m_solver.set_eliminated(v, false); } SASSERT(l != sat::null_literal); if (root) @@ -146,6 +160,18 @@ struct goal2sat::imp { m_result_stack.push_back(l); } + bool convert_app(app* t, bool root, bool sign) { + if (t->get_family_id() == pb.get_family_id()) { + ensure_extension(); + m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); + return false; + } + else { + convert_atom(t, root, sign); + return true; + } + } + bool process_cached(app * t, bool root, bool sign) { sat::literal l; if (m_cache.find(t, l)) { @@ -160,6 +186,7 @@ struct goal2sat::imp { return false; } + bool visit(expr * t, bool root, bool sign) { if (!is_app(t)) { convert_atom(t, root, sign); @@ -168,8 +195,7 @@ struct goal2sat::imp { if (process_cached(to_app(t), root, sign)) return true; if (to_app(t)->get_family_id() != m.get_basic_family_id()) { - convert_atom(t, root, sign); - return true; + return convert_app(to_app(t), root, sign); } switch (to_app(t)->get_decl_kind()) { case OP_NOT: @@ -184,8 +210,10 @@ struct goal2sat::imp { m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); return false; } - convert_atom(t, root, sign); - return true; + else { + convert_atom(t, root, sign); + return true; + } case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: { @@ -283,7 +311,6 @@ struct goal2sat::imp { } } - void convert_ite(app * n, bool root, bool sign) { unsigned sz = m_result_stack.size(); SASSERT(sz >= 3); @@ -321,7 +348,7 @@ struct goal2sat::imp { } } - void convert_iff(app * t, bool root, bool sign) { + void convert_iff2(app * t, bool root, bool sign) { TRACE("goal2sat", tout << "convert_iff " << root << " " << sign << "\n" << mk_ismt2_pp(t, m) << "\n";); unsigned sz = m_result_stack.size(); SASSERT(sz >= 2); @@ -354,26 +381,345 @@ struct goal2sat::imp { } } + void convert_iff(app * t, bool root, bool sign) { + TRACE("goal2sat", tout << "convert_iff " << root << " " << sign << "\n" << mk_ismt2_pp(t, m) << "\n";); + unsigned sz = m_result_stack.size(); + unsigned num = get_num_args(t); + SASSERT(sz >= num && num >= 2); + if (num == 2) { + convert_iff2(t, root, sign); + return; + } + sat::literal_vector lits; + sat::bool_var v = m_solver.mk_var(true); + lits.push_back(sat::literal(v, true)); + convert_pb_args(num, lits); + // ensure that = is converted to xor + for (unsigned i = 1; i + 1 < lits.size(); ++i) { + lits[i].neg(); + } + ensure_extension(); + m_ext->add_xr(lits); + sat::literal lit(v, sign); + if (root) { + m_result_stack.reset(); + mk_clause(lit); + } + else { + m_result_stack.shrink(sz - num); + m_result_stack.push_back(lit); + } + } + + void convert_pb_args(unsigned num_args, sat::literal_vector& lits) { + unsigned sz = m_result_stack.size(); + for (unsigned i = 0; i < num_args; ++i) { + sat::literal lit(m_result_stack[sz - num_args + i]); + if (!m_solver.is_external(lit.var())) { + m_solver.set_external(lit.var()); + } + lits.push_back(lit); + } + } + + typedef std::pair wliteral; + + void check_unsigned(rational const& c) { + if (!c.is_unsigned()) { + throw default_exception("unsigned coefficient expected"); + } + } + + void convert_to_wlits(app* t, sat::literal_vector const& lits, svector& wlits) { + for (unsigned i = 0; i < lits.size(); ++i) { + rational c = pb.get_coeff(t, i); + check_unsigned(c); + wlits.push_back(std::make_pair(c.get_unsigned(), lits[i])); + } + } + + void convert_pb_args(app* t, svector& wlits) { + sat::literal_vector lits; + convert_pb_args(t->get_num_args(), lits); + convert_to_wlits(t, lits, wlits); + } + + void convert_pb_ge(app* t, bool root, bool sign) { + rational k = pb.get_k(t); + check_unsigned(k); + svector wlits; + convert_pb_args(t, wlits); + unsigned sz = m_result_stack.size(); + if (root) { + m_result_stack.reset(); + m_ext->add_pb_ge(sat::null_bool_var, wlits, k.get_unsigned()); + } + else { + sat::bool_var v = m_solver.mk_var(true); + sat::literal lit(v, sign); + m_ext->add_pb_ge(v, wlits, k.get_unsigned()); + TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); + m_result_stack.shrink(sz - t->get_num_args()); + m_result_stack.push_back(lit); + } + } + + void convert_pb_le(app* t, bool root, bool sign) { + rational k = pb.get_k(t); + k.neg(); + svector wlits; + convert_pb_args(t, wlits); + for (wliteral& wl : wlits) { + wl.second.neg(); + k += rational(wl.first); + } + check_unsigned(k); + unsigned sz = m_result_stack.size(); + if (root) { + m_result_stack.reset(); + m_ext->add_pb_ge(sat::null_bool_var, wlits, k.get_unsigned()); + } + else { + sat::bool_var v = m_solver.mk_var(true); + sat::literal lit(v, sign); + m_ext->add_pb_ge(v, wlits, k.get_unsigned()); + TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); + m_result_stack.shrink(sz - t->get_num_args()); + m_result_stack.push_back(lit); + } + } + + void convert_pb_eq(app* t, bool root, bool sign) { + IF_VERBOSE(0, verbose_stream() << "pbeq: " << mk_pp(t, m) << "\n";); + rational k = pb.get_k(t); + SASSERT(k.is_unsigned()); + svector wlits; + convert_pb_args(t, wlits); + sat::bool_var v1 = (root && !sign) ? sat::null_bool_var : m_solver.mk_var(true); + sat::bool_var v2 = (root && !sign) ? sat::null_bool_var : m_solver.mk_var(true); + m_ext->add_pb_ge(v1, wlits, k.get_unsigned()); + k.neg(); + for (wliteral& wl : wlits) { + wl.second.neg(); + k += rational(wl.first); + } + check_unsigned(k); + m_ext->add_pb_ge(v2, wlits, k.get_unsigned()); + if (root && !sign) { + m_result_stack.reset(); + } + else { + sat::literal l1(v1, false), l2(v2, false); + sat::bool_var v = m_solver.mk_var(); + sat::literal l(v, false); + mk_clause(~l, l1); + mk_clause(~l, l2); + mk_clause(~l1, ~l2, l); + m_cache.insert(t, l); + m_result_stack.shrink(m_result_stack.size() - t->get_num_args()); + if (sign) l.neg(); + m_result_stack.push_back(l); + if (root) { + m_result_stack.reset(); + mk_clause(l); + } + } + } + + void convert_at_least_k(app* t, rational const& k, bool root, bool sign) { + SASSERT(k.is_unsigned()); + sat::literal_vector lits; + unsigned sz = m_result_stack.size(); + convert_pb_args(t->get_num_args(), lits); + if (root) { + m_result_stack.reset(); + m_ext->add_at_least(sat::null_bool_var, lits, k.get_unsigned()); + } + else { + sat::bool_var v = m_solver.mk_var(true); + sat::literal lit(v, false); + m_ext->add_at_least(v, lits, k.get_unsigned()); + m_cache.insert(t, lit); + if (sign) lit.neg(); + TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); + m_result_stack.shrink(sz - t->get_num_args()); + m_result_stack.push_back(lit); + } + } + + void convert_at_most_k(app* t, rational const& k, bool root, bool sign) { + SASSERT(k.is_unsigned()); + sat::literal_vector lits; + unsigned sz = m_result_stack.size(); + convert_pb_args(t->get_num_args(), lits); + for (sat::literal& l : lits) { + l.neg(); + } + if (root) { + m_result_stack.reset(); + m_ext->add_at_least(sat::null_bool_var, lits, lits.size() - k.get_unsigned()); + } + else { + sat::bool_var v = m_solver.mk_var(true); + sat::literal lit(v, false); + m_ext->add_at_least(v, lits, lits.size() - k.get_unsigned()); + m_cache.insert(t, lit); + m_result_stack.shrink(sz - t->get_num_args()); + if (sign) lit.neg(); + m_result_stack.push_back(lit); + } + } + + void convert_eq_k(app* t, rational const& k, bool root, bool sign) { + SASSERT(k.is_unsigned()); + sat::literal_vector lits; + convert_pb_args(t->get_num_args(), lits); + sat::bool_var v1 = (root && !sign) ? sat::null_bool_var : m_solver.mk_var(true); + sat::bool_var v2 = (root && !sign) ? sat::null_bool_var : m_solver.mk_var(true); + m_ext->add_at_least(v1, lits, k.get_unsigned()); + for (sat::literal& l : lits) { + l.neg(); + } + m_ext->add_at_least(v2, lits, lits.size() - k.get_unsigned()); + + + if (root && !sign) { + m_result_stack.reset(); + } + else { + sat::literal l1(v1, false), l2(v2, false); + sat::bool_var v = m_solver.mk_var(); + sat::literal l(v, false); + mk_clause(~l, l1); + mk_clause(~l, l2); + mk_clause(~l1, ~l2, l); + m_cache.insert(t, l); + m_result_stack.shrink(m_result_stack.size() - t->get_num_args()); + if (sign) l.neg(); + m_result_stack.push_back(l); + if (root) { + mk_clause(l); + m_result_stack.reset(); + } + } + } + + void ensure_extension() { + if (!m_ext) { + sat::extension* ext = m_solver.get_extension(); + if (ext) { + m_ext = dynamic_cast(ext); + SASSERT(m_ext); + } + if (!m_ext) { + m_ext = alloc(sat::ba_solver); + m_solver.set_extension(m_ext); + } + } + } + void convert(app * t, bool root, bool sign) { - SASSERT(t->get_family_id() == m.get_basic_family_id()); - switch (to_app(t)->get_decl_kind()) { - case OP_OR: - convert_or(t, root, sign); - break; - case OP_AND: - convert_and(t, root, sign); - break; - case OP_ITE: - convert_ite(t, root, sign); - break; - case OP_IFF: - case OP_EQ: - convert_iff(t, root, sign); - break; - default: + if (t->get_family_id() == m.get_basic_family_id()) { + switch (to_app(t)->get_decl_kind()) { + case OP_OR: + convert_or(t, root, sign); + break; + case OP_AND: + convert_and(t, root, sign); + break; + case OP_ITE: + convert_ite(t, root, sign); + break; + case OP_IFF: + case OP_EQ: + convert_iff(t, root, sign); + break; + default: + UNREACHABLE(); + } + } + else if (t->get_family_id() == pb.get_family_id()) { + ensure_extension(); + rational k; + switch (t->get_decl_kind()) { + case OP_AT_MOST_K: + k = pb.get_k(t); + convert_at_most_k(t, k, root, sign); + break; + case OP_AT_LEAST_K: + k = pb.get_k(t); + convert_at_least_k(t, k, root, sign); + break; + case OP_PB_LE: + if (pb.has_unit_coefficients(t)) { + k = pb.get_k(t); + convert_at_most_k(t, k, root, sign); + } + else { + convert_pb_le(t, root, sign); + } + break; + case OP_PB_GE: + if (pb.has_unit_coefficients(t)) { + k = pb.get_k(t); + convert_at_least_k(t, k, root, sign); + } + else { + convert_pb_ge(t, root, sign); + } + break; + case OP_PB_EQ: + if (pb.has_unit_coefficients(t)) { + k = pb.get_k(t); + convert_eq_k(t, k, root, sign); + } + else { + convert_pb_eq(t, root, sign); + } + break; + default: + UNREACHABLE(); + } + } + else { UNREACHABLE(); } } + + + unsigned get_num_args(app* t) { + + if (m.is_iff(t) && m_xor_solver) { + unsigned n = 2; + while (m.is_iff(t->get_arg(1))) { + ++n; + t = to_app(t->get_arg(1)); + } + return n; + } + else { + return t->get_num_args(); + } + } + + expr* get_arg(app* t, unsigned idx) { + if (m.is_iff(t) && m_xor_solver) { + while (idx >= 1) { + SASSERT(m.is_iff(t)); + t = to_app(t->get_arg(1)); + --idx; + } + if (m.is_iff(t)) { + return t->get_arg(idx); + } + else { + return t; + } + } + else { + return t->get_arg(idx); + } + } void process(expr * n) { //SASSERT(m_result_stack.empty()); @@ -405,9 +751,9 @@ struct goal2sat::imp { visit(t->get_arg(0), root, !sign); continue; } - unsigned num = t->get_num_args(); + unsigned num = get_num_args(t); while (fr.m_idx < num) { - expr * arg = t->get_arg(fr.m_idx); + expr * arg = get_arg(t, fr.m_idx); fr.m_idx++; if (!visit(arg, false, false)) goto loop; @@ -457,8 +803,7 @@ struct goal2sat::imp { fmls.reset(); m.linearize(g.dep(idx), deps); fmls.push_back(f); - for (unsigned i = 0; i < deps.size(); ++i) { - expr * d = deps[i]; + for (expr * d : deps) { expr * d1 = d; SASSERT(m.is_bool(d)); bool sign = m.is_not(d, d1); @@ -477,7 +822,7 @@ struct goal2sat::imp { } f = m.mk_or(fmls.size(), fmls.c_ptr()); } - TRACE("sat", tout << mk_pp(f, m) << "\n";); + TRACE("goal2sat", tout << f << "\n";); process(f); skip_dep: ; @@ -544,9 +889,10 @@ struct goal2sat::scoped_set_imp { }; -void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external) { +void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external, bool is_lemma) { imp proc(g.m(), p, t, m, dep2asm, default_external); scoped_set_imp set(this, &proc); + proc.set_lemma_mode(is_lemma); proc(g); dealloc(m_interpreted_atoms); m_interpreted_atoms = alloc(expr_ref_vector, g.m()); @@ -560,116 +906,166 @@ void goal2sat::get_interpreted_atoms(expr_ref_vector& atoms) { } +sat2goal::mc::mc(ast_manager& m): m(m), m_var2expr(m) {} + +void sat2goal::mc::flush_smc(sat::solver& s, atom2bool_var const& map) { + s.flush(m_smc); + m_var2expr.resize(s.num_vars()); + map.mk_var_inv(m_var2expr); +} + +void sat2goal::mc::flush_gmc() { + sat::literal_vector updates; + m_smc.expand(updates); + m_smc.reset(); + if (!m_gmc) m_gmc = alloc(generic_model_converter, m, "sat2goal"); + // now gmc owns the model converter + sat::literal_vector clause; + expr_ref_vector tail(m); + expr_ref def(m); + for (unsigned i = 0; i < updates.size(); ++i) { + sat::literal l = updates[i]; + if (l == sat::null_literal) { + sat::literal lit0 = clause[0]; + for (unsigned i = 1; i < clause.size(); ++i) { + tail.push_back(lit2expr(~clause[i])); + } + def = m.mk_or(lit2expr(lit0), mk_and(tail)); + if (lit0.sign()) { + lit0.neg(); + def = m.mk_not(def); + } + m_gmc->add(lit2expr(lit0), def); + clause.reset(); + tail.reset(); + } + // short circuit for equivalences: + else if (clause.empty() && tail.empty() && + i + 5 < updates.size() && + updates[i] == ~updates[i + 3] && + updates[i + 1] == ~updates[i + 4] && + updates[i + 2] == sat::null_literal && + updates[i + 5] == sat::null_literal) { + sat::literal r = ~updates[i+1]; + if (l.sign()) { + l.neg(); + r.neg(); + } + m_gmc->add(lit2expr(l), lit2expr(r)); + i += 5; + } + else { + clause.push_back(l); + } + } +} + +model_converter* sat2goal::mc::translate(ast_translation& translator) { + mc* result = alloc(mc, translator.to()); + result->m_smc.copy(m_smc); + result->m_gmc = m_gmc ? dynamic_cast(m_gmc->translate(translator)) : nullptr; + for (app* e : m_var2expr) { + result->m_var2expr.push_back(translator(e)); + } + return result; +} + +void sat2goal::mc::collect(ast_pp_util& visitor) { + flush_gmc(); + if (m_gmc) m_gmc->collect(visitor); +} + +void sat2goal::mc::display(std::ostream& out) { + flush_gmc(); + if (m_gmc) m_gmc->display(out); +} + +void sat2goal::mc::get_units(obj_map& units) { + flush_gmc(); + if (m_gmc) m_gmc->get_units(units); +} + + +void sat2goal::mc::operator()(model_ref & md) { + model_evaluator ev(*md); + ev.set_model_completion(false); + + // create a SAT model using md + sat::model sat_md; + expr_ref val(m); + VERIFY(!m_var2expr.empty()); + for (expr * atom : m_var2expr) { + if (!atom) { + sat_md.push_back(l_undef); + continue; + } + ev(atom, val); + if (m.is_true(val)) + sat_md.push_back(l_true); + else if (m.is_false(val)) + sat_md.push_back(l_false); + else + sat_md.push_back(l_undef); + } + + // apply SAT model converter + m_smc(sat_md); + + // register value of non-auxiliary boolean variables back into md + unsigned sz = m_var2expr.size(); + for (sat::bool_var v = 0; v < sz; v++) { + app * atom = m_var2expr.get(v); + if (atom && is_uninterp_const(atom)) { + func_decl * d = atom->get_decl(); + lbool new_val = sat_md[v]; + if (new_val == l_true) + md->register_decl(d, m.mk_true()); + else if (new_val == l_false) + md->register_decl(d, m.mk_false()); + } + } + // apply externalized model converter + if (m_gmc) (*m_gmc)(md); + TRACE("sat_mc", tout << "after sat_mc\n"; model_v2_pp(tout, *md);); +} + + +void sat2goal::mc::operator()(expr_ref& fml) { + flush_gmc(); + if (m_gmc) (*m_gmc)(fml); +} + +void sat2goal::mc::insert(sat::bool_var v, app * atom, bool aux) { + SASSERT(!m_var2expr.get(v, nullptr)); + m_var2expr.reserve(v + 1); + m_var2expr.set(v, atom); + if (aux) { + SASSERT(is_uninterp_const(atom)); + SASSERT(m.is_bool(atom)); + if (!m_gmc) m_gmc = alloc(generic_model_converter, m, "sat2goal"); + m_gmc->hide(atom->get_decl()); + } +} + +expr_ref sat2goal::mc::lit2expr(sat::literal l) { + if (!m_var2expr.get(l.var())) { + app* aux = m.mk_fresh_const(0, m.mk_bool_sort()); + m_var2expr.set(l.var(), aux); + if (!m_gmc) m_gmc = alloc(generic_model_converter, m, "sat2goal"); + m_gmc->hide(aux->get_decl()); + } + VERIFY(m_var2expr.get(l.var())); + expr_ref result(m_var2expr.get(l.var()), m); + if (l.sign()) { + result = m.mk_not(result); + } + return result; +} + + struct sat2goal::imp { - // Wrapper for sat::model_converter: converts it into an "AST level" model_converter. - class sat_model_converter : public model_converter { - sat::model_converter m_mc; - // TODO: the following mapping is storing a lot of useless information, and may be a performance bottleneck. - // We need to save only the expressions associated with variables that occur in m_mc. - // This information may be stored as a vector of pairs. - // The mapping is only created during the model conversion. - expr_ref_vector m_var2expr; - filter_model_converter_ref m_fmc; // filter for eliminating fresh variables introduced in the assertion-set --> sat conversion - - sat_model_converter(ast_manager & m): - m_var2expr(m) { - } - - public: - sat_model_converter(ast_manager & m, sat::solver const & s):m_var2expr(m) { - m_mc.copy(s.get_model_converter()); - m_fmc = alloc(filter_model_converter, m); - } - - ast_manager & m() { return m_var2expr.get_manager(); } - - void insert(expr * atom, bool aux) { - m_var2expr.push_back(atom); - if (aux) { - SASSERT(is_uninterp_const(atom)); - SASSERT(m().is_bool(atom)); - m_fmc->insert(to_app(atom)->get_decl()); - } - } - - void operator()(model_ref & md, unsigned goal_idx) override { - SASSERT(goal_idx == 0); - TRACE("sat_mc", tout << "before sat_mc\n"; model_v2_pp(tout, *md); display(tout);); - // REMARK: potential problem - // model_evaluator can't evaluate quantifiers. Then, - // an eliminated variable that depends on a quantified expression can't be recovered. - // A similar problem also affects any model_converter that uses elim_var_model_converter. - // - // Possible solution: - // model_converters reject any variable elimination that depends on a quantified expression. - - model_evaluator ev(*md); - ev.set_model_completion(false); - - // create a SAT model using md - sat::model sat_md; - unsigned sz = m_var2expr.size(); - expr_ref val(m()); - for (sat::bool_var v = 0; v < sz; v++) { - expr * atom = m_var2expr.get(v); - ev(atom, val); - if (m().is_true(val)) - sat_md.push_back(l_true); - else if (m().is_false(val)) - sat_md.push_back(l_false); - else - sat_md.push_back(l_undef); - } - - // apply SAT model converter - m_mc(sat_md); - - // register value of non-auxiliary boolean variables back into md - sz = m_var2expr.size(); - for (sat::bool_var v = 0; v < sz; v++) { - expr * atom = m_var2expr.get(v); - if (is_uninterp_const(atom)) { - func_decl * d = to_app(atom)->get_decl(); - lbool new_val = sat_md[v]; - if (new_val == l_true) - md->register_decl(d, m().mk_true()); - else if (new_val == l_false) - md->register_decl(d, m().mk_false()); - } - } - - // apply filter model converter - (*m_fmc)(md); - TRACE("sat_mc", tout << "after sat_mc\n"; model_v2_pp(tout, *md);); - } - - model_converter * translate(ast_translation & translator) override { - sat_model_converter * res = alloc(sat_model_converter, translator.to()); - res->m_fmc = static_cast(m_fmc->translate(translator)); - unsigned sz = m_var2expr.size(); - for (unsigned i = 0; i < sz; i++) - res->m_var2expr.push_back(translator(m_var2expr.get(i))); - return res; - } - - void display(std::ostream & out) override { - out << "(sat-model-converter\n"; - m_mc.display(out); - sat::bool_var_set vars; - m_mc.collect_vars(vars); - out << "(atoms"; - unsigned sz = m_var2expr.size(); - for (unsigned i = 0; i < sz; i++) { - if (vars.contains(i)) { - out << "\n (" << i << "\n " << mk_ismt2_pp(m_var2expr.get(i), m(), 2) << ")"; - } - } - out << ")\n"; - m_fmc->display(out); - out << ")\n"; - } - }; + typedef mc sat_model_converter; ast_manager & m; expr_ref_vector m_lit2expr; @@ -692,86 +1088,150 @@ struct sat2goal::imp { throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } - void init_lit2expr(sat::solver const & s, atom2bool_var const & map, model_converter_ref & mc, bool produce_models) { - ref _mc; - if (produce_models) - _mc = alloc(sat_model_converter, m, s); - unsigned num_vars = s.num_vars(); - m_lit2expr.resize(num_vars * 2); - map.mk_inv(m_lit2expr); - sort * b = m.mk_bool_sort(); - for (sat::bool_var v = 0; v < num_vars; v++) { - checkpoint(); - sat::literal l(v, false); - if (m_lit2expr.get(l.index()) == nullptr) { - SASSERT(m_lit2expr.get((~l).index()) == 0); - app * aux = m.mk_fresh_const(nullptr, b); - if (_mc) - _mc->insert(aux, true); - m_lit2expr.set(l.index(), aux); - m_lit2expr.set((~l).index(), m.mk_not(aux)); + expr * lit2expr(ref& mc, sat::literal l) { + if (!m_lit2expr.get(l.index())) { + SASSERT(m_lit2expr.get((~l).index()) == 0); + app* aux = mc ? mc->var2expr(l.var()) : nullptr; + if (!aux) { + aux = m.mk_fresh_const(0, m.mk_bool_sort()); + if (mc) { + mc->insert(l.var(), aux, true); + } } - else { - if (_mc) - _mc->insert(m_lit2expr.get(l.index()), false); - SASSERT(m_lit2expr.get((~l).index()) != 0); - } - } - mc = _mc.get(); - } - - expr * lit2expr(sat::literal l) { + sat::literal lit(l.var(), false); + m_lit2expr.set(lit.index(), aux); + m_lit2expr.set((~lit).index(), m.mk_not(aux)); + } return m_lit2expr.get(l.index()); } - void assert_clauses(sat::clause * const * begin, sat::clause * const * end, goal & r) { + void assert_pb(ref& mc, goal& r, sat::ba_solver::pb const& p) { + pb_util pb(m); ptr_buffer lits; - for (sat::clause * const * it = begin; it != end; it++) { + vector coeffs; + for (auto const& wl : p) { + lits.push_back(lit2expr(mc, wl.second)); + coeffs.push_back(rational(wl.first)); + } + rational k(p.k()); + expr_ref fml(pb.mk_ge(p.size(), coeffs.c_ptr(), lits.c_ptr(), k), m); + + if (p.lit() != sat::null_literal) { + fml = m.mk_eq(lit2expr(mc, p.lit()), fml); + } + r.assert_expr(fml); + } + + void assert_card(ref& mc, goal& r, sat::ba_solver::card const& c) { + pb_util pb(m); + ptr_buffer lits; + for (sat::literal l : c) { + lits.push_back(lit2expr(mc, l)); + } + expr_ref fml(pb.mk_at_least_k(c.size(), lits.c_ptr(), c.k()), m); + + if (c.lit() != sat::null_literal) { + fml = m.mk_eq(lit2expr(mc, c.lit()), fml); + } + r.assert_expr(fml); + } + + void assert_xor(ref& mc, goal & r, sat::ba_solver::xr const& x) { + ptr_buffer lits; + for (sat::literal l : x) { + lits.push_back(lit2expr(mc, l)); + } + expr_ref fml(m.mk_xor(x.size(), lits.c_ptr()), m); + + if (x.lit() != sat::null_literal) { + fml = m.mk_eq(lit2expr(mc, x.lit()), fml); + } + r.assert_expr(fml); + } + + void assert_clauses(ref& mc, sat::solver const & s, sat::clause_vector const& clauses, goal & r, bool asserted) { + ptr_buffer lits; + for (sat::clause* cp : clauses) { checkpoint(); lits.reset(); - sat::clause const & c = *(*it); - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - lits.push_back(lit2expr(c[i])); + sat::clause const & c = *cp; + if (asserted || m_learned || c.glue() <= s.get_config().m_gc_small_lbd) { + for (sat::literal l : c) { + lits.push_back(lit2expr(mc, l)); + } + r.assert_expr(m.mk_or(lits.size(), lits.c_ptr())); } - r.assert_expr(m.mk_or(lits.size(), lits.c_ptr())); } } - void operator()(sat::solver const & s, atom2bool_var const & map, goal & r, model_converter_ref & mc) { + sat::ba_solver* get_ba_solver(sat::solver const& s) { + return dynamic_cast(s.get_extension()); + } + + void operator()(sat::solver & s, atom2bool_var const & map, goal & r, ref & mc) { if (s.inconsistent()) { r.assert_expr(m.mk_false()); return; } - init_lit2expr(s, map, mc, r.models_enabled()); - // collect units - unsigned num_vars = s.num_vars(); - for (sat::bool_var v = 0; v < num_vars; v++) { - checkpoint(); - switch (s.value(v)) { - case l_true: - r.assert_expr(lit2expr(sat::literal(v, false))); - break; - case l_false: - r.assert_expr(lit2expr(sat::literal(v, true))); - break; - case l_undef: - break; - } + if (r.models_enabled() && !mc) { + mc = alloc(sat_model_converter, m); } + if (mc) mc->flush_smc(s, map); + m_lit2expr.resize(s.num_vars() * 2); + map.mk_inv(m_lit2expr); + // collect units + unsigned trail_sz = s.init_trail_size(); + for (unsigned i = 0; i < trail_sz; ++i) { + checkpoint(); + r.assert_expr(lit2expr(mc, s.trail_literal(i))); + } + // collect binary clauses svector bin_clauses; s.collect_bin_clauses(bin_clauses, m_learned); - svector::iterator it = bin_clauses.begin(); - svector::iterator end = bin_clauses.end(); - for (; it != end; ++it) { + for (sat::solver::bin_clause const& bc : bin_clauses) { checkpoint(); - r.assert_expr(m.mk_or(lit2expr(it->first), lit2expr(it->second))); + r.assert_expr(m.mk_or(lit2expr(mc, bc.first), lit2expr(mc, bc.second))); } // collect clauses - assert_clauses(s.begin_clauses(), s.end_clauses(), r); - if (m_learned) - assert_clauses(s.begin_learned(), s.end_learned(), r); + assert_clauses(mc, s, s.clauses(), r, true); + + sat::ba_solver* ext = get_ba_solver(s); + if (ext) { + for (auto* c : ext->constraints()) { + switch (c->tag()) { + case sat::ba_solver::card_t: + assert_card(mc, r, c->to_card()); + break; + case sat::ba_solver::pb_t: + assert_pb(mc, r, c->to_pb()); + break; + case sat::ba_solver::xr_t: + assert_xor(mc, r, c->to_xr()); + break; + } + } + } + } + + void add_clause(ref& mc, sat::literal_vector const& lits, expr_ref_vector& lemmas) { + expr_ref_vector lemma(m); + for (sat::literal l : lits) { + expr* e = lit2expr(mc, l); + if (!e) return; + lemma.push_back(e); + } + lemmas.push_back(mk_or(lemma)); + } + + void add_clause(ref& mc, sat::clause const& c, expr_ref_vector& lemmas) { + expr_ref_vector lemma(m); + for (sat::literal l : c) { + expr* e = lit2expr(mc, l); + if (!e) return; + lemma.push_back(e); + } + lemmas.push_back(mk_or(lemma)); } }; @@ -794,10 +1254,11 @@ struct sat2goal::scoped_set_imp { } }; -void sat2goal::operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p, - goal & g, model_converter_ref & mc) { +void sat2goal::operator()(sat::solver & t, atom2bool_var const & m, params_ref const & p, + goal & g, ref & mc) { imp proc(g.m(), p); scoped_set_imp set(this, &proc); proc(t, m, g, mc); } + diff --git a/src/sat/tactic/goal2sat.h b/src/sat/tactic/goal2sat.h index 199d79f9d..32b89fe5d 100644 --- a/src/sat/tactic/goal2sat.h +++ b/src/sat/tactic/goal2sat.h @@ -32,6 +32,7 @@ Notes: #include "tactic/goal.h" #include "sat/sat_solver.h" #include "tactic/model_converter.h" +#include "tactic/generic_model_converter.h" #include "sat/tactic/atom2bool_var.h" class goal2sat { @@ -50,6 +51,7 @@ public: static bool has_unsupported_bool(goal const & s); + /** \brief "Compile" the goal into the given sat solver. Store a mapping from atoms to boolean variables into m. @@ -60,7 +62,7 @@ public: \warning conversion throws a tactic_exception, if it is interrupted (by set_cancel), an unsupported operator is found, or memory consumption limit is reached (set with param :max-memory). */ - void operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false); + void operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false, bool is_lemma = false); void get_interpreted_atoms(expr_ref_vector& atoms); @@ -72,8 +74,35 @@ class sat2goal { imp * m_imp; struct scoped_set_imp; public: + + class mc : public model_converter { + ast_manager& m; + sat::model_converter m_smc; + generic_model_converter_ref m_gmc; + app_ref_vector m_var2expr; + + // flushes from m_smc to m_gmc; + void flush_gmc(); + + public: + mc(ast_manager& m); + virtual ~mc() {} + // flush model converter from SAT solver to this structure. + void flush_smc(sat::solver& s, atom2bool_var const& map); + void operator()(model_ref& md) override; + void operator()(expr_ref& fml) override; + model_converter* translate(ast_translation& translator) override; + void collect(ast_pp_util& visitor) override; + void display(std::ostream& out) override; + void get_units(obj_map& units) override; + app* var2expr(sat::bool_var v) const { return m_var2expr.get(v, nullptr); } + expr_ref lit2expr(sat::literal l); + void insert(sat::bool_var v, app * atom, bool aux); + }; + sat2goal(); + static void collect_param_descrs(param_descrs & r); /** @@ -84,7 +113,7 @@ public: \warning conversion throws a tactic_exception, if it is interrupted (by set_cancel), or memory consumption limit is reached (set with param :max-memory). */ - void operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p, goal & s, model_converter_ref & mc); + void operator()(sat::solver & t, atom2bool_var const & m, params_ref const & p, goal & s, ref & mc); }; diff --git a/src/sat/tactic/sat_tactic.cpp b/src/sat/tactic/sat_tactic.cpp index 4bb644a43..149a4e853 100644 --- a/src/sat/tactic/sat_tactic.cpp +++ b/src/sat/tactic/sat_tactic.cpp @@ -18,9 +18,10 @@ Notes: --*/ #include "ast/ast_pp.h" #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" #include "sat/tactic/goal2sat.h" #include "sat/sat_solver.h" +#include "solver/parallel_tactic.h" +#include "solver/parallel_params.hpp" #include "model/model_v2_pp.h" class sat_tactic : public tactic { @@ -34,17 +35,13 @@ class sat_tactic : public tactic { imp(ast_manager & _m, params_ref const & p): m(_m), - m_solver(p, m.limit(), nullptr), + m_solver(p, m.limit()), m_params(p) { SASSERT(!m.proofs_enabled()); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = nullptr; pc = nullptr; core = nullptr; + void operator()(goal_ref const & g, + goal_ref_buffer & result) { fail_if_proof_generation("sat", g); bool produce_models = g->models_enabled(); bool produce_core = g->unsat_core_enabled(); @@ -68,11 +65,6 @@ class sat_tactic : public tactic { TRACE("sat_dimacs", m_solver.display_dimacs(tout);); dep2assumptions(dep2asm, assumptions); lbool r = m_solver.check(assumptions.size(), assumptions.c_ptr()); - if (r == l_undef && m_solver.get_config().m_dimacs_display) { - for (auto const& kv : map) { - std::cout << "c " << kv.m_value << " " << mk_pp(kv.m_key, g->m()) << "\n"; - } - } if (r == l_false) { expr_dependency * lcore = nullptr; if (produce_core) { @@ -109,7 +101,7 @@ class sat_tactic : public tactic { } } TRACE("sat_tactic", model_v2_pp(tout, *md);); - mc = model2model_converter(md.get()); + g->add(model2model_converter(md.get())); } } else { @@ -118,7 +110,9 @@ class sat_tactic : public tactic { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "\"formula constains interpreted atoms, recovering formula from sat solver...\"\n";); #endif m_solver.pop_to_base_level(); + ref mc; m_sat2goal(m_solver, map, m_params, *(g.get()), mc); + g->add(mc.get()); } g->inc_depth(); result.push_back(g.get()); @@ -180,15 +174,12 @@ public: sat::solver::collect_param_descrs(r); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { imp proc(g->m(), m_params); scoped_set_imp set(this, &proc); try { - proc(g, result, mc, pc, core); + proc(g, result); proc.m_solver.collect_statistics(m_stats); } catch (sat::solver_exception & ex) { @@ -226,3 +217,4 @@ tactic * mk_sat_preprocessor_tactic(ast_manager & m, params_ref const & p) { return t; } + diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index 9cc13b897..6596cc3b7 100644 --- a/src/shell/datalog_frontend.cpp +++ b/src/shell/datalog_frontend.cpp @@ -246,7 +246,7 @@ unsigned read_datalog(char const * file) { false); } - catch (out_of_memory_error) { + catch (const out_of_memory_error &) { std::cout << "\n\nOUT OF MEMORY!\n\n"; display_statistics( std::cout, diff --git a/src/shell/dimacs_frontend.cpp b/src/shell/dimacs_frontend.cpp index eafaa8960..3a2af72cf 100644 --- a/src/shell/dimacs_frontend.cpp +++ b/src/shell/dimacs_frontend.cpp @@ -21,9 +21,9 @@ Revision History: #include #include "util/timeout.h" #include "util/rlimit.h" +#include "util/gparams.h" #include "sat/dimacs.h" #include "sat/sat_solver.h" -#include "util/gparams.h" extern bool g_display_statistics; static sat::solver * g_solver = nullptr; @@ -126,6 +126,41 @@ static void track_clauses(sat::solver const& src, } } +void verify_solution(char const * file_name) { + params_ref p = gparams::get_module("sat"); + p.set_bool("produce_models", true); + reslimit limit; + sat::solver solver(p, limit); + std::ifstream in(file_name); + if (in.bad() || in.fail()) { + std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; + exit(ERR_OPEN_FILE); + } + parse_dimacs(in, solver); + + sat::model const & m = g_solver->get_model(); + for (unsigned i = 1; i < m.size(); i++) { + sat::literal lit(i, false); + switch (m[i]) { + case l_false: lit.neg(); break; + case l_undef: break; + case l_true: break; + } + solver.mk_clause(1, &lit); + } + lbool r = solver.check(); + switch (r) { + case l_false: + std::cout << "model checking failed\n"; + break; + case l_true: + std::cout << "model validated\n"; + break; + default: + std::cout << "inconclusive model\n"; + break; + } +} unsigned read_dimacs(char const * file_name) { g_start_time = clock(); @@ -134,7 +169,7 @@ unsigned read_dimacs(char const * file_name) { params_ref p = gparams::get_module("sat"); p.set_bool("produce_models", true); reslimit limit; - sat::solver solver(p, limit, nullptr); + sat::solver solver(p, limit); g_solver = &solver; if (file_name) { @@ -152,7 +187,7 @@ unsigned read_dimacs(char const * file_name) { lbool r; vector tracking_clauses; - sat::solver solver2(p, limit, nullptr); + sat::solver solver2(p, limit); if (p.get_bool("dimacs.core", false)) { g_solver = &solver2; sat::literal_vector assumptions; @@ -165,6 +200,7 @@ unsigned read_dimacs(char const * file_name) { switch (r) { case l_true: std::cout << "sat\n"; + if (file_name) verify_solution(file_name); display_model(*g_solver); break; case l_undef: diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp index c79396cd1..ad9bcbd54 100644 --- a/src/shell/lp_frontend.cpp +++ b/src/shell/lp_frontend.cpp @@ -55,8 +55,8 @@ struct front_end_resource_limit : public lp::lp_resource_limit { void run_solver(lp_params & params, char const * mps_file_name) { reslimit rlim; - unsigned timeout = gparams::get().get_uint("timeout", 0); - unsigned rlimit = gparams::get().get_uint("rlimit", 0); + unsigned timeout = gparams::get_ref().get_uint("timeout", 0); + unsigned rlimit = gparams::get_ref().get_uint("rlimit", 0); front_end_resource_limit lp_limit(rlim); scoped_rlimit _rlimit(rlim, rlimit); diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 40c98a686..d367b8ec6 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -33,12 +33,13 @@ Revision History: #include "util/timeout.h" #include "util/z3_exception.h" #include "util/error_codes.h" +#include "util/file_path.h" #include "util/gparams.h" #include "util/env_params.h" #include "util/file_path.h" #include "shell/lp_frontend.h" -typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_Z3_LOG, IN_MPS } input_kind; +typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS } input_kind; std::string g_aux_input_file; char const * g_input_file = nullptr; @@ -71,10 +72,12 @@ void display_usage() { std::cout << "]. (C) Copyright 2006-2016 Microsoft Corp.\n"; std::cout << "Usage: z3 [options] [-file:]file\n"; std::cout << "\nInput format:\n"; - std::cout << " -smt use parser for SMT input format.\n"; std::cout << " -smt2 use parser for SMT 2 input format.\n"; std::cout << " -dl use parser for Datalog input format.\n"; std::cout << " -dimacs use parser for DIMACS input format.\n"; + std::cout << " -wcnf use parser for Weighted CNF DIMACS input format.\n"; + std::cout << " -opb use parser for PB optimization input format.\n"; + std::cout << " -lp use parser for a modest subset of CPLEX LP input format.\n"; std::cout << " -log use parser for Z3 log input format.\n"; std::cout << " -in read formula from standard input.\n"; std::cout << "\nMiscellaneous:\n"; @@ -185,9 +188,12 @@ void parse_cmd_line_args(int argc, char ** argv) { else if (strcmp(opt_name, "wcnf") == 0) { g_input_kind = IN_WCNF; } - else if (strcmp(opt_name, "bpo") == 0) { + else if (strcmp(opt_name, "pbo") == 0) { g_input_kind = IN_OPB; } + else if (strcmp(opt_name, "lp") == 0) { + g_input_kind = IN_LP; + } else if (strcmp(opt_name, "log") == 0) { g_input_kind = IN_Z3_LOG; } @@ -319,6 +325,9 @@ int STD_CALL main(int argc, char ** argv) { else if (strcmp(ext, "opb") == 0) { g_input_kind = IN_OPB; } + else if (strcmp(ext, "lp") == 0) { + g_input_kind = IN_LP; + } else if (strcmp(ext, "log") == 0) { g_input_kind = IN_Z3_LOG; } @@ -340,10 +349,13 @@ int STD_CALL main(int argc, char ** argv) { return_value = read_dimacs(g_input_file); break; case IN_WCNF: - return_value = parse_opt(g_input_file, true); + return_value = parse_opt(g_input_file, wcnf_t); break; case IN_OPB: - return_value = parse_opt(g_input_file, false); + return_value = parse_opt(g_input_file, opb_t); + break; + case IN_LP: + return_value = parse_opt(g_input_file, lp_t); break; case IN_DATALOG: read_datalog(g_input_file); diff --git a/src/shell/opt_frontend.cpp b/src/shell/opt_frontend.cpp index 714188505..5af5bfdd7 100644 --- a/src/shell/opt_frontend.cpp +++ b/src/shell/opt_frontend.cpp @@ -7,12 +7,17 @@ Copyright (c) 2015 Microsoft Corporation #include #include #include -#include "opt/opt_context.h" -#include "ast/ast_util.h" -#include "ast/arith_decl_plugin.h" #include "util/gparams.h" #include "util/timeout.h" +#include "util/cancel_eh.h" +#include "util/scoped_timer.h" +#include "ast/ast_util.h" +#include "ast/arith_decl_plugin.h" +#include "ast/ast_pp.h" #include "ast/reg_decl_plugins.h" +#include "model/model_smt2_pp.h" +#include "opt/opt_context.h" +#include "shell/opt_frontend.h" #include "opt/opt_parse.h" extern bool g_display_statistics; @@ -24,18 +29,24 @@ static unsigned_vector g_handles; static void display_results() { - if (g_opt) { - for (unsigned i = 0; i < g_handles.size(); ++i) { - expr_ref lo = g_opt->get_lower(g_handles[i]); - expr_ref hi = g_opt->get_upper(g_handles[i]); - if (lo == hi) { - std::cout << " " << lo << "\n"; - } - else { - std::cout << " [" << lo << ":" << hi << "]\n"; - } - } - } + IF_VERBOSE(1, + if (g_opt) { + model_ref mdl; + g_opt->get_model(mdl); + if (mdl) { + model_smt2_pp(verbose_stream(), g_opt->get_manager(), *mdl, 0); + } + for (unsigned h : g_handles) { + expr_ref lo = g_opt->get_lower(h); + expr_ref hi = g_opt->get_upper(h); + if (lo == hi) { + std::cout << " " << lo << "\n"; + } + else { + std::cout << " [" << lo << ":" << hi << "]\n"; + } + } + }); } static void display_statistics() { @@ -73,41 +84,50 @@ static void on_timeout() { } } -static unsigned parse_opt(std::istream& in, bool is_wcnf) { +static unsigned parse_opt(std::istream& in, opt_format f) { ast_manager m; reg_decl_plugins(m); opt::context opt(m); g_opt = &opt; params_ref p = gparams::get_module("opt"); opt.updt_params(p); - if (is_wcnf) { + switch (f) { + case wcnf_t: parse_wcnf(opt, in, g_handles); - } - else { + break; + case opb_t: parse_opb(opt, in, g_handles); + break; + case lp_t: + parse_lp(opt, in, g_handles); + break; } try { + cancel_eh eh(m.limit()); + unsigned timeout = std::stoul(gparams::get_value("timeout")); + unsigned rlimit = std::stoi(gparams::get_value("rlimit")); + scoped_timer timer(timeout, &eh); + scoped_rlimit _rlimit(m.limit(), rlimit); lbool r = opt.optimize(); switch (r) { case l_true: std::cout << "sat\n"; break; case l_false: std::cout << "unsat\n"; break; case l_undef: std::cout << "unknown\n"; break; } - DEBUG_CODE( - if (false && r == l_true) { - model_ref mdl; - opt.get_model(mdl); - expr_ref_vector hard(m); - opt.get_hard_constraints(hard); - for (unsigned i = 0; i < hard.size(); ++i) { - std::cout << "validate: " << i << "\n"; - expr_ref tmp(m); - VERIFY(mdl->eval(hard[i].get(), tmp)); - if (!m.is_true(tmp)) { - std::cout << tmp << "\n"; - } + + if (r != l_false && gparams::get_ref().get_bool("model_validate", false)) { + model_ref mdl; + opt.get_model(mdl); + expr_ref_vector hard(m); + opt.get_hard_constraints(hard); + for (expr* h : hard) { + expr_ref tmp(m); + VERIFY(mdl->eval(h, tmp)); + if (!m.is_true(tmp)) { + std::cout << mk_pp(h, m) << " " << tmp << "\n"; } - }); + } + } } catch (z3_exception & ex) { std::cerr << ex.msg() << "\n"; @@ -121,7 +141,7 @@ static unsigned parse_opt(std::istream& in, bool is_wcnf) { return 0; } -unsigned parse_opt(char const* file_name, bool is_wcnf) { +unsigned parse_opt(char const* file_name, opt_format f) { g_first_interrupt = true; g_start_time = static_cast(clock()); register_on_timeout_proc(on_timeout); @@ -132,10 +152,10 @@ unsigned parse_opt(char const* file_name, bool is_wcnf) { std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; exit(ERR_OPEN_FILE); } - return parse_opt(in, is_wcnf); + return parse_opt(in, f); } else { - return parse_opt(std::cin, is_wcnf); + return parse_opt(std::cin, f); } } diff --git a/src/shell/opt_frontend.h b/src/shell/opt_frontend.h index 65ec606a5..1266ed76b 100644 --- a/src/shell/opt_frontend.h +++ b/src/shell/opt_frontend.h @@ -13,7 +13,9 @@ Author: #ifndef OPT_FRONTEND_H_ #define OPT_FRONTEND_H_ -unsigned parse_opt(char const* file_name, bool is_wcnf); +enum opt_format { opb_t, wcnf_t, lp_t }; + +unsigned parse_opt(char const* file_name, opt_format f); #endif /* OPT_FRONTEND_H_ */ diff --git a/src/smt/CMakeLists.txt b/src/smt/CMakeLists.txt index e102bd28b..fb1997fb4 100644 --- a/src/smt/CMakeLists.txt +++ b/src/smt/CMakeLists.txt @@ -70,6 +70,7 @@ z3_add_component(smt euclid fpa grobner + nlsat lp macros normal_forms diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index ab9bb0895..c00fb93b1 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -334,6 +334,7 @@ void asserted_formulas::find_macros_core() { reduce_and_solve(); } + void asserted_formulas::apply_quasi_macros() { TRACE("before_quasi_macros", display(tout);); vector new_fmls; @@ -419,7 +420,7 @@ void asserted_formulas::simplify_fmls::operator()() { void asserted_formulas::reduce_and_solve() { - IF_IVERBOSE(10, verbose_stream() << "(smt.reducing)\n";); + IF_VERBOSE(10, verbose_stream() << "(smt.reducing)\n";); flush_cache(); // collect garbage m_reduce_asserted_formulas(); } @@ -499,6 +500,7 @@ unsigned asserted_formulas::propagate_values(unsigned i) { void asserted_formulas::update_substitution(expr* n, proof* pr) { expr* lhs, *rhs, *n1; + proof_ref pr1(m); if (is_ground(n) && (m.is_eq(n, lhs, rhs) || m.is_iff(n, lhs, rhs))) { compute_depth(lhs); compute_depth(rhs); @@ -509,12 +511,12 @@ void asserted_formulas::update_substitution(expr* n, proof* pr) { } if (is_gt(rhs, lhs)) { TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";); - m_scoped_substitution.insert(rhs, lhs, m.proofs_enabled() ? m.mk_symmetry(pr) : nullptr); + pr1 = m.proofs_enabled() ? m.mk_symmetry(pr) : nullptr; + m_scoped_substitution.insert(rhs, lhs, pr1); return; } TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";); } - proof_ref pr1(m); if (m.is_not(n, n1)) { pr1 = m.proofs_enabled() ? m.mk_iff_false(pr) : nullptr; m_scoped_substitution.insert(n1, m.mk_false(), pr1); @@ -525,6 +527,7 @@ void asserted_formulas::update_substitution(expr* n, proof* pr) { } } + /** \brief implement a Knuth-Bendix ordering on expressions. */ @@ -630,6 +633,7 @@ unsigned asserted_formulas::get_total_size() const { return r; } + #ifdef Z3DEBUG void pp(asserted_formulas & f) { f.display(std::cout); diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index a8eb81a2e..3ad20cf90 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -39,6 +39,7 @@ void smt_params::updt_local_params(params_ref const & _p) { m_timeout = p.timeout(); m_rlimit = p.rlimit(); m_max_conflicts = p.max_conflicts(); + m_restart_max = p.restart_max(); m_core_validate = p.core_validate(); m_logic = _p.get_sym("logic", m_logic); m_string_solver = p.string_solver(); diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index b01499c04..32b634626 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -99,6 +99,7 @@ struct smt_params : public preprocessor_params, unsigned m_phase_caching_off; bool m_minimize_lemmas; unsigned m_max_conflicts; + unsigned m_restart_max; bool m_simplify_clauses; unsigned m_tick; bool m_display_features; diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 1f691d587..a85365de0 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -21,6 +21,7 @@ def_module_params(module_name='smt', ('timeout', UINT, UINT_MAX, 'timeout (in milliseconds) (UINT_MAX and 0 mean no timeout)'), ('rlimit', UINT, 0, 'resource limit (0 means no limit)'), ('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts before giving up.'), + ('restart.max', UINT, UINT_MAX, 'maximal number of restarts.'), ('mbqi', BOOL, True, 'model based quantifier instantiation (MBQI)'), ('mbqi.max_cexs', UINT, 1, 'initial maximal number of counterexamples used in MBQI, each counterexample generates a quantifier instantiation'), ('mbqi.max_cexs_incr', UINT, 0, 'increment for MBQI_MAX_CEXS, the increment is performed after each round of MBQI'), @@ -38,7 +39,7 @@ def_module_params(module_name='smt', ('bv.reflect', BOOL, True, 'create enode for every bit-vector term'), ('bv.enable_int2bv', BOOL, True, '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.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 4 - utvpi, 5 - infinitary lra, 6 - lra solver'), ('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation'), ('arith.nl.gb', BOOL, True, 'groebner Basis computation, this option is ignored when arith.nl=false'), ('arith.nl.branching', BOOL, True, 'branching on integer variables in non linear clusters'), diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index 1fe7e1163..eb1459058 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -23,12 +23,13 @@ Revision History: #include "util/params.h" enum arith_solver_id { - AS_NO_ARITH, - AS_DIFF_LOGIC, - AS_ARITH, - AS_DENSE_DIFF_LOGIC, - AS_UTVPI, - AS_OPTINF + AS_NO_ARITH, // 0 + AS_DIFF_LOGIC, // 1 + AS_ARITH, // 2 + AS_DENSE_DIFF_LOGIC, // 3 + AS_UTVPI, // 4 + AS_OPTINF, // 5 + AS_LRA // 6 }; enum bound_prop_mode { diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 5b0b3eacd..86147e259 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -628,10 +628,7 @@ namespace smt { */ void context::remove_parents_from_cg_table(enode * r1) { // Remove parents from the congruence table - enode_vector::iterator it = r1->begin_parents(); - enode_vector::iterator end = r1->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode * parent : r1->get_parents()) { #if 0 { static unsigned num_eqs = 0; @@ -676,10 +673,7 @@ namespace smt { */ void context::reinsert_parents_into_cg_table(enode * r1, enode * r2, enode * n1, enode * n2, eq_justification js) { enode_vector & r2_parents = r2->m_parents; - enode_vector::iterator it = r1->begin_parents(); - enode_vector::iterator end = r1->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode * parent : r1->get_parents()) { if (!parent->is_marked()) continue; parent->unset_mark(); @@ -1011,10 +1005,7 @@ namespace smt { r2->m_parents.shrink(r2_num_parents); // try to reinsert parents of r1 that are not cgr - it = r1->begin_parents(); - end = r1->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode * parent : r1->get_parents()) { TRACE("add_eq_parents", tout << "visiting: #" << parent->get_owner_id() << "\n";); if (parent->is_cgc_enabled()) { enode * cg = parent->m_cg; @@ -1210,10 +1201,7 @@ namespace smt { bool context::is_diseq_slow(enode * n1, enode * n2) const { if (n1->get_num_parents() > n2->get_num_parents()) std::swap(n1, n2); - enode_vector::iterator it = n1->begin_parents(); - enode_vector::iterator end = n1->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode * parent : n1->get_parents()) { if (parent->is_eq() && is_relevant(parent->get_owner()) && get_assignment(enode2bool_var(parent)) == l_false && ((parent->get_arg(0)->get_root() == n1->get_root() && parent->get_arg(1)->get_root() == n2->get_root()) || (parent->get_arg(1)->get_root() == n1->get_root() && parent->get_arg(0)->get_root() == n2->get_root()))) { @@ -1245,10 +1233,7 @@ namespace smt { return false; if (r1->get_num_parents() < SMALL_NUM_PARENTS) { TRACE("is_ext_diseq", tout << mk_bounded_pp(n1->get_owner(), m_manager) << " " << mk_bounded_pp(n2->get_owner(), m_manager) << " " << depth << "\n";); - enode_vector::iterator it1 = r1->begin_parents(); - enode_vector::iterator end1 = r1->end_parents(); - for (; it1 != end1; ++it1) { - enode * p1 = *it1; + for (enode* p1 : r1->get_parents()) { if (!is_relevant(p1)) continue; if (p1->is_eq()) @@ -1258,10 +1243,7 @@ namespace smt { func_decl * f = p1->get_decl(); TRACE("is_ext_diseq", tout << "p1: " << mk_bounded_pp(p1->get_owner(), m_manager) << "\n";); unsigned num_args = p1->get_num_args(); - enode_vector::iterator it2 = r2->begin_parents(); - enode_vector::iterator end2 = r2->end_parents(); - for (; it2 != end2; ++it2) { - enode * p2 = *it2; + for (enode * p2 : r2->get_parents()) { if (!is_relevant(p2)) continue; if (p2->is_eq()) @@ -1299,10 +1281,7 @@ namespace smt { } almost_cg_table & table = *(m_almost_cg_tables[depth]); table.reset(r1, r2); - enode_vector::iterator it1 = r1->begin_parents(); - enode_vector::iterator end1 = r1->end_parents(); - for (; it1 != end1; ++it1) { - enode * p1 = *it1; + for (enode* p1 : r1->get_parents()) { if (!is_relevant(p1)) continue; if (p1->is_eq()) @@ -1313,10 +1292,7 @@ namespace smt { } if (table.empty()) return false; - enode_vector::iterator it2 = r2->begin_parents(); - enode_vector::iterator end2 = r2->end_parents(); - for (; it2 != end2; ++it2) { - enode * p2 = *it2; + for (enode * p2 : r2->get_parents()) { if (!is_relevant(p2)) continue; if (p2->is_eq()) @@ -1523,10 +1499,7 @@ namespace smt { } TRACE("push_new_th_diseqs", tout << "#" << r->get_owner_id() << " v" << v << "\n";); theory_id th_id = th->get_id(); - enode_vector::iterator it = r->begin_parents(); - enode_vector::iterator end = r->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode * parent : r->get_parents()) { CTRACE("parent_bug", parent == 0, tout << "#" << r->get_owner_id() << ", num_parents: " << r->get_num_parents() << "\n"; display(tout);); if (parent->is_eq()) { bool_var bv = get_bool_var_of_id(parent->get_owner_id()); @@ -1830,6 +1803,15 @@ namespace smt { m_bvar_inc *= INV_ACTIVITY_LIMIT; } + expr* context::next_decision() { + bool_var var; + lbool phase; + m_case_split_queue->next_case_split(var, phase); + if (var == null_bool_var) return m_manager.mk_true(); + m_case_split_queue->unassign_var_eh(var); + return bool_var2expr(var); + } + /** \brief Execute next clase split, return false if there are no more case splits to be performed. @@ -1990,6 +1972,15 @@ namespace smt { m_watches[(~cls->get_literal(idx)).index()].remove_clause(cls); } + /** + \brief Remove boolean variable from watch lists. + */ + void context::remove_watch(bool_var v) { + literal lit(v); + m_watches[lit.index()].reset(); + m_watches[(~lit).index()].reset(); + } + /** \brief Update the index used for backward subsumption. */ @@ -2201,9 +2192,7 @@ namespace smt { TRACE("cached_generation", tout << "caching: #" << n->get_id() << " " << e->get_generation() << "\n";); m_cached_generation.insert(n, e->get_generation()); } - unsigned num_args = to_app(n)->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = to_app(n)->get_arg(i); + for (expr * arg : *to_app(n)) { if (is_app(arg) || is_quantifier(arg)) todo.push_back(arg); } @@ -3407,6 +3396,7 @@ namespace smt { m_num_conflicts = 0; m_num_conflicts_since_restart = 0; m_num_conflicts_since_lemma_gc = 0; + m_num_restarts = 0; m_restart_threshold = m_fparams.m_restart_initial; m_restart_outer_threshold = m_fparams.m_restart_initial; m_agility = 0.0; @@ -3504,6 +3494,7 @@ namespace smt { bool context::restart(lbool& status, unsigned curr_lvl) { + if (m_last_search_failure != OK) { if (status != l_false) { // build candidate model before returning @@ -3537,7 +3528,7 @@ namespace smt { inc_limits(); if (status == l_true || !m_fparams.m_restart_adaptive || m_agility < m_fparams.m_restart_agility_threshold) { SASSERT(!inconsistent()); - IF_VERBOSE(2, verbose_stream() << "(smt.restarting :propagations " << m_stats.m_num_propagations + IF_VERBOSE(2, verbose_stream() << "(smt.restarting :propagations " << m_stats.m_num_propagations << " :decisions " << m_stats.m_num_decisions << " :conflicts " << m_stats.m_num_conflicts << " :restart " << m_restart_threshold; if (m_fparams.m_restart_strategy == RS_IN_OUT_GEOMETRIC) { @@ -3546,9 +3537,10 @@ namespace smt { if (m_fparams.m_restart_adaptive) { verbose_stream() << " :agility " << m_agility; } - verbose_stream() << ")" << std::endl; verbose_stream().flush();); + verbose_stream() << ")\n"); // execute the restart m_stats.m_num_restarts++; + m_num_restarts++; if (m_scope_lvl > curr_lvl) { pop_scope(m_scope_lvl - curr_lvl); SASSERT(at_search_level()); @@ -3565,6 +3557,11 @@ namespace smt { status = l_false; return false; } + if (m_num_restarts >= m_fparams.m_restart_max) { + status = l_undef; + m_last_search_failure = NUM_CONFLICTS; + return false; + } } if (m_fparams.m_simplify_clauses) simplify_clauses(); @@ -3870,6 +3867,9 @@ namespace smt { svector expr_signs; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; + if (get_assignment(l) != l_false) { + std::cout << l << " " << get_assignment(l) << "\n"; + } SASSERT(get_assignment(l) == l_false); expr_lits.push_back(bool_var2expr(l.var())); expr_signs.push_back(l.sign()); @@ -4232,10 +4232,7 @@ namespace smt { theory_var_list * l = n->get_th_var_list(); theory_id th_id = l->get_th_id(); - enode_vector::const_iterator it = n->begin_parents(); - enode_vector::const_iterator end = n->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode* parent : n->get_parents()) { family_id fid = parent->get_owner()->get_family_id(); if (fid != th_id && fid != m_manager.get_basic_family_id()) { TRACE("is_shared", tout << mk_pp(n->get_owner(), m_manager) << "\nis shared because of:\n" << mk_pp(parent->get_owner(), m_manager) << "\n";); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 1d423ef64..106f05112 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -320,6 +320,7 @@ namespace smt { } #endif + clause_vector const& get_lemmas() const { return m_lemmas; } literal get_literal(expr * n) const; @@ -622,8 +623,6 @@ namespace smt { void remove_cls_occs(clause * cls); - void mark_as_deleted(clause * cls); - void del_clause(clause * cls); void del_clauses(clause_vector & v, unsigned old_size); @@ -648,6 +647,14 @@ namespace smt { void reassert_units(unsigned units_to_reassert_lim); + public: + // \brief exposed for PB solver to participate in GC + + void remove_watch(bool_var v); + + void mark_as_deleted(clause * cls); + + // ----------------------------------- // // Internalization @@ -890,6 +897,8 @@ namespace smt { unsigned m_num_conflicts; unsigned m_num_conflicts_since_restart; unsigned m_num_conflicts_since_lemma_gc; + unsigned m_num_restarts; + unsigned m_num_simplifications; unsigned m_restart_threshold; unsigned m_restart_outer_threshold; unsigned m_luby_idx; @@ -1031,6 +1040,8 @@ namespace smt { enode * get_enode_eq_to(func_decl * f, unsigned num_args, enode * const * args); + expr* next_decision(); + protected: bool decide(); diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index f072a1d13..b8439c8ed 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -341,10 +341,7 @@ namespace smt { } void context::display_parent_eqs(std::ostream & out, enode * n) const { - enode_vector::iterator it = n->begin_parents(); - enode_vector::iterator end = n->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode* parent : n->get_parents()) { if (parent->is_eq()) display_eq_detail(out, parent); } @@ -586,7 +583,7 @@ namespace smt { case b_justification::CLAUSE: { clause * cls = j.get_clause(); out << "clause "; - if (cls) display_literals(out, cls->get_num_literals(), cls->begin_literals()); + if (cls) out << literal_vector(cls->get_num_literals(), cls->begin_literals()); break; } case b_justification::JUSTIFICATION: { diff --git a/src/smt/smt_enode.h b/src/smt/smt_enode.h index 3e7f0984d..14f78e265 100644 --- a/src/smt/smt_enode.h +++ b/src/smt/smt_enode.h @@ -227,15 +227,28 @@ namespace smt { return m_args; } - class args { + class const_args { enode const& n; public: - args(enode const& n):n(n) {} - args(enode const* n):n(*n) {} - enode_vector::const_iterator begin() const { return n.get_args(); } - enode_vector::const_iterator end() const { return n.get_args() + n.get_num_args(); } + const_args(enode const& n):n(n) {} + const_args(enode const* n):n(*n) {} + enode_vector::const_iterator begin() const { return n.m_args; } + enode_vector::const_iterator end() const { return n.m_args + n.get_num_args(); } }; + class args { + enode & n; + public: + args(enode & n):n(n) {} + args(enode * n):n(*n) {} + enode_vector::iterator begin() const { return n.m_args; } + enode_vector::iterator end() const { return n.m_args + n.get_num_args(); } + }; + + const_args get_const_args() const { return const_args(this); } + + // args get_args() { return args(this); } + // unsigned get_id() const { // return m_id; // } @@ -305,15 +318,27 @@ namespace smt { return m_commutative; } - class parents { + class const_parents { enode const& n; public: - parents(enode const& _n):n(_n) {} - parents(enode const* _n):n(*_n) {} + const_parents(enode const& _n):n(_n) {} + const_parents(enode const* _n):n(*_n) {} enode_vector::const_iterator begin() const { return n.begin_parents(); } enode_vector::const_iterator end() const { return n.end_parents(); } }; + class parents { + enode& n; + public: + parents(enode & _n):n(_n) {} + parents(enode * _n):n(*_n) {} + enode_vector::iterator begin() const { return n.begin_parents(); } + enode_vector::iterator end() const { return n.end_parents(); } + }; + + parents get_parents() { return parents(this); } + + const_parents get_const_parents() const { return const_parents(this); } unsigned get_num_parents() const { return m_parents.size(); diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 3d999c3b7..dc7c32cc1 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -611,7 +611,6 @@ namespace smt { case OP_XOR: UNREACHABLE(); case OP_OEQ: - case OP_INTERP: UNREACHABLE(); default: break; @@ -1345,6 +1344,7 @@ namespace smt { cls->swap_lits(1, w2_idx); TRACE("mk_th_lemma", display_clause(tout, cls); tout << "\n";); } + // display_clause(std::cout, cls); std::cout << "\n"; m_lemmas.push_back(cls); add_watch_literal(cls, 0); add_watch_literal(cls, 1); diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp index 846d0235d..a6413aef9 100644 --- a/src/smt/smt_kernel.cpp +++ b/src/smt/smt_kernel.cpp @@ -174,11 +174,15 @@ namespace smt { void get_guessed_literals(expr_ref_vector & result) { m_kernel.get_guessed_literals(result); } - + + expr* next_decision() { + return m_kernel.next_decision(); + } + void collect_statistics(::statistics & st) const { m_kernel.collect_statistics(st); } - + void reset_statistics() { } @@ -343,6 +347,10 @@ namespace smt { m_imp->get_guessed_literals(result); } + expr* kernel::next_decision() { + return m_imp->next_decision(); + } + void kernel::display(std::ostream & out) const { m_imp->display(out); } diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index 141ab1dae..7b2f774ad 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -212,6 +212,11 @@ namespace smt { */ void get_guessed_literals(expr_ref_vector & result); + /** + \brief return the next case split literal. + */ + expr* next_decision(); + /** \brief (For debubbing purposes) Prints the state of the kernel */ diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index 98b55c3dd..9ba2c6165 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -387,6 +387,7 @@ namespace smt { enode * n = *it3; if (is_uninterp_const(n->get_owner()) && m_context->is_relevant(n)) { func_decl * d = n->get_owner()->get_decl(); + TRACE("mg_top_sort", tout << d->get_name() << " " << (m_hidden_ufs.contains(d)?"hidden":"visible") << "\n";); if (m_hidden_ufs.contains(d)) continue; expr * val = get_value(n); m_model->register_decl(d, val); diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 383d98e67..83b15fd90 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -736,8 +736,6 @@ namespace smt { } void setup::setup_r_arith() { - // to disable theory lra - // m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params)); } @@ -803,6 +801,9 @@ namespace smt { case AS_OPTINF: m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); break; + case AS_LRA: + setup_r_arith(); + break; default: if (m_params.m_arith_int_only && int_only) m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index d813f249a..d282a59da 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -30,9 +30,35 @@ Notes: namespace smt { class smt_solver : public solver_na2as { + + struct cuber { + smt_solver& m_solver; + unsigned m_round; + expr_ref m_result; + cuber(smt_solver& s): + m_solver(s), + m_round(0), + m_result(s.get_manager()) {} + expr_ref cube() { + switch (m_round) { + case 0: + m_result = m_solver.m_context.next_decision(); + break; + case 1: + m_result = m_solver.get_manager().mk_not(m_result); + break; + default: + m_result = m_solver.get_manager().mk_false(); + break; + } + ++m_round; + return m_result; + } + }; + smt_params m_smt_params; smt::kernel m_context; - progress_callback * m_callback; + cuber* m_cuber; symbol m_logic; bool m_minimizing_core; bool m_core_extend_patterns; @@ -45,6 +71,7 @@ namespace smt { solver_na2as(m), m_smt_params(p), m_context(m, m_smt_params), + m_cuber(nullptr), m_minimizing_core(false), m_core_extend_patterns(false), m_core_extend_patterns_max_distance(UINT_MAX), @@ -61,15 +88,24 @@ namespace smt { smt_solver * result = alloc(smt_solver, m, p, m_logic); smt::kernel::copy(m_context, result->m_context); - for (auto & kv : m_name2assertion) - result->m_name2assertion.insert(translator(kv.m_key), - translator(kv.m_value)); + + if (mc0()) + result->set_model_converter(mc0()->translate(translator)); + + for (auto & kv : m_name2assertion) { + expr* val = translator(kv.m_value); + expr* t = translator(kv.m_key); + result->m_name2assertion.insert(t, val); + result->solver_na2as::assert_expr(val, t); + m.inc_ref(val); + } return result; } ~smt_solver() override { dec_ref_values(get_manager(), m_name2assertion); + dealloc(m_cuber); } void updt_params(params_ref const & p) override { @@ -99,15 +135,15 @@ namespace smt { return m_context.find_mutexes(vars, mutexes); } - void assert_expr(expr * t) override { + void assert_expr_core(expr * t) override { m_context.assert_expr(t); } - void assert_expr(expr * t, expr * a) override { + void assert_expr_core2(expr * t, expr * a) override { if (m_name2assertion.contains(a)) { throw default_exception("named assertion defined twice"); } - solver_na2as::assert_expr(t, a); + solver_na2as::assert_expr_core2(t, a); get_manager().inc_ref(t); m_name2assertion.insert(a, t); } @@ -177,7 +213,7 @@ namespace smt { add_nonlocal_pattern_literals_to_core(r); } - void get_model(model_ref & m) override { + void get_model_core(model_ref & m) override { m_context.get_model(m); } @@ -202,7 +238,6 @@ namespace smt { ast_manager & get_manager() const override { return m_context.m(); } void set_progress_callback(progress_callback * callback) override { - m_callback = callback; m_context.set_progress_callback(callback); } @@ -215,6 +250,26 @@ namespace smt { return m_context.get_formula(idx); } + expr_ref_vector cube(expr_ref_vector& vars, unsigned cutoff) override { + ast_manager& m = get_manager(); + if (!m_cuber) { + m_cuber = alloc(cuber, *this); + } + expr_ref result = m_cuber->cube(); + expr_ref_vector lits(m); + if (m.is_false(result)) { + dealloc(m_cuber); + m_cuber = nullptr; + } + if (m.is_true(result)) { + dealloc(m_cuber); + m_cuber = nullptr; + return lits; + } + lits.push_back(result); + return lits; + } + struct collect_fds_proc { ast_manager & m; func_decl_set & m_fds; diff --git a/src/smt/tactic/ctx_solver_simplify_tactic.cpp b/src/smt/tactic/ctx_solver_simplify_tactic.cpp index 417922858..4230b1317 100644 --- a/src/smt/tactic/ctx_solver_simplify_tactic.cpp +++ b/src/smt/tactic/ctx_solver_simplify_tactic.cpp @@ -69,12 +69,8 @@ public: void reset_statistics() override { m_num_steps = 0; } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - mc = nullptr; pc = nullptr; core = nullptr; + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { reduce(*(in.get())); in->inc_depth(); result.push_back(in.get()); diff --git a/src/smt/tactic/smt_tactic.cpp b/src/smt/tactic/smt_tactic.cpp index 14a9c3e11..cc0f0f207 100644 --- a/src/smt/tactic/smt_tactic.cpp +++ b/src/smt/tactic/smt_tactic.cpp @@ -16,19 +16,21 @@ Author: Notes: --*/ -#include "tactic/tactic.h" -#include "tactic/tactical.h" +#include "util/lp/lp_params.hpp" +#include "ast/rewriter/rewriter_types.h" +#include "ast/ast_util.h" #include "smt/smt_kernel.h" #include "smt/params/smt_params.h" #include "smt/params/smt_params_helper.hpp" -#include "util/lp/lp_params.hpp" -#include "ast/rewriter/rewriter_types.h" -#include "tactic/filter_model_converter.h" -#include "ast/ast_util.h" -#include "solver/solver2tactic.h" #include "smt/smt_solver.h" +#include "tactic/tactic.h" +#include "tactic/tactical.h" +#include "tactic/generic_model_converter.h" +#include "solver/solver2tactic.h" #include "solver/solver.h" #include "solver/mus.h" +#include "solver/parallel_tactic.h" +#include "solver/parallel_params.hpp" typedef obj_map expr2expr_map; @@ -145,13 +147,9 @@ public: void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + goal_ref_buffer & result) override { try { IF_VERBOSE(10, verbose_stream() << "(smt.tactic start)\n";); - mc = nullptr; pc = nullptr; core = nullptr; SASSERT(in->is_well_sorted()); ast_manager & m = in->m(); TRACE("smt_tactic", tout << this << "\nAUTO_CONFIG: " << fparams().m_auto_config << " HIDIV0: " << fparams().m_hi_div0 << " " @@ -169,7 +167,7 @@ public: expr_ref_vector clauses(m); expr2expr_map bool2dep; ptr_vector assumptions; - ref fmc; + ref fmc; if (in->unsat_core_enabled()) { extract_clauses_and_dependencies(in, clauses, assumptions, bool2dep, fmc); TRACE("mus", in->display_with_dependencies(tout); @@ -220,9 +218,13 @@ public: model_ref md; m_ctx->get_model(md); buffer r; - m_ctx->get_relevant_labels(nullptr, r); - mc = model_and_labels2model_converter(md.get(), r); + m_ctx->get_relevant_labels(0, r); + labels_vec rv; + rv.append(r.size(), r.c_ptr()); + model_converter_ref mc; + mc = model_and_labels2model_converter(md.get(), rv); mc = concat(fmc.get(), mc.get()); + in->add(mc.get()); } return; } @@ -269,8 +271,10 @@ public: model_ref md; m_ctx->get_model(md); buffer r; - m_ctx->get_relevant_labels(nullptr, r); - mc = model_and_labels2model_converter(md.get(), r); + m_ctx->get_relevant_labels(0, r); + labels_vec rv; + rv.append(r.size(), r.c_ptr()); + in->add(model_and_labels2model_converter(md.get(), rv)); } return; default: @@ -299,3 +303,18 @@ tactic * mk_smt_tactic_using(bool auto_config, params_ref const & _p) { return using_params(r, p); } +tactic * mk_psmt_tactic(ast_manager& m, params_ref const& p, symbol const& logic) { + parallel_params pp(p); + return pp.enable() ? mk_parallel_tactic(mk_smt_solver(m, p, logic), p) : mk_smt_tactic(p); +} + +tactic * mk_psmt_tactic_using(ast_manager& m, bool auto_config, params_ref const& _p, symbol const& logic) { + parallel_params pp(_p); + params_ref p = _p; + p.set_bool("auto_config", auto_config); + return using_params(pp.enable() ? mk_parallel_tactic(mk_smt_solver(m, p, logic), p) : mk_smt_tactic(p), p); +} + +tactic * mk_parallel_smt_tactic(ast_manager& m, params_ref const& p) { + return mk_parallel_tactic(mk_smt_solver(m, p, symbol::null), p); +} diff --git a/src/smt/tactic/smt_tactic.h b/src/smt/tactic/smt_tactic.h index c7b91d032..fbee950c2 100644 --- a/src/smt/tactic/smt_tactic.h +++ b/src/smt/tactic/smt_tactic.h @@ -31,8 +31,13 @@ tactic * mk_smt_tactic(params_ref const & p = params_ref()); // syntax sugar for using_params(mk_smt_tactic(), p) where p = (:auto_config, auto_config) tactic * mk_smt_tactic_using(bool auto_config = true, params_ref const & p = params_ref()); +tactic * mk_psmt_tactic(ast_manager& m, params_ref const& p, symbol const& logic = symbol::null); +tactic * mk_psmt_tactic_using(ast_manager& m, bool auto_config, params_ref const& p, symbol const& logic = symbol::null); +tactic * mk_parallel_smt_tactic(ast_manager& m, params_ref const& p); + /* ADD_TACTIC("smt", "apply a SAT based SMT solver.", "mk_smt_tactic(p)") + ADD_TACTIC("psmt", "builtin strategy for SMT tactic in parallel.", "mk_parallel_smt_tactic(m, p)") */ #endif diff --git a/src/smt/tactic/unit_subsumption_tactic.cpp b/src/smt/tactic/unit_subsumption_tactic.cpp index 3adf1cb51..7e960196d 100644 --- a/src/smt/tactic/unit_subsumption_tactic.cpp +++ b/src/smt/tactic/unit_subsumption_tactic.cpp @@ -39,11 +39,8 @@ struct unit_subsumption_tactic : public tactic { void cleanup() override {} - void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) override { + void operator()(/* in */ goal_ref const & in, + /* out */ goal_ref_buffer & result) override { reduce_core(in, result); } @@ -109,9 +106,7 @@ struct unit_subsumption_tactic : public tactic { } void insert_result(goal_ref& result) { - for (unsigned i = 0; i < m_deleted.size(); ++i) { - result->update(m_deleted[i], m.mk_true()); // TBD proof? - } + for (auto d : m_deleted) result->update(d, m.mk_true()); // TBD proof? } void init(goal_ref const& g) { diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 5040f1034..992a87dab 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -31,6 +31,7 @@ Revision History: #include "smt/params/theory_arith_params.h" #include "smt/arith_eq_adapter.h" #include "smt/proto_model/numeral_factory.h" +#include "smt/smt_context.h" #include "util/obj_pair_hashtable.h" #include "smt/old_interval.h" #include "math/grobner/grobner.h" @@ -1078,10 +1079,10 @@ namespace smt { // Optimization // // ----------------------------------- + expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_numeral const& val); inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared) override; inf_eps_rational value(theory_var v) override; theory_var add_objective(app* term) override; - expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_numeral const& val); void enable_record_conflict(expr* bound); void record_conflict(unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 6a19f2f6e..daf285a54 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -23,7 +23,7 @@ Revision History: #include "smt/theory_arith.h" #include "smt/smt_farkas_util.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" namespace smt { @@ -1117,14 +1117,14 @@ namespace smt { This allows to handle inequalities with non-standard numbers. */ template - expr_ref theory_arith::mk_ge(filter_model_converter& fm, theory_var v, inf_numeral const& val) { + expr_ref theory_arith::mk_ge(generic_model_converter& fm, theory_var v, inf_numeral const& val) { ast_manager& m = get_manager(); context& ctx = get_context(); std::ostringstream strm; strm << val << " <= " << mk_pp(get_enode(v)->get_owner(), get_manager()); app* b = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); if (!ctx.b_internalized(b)) { - fm.insert(b->get_decl()); + fm.hide(b->get_decl()); bool_var bv = ctx.mk_bool_var(b); ctx.set_var_theory(bv, get_id()); // ctx.set_enode_flag(bv, true); diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index 528324040..b5281f218 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -200,10 +200,12 @@ namespace smt { SASSERT(is_int(v)); SASSERT(!get_value(v).is_int()); m_stats.m_branches++; - TRACE("arith_int", tout << "branching v" << v << " = " << get_value(v) << "\n"; - display_var(tout, v);); numeral k = ceil(get_value(v)); rational _k = k.to_rational(); + TRACE("arith_int", tout << "branching v" << v << " = " << get_value(v) << "\n"; + display_var(tout, v); + tout << "k = " << k << ", _k = "<< _k << std::endl; + ); expr_ref bound(get_manager()); expr* e = get_enode(v)->get_owner(); bound = m_util.mk_ge(e, m_util.mk_numeral(_k, m_util.is_int(e))); diff --git a/src/smt/theory_dense_diff_logic.h b/src/smt/theory_dense_diff_logic.h index dbf6a13a9..b7845a736 100644 --- a/src/smt/theory_dense_diff_logic.h +++ b/src/smt/theory_dense_diff_logic.h @@ -271,7 +271,7 @@ namespace smt { inf_eps_rational value(theory_var v) override; theory_var add_objective(app* term) override; virtual expr_ref mk_gt(theory_var v, inf_eps const& val); - expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val); + expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_eps const& val); // ----------------------------------- // diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index 86e1fd5e8..3dfba6b1b 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -1055,7 +1055,7 @@ namespace smt { template expr_ref theory_dense_diff_logic::mk_ge( - filter_model_converter& fm, theory_var v, inf_eps const& val) { + generic_model_converter& fm, theory_var v, inf_eps const& val) { return mk_ineq(v, val, false); } diff --git a/src/smt/theory_diff_logic.h b/src/smt/theory_diff_logic.h index 6090b2f5f..6213c36b0 100644 --- a/src/smt/theory_diff_logic.h +++ b/src/smt/theory_diff_logic.h @@ -321,10 +321,10 @@ namespace smt { // // ----------------------------------- + expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_eps const& val); inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) override; inf_eps value(theory_var v) override; theory_var add_objective(app* term) override; - expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val); bool internalize_objective(expr * n, rational const& m, rational& r, objective_term & objective); diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index fc4ff25c7..48991da8e 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -1338,7 +1338,7 @@ expr_ref theory_diff_logic::mk_gt(theory_var v, inf_eps const& val) { } template -expr_ref theory_diff_logic::mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val) { +expr_ref theory_diff_logic::mk_ge(generic_model_converter& fm, theory_var v, inf_eps const& val) { return mk_ineq(v, val, false); } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index f475a8279..de9b41f64 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -36,7 +36,7 @@ Revision History: #include "smt/smt_model_generator.h" #include "smt/arith_eq_adapter.h" #include "util/nat_set.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" namespace lra_lp { enum bound_kind { lower_t, upper_t }; @@ -2417,7 +2417,7 @@ namespace smt { } } - expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val) { + expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_rational const& val) { rational r = val.get_rational(); bool is_strict = val.get_infinitesimal().is_pos(); app_ref b(m); @@ -2429,7 +2429,7 @@ namespace smt { b = a.mk_ge(mk_obj(v), a.mk_numeral(r, is_int)); } if (!ctx().b_internalized(b)) { - fm.insert(b->get_decl()); + fm.hide(b); bool_var bv = ctx().mk_bool_var(b); ctx().set_var_theory(bv, get_id()); // ctx().set_enode_flag(bv, true); @@ -2620,7 +2620,7 @@ namespace smt { theory_var theory_lra::add_objective(app* term) { return m_imp->add_objective(term); } - expr_ref theory_lra::mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val) { + expr_ref theory_lra::mk_ge(generic_model_converter& fm, theory_var v, inf_rational const& val) { return m_imp->mk_ge(fm, v, val); } diff --git a/src/smt/theory_lra.h b/src/smt/theory_lra.h index f3ef77495..4b1f67b79 100644 --- a/src/smt/theory_lra.h +++ b/src/smt/theory_lra.h @@ -86,12 +86,10 @@ namespace smt { void collect_statistics(::statistics & st) const override; // optimization + expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_rational const& val); inf_eps value(theory_var) override; inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) override; theory_var add_objective(app* term) override; - virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val); - - }; } diff --git a/src/smt/theory_opt.h b/src/smt/theory_opt.h index 49f436ea5..2947d86c1 100644 --- a/src/smt/theory_opt.h +++ b/src/smt/theory_opt.h @@ -25,7 +25,7 @@ Notes: #ifndef THEORY_OPT_H_ #define THEORY_OPT_H_ -class filter_model_converter; +class generic_model_converter; namespace smt { class theory_opt { public: diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 25868e944..d45590bff 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -21,6 +21,7 @@ Notes: #include #include "smt/theory_pb.h" #include "smt/smt_context.h" +#include "smt/smt_kernel.h" #include "ast/ast_pp.h" #include "util/sorting_network.h" #include "util/uint_set.h" @@ -65,9 +66,6 @@ namespace smt { }; }; - const unsigned theory_pb::null_index = UINT_MAX; - - unsigned theory_pb::arg_t::get_hash() const { return get_composite_hash(*this, size()); } @@ -227,18 +225,234 @@ namespace smt { SASSERT(sum >= k()); return true; } + + // ----------------------------- + // cardinality constraints + + void theory_pb::card::negate() { + m_lit.neg(); + unsigned sz = size(); + for (unsigned i = 0; i < sz; ++i) { + m_args[i].neg(); + } + m_bound = sz - m_bound + 1; + SASSERT(sz >= m_bound && m_bound > 0); + } + + app_ref theory_pb::card::to_expr(theory_pb& th) { + ast_manager& m = th.get_manager(); + expr_ref_vector args(m); + for (unsigned i = 0; i < size(); ++i) { + args.push_back(th.literal2expr(m_args[i])); + } + return app_ref(th.pb.mk_at_least_k(args.size(), args.c_ptr(), k()), m); + } + + lbool theory_pb::card::assign(theory_pb& th, literal alit) { + // literal is assigned to false. + context& ctx = th.get_context(); + unsigned sz = size(); + unsigned bound = k(); + TRACE("pb", tout << "assign: " << m_lit << " " << ~alit << " " << bound << "\n";); + + SASSERT(0 < bound && bound < sz); + SASSERT(ctx.get_assignment(alit) == l_false); + SASSERT(ctx.get_assignment(m_lit) == l_true); + unsigned index = 0; + for (index = 0; index <= bound; ++index) { + if (lit(index) == alit) { + break; + } + } + if (index == bound + 1) { + // literal is no longer watched. + return l_undef; + } + SASSERT(index <= bound); + SASSERT(lit(index) == alit); + + // find a literal to swap with: + for (unsigned i = bound + 1; i < sz; ++i) { + literal lit2 = lit(i); + if (ctx.get_assignment(lit2) != l_false) { + TRACE("pb", tout << "swap " << lit2 << "\n";); + std::swap(m_args[index], m_args[i]); + th.watch_literal(lit2, this); + return l_undef; + } + } + + // conflict + if (bound != index && ctx.get_assignment(lit(bound)) == l_false) { + TRACE("pb", tout << "conflict " << lit(bound) << " " << alit << "\n";); + set_conflict(th, alit); + return l_false; + } + + TRACE("pb", tout << "no swap " << index << " " << alit << "\n";); + // there are no literals to swap with, + // prepare for unit propagation by swapping the false literal into + // position bound. Then literals in positions 0..bound-1 have to be + // assigned l_true. + if (index != bound) { + std::swap(m_args[index], m_args[bound]); + } + SASSERT(th.validate_unit_propagation(*this)); + + for (unsigned i = 0; i < bound && !ctx.inconsistent(); ++i) { + th.add_assign(*this, lit(i)); + } + + return ctx.inconsistent() ? l_false : l_true; + } + + /** + \brief The conflict clause position for cardinality constraint have the following properties: + 0. The position for the literal corresponding to the cardinality constraint. + 1. The literal at position 0 of the cardinality constraint. + 2. The asserting literal. + 3. .. the remaining false literals. + */ + void theory_pb::card::set_conflict(theory_pb& th, literal l) { + SASSERT(validate_conflict(th)); + context& ctx = th.get_context(); + (void)ctx; + literal_vector& lits = th.get_literals(); + SASSERT(ctx.get_assignment(l) == l_false); + SASSERT(ctx.get_assignment(lit()) == l_true); + lits.push_back(~lit()); + lits.push_back(l); + unsigned sz = size(); + for (unsigned i = m_bound; i < sz; ++i) { + SASSERT(ctx.get_assignment(m_args[i]) == l_false); + lits.push_back(m_args[i]); + } + th.add_clause(*this, lits); + } + + bool theory_pb::card::validate_conflict(theory_pb& th) { + context& ctx = th.get_context(); + unsigned num_false = 0; + for (unsigned i = 0; i < size(); ++i) { + if (ctx.get_assignment(m_args[i]) == l_false) { + ++num_false; + } + } + return size() - num_false < m_bound; + } + + bool theory_pb::card::validate_assign(theory_pb& th, literal_vector const& lits, literal l) { + context& ctx = th.get_context(); + VERIFY(ctx.get_assignment(l) == l_undef); + for (unsigned i = 0; i < lits.size(); ++i) { + SASSERT(ctx.get_assignment(lits[i]) == l_true); + } + return size() - lits.size() <= m_bound; + } + + void theory_pb::card::init_watch(theory_pb& th, bool is_true) { + context& ctx = th.get_context(); + th.clear_watch(*this); + if (lit().sign() == is_true) { + negate(); + } + SASSERT(ctx.get_assignment(lit()) == l_true); + unsigned j = 0, sz = size(), bound = k(); + if (bound == sz) { + for (unsigned i = 0; i < sz && !ctx.inconsistent(); ++i) { + th.add_assign(*this, lit(i)); + } + return; + } + // put the non-false literals into the head. + for (unsigned i = 0; i < sz; ++i) { + if (ctx.get_assignment(lit(i)) != l_false) { + if (j != i) { + std::swap(m_args[i], m_args[j]); + } + ++j; + } + } + DEBUG_CODE( + bool is_false = false; + for (unsigned k = 0; k < sz; ++k) { + SASSERT(!is_false || ctx.get_assignment(lit(k)) == l_false); + is_false = ctx.get_assignment(lit(k)) == l_false; + }); + + // j is the number of non-false, sz - j the number of false. + if (j < bound) { + SASSERT(0 < bound && bound < sz); + literal alit = lit(j); + + // + // we need the assignment level of the asserting literal to be maximal. + // such that conflict resolution can use the asserting literal as a starting + // point. + // + + for (unsigned i = bound; i < sz; ++i) { + if (ctx.get_assign_level(alit) < ctx.get_assign_level(lit(i))) { + std::swap(m_args[j], m_args[i]); + alit = lit(j); + } + } + set_conflict(th, alit); + } + else if (j == bound) { + for (unsigned i = 0; i < bound && !ctx.inconsistent(); ++i) { + th.add_assign(*this, lit(i)); + } + } + else { + for (unsigned i = 0; i <= bound; ++i) { + th.watch_literal(lit(i), this); + } + } + } + + + void theory_pb::card::add_arg(literal lit) { + if (lit == false_literal) { + return; + } + else if (lit == true_literal) { + if (m_bound > 0) { + --m_bound; + } + } + else { + m_args.push_back(lit); + } + + } + + void theory_pb::card::inc_propagations(theory_pb& th) { + ++m_num_propagations; + if (m_compiled == l_false && m_num_propagations >= m_compilation_threshold) { + // m_compiled = l_undef; + // th.m_to_compile.push_back(&c); + } + } + + // ------------------------ + // theory_pb + theory_pb::theory_pb(ast_manager& m, theory_pb_params& p): theory(m.mk_family_id("pb")), m_params(p), - m_simplex(m.limit()), - m_util(m), - m_max_compiled_coeff(rational(8)) + pb(m), + m_max_compiled_coeff(rational(8)), + m_cardinality_lemma(false), + m_restart_lim(3), + m_restart_inc(0), + m_antecedent_exprs(m), + m_cardinality_exprs(m) { m_learn_complements = p.m_pb_learn_complements; m_conflict_frequency = p.m_pb_conflict_frequency; m_enable_compilation = p.m_pb_enable_compilation; - m_enable_simplex = p.m_pb_enable_simplex; } theory_pb::~theory_pb() { @@ -249,195 +463,36 @@ namespace smt { return alloc(theory_pb, new_ctx->get_manager(), m_params); } - class theory_pb::remove_var : public trail { - theory_pb& pb; - unsigned v; - public: - remove_var(theory_pb& pb, unsigned v): pb(pb), v(v) {} - void undo(context& ctx) override { - pb.m_vars.remove(v); - pb.m_simplex.unset_lower(v); - pb.m_simplex.unset_upper(v); - } - }; - - class theory_pb::undo_bound : public trail { - theory_pb& pb; - unsigned m_v; - bool m_is_lower; - scoped_eps_numeral m_last_bound; - bool m_last_bound_valid; - literal m_last_explain; - - public: - undo_bound(theory_pb& pb, unsigned v, - bool is_lower, - scoped_eps_numeral& last_bound, - bool last_bound_valid, - literal last_explain): - pb(pb), - m_v(v), - m_is_lower(is_lower), - m_last_bound(last_bound), - m_last_bound_valid(last_bound_valid), - m_last_explain(last_explain) {} - - void undo(context& ctx) override { - if (m_is_lower) { - if (m_last_bound_valid) { - pb.m_simplex.set_lower(m_v, m_last_bound); - } - else { - pb.m_simplex.unset_lower(m_v); - } - pb.set_explain(pb.m_explain_lower, m_v, m_last_explain); - } - else { - if (m_last_bound_valid) { - pb.m_simplex.set_upper(m_v, m_last_bound); - } - else { - pb.m_simplex.unset_upper(m_v); - } - pb.set_explain(pb.m_explain_upper, m_v, m_last_explain); - } - m_last_bound.reset(); - } - }; - - literal theory_pb::set_explain(literal_vector& explains, unsigned var, literal expl) { - if (var >= explains.size()) { - explains.resize(var+1, null_literal); - } - literal last_explain = explains[var]; - explains[var] = expl; - return last_explain; - } - - bool theory_pb::update_bound(bool_var v, literal explain, bool is_lower, mpq_inf const& bound) { - if (is_lower) { - if (m_simplex.above_lower(v, bound)) { - scoped_eps_numeral last_bound(m_mpq_inf_mgr); - if (m_simplex.upper_valid(v)) { - m_simplex.get_upper(v, last_bound); - if (m_mpq_inf_mgr.gt(bound, last_bound)) { - literal lit = m_explain_upper.get(v, null_literal); - TRACE("pb", tout << ~lit << " " << ~explain << "\n";); - get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain)); - return false; - } - } - bool last_bound_valid = m_simplex.lower_valid(v); - if (last_bound_valid) { - m_simplex.get_lower(v, last_bound); - } - m_simplex.set_lower(v, bound); - literal last_explain = set_explain(m_explain_lower, v, explain); - get_context().push_trail(undo_bound(*this, v, true, last_bound, last_bound_valid, last_explain)); - } - } - else { - if (m_simplex.below_upper(v, bound)) { - scoped_eps_numeral last_bound(m_mpq_inf_mgr); - if (m_simplex.lower_valid(v)) { - m_simplex.get_lower(v, last_bound); - if (m_mpq_inf_mgr.gt(last_bound, bound)) { - literal lit = m_explain_lower.get(v, null_literal); - TRACE("pb", tout << ~lit << " " << ~explain << "\n";); - get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain)); - return false; - } - } - bool last_bound_valid = m_simplex.upper_valid(v); - if (last_bound_valid) { - m_simplex.get_upper(v, last_bound); - } - m_simplex.set_upper(v, bound); - literal last_explain = set_explain(m_explain_upper, v, explain); - get_context().push_trail(undo_bound(*this, v, false, last_bound, last_bound_valid, last_explain)); - } - } - return true; - }; - - bool theory_pb::check_feasible() { - context& ctx = get_context(); - lbool is_sat = m_simplex.make_feasible(); - if (l_false != is_sat) { - return true; - } - - row r = m_simplex.get_infeasible_row(); - // m_simplex.display_row(std::cout, r, true); - mpz const& coeff = m_simplex.get_base_coeff(r); - bool_var base_var = m_simplex.get_base_var(r); - SASSERT(m_simplex.below_lower(base_var) || m_simplex.above_upper(base_var)); - bool cant_increase = m_simplex.below_lower(base_var)?m_mpz_mgr.is_pos(coeff):m_mpz_mgr.is_neg(coeff); - - literal_vector explains; - row_iterator it = m_simplex.row_begin(r), end = m_simplex.row_end(r); - for (; it != end; ++it) { - bool_var v = it->m_var; - if (v == base_var) { - if (m_simplex.below_lower(base_var)) { - explains.push_back(m_explain_lower.get(v, null_literal)); - } - else { - explains.push_back(m_explain_upper.get(v, null_literal)); - } - } - else if (cant_increase == m_mpz_mgr.is_pos(it->m_coeff)) { - explains.push_back(m_explain_lower.get(v, null_literal)); - } - else { - explains.push_back(m_explain_upper.get(v, null_literal)); - } - } - - literal_vector lits; - for (unsigned i = 0; i < explains.size(); ++i) { - literal lit(explains[i]); - if (lit != null_literal) { - lits.push_back(~lit); - } - } - - m_stats.m_num_conflicts++; - justification* js = nullptr; - if (proofs_enabled()) { - js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); - } - TRACE("pb", tout << lits << "\n";); - ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, nullptr); - - return false; - } - bool theory_pb::internalize_atom(app * atom, bool gate_ctx) { context& ctx = get_context(); + TRACE("pb", tout << mk_pp(atom, get_manager()) << "\n";); if (ctx.b_internalized(atom)) { - return false; + return true; } SASSERT(!ctx.b_internalized(atom)); m_stats.m_num_predicates++; - if (m_util.is_aux_bool(atom)) { + if (pb.is_aux_bool(atom)) { bool_var abv = ctx.mk_bool_var(atom); ctx.set_var_theory(abv, get_id()); return true; } - SASSERT(m_util.is_at_most_k(atom) || m_util.is_le(atom) || - m_util.is_ge(atom) || m_util.is_at_least_k(atom) || - m_util.is_eq(atom)); + if (internalize_card(atom, gate_ctx)) { + return true; + } + + SASSERT(pb.is_at_most_k(atom) || pb.is_le(atom) || + pb.is_ge(atom) || pb.is_at_least_k(atom) || + pb.is_eq(atom)); unsigned num_args = atom->get_num_args(); bool_var abv = ctx.mk_bool_var(atom); ctx.set_var_theory(abv, get_id()); - ineq* c = alloc(ineq, m_mpz_mgr, literal(abv), m_util.is_eq(atom)); - c->m_args[0].m_k = m_util.get_k(atom); + ineq* c = alloc(ineq, m_mpz_mgr, literal(abv), pb.is_eq(atom)); + c->m_args[0].m_k = pb.get_k(atom); numeral& k = c->m_args[0].m_k; arg_t& args = c->m_args[0]; @@ -445,7 +500,7 @@ namespace smt { for (unsigned i = 0; i < num_args; ++i) { expr* arg = atom->get_arg(i); literal l = compile_arg(arg); - numeral c = m_util.get_coeff(atom, i); + numeral c = pb.get_coeff(atom, i); switch (ctx.get_assignment(l)) { case l_true: k -= c; @@ -457,7 +512,7 @@ namespace smt { break; } } - if (m_util.is_at_most_k(atom) || m_util.is_le(atom)) { + if (pb.is_at_most_k(atom) || pb.is_le(atom)) { // turn W <= k into -W >= -k for (unsigned i = 0; i < args.size(); ++i) { args[i].second = -args[i].second; @@ -465,7 +520,7 @@ namespace smt { k = -k; } else { - SASSERT(m_util.is_at_least_k(atom) || m_util.is_ge(atom) || m_util.is_eq(atom)); + SASSERT(pb.is_at_least_k(atom) || pb.is_ge(atom) || pb.is_eq(atom)); } TRACE("pb", display(tout, *c, true);); //app_ref fml1(m), fml2(m); @@ -494,8 +549,8 @@ namespace smt { break; } - if (c->k().is_one() && c->is_ge() && !m_enable_simplex) { - literal_vector& lits = get_lits(); + if (c->k().is_one() && c->is_ge()) { + literal_vector& lits = get_literals(); lits.push_back(~lit); for (unsigned i = 0; i < c->size(); ++i) { lits.push_back(c->lit(i)); @@ -540,64 +595,6 @@ namespace smt { m_var_infos[abv].m_ineq = c; m_ineqs_trail.push_back(abv); - if (m_enable_simplex) { - // - // TBD: using abv as slack identity doesn't quite - // work if psuedo-Booleans are used - // in a nested way. So assume - // - - arg_t rep(c->args()); - rep.remove_negations(); // normalize representative - numeral k = rep.k(); - theory_var slack; - bool_var abv2; - TRACE("pb", display(tout << abv <<"\n", rep);); - if (m_ineq_rep.find(rep, abv2)) { - slack = abv2; - TRACE("pb", - tout << "Old row: " << abv << " |-> " << slack << " "; - tout << m_ineq_row_info.find(abv2).m_bound << " vs. " << k << "\n"; - display(tout, rep);); - } - else { - m_ineq_rep.insert(rep, abv); - svector vars; - scoped_mpz_vector coeffs(m_mpz_mgr); - for (unsigned i = 0; i < rep.size(); ++i) { - unsigned v = rep.lit(i).var(); - m_simplex.ensure_var(v); - vars.push_back(v); - if (!m_vars.contains(v)) { - mpq_inf zero(mpq(0),mpq(0)), one(mpq(1),mpq(0)); - switch(ctx.get_assignment(rep.lit(i))) { - case l_true: - VERIFY(update_bound(v, literal(v), true, one)); - m_simplex.set_lower(v, one); - break; - case l_false: - VERIFY(update_bound(v, ~literal(v), false, zero)); - m_simplex.set_upper(v, zero); - break; - default: - m_simplex.set_lower(v, zero); - m_simplex.set_upper(v, one); - break; - } - m_vars.insert(v); - ctx.push_trail(remove_var(*this, v)); - } - coeffs.push_back(rep.coeff(i).to_mpq().numerator()); - } - slack = abv; - m_simplex.ensure_var(slack); - vars.push_back(slack); - coeffs.push_back(mpz(-1)); - m_simplex.add_row(slack, vars.size(), vars.c_ptr(), coeffs.c_ptr()); - TRACE("pb", tout << "New row: " << abv << " " << k << "\n"; display(tout, rep);); - } - m_ineq_row_info.insert(abv, row_info(slack, k, rep)); - } TRACE("pb", display(tout, *c);); @@ -638,7 +635,6 @@ namespace smt { // is available. if (!has_bv) { app_ref tmp(m), fml(m); - pb_util pb(m); tmp = pb.mk_fresh_bool(); fml = m.mk_iff(tmp, arg); TRACE("pb", tout << "create proxy " << fml << "\n";); @@ -653,7 +649,7 @@ namespace smt { return negate?~literal(bv):literal(bv); } - void theory_pb::del_watch(watch_list& watch, unsigned index, ineq& c, unsigned ineq_index) { + void theory_pb::del_watch(ineq_watch& watch, unsigned index, ineq& c, unsigned ineq_index) { SASSERT(c.is_ge()); if (index < watch.size()) { std::swap(watch[index], watch[watch.size()-1]); @@ -703,6 +699,7 @@ namespace smt { } } + void theory_pb::watch_literal(literal lit, ineq* c) { init_watch(lit.var()); ptr_vector* ineqs = m_var_infos[lit.var()].m_lit_watch[lit.sign()]; @@ -713,6 +710,7 @@ namespace smt { ineqs->push_back(c); } + void theory_pb::watch_var(bool_var v, ineq* c) { init_watch(v); ptr_vector* ineqs = m_var_infos[v].m_var_watch; @@ -747,6 +745,232 @@ namespace smt { } } + // ---------------------------- + // cardinality constraints + + + class theory_pb::card_justification : public justification { + card& m_card; + family_id m_fid; + public: + card_justification(card& c, family_id fid) + : justification(true), m_card(c), m_fid(fid) {} + + card& get_card() { return m_card; } + + virtual void get_antecedents(conflict_resolution& cr) { + cr.mark_literal(m_card.lit()); + for (unsigned i = m_card.k(); i < m_card.size(); ++i) { + cr.mark_literal(~m_card.lit(i)); + } + } + + virtual theory_id get_from_theory() const { + return m_fid; + } + + virtual proof* mk_proof(smt::conflict_resolution& cr) { return 0; } + + + }; + + + bool theory_pb::is_cardinality_constraint(app * atom) { + if (pb.is_ge(atom) && pb.has_unit_coefficients(atom)) { + return true; + } + if (pb.is_at_least_k(atom)) { + return true; + } + return false; + } + + bool theory_pb::internalize_card(app * atom, bool gate_ctx) { + context& ctx = get_context(); + if (ctx.b_internalized(atom)) { + return true; + } + if (!is_cardinality_constraint(atom)) { + return false; + } + unsigned num_args = atom->get_num_args(); + bool_var abv = ctx.mk_bool_var(atom); + ctx.set_var_theory(abv, get_id()); + unsigned bound = pb.get_k(atom).get_unsigned(); + literal lit(abv); + + if (bound == 0) { + ctx.mk_th_axiom(get_id(), 1, &lit); + return true; + } + if (bound > num_args) { + lit.neg(); + ctx.mk_th_axiom(get_id(), 1, &lit); + return true; + } + + // hack to differentiate constraints that come from input vs. lemmas. + bool aux = pb.is_at_least_k(atom); + + card* c = alloc(card, lit, bound, aux); + + for (expr* arg : *atom) { + c->add_arg(compile_arg(arg)); + } + + if (bound == c->size() || bound == 1) { + // + } + + if (bound == c->size()) { + card2conjunction(*c); + dealloc(c); + } + else if (1 == c->size()) { + card2disjunction(*c); + dealloc(c); + } + else { + SASSERT(0 < c->k() && c->k() < c->size()); + // initialize compilation thresholds, TBD + init_watch(abv); + m_var_infos[abv].m_card = c; + m_card_trail.push_back(abv); + } + return true; + } + + // \brief define cardinality constraint as conjunction. + // + void theory_pb::card2conjunction(card const& c) { + context& ctx = get_context(); + literal lit = c.lit(); + literal_vector& lits = get_literals(); + for (unsigned i = 0; i < c.size(); ++i) { + lits.push_back(~c.lit(i)); + } + lits.push_back(lit); + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + for (unsigned i = 0; i < c.size(); ++i) { + literal lits2[2] = { ~lit, c.lit(i) }; + ctx.mk_th_axiom(get_id(), 2, lits2); + } + } + + void theory_pb::card2disjunction(card const& c) { + context& ctx = get_context(); + literal lit = c.lit(); + literal_vector& lits = get_literals(); + for (unsigned i = 0; i < c.size(); ++i) { + lits.push_back(c.lit(i)); + } + lits.push_back(~lit); + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + for (unsigned i = 0; i < c.size(); ++i) { + literal lits2[2] = { lit, ~c.lit(i) }; + ctx.mk_th_axiom(get_id(), 2, lits2); + } + } + + void theory_pb::watch_literal(literal lit, card* c) { + init_watch(lit.var()); + ptr_vector* cards = m_var_infos[lit.var()].m_lit_cwatch[lit.sign()]; + if (cards == 0) { + cards = alloc(ptr_vector); + m_var_infos[lit.var()].m_lit_cwatch[lit.sign()] = cards; + } + cards->push_back(c); + } + + + void theory_pb::unwatch_literal(literal lit, card* c) { + if (m_var_infos.size() <= static_cast(lit.var())) { + return; + } + ptr_vector* cards = m_var_infos[lit.var()].m_lit_cwatch[lit.sign()]; + if (cards) { + remove(*cards, c); + } + } + + void theory_pb::remove(ptr_vector& cards, card* c) { + for (unsigned j = 0; j < cards.size(); ++j) { + if (cards[j] == c) { + std::swap(cards[j], cards[cards.size()-1]); + cards.pop_back(); + break; + } + } + } + + std::ostream& theory_pb::display(std::ostream& out, card const& c, bool values) const { + context& ctx = get_context(); + out << c.lit(); + if (c.lit() != null_literal) { + if (values) { + out << "@(" << ctx.get_assignment(c.lit()); + if (ctx.get_assignment(c.lit()) != l_undef) { + out << ":" << ctx.get_assign_level(c.lit()); + } + out << ")"; + } + ctx.display_literal_verbose(out, c.lit()); out << "\n"; + } + else { + out << " "; + } + for (unsigned i = 0; i < c.size(); ++i) { + literal l = c.lit(i); + out << l; + if (values) { + out << "@(" << ctx.get_assignment(l); + if (ctx.get_assignment(l) != l_undef) { + out << ":" << ctx.get_assign_level(l); + } + out << ") "; + } + } + out << " >= " << c.k() << "\n"; + if (c.all_propagations()) out << "propagations: " << c.all_propagations() << "\n"; + return out; + } + + + void theory_pb::add_clause(card& c, literal_vector const& lits) { + m_stats.m_num_conflicts++; + context& ctx = get_context(); + justification* js = 0; + if (proofs_enabled()) { + js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); + } + c.inc_propagations(*this); + if (!resolve_conflict(c, lits)) { + ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); + } + SASSERT(ctx.inconsistent()); + } + + void theory_pb::add_assign(card& c, literal l) { + context& ctx = get_context(); + if (ctx.get_assignment(l) == l_true) { + return; + } + c.inc_propagations(*this); + m_stats.m_num_propagations++; + TRACE("pb", tout << "#prop: " << c.num_propagations() << " - " << c.lit() << " => " << l << "\n";); + SASSERT(validate_unit_propagation(c)); + ctx.assign(l, ctx.mk_justification(card_justification(c, get_id()))); + } + + void theory_pb::clear_watch(card& c) { + unsigned sz = std::min(c.k() + 1, c.size()); + for (unsigned i = 0; i < sz; ++i) { + unwatch_literal(c.lit(i), &c); + } + } + + // + void theory_pb::collect_statistics(::statistics& st) const { st.update("pb conflicts", m_stats.m_num_conflicts); st.update("pb propagations", m_stats.m_num_propagations); @@ -754,7 +978,6 @@ namespace smt { st.update("pb compilations", m_stats.m_num_compiles); st.update("pb compiled clauses", m_stats.m_num_compiled_clauses); st.update("pb compiled vars", m_stats.m_num_compiled_vars); - m_simplex.collect_statistics(st); } void theory_pb::reset_eh() { @@ -764,8 +987,11 @@ namespace smt { } m_ineqs_trail.reset(); m_ineqs_lim.reset(); + m_card_trail.reset(); + m_card_lim.reset(); m_stats.reset(); m_to_compile.reset(); + m_cardinality_lemma = false; } void theory_pb::new_eq_eh(theory_var v1, theory_var v2) { @@ -779,23 +1005,13 @@ namespace smt { } void theory_pb::assign_eh(bool_var v, bool is_true) { - ptr_vector* ineqs = nullptr; + ptr_vector* ineqs = 0; + context& ctx = get_context(); literal nlit(v, is_true); init_watch(v); TRACE("pb", tout << "assign: " << ~nlit << "\n";); ineqs = m_var_infos[v].m_lit_watch[nlit.sign()]; if (ineqs != nullptr) { - if (m_enable_simplex) { - mpq_inf num(mpq(is_true?1:0),mpq(0)); - if (!update_bound(v, ~nlit, is_true, num)) { - return; - } - - if (!check_feasible()) { - return; - } - } - for (unsigned i = 0; i < ineqs->size(); ++i) { SASSERT((*ineqs)[i]->is_ge()); if (assign_watch_ge(v, is_true, *ineqs, i)) { @@ -813,27 +1029,6 @@ namespace smt { } ineq* c = m_var_infos[v].m_ineq; if (c != nullptr) { - if (m_enable_simplex) { - row_info const& info = m_ineq_row_info.find(v); - unsynch_mpq_manager mgr; - scoped_eps_numeral coeff(m_mpq_inf_mgr); - coeff = std::make_pair(mgr.dup(info.m_bound.to_mpq()), mpq(0)); - unsigned slack = info.m_slack; - if (is_true) { - update_bound(slack, literal(v), true, coeff); - if (c->is_eq()) { - update_bound(slack, literal(v), false, coeff); - } - } - else if (c->is_ge()) { - m_mpq_inf_mgr.sub(coeff, std::make_pair(mpq(1),mpq(0)), coeff); - update_bound(slack, ~literal(v), false, coeff); - } - - if (!check_feasible()) { - return; - } - } if (c->is_ge()) { assign_ineq(*c, is_true); } @@ -841,11 +1036,45 @@ namespace smt { assign_eq(*c, is_true); } } + + ptr_vector* cards = m_var_infos[v].m_lit_cwatch[nlit.sign()]; + if (cards != 0 && !cards->empty() && !ctx.inconsistent()) { + ptr_vector::iterator it = cards->begin(), it2 = it, end = cards->end(); + for (; it != end; ++it) { + if (ctx.get_assignment((*it)->lit()) != l_true) { + continue; + } + switch ((*it)->assign(*this, nlit)) { + case l_false: // conflict + for (; it != end; ++it, ++it2) { + *it2 = *it; + } + SASSERT(ctx.inconsistent()); + cards->set_end(it2); + return; + case l_undef: // watch literal was swapped + break; + case l_true: // unit propagation, keep watching the literal + if (it2 != it) { + *it2 = *it; + } + ++it2; + break; + } + } + cards->set_end(it2); + } + + card* crd = m_var_infos[v].m_card; + if (crd != 0 && !ctx.inconsistent()) { + crd->init_watch(*this, is_true); + } + } literal_vector& theory_pb::get_all_literals(ineq& c, bool negate) { context& ctx = get_context(); - literal_vector& lits = get_lits(); + literal_vector& lits = get_literals(); for (unsigned i = 0; i < c.size(); ++i) { literal l = c.lit(i); switch(ctx.get_assignment(l)) { @@ -860,14 +1089,13 @@ namespace smt { } } return lits; - } literal_vector& theory_pb::get_helpful_literals(ineq& c, bool negate) { scoped_mpz sum(m_mpz_mgr); mpz const& k = c.mpz_k(); context& ctx = get_context(); - literal_vector& lits = get_lits(); + literal_vector& lits = get_literals(); for (unsigned i = 0; sum < k && i < c.size(); ++i) { literal l = c.lit(i); if (ctx.get_assignment(l) == l_true) { @@ -882,7 +1110,7 @@ namespace smt { literal_vector& theory_pb::get_unhelpful_literals(ineq& c, bool negate) { context& ctx = get_context(); - literal_vector& lits = get_lits(); + literal_vector& lits = get_literals(); for (unsigned i = 0; i < c.size(); ++i) { literal l = c.lit(i); if (ctx.get_assignment(l) == l_false) { @@ -905,6 +1133,7 @@ namespace smt { } }; + class theory_pb::negate_ineq : public trail { ineq& c; public: @@ -927,7 +1156,6 @@ namespace smt { ctx.push_trail(value_trail(c.m_nfixed)); ctx.push_trail(rewatch_vars(*this, c)); - clear_watch(c); SASSERT(c.is_ge()); unsigned sz = c.size(); if (c.lit().sign() == is_true) { @@ -966,10 +1194,8 @@ namespace smt { literal_vector& lits = get_unhelpful_literals(c, true); lits.push_back(c.lit()); for (unsigned i = 0; i < sz; ++i) { - if (ctx.get_assignment(c.lit(i)) == l_undef) { - DEBUG_CODE(validate_assign(c, lits, c.lit(i));); - add_assign(c, lits, c.lit(i)); - } + DEBUG_CODE(validate_assign(c, lits, c.lit(i));); + add_assign(c, lits, c.lit(i)); } } } @@ -1086,11 +1312,10 @@ namespace smt { inequalities are unit literals and formulas in negation normal form (inequalities are closed under negation). */ - bool theory_pb::assign_watch_ge(bool_var v, bool is_true, watch_list& watch, unsigned watch_index) { + bool theory_pb::assign_watch_ge(bool_var v, bool is_true, ineq_watch& watch, unsigned watch_index) { bool removed = false; context& ctx = get_context(); ineq& c = *watch[watch_index]; - //display(std::cout << v << " ", c, true); unsigned w = c.find_lit(v, 0, c.watch_size()); SASSERT(ctx.get_assignment(c.lit()) == l_true); SASSERT(is_true == c.lit(w).sign()); @@ -1167,8 +1392,8 @@ namespace smt { ast_manager& m; theory_pb& th; pb_util pb; - typedef smt::literal literal; - typedef smt::literal_vector literal_vector; + typedef smt::literal pliteral; + typedef smt::literal_vector pliteral_vector; psort_expr(context& c, theory_pb& th): ctx(c), @@ -1176,7 +1401,7 @@ namespace smt { th(th), pb(m) {} - literal fresh() { + literal fresh(char const* ) { app_ref y(m); y = pb.mk_fresh_bool(); return literal(ctx.mk_bool_var(y)); @@ -1239,6 +1464,81 @@ namespace smt { compile_ineq(*m_to_compile[i]); } m_to_compile.reset(); + + return; + + if (m_restart_lim <= m_restart_inc) { + m_restart_inc = 0; + if (gc()) { + m_restart_lim = 3; + } + else { + m_restart_lim *= 4; + m_restart_lim /= 3; + } + } + ++m_restart_inc; + } + + bool theory_pb::gc() { + + context& ctx = get_context(); + + unsigned z = 0, nz = 0; + m_occs.reset(); + for (unsigned i = 0; i < m_card_trail.size(); ++i) { + bool_var v = m_card_trail[i]; + if (v == null_bool_var) continue; + card* c = m_var_infos[v].m_card; + if (c) { + c->reset_propagations(); + literal lit = c->lit(); + if (c->is_aux() && ctx.get_assign_level(lit) > ctx.get_search_level()) { + double activity = ctx.get_activity(v); + if (activity <= 0) { + nz++; + } + else { + z++; + clear_watch(*c); + m_var_infos[v].m_card = 0; + dealloc(c); + m_card_trail[i] = null_bool_var; + ctx.remove_watch(v); + // TBD: maybe v was used in a clause for propagation. + m_occs.insert(v); + } + } + } + } + clause_vector const& lemmas = ctx.get_lemmas(); + for (unsigned i = 0; i < lemmas.size(); ++i) { + clause* cl = lemmas[i]; + if (!cl->deleted()) { + unsigned sz = cl->get_num_literals(); + for (unsigned j = 0; j < sz; ++j) { + literal lit = cl->get_literal(j); + if (m_occs.contains(lit.var())) { + //std::cout << "deleting clause " << lit << " " << sz << "\n"; + //ctx.mark_as_deleted(cl); + break; + } + } + } + } + + std::cout << "zs: " << z << " nzs: " << nz << " lemmas: " << ctx.get_lemmas().size() << " trail: " << m_card_trail.size() << "\n"; + return z*10 >= nz; + + m_occs.reset(); + for (unsigned i = 0; i < lemmas.size(); ++i) { + clause* cl = lemmas[i]; + unsigned sz = cl->get_num_literals(); + for (unsigned j = 0; j < sz; ++j) { + unsigned idx = cl->get_literal(j).index(); + m_occs.insert(idx); + } + } } @@ -1317,6 +1617,7 @@ namespace smt { void theory_pb::push_scope_eh() { m_ineqs_lim.push_back(m_ineqs_trail.size()); + m_card_lim.push_back(m_card_trail.size()); } void theory_pb::pop_scope_eh(unsigned num_scopes) { @@ -1330,20 +1631,26 @@ namespace smt { clear_watch(*c); m_var_infos[v].m_ineq = nullptr; m_ineqs_trail.pop_back(); - if (m_enable_simplex) { - row_info r_info; - VERIFY(m_ineq_row_info.find(v, r_info)); - m_ineq_row_info.erase(v); - bool_var v2 = m_ineq_rep.find(r_info.m_rep); - if (v == v2) { - m_simplex.del_row(r_info.m_slack); - m_ineq_rep.erase(r_info.m_rep); - } - } m_to_compile.erase(c); dealloc(c); } m_ineqs_lim.resize(new_lim); + + + new_lim = m_card_lim.size() - num_scopes; + sz = m_card_lim[new_lim]; + while (m_card_trail.size() > sz) { + bool_var v = m_card_trail.back(); + m_card_trail.pop_back(); + if (v != null_bool_var) { + card* c = m_var_infos[v].m_card; + clear_watch(*c); + m_var_infos[v].m_card = 0; + dealloc(c); + } + } + + m_card_lim.resize(new_lim); } void theory_pb::clear_watch(ineq& c) { @@ -1408,11 +1715,6 @@ namespace smt { } } - literal_vector& theory_pb::get_lits() { - m_literals.reset(); - return m_literals; - } - class theory_pb::pb_justification : public theory_propagation_justification { ineq& m_ineq; public: @@ -1428,364 +1730,665 @@ namespace smt { inc_propagations(c); m_stats.m_num_propagations++; context& ctx = get_context(); - TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - "; - for (unsigned i = 0; i < lits.size(); ++i) { - tout << lits[i] << " "; - } - tout << "=> " << l << "\n"; + TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - " << lits; + tout << " => " << l << "\n"; display(tout, c, true);); + SASSERT(validate_antecedents(lits)); ctx.assign(l, ctx.mk_justification( pb_justification( c, get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), l))); } - + void theory_pb::add_clause(ineq& c, literal_vector const& lits) { inc_propagations(c); m_stats.m_num_conflicts++; context& ctx = get_context(); -#if 0 - if (m_stats.m_num_conflicts == 1000) { - display(std::cout); - } -#endif - TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - "; - for (unsigned i = 0; i < lits.size(); ++i) { - tout << lits[i] << " "; - } - tout << "\n"; - display(tout, c, true);); - - justification* js = nullptr; - - if (m_conflict_frequency == 0 || (m_conflict_frequency -1 == (c.m_num_propagations % m_conflict_frequency))) { - resolve_conflict(c); - } + TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - " << lits << "\n"; + display(tout, c, true);); + justification* js = 0; if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); } - TRACE("pb", tout << lits << "\n";); - ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, nullptr); + ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); } - void theory_pb::set_mark(bool_var v, unsigned idx) { - SASSERT(v != null_bool_var); - if (v >= static_cast(m_conseq_index.size())) { - m_conseq_index.resize(v+1, null_index); + int theory_pb::get_coeff(bool_var v) const { + return m_coeffs.get(v, 0); + } + + int theory_pb::get_abs_coeff(bool_var v) const { + int coeff = get_coeff(v); + if (coeff < 0) coeff = -coeff; + return coeff; + } + + void theory_pb::reset_coeffs() { + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + m_coeffs[m_active_vars[i]] = 0; } - SASSERT(!is_marked(v) || m_conseq_index[v] == idx); - m_marked.push_back(v); - m_conseq_index[v] = idx; + m_active_vars.reset(); } - bool theory_pb::is_marked(bool_var v) const { - return - (v < static_cast(m_conseq_index.size())) && - (m_conseq_index[v] != null_index); - } - - void theory_pb::unset_mark(bool_var v) { - SASSERT(v != null_bool_var); - if (v < static_cast(m_conseq_index.size())) { - m_conseq_index[v] = null_index; - } - } - - void theory_pb::unset_marks() { - for (unsigned i = 0; i < m_marked.size(); ++i) { - unset_mark(m_marked[i]); - } - m_marked.reset(); - } - - void theory_pb::process_antecedent(literal l, numeral coeff) { + void theory_pb::process_antecedent(literal l, int offset) { context& ctx = get_context(); + SASSERT(ctx.get_assignment(l) == l_false); bool_var v = l.var(); unsigned lvl = ctx.get_assign_level(v); - if (ctx.get_assignment(l) != l_false) { - m_lemma.m_k -= coeff; - if (m_learn_complements && is_marked(v)) { - SASSERT(ctx.get_assignment(l) == l_true); - numeral& lcoeff = m_lemma[m_conseq_index[v]].second; - lcoeff -= coeff; - if (!lcoeff.is_pos()) { - // perhaps let lemma simplification change coefficient - // when negative? - remove_from_lemma(m_conseq_index[v]); - } - } + if (lvl > ctx.get_base_level() && !ctx.is_marked(v) && lvl == m_conflict_lvl) { + ctx.set_mark(v); + ++m_num_marks; } - else if (lvl > ctx.get_base_level()) { - if (is_marked(v)) { - m_lemma[m_conseq_index[v]].second += coeff; - SASSERT(m_lemma[m_conseq_index[v]].second.is_pos()); - } - else { - if (lvl == m_conflict_lvl) { - TRACE("pb", tout << "add mark: " << l << " " << coeff << "\n";); - ++m_num_marks; - } - set_mark(v, m_lemma.size()); - m_lemma.push_back(std::make_pair(l, coeff)); - } - TRACE("pb_verbose", tout - << "ante: " << m_lemma.lit(m_conseq_index[v]) << "*" - << m_lemma.coeff(m_conseq_index[v]) << " " << lvl << "\n";); + inc_coeff(l, offset); + } + + void theory_pb::process_card(card& c, int offset) { + context& ctx = get_context(); + SASSERT(c.k() <= c.size()); + SASSERT(ctx.get_assignment(c.lit()) == l_true); + for (unsigned i = c.k(); i < c.size(); ++i) { + process_antecedent(c.lit(i), offset); + } + for (unsigned i = 0; i < c.k(); ++i) { + inc_coeff(c.lit(i), offset); + } + if (ctx.get_assign_level(c.lit()) > ctx.get_base_level()) { + m_antecedents.push_back(c.lit()); } } - void theory_pb::process_ineq(ineq& c, literal conseq, numeral coeff1) { - - // - // Create CUT. - // - - // - // . find coeff2 - // . find lcm of coefficients to conseq. - // . multiply m_lemma by lcm/coeff coefficient to align. - // . create lcm/coeff_2 to multiply on this side. - // . cut resolve constraints. - // - + bool theory_pb::validate_lemma() { + int value = -m_bound; context& ctx = get_context(); - numeral coeff2 = (conseq==null_literal)?numeral::one():numeral::zero(); - for (unsigned i = 0; i < c.size(); ++i) { - if (c.lit(i) == conseq) { - coeff2 = c.coeff(i); + normalize_active_coeffs(); + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + int coeff = get_coeff(v); + SASSERT(coeff != 0); + if (coeff < 0 && ctx.get_assignment(v) != l_true) { + value -= coeff; + } + else if (coeff > 0 && ctx.get_assignment(v) != l_false) { + value += coeff; + } + } + // std::cout << "bound: " << m_bound << " value " << value << " coeffs: " << m_active_vars.size() << " lemma is " << (value >= 0 ? "sat" : "unsat") << "\n"; + return value < 0; + } + + bool theory_pb::validate_implies(app_ref& A, app_ref& B) { + static bool validating = true; // false; + if (validating) return true; + validating = true; + ast_manager& m = get_manager(); + smt_params fp; + kernel k(m, fp); + expr_ref notB(m.mk_not(B), m); + k.assert_expr(A); + k.assert_expr(notB); + lbool is_sat = k.check(); + validating = false; + std::cout << is_sat << "\n"; + if (is_sat == l_true) { + std::cout << A << "\n"; + std::cout << B << "\n"; + } + SASSERT(is_sat != l_true); + return true; + } + + app_ref theory_pb::justification2expr(b_justification& js, literal conseq) { + ast_manager& m = get_manager(); + app_ref result(m.mk_true(), m); + expr_ref_vector args(m); + vector coeffs; + switch(js.get_kind()) { + + case b_justification::CLAUSE: { + clause& cls = *js.get_clause(); + justification* cjs = cls.get_justification(); + if (cjs && !is_proof_justification(*cjs)) { + break; + } + for (unsigned i = 0; i < cls.get_num_literals(); ++i) { + literal lit = cls.get_literal(i); + args.push_back(literal2expr(lit)); + } + result = m.mk_or(args.size(), args.c_ptr()); + break; + } + case b_justification::BIN_CLAUSE: + result = m.mk_or(literal2expr(conseq), literal2expr(~js.get_literal())); + break; + case b_justification::AXIOM: + break; + case b_justification::JUSTIFICATION: { + justification* j = js.get_justification(); + card_justification* pbj = 0; + if (j->get_from_theory() == get_id()) { + pbj = dynamic_cast(j); + } + if (pbj != 0) { + card& c2 = pbj->get_card(); + result = card2expr(c2); + } + break; + } + default: + break; + } + return result; + } + + int theory_pb::arg_max(int& max_coeff) { + max_coeff = 0; + int arg_max = -1; + while (!m_active_coeffs.empty()) { + max_coeff = m_active_coeffs.back(); + if (m_coeff2args[max_coeff].empty()) { + m_active_coeffs.pop_back(); + } + else { + arg_max = m_coeff2args[max_coeff].back(); + m_coeff2args[max_coeff].pop_back(); break; } } - SASSERT(coeff2.is_pos()); - numeral lc = lcm(coeff1, coeff2); - numeral g = lc/coeff1; - SASSERT(g.is_int()); - if (g > numeral::one()) { - for (unsigned i = 0; i < m_lemma.size(); ++i) { - m_lemma[i].second *= g; + return arg_max; + } + + literal theory_pb::get_asserting_literal(literal p) { + if (get_abs_coeff(p.var()) != 0) { + return p; + } + context& ctx = get_context(); + unsigned lvl = 0; + + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + literal lit(v, get_coeff(v) < 0); + if (ctx.get_assignment(lit) == l_false && ctx.get_assign_level(lit) > lvl) { + p = lit; } - m_lemma.m_k *= g; - } - g = lc/coeff2; - SASSERT(g.is_int()); - m_lemma.m_k += g*c.k(); - - for (unsigned i = 0; i < c.size(); ++i) { - process_antecedent(c.lit(i), g*c.coeff(i)); } - SASSERT(ctx.get_assignment(c.lit()) == l_true); - if (ctx.get_assign_level(c.lit()) > ctx.get_base_level()) { - m_ineq_literals.push_back(c.lit()); + return p; + } + + void theory_pb::reset_arg_max() { + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + int coeff = get_abs_coeff(m_active_vars[i]); + if (static_cast(m_coeff2args.size()) > coeff) { + m_coeff2args[coeff].reset(); + } } } - - // - // modeled after sat_solver/smt_context - // - bool theory_pb::resolve_conflict(ineq& c) { - - if (!c.is_ge()) { + + bool theory_pb::init_arg_max() { + if (m_coeff2args.size() < (1 << 10)) { + m_coeff2args.resize(1 << 10); + } + m_active_coeffs.reset(); + if (m_active_vars.empty()) { return false; } - TRACE("pb", display(tout, c, true);); + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + int coeff = get_abs_coeff(v); + if (coeff >= static_cast(m_coeff2args.size())) { + reset_arg_max(); + return false; + } + if (m_coeff2args[coeff].empty()) { + m_active_coeffs.push_back(coeff); + } + m_coeff2args[coeff].push_back(v); + } + std::sort(m_active_coeffs.begin(), m_active_coeffs.end()); + return true; + } - bool_var v; - literal conseq; + void theory_pb::add_cardinality_lemma() { context& ctx = get_context(); - unsigned& lvl = m_conflict_lvl = 0; - for (unsigned i = 0; i < c.size(); ++i) { - if (ctx.get_assignment(c.lit(i)) == l_false) { - lvl = std::max(lvl, ctx.get_assign_level(c.lit(i))); + normalize_active_coeffs(); + int s = 0; + int new_bound = 0; + if (!init_arg_max()) { + return; + } + // TBD: can be optimized + while (s < m_bound) { + int coeff; + int arg = arg_max(coeff); + if (arg == -1) break; + s += coeff; + ++new_bound; + } + int slack = m_active_coeffs.empty() ? m_bound : (std::min(m_bound, static_cast(m_active_coeffs[0]) - 1)); + reset_arg_max(); + + while (slack > 0) { + bool found = false; + int v = 0; + int coeff = 0; + for (unsigned i = 0; !found && i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + coeff = get_abs_coeff(v); + if (0 < coeff && coeff < slack) { + found = true; + } + } + if (!found) { + break; + } + slack -= coeff; + m_coeffs[v] = 0; // deactivate coefficient. + } + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + int coeff = get_coeff(v); + if (coeff < 0) { + m_coeffs[v] = -1; + } + else if (coeff > 0) { + m_coeffs[v] = 1; + } + } + + m_bound = new_bound; + if (!validate_lemma()) { + return; + } + SASSERT(m_bound > 0); + if (m_bound > static_cast(m_active_vars.size())) { + return; + } + if (m_bound == static_cast(m_active_vars.size())) { + return; + } + + m_antecedent_exprs.reset(); + m_antecedent_signs.reset(); + m_cardinality_exprs.reset(); + m_cardinality_signs.reset(); + for (unsigned i = 0; i < m_antecedents.size(); ++i) { + literal lit = m_antecedents[i]; + m_antecedent_exprs.push_back(ctx.bool_var2expr(lit.var())); + m_antecedent_signs.push_back(lit.sign()); + } + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + m_cardinality_exprs.push_back(ctx.bool_var2expr(v)); + m_cardinality_signs.push_back(get_coeff(v) < 0); + } + m_cardinality_lemma = true; + } + + void theory_pb::normalize_active_coeffs() { + while (!m_active_var_set.empty()) m_active_var_set.erase(); + unsigned i = 0, j = 0, sz = m_active_vars.size(); + for (; i < sz; ++i) { + bool_var v = m_active_vars[i]; + if (!m_active_var_set.contains(v) && get_coeff(v) != 0) { + m_active_var_set.insert(v); + if (j != i) { + m_active_vars[j] = m_active_vars[i]; + } + ++j; } } - if (lvl < ctx.get_assign_level(c.lit()) || lvl == ctx.get_base_level()) { + sz = j; + m_active_vars.shrink(sz); + } + + void theory_pb::inc_coeff(literal l, int offset) { + SASSERT(offset > 0); + bool_var v = l.var(); + SASSERT(v != null_bool_var); + if (static_cast(m_coeffs.size()) <= v) { + m_coeffs.resize(v + 1, 0); + } + int coeff0 = m_coeffs[v]; + if (coeff0 == 0) { + m_active_vars.push_back(v); + } + + int inc = l.sign() ? -offset : offset; + int coeff1 = inc + coeff0; + m_coeffs[v] = coeff1; + + if (coeff0 > 0 && inc < 0) { + m_bound -= coeff0 - std::max(0, coeff1); + } + else if (coeff0 < 0 && inc > 0) { + m_bound -= std::min(0, coeff1) - coeff0; + } + } + + /** + \brief attempt a cut and simplification of constraints. + */ + void theory_pb::cut() { + unsigned g = 0; + for (unsigned i = 0; g != 1 && i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + int coeff = get_abs_coeff(v); + if (coeff == 0) { + continue; + } + if (m_bound < coeff) { + if (get_coeff(v) > 0) { + m_coeffs[v] = m_bound; + } + else { + m_coeffs[v] = -m_bound; + } + coeff = m_bound; + } + SASSERT(0 < coeff && coeff <= m_bound); + if (g == 0) { + g = static_cast(coeff); + } + else { + g = u_gcd(g, static_cast(coeff)); + } + } + if (g >= 2) { + normalize_active_coeffs(); + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + m_coeffs[m_active_vars[i]] /= g; + } + m_bound = (m_bound + g - 1) / g; + std::cout << "CUT " << g << "\n"; + TRACE("pb", display_resolved_lemma(tout << "cut\n");); + } + } + + bool theory_pb::can_propagate() { return m_cardinality_lemma; } + + void theory_pb::propagate() { + context& ctx = get_context(); + ast_manager& m = get_manager(); + if (!m_cardinality_lemma) { + return; + } + m_cardinality_lemma = false; + if (ctx.inconsistent()) { + return; + } + m_antecedents.reset(); + + for (unsigned i = 0; i < m_antecedent_exprs.size(); ++i) { + expr* a = m_antecedent_exprs[i].get(); + if (!ctx.b_internalized(a)) { + std::cout << "not internalized " << mk_pp(a, m) << "\n"; + return; + } + m_antecedents.push_back(~literal(ctx.get_bool_var(a), m_antecedent_signs[i])); + } + for (unsigned i = 0; i < m_cardinality_exprs.size(); ++i) { + expr* a = m_cardinality_exprs[i].get(); + if (!ctx.b_internalized(a)) { + std::cout << "not internalized " << mk_pp(a, m) << "\n"; + return; + } + if (m_cardinality_signs[i]) { + m_cardinality_exprs[i] = m.mk_not(a); + } + } + app_ref atl(pb.mk_at_least_k(m_cardinality_exprs.size(), m_cardinality_exprs.c_ptr(), m_bound), m); + VERIFY(internalize_card(atl, false)); + bool_var abv = ctx.get_bool_var(atl); + m_antecedents.push_back(literal(abv)); + justification* js = 0; + if (proofs_enabled()) { + js = 0; // + } + ctx.mk_clause(m_antecedents.size(), m_antecedents.c_ptr(), js, CLS_AUX_LEMMA, 0); + } + + bool theory_pb::resolve_conflict(card& c, literal_vector const& confl) { + + TRACE("pb", display(tout, c, true); ); + + bool_var v; + context& ctx = get_context(); + ast_manager& m = get_manager(); + m_conflict_lvl = 0; + m_cardinality_lemma = false; + for (unsigned i = 0; i < confl.size(); ++i) { + literal lit = confl[i]; + SASSERT(ctx.get_assignment(lit) == l_false); + m_conflict_lvl = std::max(m_conflict_lvl, ctx.get_assign_level(lit)); + } + if (m_conflict_lvl < ctx.get_assign_level(c.lit()) || m_conflict_lvl == ctx.get_base_level()) { return false; } - unset_marks(); - m_num_marks = 0; - m_lemma.reset(); - m_lemma.m_k.reset(); - m_ineq_literals.reset(); - process_ineq(c, null_literal, numeral::one()); // add consequent to lemma. + // std::cout << c.lit() << "\n"; + reset_coeffs(); + m_num_marks = 0; + m_bound = c.k(); + m_antecedents.reset(); + m_resolved.reset(); + literal_vector ante; + + process_card(c, 1); + + app_ref A(m), B(m), C(m); + DEBUG_CODE(A = c.to_expr(*this);); + // point into stack of assigned literals literal_vector const& lits = ctx.assigned_literals(); SASSERT(!lits.empty()); unsigned idx = lits.size()-1; - + b_justification js; + literal conseq = ~confl[2]; + int bound = 1; + while (m_num_marks > 0) { - TRACE("pb_verbose", display(tout << "lemma ", m_lemma);); + v = conseq.var(); - lbool is_sat = m_lemma.normalize(false); - if (is_sat == l_false) { - break; + int offset = get_abs_coeff(v); + + if (offset == 0) { + goto process_next_resolvent; } - if (is_sat == l_true) { - IF_VERBOSE(0, verbose_stream() << "lemma already evaluated\n";); - TRACE("pb", tout << "lemma already evaluated\n";); + SASSERT(validate_lemma()); + if (offset > 1000) { + while (m_num_marks > 0 && idx > 0) { + v = lits[idx].var(); + if (ctx.is_marked(v)) { + ctx.unset_mark(v); + } + --idx; + } return false; } - TRACE("pb", display(tout, m_lemma);); - SASSERT(m_lemma.well_formed()); - // - // find the next marked variable in the assignment stack - // - do { - conseq = lits[idx]; - v = conseq.var(); - --idx; - } - while (!is_marked(v) && idx > 0); - if (idx == 0 && !is_marked(v)) { - // - // Yes, this can (currently) happen because - // the decisions for performing unit propagation - // are made asynchronously. - // In other words, PB unit propagation does not follow the - // same order as the assignment stack. - // It is not a correctness bug but causes to miss lemmas. - // - IF_VERBOSE(12, display_resolved_lemma(verbose_stream());); - TRACE("pb", display_resolved_lemma(tout);); - return false; - } + SASSERT(offset > 0); + + js = ctx.get_justification(v); + + TRACE("pb", + display_resolved_lemma(tout << conseq << "\n"); + ctx.display(tout, js);); + + m_resolved.push_back(conseq); + - unsigned conseq_index = m_conseq_index[v]; - numeral conseq_coeff = m_lemma.coeff(conseq_index); - - TRACE("pb", display(tout, m_lemma, true); - tout << "conseq: " << conseq << " at index: " << conseq_index << "\n";); - - SASSERT(~conseq == m_lemma.lit(conseq_index)); - - remove_from_lemma(conseq_index); - - b_justification js = ctx.get_justification(v); - // // Resolve selected conseq with antecedents. // + + bound = 1; switch(js.get_kind()) { case b_justification::CLAUSE: { + inc_coeff(conseq, offset); clause& cls = *js.get_clause(); justification* cjs = cls.get_justification(); if (cjs && !is_proof_justification(*cjs)) { TRACE("pb", tout << "skipping justification for clause over: " << conseq << " " << typeid(*cjs).name() << "\n";); - m_ineq_literals.push_back(conseq); break; } unsigned num_lits = cls.get_num_literals(); if (cls.get_literal(0) == conseq) { - process_antecedent(cls.get_literal(1), conseq_coeff); + process_antecedent(cls.get_literal(1), offset); } else { SASSERT(cls.get_literal(1) == conseq); - process_antecedent(cls.get_literal(0), conseq_coeff); + process_antecedent(cls.get_literal(0), offset); } for (unsigned i = 2; i < num_lits; ++i) { - process_antecedent(cls.get_literal(i), conseq_coeff); + process_antecedent(cls.get_literal(i), offset); } - TRACE("pb", for (unsigned i = 0; i < num_lits; ++i) tout << cls.get_literal(i) << " "; tout << "\n";); + TRACE("pb", tout << literal_vector(cls.get_num_literals(), cls.begin_literals()) << "\n";); break; } case b_justification::BIN_CLAUSE: - process_antecedent(~js.get_literal(), conseq_coeff); - TRACE("pb", tout << "binary: " << js.get_literal() << "\n";); + inc_coeff(conseq, offset); + process_antecedent(~js.get_literal(), offset); break; case b_justification::AXIOM: - if (ctx.get_assign_level(v) > ctx.get_base_level()) { - m_ineq_literals.push_back(conseq); - } - TRACE("pb", tout << "axiom " << conseq << "\n";); break; case b_justification::JUSTIFICATION: { justification* j = js.get_justification(); - pb_justification* pbj = nullptr; + card_justification* pbj = nullptr; - if (!conseq.sign() && j->get_from_theory() == get_id()) { - pbj = dynamic_cast(j); + if (j->get_from_theory() == get_id()) { + pbj = dynamic_cast(j); } - if (pbj && pbj->get_ineq().is_eq()) { - // only resolve >= that are positive consequences. - pbj = nullptr; - } - if (pbj && pbj->get_ineq().lit() == conseq) { - // can't resolve against literal representing inequality. - pbj = nullptr; - } - if (pbj) { - // weaken the lemma and resolve. - TRACE("pb", display(tout << "resolve with inequality", pbj->get_ineq(), true);); - process_ineq(pbj->get_ineq(), conseq, conseq_coeff); + if (pbj == nullptr) { + TRACE("pb", tout << "skip justification for " << conseq << "\n";); + inc_coeff(conseq, offset); } else { - TRACE("pb", tout << "skipping justification for " << conseq - << " from theory " << j->get_from_theory() << " " - << typeid(*j).name() << "\n";); - m_ineq_literals.push_back(conseq); + card& c2 = pbj->get_card(); + process_card(c2, offset); + bound = c2.k(); } + + // std::cout << " offset: " << offset << " bound: " << bound << "\n"; break; } default: UNREACHABLE(); } - } + m_bound += offset * bound; - TRACE("pb", - for (unsigned i = 0; i < m_ineq_literals.size(); ++i) { - tout << m_ineq_literals[i] << " "; - } - display(tout << "=> ", m_lemma);); + DEBUG_CODE( + B = justification2expr(js, conseq); + C = active2expr(); + B = m.mk_and(A, B); + validate_implies(B, C); + A = C;); - // 3x + 3y + z + u >= 4 - // ~x /\ ~y => z + u >= + cut(); - IF_VERBOSE(14, display(verbose_stream() << "lemma1: ", m_lemma);); - hoist_maximal_values(); - lbool is_true = m_lemma.normalize(false); - m_lemma.prune(false); + process_next_resolvent: - IF_VERBOSE(14, display(verbose_stream() << "lemma2: ", m_lemma);); - //unsigned l_size = m_ineq_literals.size() + ((is_true==l_false)?0:m_lemma.size()); - //if (s_min_l_size >= l_size) { - // verbose_stream() << "(pb.conflict min size: " << l_size << ")\n"; - // s_min_l_size = l_size; - //} - IF_VERBOSE(1, verbose_stream() << "(pb.conflict " << m_ineq_literals.size() << " " << m_lemma.size() << ")\n";); - switch(is_true) { - case l_true: - UNREACHABLE(); - return false; - case l_false: - inc_propagations(c); - m_stats.m_num_conflicts++; - for (unsigned i = 0; i < m_ineq_literals.size(); ++i) { - m_ineq_literals[i].neg(); + // find the next marked variable in the assignment stack + // + while (true) { + conseq = lits[idx]; + v = conseq.var(); + if (ctx.is_marked(v)) break; + SASSERT(idx > 0); + --idx; } - TRACE("pb", tout << m_ineq_literals << "\n";); - ctx.mk_clause(m_ineq_literals.size(), m_ineq_literals.c_ptr(), justify(m_ineq_literals), CLS_AUX_LEMMA, nullptr); - break; - default: { - app_ref tmp = m_lemma.to_expr(false, ctx, get_manager()); - internalize_atom(tmp, false); - ctx.mark_as_relevant(tmp.get()); - literal l(ctx.get_bool_var(tmp)); - add_assign(c, m_ineq_literals, l); - break; + + SASSERT(ctx.get_assign_level(v) == m_conflict_lvl); + ctx.unset_mark(v); + --idx; + --m_num_marks; } + SASSERT(validate_lemma()); + + TRACE("pb", display_resolved_lemma(tout << "done\n");); + + + normalize_active_coeffs(); + + if (m_bound > 0 && m_active_vars.empty()) { + return false; } - return true; + + int slack = -m_bound; + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + slack += get_abs_coeff(v); + } + +#if 1 + //std::cout << slack << " " << m_bound << "\n"; + unsigned i = 0; + literal_vector const& alits = ctx.assigned_literals(); + + literal alit = get_asserting_literal(~conseq); + slack -= get_abs_coeff(alit.var()); + + for (i = alits.size(); 0 <= slack && i > 0; ) { + --i; + literal lit = alits[i]; + bool_var v = lit.var(); + // -3*x >= k + if (m_active_var_set.contains(v) && v != alit.var()) { + int coeff = get_coeff(v); + //std::cout << coeff << " " << lit << "\n"; + if (coeff < 0 && !lit.sign()) { + slack += coeff; + m_antecedents.push_back(lit); + //std::cout << "ante: " << lit << "\n"; + } + else if (coeff > 0 && lit.sign()) { + slack -= coeff; + m_antecedents.push_back(lit); + //std::cout << "ante: " << lit << "\n"; + } + } + } + SASSERT(slack < 0); + +#else + + literal alit = get_asserting_literal(~conseq); + slack -= get_abs_coeff(alit.var()); + + for (unsigned i = 0; 0 <= slack; ++i) { + SASSERT(i < m_active_vars.size()); + bool_var v = m_active_vars[i]; + literal lit(v, get_coeff(v) < 0); + if (v != alit.var() && ctx.get_assignment(lit) == l_false) { + m_antecedents.push_back(~lit); + slack -= get_abs_coeff(v); + } + if (slack < 0) { + std::cout << i << " " << m_active_vars.size() << "\n"; + } + } +#endif + SASSERT(validate_antecedents(m_antecedents)); + ctx.assign(alit, ctx.mk_justification(theory_propagation_justification(get_id(), ctx.get_region(), m_antecedents.size(), m_antecedents.c_ptr(), alit, 0, 0))); + + DEBUG_CODE( + m_antecedents.push_back(~alit); + expr_ref_vector args(m); + for (unsigned i = 0; i < m_antecedents.size(); ++i) { + args.push_back(literal2expr(m_antecedents[i])); + } + B = m.mk_not(m.mk_and(args.size(), args.c_ptr())); + validate_implies(A, B); ); + // add_cardinality_lemma(); + return true; } bool theory_pb::is_proof_justification(justification const& j) const { @@ -1809,30 +2412,6 @@ namespace smt { return js; } - void theory_pb::hoist_maximal_values() { - for (unsigned i = 0; i < m_lemma.size(); ++i) { - if (m_lemma.coeff(i) >= m_lemma.k()) { - m_ineq_literals.push_back(~m_lemma.lit(i)); - std::swap(m_lemma[i], m_lemma[m_lemma.size()-1]); - m_lemma.pop_back(); - --i; - } - } - } - - void theory_pb::remove_from_lemma(unsigned idx) { - // Remove conseq from lemma: - literal lit = m_lemma.lit(idx); - unsigned last = m_lemma.size()-1; - if (idx != last) { - m_lemma[idx] = m_lemma[last]; - m_conseq_index[m_lemma.lit(idx).var()] = idx; - } - m_lemma.pop_back(); - unset_mark(lit.var()); - --m_num_marks; - } - // debug methods void theory_pb::validate_watch(ineq const& c) const { @@ -1850,9 +2429,9 @@ namespace smt { void theory_pb::validate_assign(ineq const& c, literal_vector const& lits, literal l) const { uint_set nlits; - for (unsigned i = 0; i < lits.size(); ++i) { - SASSERT(get_context().get_assignment(lits[i]) == l_true); - nlits.insert((~lits[i]).index()); + for (literal lit : lits) { + SASSERT(get_context().get_assignment(lit) == l_true); + nlits.insert((~lit).index()); } SASSERT(get_context().get_assignment(l) == l_undef); SASSERT(get_context().get_assignment(c.lit()) == l_true); @@ -1866,9 +2445,7 @@ namespace smt { } CTRACE("pb", (sum >= c.k()), display(tout << "invalid assign" , c, true); - for (unsigned i = 0; i < lits.size(); ++i) { - tout << lits[i] << " "; - } + for (literal lit : lits) tout << lit << " "; tout << " => " << l << "\n";); SASSERT(sum < c.k()); } @@ -1914,41 +2491,93 @@ namespace smt { SASSERT(!c.is_eq() || (sum == c.k()) == (ctx.get_assignment(c.lit()) == l_true)); } + bool theory_pb::validate_antecedents(literal_vector const& lits) { + context& ctx = get_context(); + for (unsigned i = 0; i < lits.size(); ++i) { + if (ctx.get_assignment(lits[i]) != l_true) { + return false; + } + } + return true; + } + + bool theory_pb::validate_unit_propagation(card const& c) { + context& ctx = get_context(); + for (unsigned i = c.k(); i < c.size(); ++i) { + VERIFY(ctx.get_assignment(c.lit(i)) == l_false); + } + return true; + } + + app_ref theory_pb::literal2expr(literal lit) { + ast_manager& m = get_manager(); + app_ref arg(m.mk_const(symbol(lit.var()), m.mk_bool_sort()), m); + return app_ref(lit.sign() ? m.mk_not(arg) : arg, m); + } + + app_ref theory_pb::active2expr() { + ast_manager& m = get_manager(); + expr_ref_vector args(m); + vector coeffs; + normalize_active_coeffs(); + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + literal lit(v, get_coeff(v) < 0); + args.push_back(literal2expr(lit)); + coeffs.push_back(rational(get_abs_coeff(v))); + } + rational k(m_bound); + return app_ref(pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), k), m); + } + // display methods void theory_pb::display_resolved_lemma(std::ostream& out) const { context& ctx = get_context(); - literal_vector const& lits = ctx.assigned_literals(); bool_var v; unsigned lvl; out << "num marks: " << m_num_marks << "\n"; out << "conflict level: " << m_conflict_lvl << "\n"; - for (unsigned i = 0; i < lits.size(); ++i) { - v = lits[i].var(); + for (unsigned i = 0; i < m_resolved.size(); ++i) { + v = m_resolved[i].var(); lvl = ctx.get_assign_level(v); - out << lits[i] - << "@ " << lvl - << " " << (is_marked(v)?"m":"u") - << "\n"; - - if (lvl == m_conflict_lvl && is_marked(v)) { - out << "skipped: " << lits[i] << ":"<< i << "\n"; - } + out << lvl << ": " << m_resolved[i] << " "; + ctx.display(out, ctx.get_justification(v)); } - display(out, m_lemma, true); - unsigned nc = 0; - for (unsigned i = 0; i < m_lemma.size(); ++i) { - v = m_lemma.lit(i).var(); - lvl = ctx.get_assign_level(v); - if (lvl == m_conflict_lvl) ++nc; - out << m_lemma.lit(i) - << "@" << lvl - << " " << (is_marked(v)?"m":"u") - << " " << ctx.get_assignment(m_lemma.lit(i)) - << "\n"; + if (!m_antecedents.empty()) { + out << m_antecedents << " ==> "; } - out << "num conflicts: " << nc << "\n"; + uint_set seen; + bool first = true; + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + if (seen.contains(v)) { + continue; + } + seen.insert(v); + int coeff = get_coeff(v); + if (coeff == 0) { + continue; + } + if (!first) { + out << " + "; + } + if (coeff == 1) { + out << literal(v); + } + else if (coeff == -1) { + out << literal(v, true); + } + else if (coeff > 0) { + out << coeff << " * " << literal(v); + } + else { + out << (-coeff) << " * " << literal(v, true); + } + first = false; + } + out << " >= " << m_bound << "\n"; } std::ostream& theory_pb::display(std::ostream& out, arg_t const& c, bool values) const { @@ -2087,9 +2716,9 @@ namespace smt { } void theory_pb::display_watch(std::ostream& out, bool_var v, bool sign) const { - watch_list const* w = m_var_infos[v].m_lit_watch[sign]; + ineq_watch const* w = m_var_infos[v].m_lit_watch[sign]; if (!w) return; - watch_list const& wl = *w; + ineq_watch const& wl = *w; out << "watch: " << literal(v, sign) << " |-> "; for (unsigned i = 0; i < wl.size(); ++i) { out << wl[i]->lit() << " "; @@ -2103,10 +2732,10 @@ namespace smt { display_watch(out, vi, true); } for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { - watch_list const* w = m_var_infos[vi].m_var_watch; + ineq_watch const* w = m_var_infos[vi].m_var_watch; if (!w) continue; out << "watch (v): " << literal(vi) << " |-> "; - watch_list const& wl = *w; + ineq_watch const& wl = *w; for (unsigned i = 0; i < wl.size(); ++i) { out << wl[i]->lit() << " "; } @@ -2118,6 +2747,14 @@ namespace smt { display(out, *c, true); } } + + for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { + card* c = m_var_infos[vi].m_card; + if (c) { + display(out, *c, true); + } + } + } diff --git a/src/smt/theory_pb.h b/src/smt/theory_pb.h index 07c763469..7e9c55a12 100644 --- a/src/smt/theory_pb.h +++ b/src/smt/theory_pb.h @@ -23,6 +23,7 @@ Notes: #include "smt/smt_theory.h" #include "ast/pb_decl_plugin.h" #include "smt/smt_clause.h" +#include "smt/smt_b_justification.h" #include "smt/params/theory_pb_params.h" #include "math/simplex/simplex.h" @@ -37,10 +38,10 @@ namespace smt { class negate_ineq; class remove_var; class undo_bound; + + class card_justification; + typedef rational numeral; - typedef simplex::simplex simplex; - typedef simplex::row row; - typedef simplex::row_iterator row_iterator; typedef unsynch_mpq_inf_manager eps_manager; typedef _scoped_numeral scoped_eps_numeral; @@ -181,27 +182,88 @@ namespace smt { }; - struct row_info { - unsigned m_slack; // slack variable in simplex tableau - numeral m_bound; // bound - arg_t m_rep; // representative - row_info(theory_var slack, numeral const& b, arg_t const& r): - m_slack(slack), m_bound(b), m_rep(r) {} - row_info(): m_slack(0) {} + // cardinality constraint args >= bound + // unit propagation on cardinality constraints is valid if the literals + // from k() up to size are false. + // In this case the literals 0..k()-1 need to be true. + // The literals in position 0..k() are watched. + // whenever they are assigned to false, then find a literal among + // k() + 1.. sz() to swap with. + // If none are available, then perform unit propagation. + // + class card { + literal m_lit; // literal repesenting predicate + literal_vector m_args; + unsigned m_bound; + unsigned m_num_propagations; + unsigned m_all_propagations; + unsigned m_compilation_threshold; + lbool m_compiled; + bool m_aux; + + public: + card(literal l, unsigned bound, bool is_aux): + m_lit(l), + m_bound(bound), + m_num_propagations(0), + m_all_propagations(0), + m_compilation_threshold(0), + m_compiled(l_false), + m_aux(is_aux) + { + SASSERT(bound > 0); + } + + literal lit() const { return m_lit; } + literal lit(unsigned i) const { return m_args[i]; } + unsigned k() const { return m_bound; } + unsigned size() const { return m_args.size(); } + unsigned all_propagations() const { return m_all_propagations; } + unsigned num_propagations() const { return m_num_propagations; } + void add_arg(literal l); + + void init_watch(theory_pb& th, bool is_true); + + lbool assign(theory_pb& th, literal lit); + + void negate(); + + app_ref to_expr(theory_pb& th); + + void inc_propagations(theory_pb& th); + + void reset_propagations() { m_all_propagations += m_num_propagations; m_num_propagations = 0; } + + bool is_aux() const { return m_aux; } + + private: + + bool validate_conflict(theory_pb& th); + + bool validate_assign(theory_pb& th, literal_vector const& lits, literal l); + + void set_conflict(theory_pb& th, literal l); }; - typedef ptr_vector watch_list; + typedef ptr_vector card_watch; + typedef ptr_vector ineq_watch; typedef map arg_map; + struct var_info { - watch_list* m_lit_watch[2]; - watch_list* m_var_watch; - ineq* m_ineq; + ineq_watch* m_lit_watch[2]; + ineq_watch* m_var_watch; + ineq* m_ineq; + + card_watch* m_lit_cwatch[2]; + card* m_card; - var_info(): m_var_watch(nullptr), m_ineq(nullptr) + var_info(): m_var_watch(0), m_ineq(0), m_card(0) { m_lit_watch[0] = nullptr; m_lit_watch[1] = nullptr; + m_lit_cwatch[0] = nullptr; + m_lit_cwatch[1] = nullptr; } void reset() { @@ -209,38 +271,39 @@ namespace smt { dealloc(m_lit_watch[1]); dealloc(m_var_watch); dealloc(m_ineq); + dealloc(m_lit_cwatch[0]); + dealloc(m_lit_cwatch[1]); + dealloc(m_card); } }; - theory_pb_params m_params; svector m_var_infos; - arg_map m_ineq_rep; // Simplex: representative inequality - u_map m_ineq_row_info; // Simplex: row information per variable - uint_set m_vars; // Simplex: 0-1 variables. - simplex m_simplex; // Simplex: tableau - literal_vector m_explain_lower; // Simplex: explanations for lower bounds - literal_vector m_explain_upper; // Simplex: explanations for upper bounds - unsynch_mpq_inf_manager m_mpq_inf_mgr; // Simplex: manage inf_mpq numerals mutable unsynch_mpz_manager m_mpz_mgr; // Simplex: manager mpz numerals unsigned_vector m_ineqs_trail; unsigned_vector m_ineqs_lim; literal_vector m_literals; // temporary vector - pb_util m_util; + pb_util pb; stats m_stats; ptr_vector m_to_compile; // inequalities to compile. unsigned m_conflict_frequency; bool m_learn_complements; bool m_enable_compilation; - bool m_enable_simplex; rational m_max_compiled_coeff; + bool m_cardinality_lemma; + unsigned m_restart_lim; + unsigned m_restart_inc; + uint_set m_occs; + // internalize_atom: literal compile_arg(expr* arg); - void add_watch(ineq& c, unsigned index); - void del_watch(watch_list& watch, unsigned index, ineq& c, unsigned ineq_index); void init_watch(bool_var v); + + // general purpose pb constraints + void add_watch(ineq& c, unsigned index); + void del_watch(ineq_watch& watch, unsigned index, ineq& c, unsigned ineq_index); void init_watch_literal(ineq& c); void init_watch_var(ineq& c); void clear_watch(ineq& c); @@ -249,14 +312,35 @@ namespace smt { void unwatch_literal(literal w, ineq* c); void unwatch_var(bool_var v, ineq* c); void remove(ptr_vector& ineqs, ineq* c); - bool assign_watch_ge(bool_var v, bool is_true, watch_list& watch, unsigned index); + + bool assign_watch_ge(bool_var v, bool is_true, ineq_watch& watch, unsigned index); void assign_watch(bool_var v, bool is_true, ineq& c); void assign_ineq(ineq& c, bool is_true); void assign_eq(ineq& c, bool is_true); + // cardinality constraints + // these are cheaper to handle than general purpose PB constraints + // and in the common case PB constraints with small coefficients can + // be handled using cardinality constraints. + + unsigned_vector m_card_trail; + unsigned_vector m_card_lim; + bool is_cardinality_constraint(app * atom); + bool internalize_card(app * atom, bool gate_ctx); + void card2conjunction(card const& c); + void card2disjunction(card const& c); + + void watch_literal(literal lit, card* c); + void unwatch_literal(literal w, card* c); + void add_clause(card& c, literal_vector const& lits); + void add_assign(card& c, literal l); + void remove(ptr_vector& cards, card* c); + void clear_watch(card& c); + bool gc(); + std::ostream& display(std::ostream& out, card const& c, bool values = false) const; + + // simplex: - literal set_explain(literal_vector& explains, unsigned var, literal expl); - bool update_bound(bool_var v, literal explain, bool is_lower, mpq_inf const& bound); bool check_feasible(); std::ostream& display(std::ostream& out, ineq const& c, bool values = false) const; @@ -284,31 +368,58 @@ namespace smt { // Conflict resolution, cutting plane derivation. // unsigned m_num_marks; + literal_vector m_resolved; unsigned m_conflict_lvl; - arg_t m_lemma; - literal_vector m_ineq_literals; - svector m_marked; - // bool_var |-> index into m_lemma - unsigned_vector m_conseq_index; - static const unsigned null_index; - bool is_marked(bool_var v) const; - void set_mark(bool_var v, unsigned idx); - void unset_mark(bool_var v); - void unset_marks(); + // Conflict PB constraints + svector m_coeffs; + svector m_active_vars; + int m_bound; + literal_vector m_antecedents; + tracked_uint_set m_active_var_set; + expr_ref_vector m_antecedent_exprs; + svector m_antecedent_signs; + expr_ref_vector m_cardinality_exprs; + svector m_cardinality_signs; - bool resolve_conflict(ineq& c); - void process_antecedent(literal l, numeral coeff); - void process_ineq(ineq& c, literal conseq, numeral coeff); - void remove_from_lemma(unsigned idx); + void normalize_active_coeffs(); + void inc_coeff(literal l, int offset); + int get_coeff(bool_var v) const; + int get_abs_coeff(bool_var v) const; + int arg_max(int& coeff); + + literal_vector& get_literals() { m_literals.reset(); return m_literals; } + + vector > m_coeff2args; + unsigned_vector m_active_coeffs; + bool init_arg_max(); + void reset_arg_max(); + + void reset_coeffs(); + void add_cardinality_lemma(); + literal get_asserting_literal(literal conseq); + + bool resolve_conflict(card& c, literal_vector const& conflict_clause); + void process_antecedent(literal l, int offset); + void process_card(card& c, int offset); + void cut(); bool is_proof_justification(justification const& j) const; + void hoist_maximal_values(); + bool validate_lemma(); void validate_final_check(); void validate_final_check(ineq& c); void validate_assign(ineq const& c, literal_vector const& lits, literal l) const; void validate_watch(ineq const& c) const; + bool validate_unit_propagation(card const& c); + bool validate_antecedents(literal_vector const& lits); + bool validate_implies(app_ref& A, app_ref& B); + app_ref active2expr(); + app_ref literal2expr(literal lit); + app_ref card2expr(card& c) { return c.to_expr(*this); } + app_ref justification2expr(b_justification& js, literal conseq); bool proofs_enabled() const { return get_manager().proofs_enabled(); } justification* justify(literal l1, literal l2); @@ -337,7 +448,8 @@ namespace smt { model_value_proc * mk_value(enode * n, model_generator & mg) override; void init_model(model_generator & m) override; bool include_func_interp(func_decl* f) override { return false; } - + bool can_propagate() override; + void propagate() override; static literal assert_ge(context& ctx, unsigned k, unsigned n, literal const* xs); }; }; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 95f804863..b704795b2 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -238,8 +238,7 @@ theory_seq::~theory_seq() { } void theory_seq::init(context* ctx) { - theory::init(ctx); - m_mk_aut.set_solver(alloc(seq_expr_solver, m, get_context().get_fparams())); + theory::init(ctx); } final_check_status theory_seq::final_check_eh() { @@ -4168,6 +4167,9 @@ eautomaton* theory_seq::get_automaton(expr* re) { if (m_re2aut.find(re, result)) { return result; } + if (!m_mk_aut.has_solver()) { + m_mk_aut.set_solver(alloc(seq_expr_solver, m, get_context().get_fparams())); + } result = m_mk_aut(re); if (result) { display_expr disp(m); diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 126594b06..0ef008927 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1423,9 +1423,9 @@ namespace smt { // len(hd) = i // str.indexof(tl, N, 0) - expr * H; // "haystack" - expr * N; // "needle" - expr * i; // start index + expr * H = nullptr; // "haystack" + expr * N = nullptr; // "needle" + expr * i = nullptr; // start index u.str.is_index(e, H, N, i); expr_ref minus_one(m_autil.mk_numeral(rational::minus_one(), true), m); @@ -6645,7 +6645,8 @@ namespace smt { expr * sub1; expr * sub2; if (u.re.is_to_re(re, sub1)) { - SASSERT(u.str.is_string(sub1)); + if (!u.str.is_string(sub1)) + throw default_exception("regular expressions must be built from string literals"); zstring str; u.str.is_string(sub1, str); return str.length(); @@ -6768,7 +6769,7 @@ namespace smt { expr * sub2; if (u.re.is_to_re(re, sub1)) { SASSERT(u.str.is_string(sub1)); - zstring(str); + zstring str; u.str.is_string(sub1, str); lens.insert(str.length()); } else if (u.re.is_concat(re, sub1, sub2)) { @@ -6842,7 +6843,8 @@ namespace smt { expr * sub1; expr * sub2; if (u.re.is_to_re(re, sub1)) { - SASSERT(u.str.is_string(sub1)); + if (!u.str.is_string(sub1)) + throw default_exception("regular expressions must be built from string literals"); zstring str; u.str.is_string(sub1, str); rational strlen(str.length()); @@ -6951,8 +6953,8 @@ namespace smt { ast_manager & m = get_manager(); expr_ref_vector rhs(m); - expr * str; - expr * re; + expr * str = nullptr; + expr * re = nullptr; u.str.is_in_re(str_in_re, str, re); expr_ref strlen(mk_strlen(str), m); @@ -9929,8 +9931,8 @@ namespace smt { bool regex_axiom_add = false; for (obj_hashtable::iterator it = regex_terms.begin(); it != regex_terms.end(); ++it) { expr * str_in_re = *it; - expr * str; - expr * re; + expr * str = nullptr; + expr * re = nullptr; u.str.is_in_re(str_in_re, str, re); lbool current_assignment = ctx.get_assignment(str_in_re); TRACE("str", tout << "regex term: " << mk_pp(str, m) << " in " << mk_pp(re, m) << " : " << current_assignment << std::endl;); @@ -9944,7 +9946,7 @@ namespace smt { if (regex_term_to_length_constraint.contains(str_in_re)) { // use existing length constraint - expr * top_level_length_constraint; + expr * top_level_length_constraint = nullptr; regex_term_to_length_constraint.find(str_in_re, top_level_length_constraint); ptr_vector extra_length_vars; @@ -10473,8 +10475,8 @@ namespace smt { // that's consistent with the current length information for (ptr_vector::iterator term_it = str_in_re_terms.begin(); term_it != str_in_re_terms.end(); ++term_it) { - expr * _unused; - expr * re; + expr * _unused = nullptr; + expr * re = nullptr; SASSERT(u.str.is_in_re(*term_it)); u.str.is_in_re(*term_it, _unused, re); diff --git a/src/smt/theory_wmaxsat.cpp b/src/smt/theory_wmaxsat.cpp index 53116cb60..75528a07e 100644 --- a/src/smt/theory_wmaxsat.cpp +++ b/src/smt/theory_wmaxsat.cpp @@ -25,7 +25,7 @@ Notes: namespace smt { - theory_wmaxsat::theory_wmaxsat(ast_manager& m, filter_model_converter& mc): + theory_wmaxsat::theory_wmaxsat(ast_manager& m, generic_model_converter& mc): theory(m.mk_family_id("weighted_maxsat")), m_mc(mc), m_vars(m), @@ -92,7 +92,7 @@ namespace smt { ast_manager& m = get_manager(); app_ref var(m), wfml(m); var = m.mk_fresh_const("w", m.mk_bool_sort()); - m_mc.insert(var->get_decl()); + m_mc.hide(var); wfml = m.mk_or(var, fml); ctx.assert_expr(wfml); m_rweights.push_back(w); diff --git a/src/smt/theory_wmaxsat.h b/src/smt/theory_wmaxsat.h index f2266a36f..b2b28ef3d 100644 --- a/src/smt/theory_wmaxsat.h +++ b/src/smt/theory_wmaxsat.h @@ -22,7 +22,7 @@ Notes: #include "smt/smt_theory.h" #include "smt/smt_clause.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" namespace smt { class theory_wmaxsat : public theory { @@ -32,7 +32,7 @@ namespace smt { void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; - filter_model_converter& m_mc; + generic_model_converter& m_mc; mutable unsynch_mpz_manager m_mpz; app_ref_vector m_vars; // Auxiliary variables per soft clause expr_ref_vector m_fmls; // Formulas per soft clause @@ -56,7 +56,7 @@ namespace smt { svector m_assigned, m_enabled; stats m_stats; public: - theory_wmaxsat(ast_manager& m, filter_model_converter& mc); + theory_wmaxsat(ast_manager& m, generic_model_converter& mc); ~theory_wmaxsat() override; void get_assignment(svector& result); expr* assert_weighted(expr* fml, rational const& w); diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt index 1ffdc35e1..c8e206f7f 100644 --- a/src/solver/CMakeLists.txt +++ b/src/solver/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(solver check_sat_result.cpp combined_solver.cpp mus.cpp + parallel_tactic.cpp smt_logics.cpp solver.cpp solver_na2as.cpp @@ -14,4 +15,6 @@ z3_add_component(solver tactic PYG_FILES combined_solver_params.pyg + parallel_params.pyg + ) diff --git a/src/solver/check_sat_result.cpp b/src/solver/check_sat_result.cpp index e7c15964e..0d3c112b3 100644 --- a/src/solver/check_sat_result.cpp +++ b/src/solver/check_sat_result.cpp @@ -53,7 +53,7 @@ void simple_check_sat_result::get_unsat_core(ptr_vector & r) { r.append(m_core.size(), m_core.c_ptr()); } -void simple_check_sat_result::get_model(model_ref & m) { +void simple_check_sat_result::get_model_core(model_ref & m) { if (m_status != l_false) m = m_model; else diff --git a/src/solver/check_sat_result.h b/src/solver/check_sat_result.h index 76956718c..716c68b00 100644 --- a/src/solver/check_sat_result.h +++ b/src/solver/check_sat_result.h @@ -23,6 +23,7 @@ Notes: #include "util/lbool.h" #include "util/statistics.h" #include "util/event_handler.h" +#include "tactic/model_converter.h" /** \brief Abstract interface for the result of a (check-sat) like command. @@ -40,6 +41,7 @@ class check_sat_result { protected: unsigned m_ref_count; lbool m_status; + model_converter_ref m_mc0; public: check_sat_result():m_ref_count(0), m_status(l_undef) {} virtual ~check_sat_result() {} @@ -54,7 +56,13 @@ public: get_unsat_core(core); r.append(core.size(), core.c_ptr()); } - virtual void get_model(model_ref & m) = 0; + void set_model_converter(model_converter* mc) { m_mc0 = mc; } + model_converter* mc0() const { return m_mc0.get(); } + virtual void get_model_core(model_ref & m) = 0; + void get_model(model_ref & m) { + get_model_core(m); + if (m && mc0()) (*mc0())(m); + } virtual proof * get_proof() = 0; virtual std::string reason_unknown() const = 0; virtual void set_reason_unknown(char const* msg) = 0; @@ -80,7 +88,7 @@ struct simple_check_sat_result : public check_sat_result { ast_manager& get_manager() const override { return m_proof.get_manager(); } void collect_statistics(statistics & st) const override; void get_unsat_core(ptr_vector & r) override; - void get_model(model_ref & m) override; + void get_model_core(model_ref & m) override; proof * get_proof() override; std::string reason_unknown() const override; void get_labels(svector & r) override; diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index 9d2d8aa46..a47302f5d 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -59,7 +59,8 @@ private: ref m_solver2; // We delay sending assertions to solver 2 // This is relevant for big benchmarks that are meant to be solved - // by a non-incremental solver. + // by a non-incremental solver. ); + bool m_solver2_initialized; bool m_ignore_solver1; @@ -137,6 +138,7 @@ public: } solver* translate(ast_manager& m, params_ref const& p) override { + TRACE("solver", tout << "translate\n";); solver* s1 = m_solver1->translate(m, p); solver* s2 = m_solver2->translate(m, p); combined_solver* r = alloc(combined_solver, s1, s2, p); @@ -165,7 +167,7 @@ public: m_solver2->set_produce_models(f); } - void assert_expr(expr * t) override { + void assert_expr_core(expr * t) override { if (m_check_sat_executed) switch_inc_mode(); m_solver1->assert_expr(t); @@ -173,7 +175,7 @@ public: m_solver2->assert_expr(t); } - void assert_expr(expr * t, expr * a) override { + void assert_expr_core2(expr * t, expr * a) override { if (m_check_sat_executed) switch_inc_mode(); m_solver1->assert_expr(t, a); @@ -205,7 +207,7 @@ public: } catch (z3_exception& ex) { if (get_manager().canceled()) { - set_reason_unknown(Z3_CANCELED_MSG); + throw; } else { set_reason_unknown(ex.msg()); @@ -276,6 +278,10 @@ public: return m_solver1->get_num_assumptions() + m_solver2->get_num_assumptions(); } + expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { + return m_solver1->cube(vars, backtrack_level); + } + expr * get_assumption(unsigned idx) const override { unsigned c1 = m_solver1->get_num_assumptions(); if (idx < c1) return m_solver1->get_assumption(idx); @@ -299,7 +305,7 @@ public: m_solver2->get_unsat_core(r); } - void get_model(model_ref & m) override { + void get_model_core(model_ref & m) override { if (m_use_solver1_results) m_solver1->get_model(m); else diff --git a/src/solver/parallel_params.pyg b/src/solver/parallel_params.pyg new file mode 100644 index 000000000..2d58cbb81 --- /dev/null +++ b/src/solver/parallel_params.pyg @@ -0,0 +1,15 @@ +def_module_params('parallel', + description='parameters for parallel solver', + class_name='parallel_params', + export=True, + params=( + ('enable', BOOL, False, 'enable parallel solver by default on selected tactics (for QF_BV)'), + ('threads.max', UINT, 10000, 'caps maximal number of threads below the number of processors'), + ('conquer.batch_size', UINT, 100, 'number of cubes to batch together for fast conquer'), + ('conquer.restart.max', UINT, 5, 'maximal number of restarts during conquer phase'), + ('conquer.delay', UINT, 10, 'delay of cubes until applying conquer'), + ('conquer.backtrack_frequency', UINT, 10, 'frequency to apply core minimization during conquer'), + ('simplify.exp', DOUBLE, 1, 'restart and inprocess max is multipled by simplify.exp ^ depth'), + ('simplify.restart.max', UINT, 5000, 'maximal number of restarts during simplification phase'), + ('simplify.inprocess.max', UINT, 2, 'maximal number of inprocessing steps during simplification'), + )) diff --git a/src/solver/parallel_tactic.cpp b/src/solver/parallel_tactic.cpp new file mode 100644 index 000000000..451a62064 --- /dev/null +++ b/src/solver/parallel_tactic.cpp @@ -0,0 +1,753 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + parallel_tactic.cpp + +Abstract: + + Parallel tactic based on cubing. + + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-9 + Miguel Neves + +Notes: + + A task comprises of a non-empty sequence of cubes, a type and parameters + + It invokes the following procedure: + 1. Clone the state with the remaining cubes if there is more than one cube. Re-enqueue the remaining cubes. + 2. Apply simplifications and pre-processing according to configuration. + 3. Cube using the parameter settings prescribed in m_params. + 4. Optionally pass the cubes as assumptions and solve each sub-cube with a prescribed resource bound. + 5. Assemble cubes that could not be solved and create a cube state. + +--*/ + +#include +#include +#include +#include +#include "util/scoped_ptr_vector.h" +#include "ast/ast_util.h" +#include "ast/ast_translation.h" +#include "solver/solver.h" +#include "solver/solver2tactic.h" +#include "tactic/tactic.h" +#include "tactic/tactical.h" +#include "solver/parallel_tactic.h" +#include "solver/parallel_params.hpp" + +class parallel_tactic : public tactic { + + + class solver_state; + + class task_queue { + std::mutex m_mutex; + std::condition_variable m_cond; + ptr_vector m_tasks; + ptr_vector m_active; + unsigned m_num_waiters; + volatile bool m_shutdown; + + void inc_wait() { + std::lock_guard lock(m_mutex); + ++m_num_waiters; + } + + void dec_wait() { + std::lock_guard lock(m_mutex); + --m_num_waiters; + } + + solver_state* try_get_task() { + solver_state* st = nullptr; + std::lock_guard lock(m_mutex); + if (!m_tasks.empty()) { + st = m_tasks.back(); + m_tasks.pop_back(); + m_active.push_back(st); + } + return st; + } + + public: + + task_queue(): + m_num_waiters(0), + m_shutdown(false) {} + + ~task_queue() { reset(); } + + void shutdown() { + if (!m_shutdown) { + m_shutdown = true; + m_cond.notify_all(); + std::lock_guard lock(m_mutex); + for (solver_state* st : m_active) { + st->m().limit().cancel(); + } + } + } + + bool in_shutdown() const { return m_shutdown; } + + void add_task(solver_state* task) { + std::lock_guard lock(m_mutex); + m_tasks.push_back(task); + if (m_num_waiters > 0) { + m_cond.notify_one(); + } + } + + bool is_idle() { + std::lock_guard lock(m_mutex); + return m_tasks.empty() && m_num_waiters > 0; + } + + solver_state* get_task() { + while (!m_shutdown) { + inc_wait(); + solver_state* st = try_get_task(); + if (st) { + dec_wait(); + return st; + } + { + std::unique_lock lock(m_mutex); + m_cond.wait(lock); + } + dec_wait(); + } + return nullptr; + } + + void task_done(solver_state* st) { + std::lock_guard lock(m_mutex); + m_active.erase(st); + if (m_tasks.empty() && m_active.empty()) { + m_shutdown = true; + m_cond.notify_all(); + } + } + + void reset() { + for (auto* t : m_tasks) dealloc(t); + for (auto* t : m_active) dealloc(t); + m_tasks.reset(); + m_active.reset(); + } + + std::ostream& display(std::ostream& out) { + std::lock_guard lock(m_mutex); + out << "num_tasks " << m_tasks.size() << " active: " << m_active.size() << "\n"; + for (solver_state* st : m_tasks) { + st->display(out); + } + return out; + } + + }; + + class cube_var { + expr_ref_vector m_vars; + expr_ref_vector m_cube; + public: + cube_var(expr_ref_vector const& c, expr_ref_vector const& vs): + m_vars(vs), m_cube(c) {} + + cube_var(cube_var const& other): + m_vars(other.m_vars), m_cube(other.m_cube) {} + + cube_var operator()(ast_translation& tr) { + expr_ref_vector vars(tr(m_vars)); + expr_ref_vector cube(tr(m_cube)); + return cube_var(cube, vars); + } + + expr_ref_vector const& cube() const { return m_cube; } + expr_ref_vector const& vars() const { return m_vars; } + }; + + class solver_state { + scoped_ptr m_manager; // ownership handle to ast_manager + vector m_cubes; // set of cubes to process by task + expr_ref_vector m_asserted_cubes; // set of cubes asserted on the current solver + expr_ref_vector m_assumptions; // set of auxiliary assumptions passed in + params_ref m_params; // configuration parameters + ref m_solver; // solver state + unsigned m_depth; // number of nested calls to cubing + double m_width; // estimate of fraction of problem handled by state + + public: + solver_state(ast_manager* m, solver* s, params_ref const& p): + m_manager(m), + m_asserted_cubes(s->get_manager()), + m_assumptions(s->get_manager()), + m_params(p), + m_solver(s), + m_depth(0), + m_width(1.0) + { + } + + ast_manager& m() { return m_solver->get_manager(); } + + solver& get_solver() { return *m_solver; } + + solver* copy_solver() { return m_solver->translate(m_solver->get_manager(), m_params); } + + solver const& get_solver() const { return *m_solver; } + + void set_assumptions(ptr_vector const& asms) { + m_assumptions.append(asms.size(), asms.c_ptr()); + } + + bool has_assumptions() const { return !m_assumptions.empty(); } + + solver_state* clone() { + SASSERT(!m_cubes.empty()); + ast_manager& m = m_solver->get_manager(); + ast_manager* new_m = alloc(ast_manager, m, m.proof_mode()); + ast_translation tr(m, *new_m); + solver* s = m_solver.get()->translate(*new_m, m_params); + solver_state* st = alloc(solver_state, new_m, s, m_params); + for (auto& c : m_cubes) st->m_cubes.push_back(c(tr)); + for (expr* c : m_asserted_cubes) st->m_asserted_cubes.push_back(tr(c)); + for (expr* c : m_assumptions) st->m_assumptions.push_back(tr(c)); + st->m_depth = m_depth; + st->m_width = m_width; + return st; + } + + vector const& cubes() const { return m_cubes; } + + // remove up to n cubes from list of cubes. + vector split_cubes(unsigned n) { + vector result; + while (n-- > 0 && !m_cubes.empty()) { + result.push_back(m_cubes.back()); + m_cubes.pop_back(); + } + return result; + } + + void set_cubes(vector& c) { + m_cubes.reset(); + m_cubes.append(c); + } + + void inc_depth(unsigned inc) { m_depth += inc; } + + void inc_width(unsigned w) { m_width *= w; } + + double get_width() const { return m_width; } + + unsigned get_depth() const { return m_depth; } + + lbool simplify() { + lbool r = l_undef; + IF_VERBOSE(2, verbose_stream() << "(parallel.tactic simplify-1)\n";); + set_simplify_params(true); // retain blocked + r = get_solver().check_sat(m_assumptions); + if (r != l_undef) return r; + IF_VERBOSE(2, verbose_stream() << "(parallel.tactic simplify-2)\n";); + set_simplify_params(false); // remove blocked + r = get_solver().check_sat(m_assumptions); + return r; + } + + void assert_cube(expr_ref_vector const& cube) { + get_solver().assert_expr(cube); + m_asserted_cubes.append(cube); + } + + void set_cube_params() { + } + + void set_conquer_params() { + set_conquer_params(get_solver()); + } + + void set_conquer_params(solver& s) { + parallel_params pp(m_params); + params_ref p; + p.copy(m_params); + p.set_bool("gc.burst", true); // apply eager gc + p.set_uint("simplify.delay", 1000); // delay simplification by 1000 conflicts + p.set_bool("lookahead_simplify", false); + p.set_uint("restart.max", pp.conquer_restart_max()); + p.set_uint("inprocess.max", UINT_MAX); // base bounds on restart.max + s.updt_params(p); + } + + void set_simplify_params(bool retain_blocked) { + parallel_params pp(m_params); + params_ref p; + p.copy(m_params); + double exp = pp.simplify_exp(); + exp = std::max(exp, 1.0); + unsigned mult = static_cast(pow(exp, m_depth - 1)); + p.set_uint("inprocess.max", pp.simplify_inprocess_max() * mult); + p.set_uint("restart.max", pp.simplify_restart_max() * mult); + p.set_bool("lookahead_simplify", true); + p.set_bool("retain_blocked_clauses", retain_blocked); + if (m_depth > 1) p.set_uint("bce_delay", 0); + get_solver().updt_params(p); + } + + bool canceled() { + return m().canceled(); + } + + std::ostream& display(std::ostream& out) { + out << ":depth " << m_depth << " :width " << m_width << "\n"; + out << ":asserted " << m_asserted_cubes.size() << "\n"; + return out; + } + }; + +private: + + solver_ref m_solver; + ast_manager& m_manager; + params_ref m_params; + sref_vector m_models; + expr_ref_vector m_core; + unsigned m_num_threads; + statistics m_stats; + task_queue m_queue; + std::mutex m_mutex; + double m_progress; + unsigned m_branches; + unsigned m_backtrack_frequency; + unsigned m_conquer_delay; + volatile bool m_has_undef; + bool m_allsat; + unsigned m_num_unsat; + int m_exn_code; + std::string m_exn_msg; + + void init() { + parallel_params pp(m_params); + m_num_threads = std::min((unsigned)omp_get_num_procs(), pp.threads_max()); + m_progress = 0; + m_has_undef = false; + m_allsat = false; + m_branches = 0; + m_num_unsat = 0; + m_backtrack_frequency = pp.conquer_backtrack_frequency(); + m_conquer_delay = pp.conquer_delay(); + m_exn_code = 0; + m_params.set_bool("override_incremental", true); + m_core.reset(); + } + + void log_branches(lbool status) { + IF_VERBOSE(0, verbose_stream() << "(tactic.parallel :progress " << m_progress << "% "; + if (status == l_true) verbose_stream() << ":status sat "; + if (status == l_undef) verbose_stream() << ":status unknown "; + verbose_stream() << ":closed " << m_num_unsat << " :open " << m_branches << ")\n";); + } + + void add_branches(unsigned b) { + if (b == 0) return; + { + std::lock_guard lock(m_mutex); + m_branches += b; + } + log_branches(l_false); + } + + void dec_branch() { + std::lock_guard lock(m_mutex); + --m_branches; + } + + void collect_core(expr_ref_vector const& core) { + std::lock_guard lock(m_mutex); + ast_translation tr(core.get_manager(), m_manager); + expr_ref_vector core1(tr(core)); + for (expr* c : core1) { + if (!m_core.contains(c)) m_core.push_back(c); + } + } + + void close_branch(solver_state& s, lbool status) { + double f = 100.0 / s.get_width(); + { + std::lock_guard lock(m_mutex); + m_progress += f; + --m_branches; + } + log_branches(status); + } + + void report_sat(solver_state& s) { + close_branch(s, l_true); + model_ref mdl; + s.get_solver().get_model(mdl); + if (mdl) { + std::lock_guard lock(m_mutex); + if (&s.m() != &m_manager) { + ast_translation tr(s.m(), m_manager); + mdl = mdl->translate(tr); + } + m_models.push_back(mdl.get()); + } + if (!m_allsat) { + m_queue.shutdown(); + } + } + + void inc_unsat() { + std::lock_guard lock(m_mutex); + ++m_num_unsat; + } + + void report_unsat(solver_state& s) { + inc_unsat(); + close_branch(s, l_false); + if (s.has_assumptions()) { + expr_ref_vector core(s.m()); + s.get_solver().get_unsat_core(core); + collect_core(core); + } + } + + void report_undef(solver_state& s) { + m_has_undef = true; + close_branch(s, l_undef); + } + + void cube_and_conquer(solver_state& s) { + ast_manager& m = s.m(); + vector cube, hard_cubes, cubes; + expr_ref_vector vars(m); + + cube_again: + // extract up to one cube and add it. + cube.reset(); + cube.append(s.split_cubes(1)); + SASSERT(cube.size() <= 1); + IF_VERBOSE(2, verbose_stream() << "(tactic.parallel :split-cube " << cube.size() << ")\n";); + if (!s.cubes().empty()) m_queue.add_task(s.clone()); + if (!cube.empty()) { + s.assert_cube(cube.get(0).cube()); + vars.reset(); + vars.append(cube.get(0).vars()); + } + + simplify_again: + s.inc_depth(1); + // simplify + if (canceled(s)) return; + switch (s.simplify()) { + case l_undef: break; + case l_true: report_sat(s); return; + case l_false: report_unsat(s); return; + } + if (canceled(s)) return; + + if (memory_pressure()) { + goto simplify_again; + } + // extract cubes. + cubes.reset(); + s.set_cube_params(); + solver_ref conquer; + + unsigned cutoff = UINT_MAX; + bool first = true; + unsigned num_backtracks = 0, width = 0; + while (cutoff > 0 && !canceled(s)) { + expr_ref_vector c = s.get_solver().cube(vars, cutoff); + if (c.empty()) { + goto simplify_again; + } + if (m.is_false(c.back())) { + break; + } + lbool is_sat = l_undef; + if (!s.has_assumptions() && width >= m_conquer_delay && !conquer) { + conquer = s.copy_solver(); + s.set_conquer_params(*conquer.get()); + } + if (conquer) { + is_sat = conquer->check_sat(c); + } + switch (is_sat) { + case l_false: + cutoff = c.size(); + backtrack(*conquer.get(), c, (num_backtracks++) % m_backtrack_frequency == 0); + if (cutoff != c.size()) { + IF_VERBOSE(0, verbose_stream() << "(tactic.parallel :backtrack " << cutoff << " -> " << c.size() << ")\n"); + cutoff = c.size(); + } + inc_unsat(); + log_branches(l_false); + break; + + case l_true: + report_sat(s); + if (conquer) { + collect_statistics(*conquer.get()); + } + return; + + case l_undef: + ++width; + IF_VERBOSE(2, verbose_stream() << "(tactic.parallel :cube " << c.size() << " :vars " << vars.size() << ")\n"); + cubes.push_back(cube_var(c, vars)); + cutoff = UINT_MAX; + break; + + } + if (cubes.size() >= conquer_batch_size()) { + spawn_cubes(s, 10*width, cubes); + first = false; + cubes.reset(); + } + } + + if (conquer) { + collect_statistics(*conquer.get()); + } + + if (cubes.empty() && first) { + report_unsat(s); + } + else if (cubes.empty()) { + dec_branch(); + } + else { + s.inc_width(width); + add_branches(cubes.size()-1); + s.set_cubes(cubes); + goto cube_again; + } + } + + void spawn_cubes(solver_state& s, unsigned width, vector& cubes) { + if (cubes.empty()) return; + add_branches(cubes.size()); + s.set_cubes(cubes); + solver_state* s1 = s.clone(); + s1->inc_width(width); + m_queue.add_task(s1); + } + + /* + * \brief backtrack from unsatisfiable core + */ + void backtrack(solver& s, expr_ref_vector& asms, bool full) { + ast_manager& m = s.get_manager(); + expr_ref_vector core(m); + obj_hashtable hcore; + s.get_unsat_core(core); + while (!asms.empty() && !core.contains(asms.back())) asms.pop_back(); + if (!full) return; + + //s.assert_expr(m.mk_not(mk_and(core))); + if (!asms.empty()) { + expr* last = asms.back(); + expr_ref not_last(mk_not(m, last), m); + asms.pop_back(); + asms.push_back(not_last); + lbool r = s.check_sat(asms); + asms.pop_back(); + if (r != l_false) { + asms.push_back(last); + return; + } + core.reset(); + s.get_unsat_core(core); + if (core.contains(not_last)) { + //s.assert_expr(m.mk_not(mk_and(core))); + r = s.check_sat(asms); + } + if (r == l_false) { + backtrack(s, asms, full); + } + } + } + + bool canceled(solver_state& s) { + if (s.canceled()) { + m_has_undef = true; + return true; + } + else { + return false; + } + } + + bool memory_pressure() { + return memory::above_high_watermark(); + } + + void run_solver() { + try { + while (solver_state* st = m_queue.get_task()) { + cube_and_conquer(*st); + collect_statistics(*st); + m_queue.task_done(st); + if (st->m().canceled()) m_queue.shutdown(); + IF_VERBOSE(1, display(verbose_stream());); + dealloc(st); + } + } + catch (z3_exception& ex) { + IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n";); + if (m_queue.in_shutdown()) return; + m_queue.shutdown(); + std::lock_guard lock(m_mutex); + if (ex.has_error_code()) { + m_exn_code = ex.error_code(); + } + else { + m_exn_msg = ex.msg(); + m_exn_code = -1; + } + } + } + + void collect_statistics(solver_state& s) { + collect_statistics(s.get_solver()); + } + + void collect_statistics(solver& s) { + std::lock_guard lock(m_mutex); + s.collect_statistics(m_stats); + } + + lbool solve(model_ref& mdl) { + add_branches(1); + vector threads; + for (unsigned i = 0; i < m_num_threads; ++i) + threads.push_back(std::thread([this]() { run_solver(); })); + for (std::thread& t : threads) + t.join(); + m_manager.limit().reset_cancel(); + if (m_exn_code == -1) + throw default_exception(m_exn_msg); + if (m_exn_code != 0) + throw z3_error(m_exn_code); + if (!m_models.empty()) { + mdl = m_models.back(); + return l_true; + } + if (m_has_undef) + return l_undef; + return l_false; + } + + std::ostream& display(std::ostream& out) { + unsigned n_models, n_unsat; + double n_progress; + { + std::lock_guard lock(m_mutex); + n_models = m_models.size(); + n_unsat = m_num_unsat; + n_progress = m_progress; + } + m_stats.display(out); + m_queue.display(out); + out << "(tactic.parallel :unsat " << n_unsat << " :progress " << n_progress << "% :models " << n_models << ")\n"; + return out; + } + +public: + + parallel_tactic(solver* s, params_ref const& p) : + m_solver(s), + m_manager(s->get_manager()), + m_params(p), + m_core(m_manager) { + init(); + } + + void operator ()(const goal_ref & g,goal_ref_buffer & result) { + fail_if_proof_generation("parallel-tactic", g); + ast_manager& m = g->m(); + solver* s = m_solver->translate(m, m_params); + solver_state* st = alloc(solver_state, 0, s, m_params); + m_queue.add_task(st); + expr_ref_vector clauses(m); + ptr_vector assumptions; + obj_map bool2dep; + ref fmc; + expr_dependency * lcore = nullptr; + proof* pr = nullptr; + extract_clauses_and_dependencies(g, clauses, assumptions, bool2dep, fmc); + for (expr * clause : clauses) { + s->assert_expr(clause); + } + st->set_assumptions(assumptions); + model_ref mdl; + lbool is_sat = solve(mdl); + switch (is_sat) { + case l_true: + g->reset(); + if (g->models_enabled()) { + g->add(concat(fmc.get(), model2model_converter(mdl.get()))); + } + break; + case l_false: + SASSERT(!g->proofs_enabled()); + for (expr * c : m_core) { + lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(c))); + } + g->assert_expr(m.mk_false(), pr, lcore); + break; + case l_undef: + if (m.canceled()) { + throw tactic_exception(Z3_CANCELED_MSG); + } + break; + } + result.push_back(g.get()); + } + + unsigned conquer_batch_size() const { + parallel_params pp(m_params); + return pp.conquer_batch_size(); + } + + void cleanup() { + m_queue.reset(); + } + + tactic* translate(ast_manager& m) { + solver* s = m_solver->translate(m, m_params); + return alloc(parallel_tactic, s, m_params); + } + + virtual void updt_params(params_ref const & p) { + m_params.copy(p); + parallel_params pp(p); + m_conquer_delay = pp.conquer_delay(); + } + + virtual void collect_statistics(statistics & st) const { + st.copy(m_stats); + st.update("par unsat", m_num_unsat); + st.update("par models", m_models.size()); + st.update("par progress", m_progress); + } + + virtual void reset_statistics() { + m_stats.reset(); + } +}; + + +tactic * mk_parallel_tactic(solver* s, params_ref const& p) { + return alloc(parallel_tactic, s, p); +} + diff --git a/src/solver/parallel_tactic.h b/src/solver/parallel_tactic.h new file mode 100644 index 000000000..ae8fb6041 --- /dev/null +++ b/src/solver/parallel_tactic.h @@ -0,0 +1,27 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + parallel_tactic.h + +Abstract: + + Parallel tactic in the style of Treengeling. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-9 + +Notes: + +--*/ +#ifndef PARALLEL_TACTIC_H_ +#define PARALLEL_TACTIC_H_ + +class tactic; +class solver; + +tactic * mk_parallel_tactic(solver* s, params_ref const& p); + +#endif diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index d49b3620f..4b812f83b 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -16,12 +16,14 @@ Author: Notes: --*/ -#include "solver/solver.h" -#include "model/model_evaluator.h" +#include "util/common_msgs.h" +#include "util/stopwatch.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/ast_pp_util.h" -#include "util/common_msgs.h" +#include "tactic/model_converter.h" +#include "solver/solver.h" +#include "model/model_evaluator.h" unsigned solver::get_num_assertions() const { @@ -36,12 +38,21 @@ expr * solver::get_assertion(unsigned idx) const { std::ostream& solver::display(std::ostream & out, unsigned n, expr* const* assumptions) const { expr_ref_vector fmls(get_manager()); - get_assertions(fmls); + stopwatch sw; + sw.start(); + get_assertions(fmls); ast_pp_util visitor(get_manager()); + model_converter_ref mc = get_model_converter(); + if (mc.get()) { + mc->collect(visitor); + } visitor.collect(fmls); visitor.collect(n, assumptions); visitor.display_decls(out); visitor.display_asserts(out, fmls, true); + if (mc.get()) { + mc->display(out); + } return out; } @@ -163,3 +174,72 @@ bool solver::is_literal(ast_manager& m, expr* e) { return is_uninterp_const(e) || (m.is_not(e, e) && is_uninterp_const(e)); } + +void solver::assert_expr(expr* f) { + expr_ref fml(f, get_manager()); + if (m_enforce_model_conversion) { + model_converter_ref mc = get_model_converter(); + if (mc) { + (*mc)(fml); + } + } + assert_expr_core(fml); +} + +void solver::assert_expr(expr* f, expr* t) { + ast_manager& m = get_manager(); + expr_ref fml(f, m); + expr_ref a(t, m); + if (m_enforce_model_conversion) { + IF_VERBOSE(0, verbose_stream() << "enforce model conversion\n";); + exit(0); + model_converter_ref mc = get_model_converter(); + if (mc) { + (*mc)(fml); + // (*mc)(a); + } + } + assert_expr_core2(fml, a); +} + +void solver::collect_param_descrs(param_descrs & r) { + r.insert("solver.enforce_model_conversion", CPK_BOOL, "(default: false) enforce model conversion when asserting formulas"); +} + +void solver::updt_params(params_ref const & p) { + m_params.copy(p); + m_enforce_model_conversion = m_params.get_bool("solver.enforce_model_conversion", false); +} + + +expr_ref_vector solver::get_units(ast_manager& m) { + expr_ref_vector fmls(m), result(m), tmp(m); + get_assertions(fmls); + obj_map units; + for (expr* f : fmls) { + if (m.is_not(f, f) && is_literal(m, f)) { + m.inc_ref(f); + units.insert(f, false); + } + else if (is_literal(m, f)) { + m.inc_ref(f); + units.insert(f, true); + } + } + model_converter_ref mc = get_model_converter(); + if (mc) { + mc->get_units(units); + } + for (auto const& kv : units) { + tmp.push_back(kv.m_key); + if (kv.m_value) + result.push_back(kv.m_key); + else + result.push_back(m.mk_not(kv.m_key)); + } + for (expr* e : tmp) { + m.dec_ref(e); + } + + return result; +} diff --git a/src/solver/solver.h b/src/solver/solver.h index 0b0acb644..2c056ff90 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -24,6 +24,7 @@ Notes: #include "util/params.h" class solver; +class model_converter; class solver_factory { public: @@ -44,7 +45,9 @@ public: */ class solver : public check_sat_result { params_ref m_params; + bool m_enforce_model_conversion; public: + solver(): m_enforce_model_conversion(false) {} ~solver() override {} /** @@ -55,18 +58,18 @@ public: /** \brief Update the solver internal settings. */ - virtual void updt_params(params_ref const & p) { m_params.copy(p); } + virtual void updt_params(params_ref const & p); /** \brief Retrieve set of parameters set on solver. */ - virtual params_ref const& get_params() { return m_params; } + virtual params_ref const& get_params() const { return m_params; } /** \brief Store in \c r a description of the configuration parameters available in this solver. */ - virtual void collect_param_descrs(param_descrs & r) {} + virtual void collect_param_descrs(param_descrs & r); /** \brief Enable/Disable model generation for this solver object. @@ -79,14 +82,16 @@ public: /** \brief Add a new formula to the assertion stack. */ - virtual void assert_expr(expr * t) = 0; + void assert_expr(expr* f); + + virtual void assert_expr_core(expr * t) = 0; void assert_expr(expr_ref_vector const& ts) { - for (unsigned i = 0; i < ts.size(); ++i) assert_expr(ts[i]); + for (expr* e : ts) assert_expr(e); } void assert_expr(ptr_vector const& ts) { - for (unsigned i = 0; i < ts.size(); ++i) assert_expr(ts[i]); + for (expr* e : ts) assert_expr(e); } /** @@ -94,7 +99,10 @@ public: The propositional variable \c a is used to track the use of \c t in a proof of unsatisfiability. */ - virtual void assert_expr(expr * t, expr * a) = 0; + + void assert_expr(expr * t, expr* a); + + virtual void assert_expr_core2(expr * t, expr * a) = 0; /** \brief Create a backtracking point. @@ -178,11 +186,28 @@ public: */ virtual lbool preferred_sat(expr_ref_vector const& asms, vector& cores); + /** + \brief extract a lookahead candidates for branching. + */ + + virtual expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) = 0; + /** \brief Display the content of this solver. */ virtual std::ostream& display(std::ostream & out, unsigned n = 0, expr* const* assumptions = nullptr) const; + /** + \brief expose model converter when solver produces partially reduced set of assertions. + */ + + virtual model_converter_ref get_model_converter() const { return m_mc0; } + + /** + \brief extract units from solver. + */ + expr_ref_vector get_units(ast_manager& m); + class scoped_push { solver& s; bool m_nopop; @@ -191,13 +216,17 @@ public: ~scoped_push() { if (!m_nopop) s.pop(1); } void disable_pop() { m_nopop = true; } }; + protected: virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences); + bool is_literal(ast_manager& m, expr* e); }; +typedef ref solver_ref; + #endif diff --git a/src/solver/solver2tactic.cpp b/src/solver/solver2tactic.cpp index 8bb3fdf78..68d4b11f6 100644 --- a/src/solver/solver2tactic.cpp +++ b/src/solver/solver2tactic.cpp @@ -19,13 +19,13 @@ Notes: #include "solver/solver.h" #include "tactic/tactic.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "solver/solver2tactic.h" #include "ast/ast_util.h" typedef obj_map expr2expr_map; -void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, expr2expr_map& bool2dep, ref& fmc) { +void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, expr2expr_map& bool2dep, ref& fmc) { expr2expr_map dep2bool; ptr_vector deps; ast_manager& m = g->m(); @@ -65,9 +65,9 @@ void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clause bool2dep.insert(b, d); assumptions.push_back(b); if (!fmc) { - fmc = alloc(filter_model_converter, m); + fmc = alloc(generic_model_converter, m, "solver2tactic"); } - fmc->insert(to_app(b)->get_decl()); + fmc->hide(to_app(b)->get_decl()); } clause.push_back(m.mk_not(b)); } @@ -101,16 +101,12 @@ public: m_solver->collect_param_descrs(r); } - void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) override { - pc = nullptr; mc = nullptr; core = nullptr; + void operator()(/* in */ goal_ref const & in, + /* out */ goal_ref_buffer & result) override { expr_ref_vector clauses(m); expr2expr_map bool2dep; ptr_vector assumptions; - ref fmc; + ref fmc; extract_clauses_and_dependencies(in, clauses, assumptions, bool2dep, fmc); ref local_solver = m_solver->translate(m, m_params); local_solver->assert_expr(clauses); @@ -120,37 +116,50 @@ public: if (in->models_enabled()) { model_ref mdl; local_solver->get_model(mdl); + model_converter_ref mc; mc = model2model_converter(mdl.get()); mc = concat(fmc.get(), mc.get()); + mc = concat(local_solver->mc0(), mc.get()); + in->add(mc.get()); } in->reset(); result.push_back(in.get()); break; case l_false: { in->reset(); + expr_dependency_ref lcore(m); proof* pr = nullptr; - expr_dependency* lcore = nullptr; if (in->proofs_enabled()) { pr = local_solver->get_proof(); - pc = proof2proof_converter(m, pr); + in->set(proof2proof_converter(m, pr)); } if (in->unsat_core_enabled()) { ptr_vector core; local_solver->get_unsat_core(core); - for (unsigned i = 0; i < core.size(); ++i) { - lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(core[i]))); + for (expr* c : core) { + lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(c))); } } in->assert_expr(m.mk_false(), pr, lcore); result.push_back(in.get()); - core = lcore; + in->set(dependency_converter::unit(lcore)); break; } case l_undef: if (m.canceled()) { throw tactic_exception(Z3_CANCELED_MSG); } - throw tactic_exception(local_solver->reason_unknown().c_str()); + model_converter_ref mc; + mc = local_solver->get_model_converter(); + mc = concat(fmc.get(), mc.get()); + in->reset(); + in->add(mc.get()); + unsigned sz = local_solver->get_num_assertions(); + for (unsigned i = 0; i < sz; ++i) { + in->assert_expr(local_solver->get_assertion(i)); + } + result.push_back(in.get()); + break; } local_solver->collect_statistics(m_st); } diff --git a/src/solver/solver2tactic.h b/src/solver/solver2tactic.h index 647a1cee4..8c6466ddb 100644 --- a/src/solver/solver2tactic.h +++ b/src/solver/solver2tactic.h @@ -20,11 +20,11 @@ Notes: #define SOLVER2TACTIC_H_ #include "tactic/tactic.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" class solver; tactic * mk_solver2tactic(solver* s); -void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, obj_map& bool2dep, ref& fmc); +void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, obj_map& bool2dep, ref& fmc); #endif diff --git a/src/solver/solver_na2as.cpp b/src/solver/solver_na2as.cpp index 402edc19a..f98e08cdb 100644 --- a/src/solver/solver_na2as.cpp +++ b/src/solver/solver_na2as.cpp @@ -30,9 +30,9 @@ solver_na2as::solver_na2as(ast_manager & m): solver_na2as::~solver_na2as() {} -void solver_na2as::assert_expr(expr * t, expr * a) { - if (a == nullptr) { - assert_expr(t); +void solver_na2as::assert_expr_core2(expr * t, expr * a) { + if (a == 0) { + assert_expr_core(t); } else { SASSERT(is_uninterp_const(a)); @@ -41,7 +41,7 @@ void solver_na2as::assert_expr(expr * t, expr * a) { m_assumptions.push_back(a); expr_ref new_t(m); new_t = m.mk_implies(a, t); - assert_expr(new_t); + assert_expr_core(new_t); } } diff --git a/src/solver/solver_na2as.h b/src/solver/solver_na2as.h index 0a041bd57..75fb27189 100644 --- a/src/solver/solver_na2as.h +++ b/src/solver/solver_na2as.h @@ -34,8 +34,8 @@ public: solver_na2as(ast_manager & m); ~solver_na2as() override; - void assert_expr(expr * t, expr * a) override; - void assert_expr(expr * t) override = 0; + void assert_expr_core2(expr * t, expr * a) override; + // virtual void assert_expr_core(expr * t) = 0; // Subclasses of solver_na2as should redefine the following *_core methods instead of these ones. lbool check_sat(unsigned num_assumptions, expr * const * assumptions) override; diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index d58895708..6d08b95af 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -49,7 +49,7 @@ public: m_in_delayed_scope(false), m_dump_counter(0) { if (is_virtual()) { - solver_na2as::assert_expr(m.mk_true(), pred); + solver_na2as::assert_expr_core2(m.mk_true(), pred); } } @@ -190,7 +190,7 @@ public: } } - void assert_expr(expr * e) override { + void assert_expr_core(expr * e) override { SASSERT(!m_pushed || get_scope_level() > 0); if (m.is_true(e)) return; if (m_in_delayed_scope) { @@ -211,7 +211,7 @@ public: } } - void get_model(model_ref & _m) override { m_base->get_model(_m); } + void get_model_core(model_ref & _m) override { m_base->get_model_core(_m); } expr * get_assumption(unsigned idx) const override { return solver_na2as::get_assumption(idx + is_virtual()); @@ -222,6 +222,8 @@ public: void get_labels(svector & r) override { return m_base->get_labels(r); } void set_progress_callback(progress_callback * callback) override { m_base->set_progress_callback(callback); } + expr_ref_vector cube(expr_ref_vector& vars, unsigned ) override { return expr_ref_vector(m); } + ast_manager& get_manager() const override { return m_base->get_manager(); } void refresh(solver* new_base) { diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index 5c3c6bc7a..9118bb658 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -36,6 +36,7 @@ class tactic2solver : public solver_na2as { unsigned_vector m_scopes; ref m_result; tactic_ref m_tactic; + ref m_mc; symbol m_logic; bool m_produce_models; bool m_produce_proofs; @@ -53,16 +54,16 @@ public: void set_produce_models(bool f) override { m_produce_models = f; } - void assert_expr(expr * t) override; + void assert_expr_core(expr * t) override; + ast_manager& get_manager() const override; void push_core() override; void pop_core(unsigned n) override; lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override; - void collect_statistics(statistics & st) const override; void get_unsat_core(ptr_vector & r) override; - void get_model(model_ref & m) override; + void get_model_core(model_ref & m) override; proof * get_proof() override; std::string reason_unknown() const override; void set_reason_unknown(char const* msg) override; @@ -73,7 +74,13 @@ public: unsigned get_num_assertions() const override; expr * get_assertion(unsigned idx) const override; - ast_manager& get_manager() const override; + + expr_ref_vector cube(expr_ref_vector& vars, unsigned ) override { + return expr_ref_vector(get_manager()); + } + + model_converter_ref get_model_converter() const override { return m_mc; } + }; ast_manager& tactic2solver::get_manager() const { return m_assertions.get_manager(); } @@ -103,11 +110,12 @@ void tactic2solver::collect_param_descrs(param_descrs & r) { m_tactic->collect_param_descrs(r); } -void tactic2solver::assert_expr(expr * t) { +void tactic2solver::assert_expr_core(expr * t) { m_assertions.push_back(t); m_result = nullptr; } + void tactic2solver::push_core() { m_scopes.push_back(m_assertions.size()); m_result = nullptr; @@ -142,7 +150,7 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass } model_ref md; - proof_ref pr(m); + proof_ref pr(m); expr_dependency_ref core(m); std::string reason_unknown = "unknown"; labels_vec labels; @@ -158,8 +166,14 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass m_result->set_status(l_undef); if (reason_unknown != "") m_result->m_unknown = reason_unknown; + if (num_assumptions == 0) { + m_assertions.reset(); + g->get_formulas(m_assertions); + } break; } + m_mc = g->mc(); + TRACE("tactic", if (m_mc) m_mc->display(tout);); } catch (z3_error & ex) { TRACE("tactic2solver", tout << "exception: " << ex.msg() << "\n";); @@ -211,9 +225,10 @@ void tactic2solver::get_unsat_core(ptr_vector & r) { } } -void tactic2solver::get_model(model_ref & m) { - if (m_result.get()) - m_result->get_model(m); +void tactic2solver::get_model_core(model_ref & m) { + if (m_result.get()) { + m_result->get_model_core(m); + } } proof * tactic2solver::get_proof() { diff --git a/src/tactic/CMakeLists.txt b/src/tactic/CMakeLists.txt index e7cfdb644..c9554b76a 100644 --- a/src/tactic/CMakeLists.txt +++ b/src/tactic/CMakeLists.txt @@ -1,8 +1,8 @@ z3_add_component(tactic SOURCES + dependency_converter.cpp equiv_proof_converter.cpp - extension_model_converter.cpp - filter_model_converter.cpp + generic_model_converter.cpp goal.cpp goal_num_occurs.cpp goal_shared_occs.cpp diff --git a/src/tactic/aig/aig.cpp b/src/tactic/aig/aig.cpp index 6afac32b8..89196808f 100644 --- a/src/tactic/aig/aig.cpp +++ b/src/tactic/aig/aig.cpp @@ -635,10 +635,8 @@ struct aig_manager::imp { } bool check_cache() const { - obj_map::iterator it = m_cache.begin(); - obj_map::iterator end = m_cache.end(); - for (; it != end; ++it) { - SASSERT(ref_count(it->m_value) > 0); + for (auto const& kv : m_cache) { + VERIFY(ref_count(kv.m_value) > 0); } return true; } @@ -1522,7 +1520,7 @@ public: } SASSERT(ref_count(r) >= 1); } - catch (aig_exception ex) { + catch (const aig_exception & ex) { dec_ref(r); throw ex; } diff --git a/src/tactic/aig/aig_tactic.cpp b/src/tactic/aig/aig_tactic.cpp index 391d4b412..720c799e0 100644 --- a/src/tactic/aig/aig_tactic.cpp +++ b/src/tactic/aig/aig_tactic.cpp @@ -67,7 +67,6 @@ public: void operator()(goal_ref const & g) { SASSERT(g->is_well_sorted()); - tactic_report report("aig", *g); mk_aig_manager mk(*this, g->m()); if (m_aig_per_assertion) { @@ -90,15 +89,12 @@ public: SASSERT(g->is_well_sorted()); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & g, goal_ref_buffer & result) override { fail_if_proof_generation("aig", g); - mc = nullptr; pc = nullptr; core = nullptr; + tactic_report report("aig", *g); operator()(g); g->inc_depth(); + TRACE("aig", g->display(tout);); result.push_back(g.get()); } diff --git a/src/tactic/arith/CMakeLists.txt b/src/tactic/arith/CMakeLists.txt index 335020838..cb025b206 100644 --- a/src/tactic/arith/CMakeLists.txt +++ b/src/tactic/arith/CMakeLists.txt @@ -9,7 +9,6 @@ z3_add_component(arith_tactics card2bv_tactic.cpp degree_shift_tactic.cpp diff_neq_tactic.cpp - elim01_tactic.cpp eq2bv_tactic.cpp factor_tactic.cpp fix_dl_var_tactic.cpp @@ -33,7 +32,6 @@ z3_add_component(arith_tactics card2bv_tactic.h degree_shift_tactic.h diff_neq_tactic.h - elim01_tactic.h eq2bv_tactic.h factor_tactic.h fix_dl_var_tactic.h diff --git a/src/tactic/arith/add_bounds_tactic.cpp b/src/tactic/arith/add_bounds_tactic.cpp index 5d64fff95..b1367ea21 100644 --- a/src/tactic/arith/add_bounds_tactic.cpp +++ b/src/tactic/arith/add_bounds_tactic.cpp @@ -111,11 +111,7 @@ class add_bounds_tactic : public tactic { }; void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = nullptr; pc = nullptr; core = nullptr; + goal_ref_buffer & result) { tactic_report report("add-bounds", *g); bound_manager bm(m); expr_fast_mark1 visited; @@ -160,12 +156,9 @@ public: r.insert("add_bound_upper", CPK_NUMERAL, "(default: 2) upper bound to be added to unbounded variables."); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(g, result, mc, pc, core); + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { + (*m_imp)(g, result); } void cleanup() override { diff --git a/src/tactic/arith/arith_bounds_tactic.cpp b/src/tactic/arith/arith_bounds_tactic.cpp index bf5381a5b..e4af53f23 100644 --- a/src/tactic/arith/arith_bounds_tactic.cpp +++ b/src/tactic/arith/arith_bounds_tactic.cpp @@ -23,11 +23,8 @@ struct arith_bounds_tactic : public tactic { ast_manager& get_manager() { return m; } - void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) override { + void operator()(/* in */ goal_ref const & in, + /* out */ goal_ref_buffer & result) override { bounds_arith_subsumption(in, result); } diff --git a/src/tactic/arith/bound_manager.cpp b/src/tactic/arith/bound_manager.cpp index 045d49f5b..7f662810b 100644 --- a/src/tactic/arith/bound_manager.cpp +++ b/src/tactic/arith/bound_manager.cpp @@ -28,6 +28,19 @@ bound_manager::~bound_manager() { reset(); } +bound_manager* bound_manager::translate(ast_manager& dst_m) { + bound_manager* result = alloc(bound_manager, dst_m); + ast_translation tr(m(), dst_m); + expr_dependency_translation edtr(tr); + for (auto& kv : m_lowers) result->m_lowers.insert(tr(kv.m_key), kv.m_value); + for (auto& kv : m_uppers) result->m_uppers.insert(tr(kv.m_key), kv.m_value); + for (auto& kv : m_lower_deps) result->m_lower_deps.insert(tr(kv.m_key), edtr(kv.m_value)); + for (auto& kv : m_upper_deps) result->m_upper_deps.insert(tr(kv.m_key), edtr(kv.m_value)); + for (expr* e : m_bounded_vars) result->m_bounded_vars.push_back(tr(e)); + return result; +} + + static decl_kind swap_decl(decl_kind k) { switch (k) { case OP_LE: return OP_GE; diff --git a/src/tactic/arith/bound_manager.h b/src/tactic/arith/bound_manager.h index 54a80ce5d..5a22778d4 100644 --- a/src/tactic/arith/bound_manager.h +++ b/src/tactic/arith/bound_manager.h @@ -47,6 +47,8 @@ public: bound_manager(ast_manager & m); ~bound_manager(); + bound_manager* translate(ast_manager& dst_m); + ast_manager & m() const { return m_util.get_manager(); } void operator()(goal const & g); diff --git a/src/tactic/arith/card2bv_tactic.cpp b/src/tactic/arith/card2bv_tactic.cpp index b86628f0b..97649cc2f 100644 --- a/src/tactic/arith/card2bv_tactic.cpp +++ b/src/tactic/arith/card2bv_tactic.cpp @@ -23,7 +23,7 @@ Notes: #include "ast/rewriter/pb2bv_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" class card2bv_tactic : public tactic { ast_manager & m; @@ -47,18 +47,16 @@ public: m_params = p; } - void collect_param_descrs(param_descrs & r) override { + void collect_param_descrs(param_descrs & r) override { + r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: true) retain cardinality constraints for solver"); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { TRACE("card2bv-before", g->display(tout);); SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); tactic_report report("card2bv", *g); th_rewriter rw1(m, m_params); pb2bv_rewriter rw2(m, m_params); @@ -73,7 +71,7 @@ public: for (unsigned idx = 0; !g->inconsistent() && idx < g->size(); idx++) { rw1(g->form(idx), new_f1, new_pr1); TRACE("card2bv", tout << "Rewriting " << mk_ismt2_pp(new_f1.get(), m) << std::endl;); - rw2(new_f1, new_f2, new_pr2); + rw2(false, new_f1, new_f2, new_pr2); if (m.proofs_enabled()) { new_pr1 = m.mk_modus_ponens(g->pr(idx), new_pr1); new_pr1 = m.mk_modus_ponens(new_pr1, new_pr2); @@ -88,11 +86,9 @@ public: func_decl_ref_vector const& fns = rw2.fresh_constants(); if (!fns.empty()) { - filter_model_converter* filter = alloc(filter_model_converter, m); - for (unsigned i = 0; i < fns.size(); ++i) { - filter->insert(fns[i]); - } - mc = filter; + generic_model_converter* filter = alloc(generic_model_converter, m, "card2bv"); + for (func_decl* f : fns) filter->hide(f); + g->add(filter); } g->inc_depth(); diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h index e11c78048..5e2051b50 100644 --- a/src/tactic/arith/card2bv_tactic.h +++ b/src/tactic/arith/card2bv_tactic.h @@ -34,8 +34,8 @@ namespace pb { class card2bv_rewriter { public: - typedef expr* literal; - typedef ptr_vector literal_vector; + typedef expr* pliteral; + typedef ptr_vector pliteral_vector; private: ast_manager& m; arith_util au; @@ -54,7 +54,7 @@ namespace pb { bool is_and(func_decl* f); bool is_atmost1(func_decl* f, unsigned sz, expr * const* args, expr_ref& result); expr_ref mk_atmost1(unsigned sz, expr * const* args); - void mk_at_most_1_small(bool last, unsigned n, literal const* xs, expr_ref_vector& result, expr_ref_vector& ors); + void mk_at_most_1_small(bool last, unsigned n, pliteral const* xs, expr_ref_vector& result, expr_ref_vector& ors); public: card2bv_rewriter(ast_manager& m); @@ -62,15 +62,15 @@ namespace pb { void mk_assert(func_decl * f, unsigned sz, expr * const* args, expr_ref & result, expr_ref_vector& lemmas); // definitions used for sorting network - literal mk_false() { return m.mk_false(); } - literal mk_true() { return m.mk_true(); } - literal mk_max(literal a, literal b) { return trail(m.mk_or(a, b)); } - literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } - literal mk_not(literal a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } - std::ostream& pp(std::ostream& out, literal lit); - literal fresh(); - literal trail(literal l); - void mk_clause(unsigned n, literal const* lits); + pliteral mk_false() { return m.mk_false(); } + pliteral mk_true() { return m.mk_true(); } + pliteral mk_max(pliteral a, pliteral b) { return trail(m.mk_or(a, b)); } + pliteral mk_min(pliteral a, pliteral b) { return trail(m.mk_and(a, b)); } + pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } + std::ostream& pp(std::ostream& out, pliteral lit); + pliteral fresh(); + pliteral trail(pliteral l); + void mk_clause(unsigned n, pliteral const* lits); }; diff --git a/src/tactic/arith/degree_shift_tactic.cpp b/src/tactic/arith/degree_shift_tactic.cpp index 59e83908f..c15285703 100644 --- a/src/tactic/arith/degree_shift_tactic.cpp +++ b/src/tactic/arith/degree_shift_tactic.cpp @@ -20,8 +20,7 @@ Revision History: --*/ #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "util/cooperate.h" #include "ast/arith_decl_plugin.h" #include "tactic/core/simplify_tactic.h" @@ -127,9 +126,7 @@ class degree_shift_tactic : public tactic { void visit_args(expr * t, expr_fast_mark1 & visited) { if (is_app(t)) { - unsigned num_args = to_app(t)->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = to_app(t)->get_arg(i); + for (expr * arg : *to_app(t)) { save_degree(arg, m_one); visit(arg, visited); } @@ -166,11 +163,9 @@ class degree_shift_tactic : public tactic { void display_candidates(std::ostream & out) { out << "candidates:\n"; - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - if (!it->m_value.is_one()) { - out << "POWER: " << it->m_value << "\n" << mk_ismt2_pp(it->m_key, m) << "\n"; + for (auto const& kv : m_var2degree) { + if (!kv.m_value.is_one()) { + out << "POWER: " << kv.m_value << "\n" << mk_ismt2_pp(kv.m_key, m) << "\n"; } } } @@ -189,63 +184,52 @@ class degree_shift_tactic : public tactic { void discard_non_candidates() { m_pinned.reset(); ptr_vector to_delete; - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - if (it->m_value.is_one()) - to_delete.push_back(it->m_key); + for (auto const& kv : m_var2degree) { + if (kv.m_value.is_one()) + to_delete.push_back(kv.m_key); else - m_pinned.push_back(it->m_key); // make sure it is not deleted during simplifications + m_pinned.push_back(kv.m_key); // make sure it is not deleted during simplifications } - ptr_vector::iterator it2 = to_delete.begin(); - ptr_vector::iterator end2 = to_delete.end(); - for (; it2 != end2; ++it2) - m_var2degree.erase(*it2); + for (app* a : to_delete) + m_var2degree.erase(a); } void prepare_substitution(model_converter_ref & mc) { SASSERT(!m_var2degree.empty()); - filter_model_converter * fmc = nullptr; - extension_model_converter * xmc = nullptr; + generic_model_converter * xmc = nullptr; if (m_produce_models) { - fmc = alloc(filter_model_converter, m); - xmc = alloc(extension_model_converter, m); - mc = concat(fmc, xmc); + xmc = alloc(generic_model_converter, m, "degree_shift"); + mc = xmc; } - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - SASSERT(it->m_value.is_int()); - SASSERT(it->m_value >= rational(2)); - app * fresh = m.mk_fresh_const(nullptr, it->m_key->get_decl()->get_range()); + for (auto const& kv : m_var2degree) { + SASSERT(kv.m_value.is_int()); + SASSERT(kv.m_value >= rational(2)); + app * fresh = m.mk_fresh_const(0, kv.m_key->get_decl()->get_range()); m_pinned.push_back(fresh); - m_var2var.insert(it->m_key, fresh); + m_var2var.insert(kv.m_key, fresh); if (m_produce_models) { - fmc->insert(fresh->get_decl()); - xmc->insert(it->m_key->get_decl(), mk_power(fresh, rational(1)/it->m_value)); + xmc->hide(fresh->get_decl()); + xmc->add(kv.m_key->get_decl(), mk_power(fresh, rational(1)/kv.m_value)); } if (m_produce_proofs) { - expr * s = mk_power(it->m_key, it->m_value); + expr * s = mk_power(kv.m_key, kv.m_value); expr * eq = m.mk_eq(fresh, s); proof * pr1 = m.mk_def_intro(eq); proof * result_pr = m.mk_apply_def(fresh, s, pr1); m_pinned.push_back(result_pr); - m_var2pr.insert(it->m_key, result_pr); + m_var2pr.insert(kv.m_key, result_pr); } } } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; m_produce_proofs = g->proofs_enabled(); m_produce_models = g->models_enabled(); tactic_report report("degree_shift", *g); collect(*g); + model_converter_ref mc; discard_non_candidates(); if (!m_var2degree.empty()) { prepare_substitution(mc); @@ -267,17 +251,15 @@ class degree_shift_tactic : public tactic { } // add >= 0 constraints for variables with even degree - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - SASSERT(it->m_value.is_int()); - SASSERT(it->m_value >= rational(2)); - if (it->m_value.is_even()) { - app * new_var = m_var2var.find(it->m_key); + for (auto const& kv : m_var2degree) { + SASSERT(kv.m_value.is_int()); + SASSERT(kv.m_value >= rational(2)); + if (kv.m_value.is_even()) { + app * new_var = m_var2var.find(kv.m_key); app * new_c = m_autil.mk_ge(new_var, m_autil.mk_numeral(rational(0), false)); proof * new_pr = nullptr; if (m_produce_proofs) { - proof * pr = m_var2pr.find(it->m_key); + proof * pr = m_var2pr.find(kv.m_key); new_pr = m.mk_th_lemma(m_autil.get_family_id(), new_c, 1, &pr); } g->assert_expr(new_c, new_pr, nullptr); @@ -285,6 +267,7 @@ class degree_shift_tactic : public tactic { } } g->inc_depth(); + g->add(mc.get()); result.push_back(g.get()); TRACE("degree_shift", g->display(tout); if (mc) mc->display(tout);); SASSERT(g->is_well_sorted()); @@ -305,12 +288,9 @@ public: dealloc(m_imp); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/tactic/arith/diff_neq_tactic.cpp b/src/tactic/arith/diff_neq_tactic.cpp index 13f30e19d..a7907b24a 100644 --- a/src/tactic/arith/diff_neq_tactic.cpp +++ b/src/tactic/arith/diff_neq_tactic.cpp @@ -313,13 +313,10 @@ class diff_neq_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); m_produce_models = g->models_enabled(); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); tactic_report report("diff-neq", *g); fail_if_proof_generation("diff-neq", g); fail_if_unsat_core_generation("diff-neq", g); @@ -332,8 +329,9 @@ class diff_neq_tactic : public tactic { bool r = search(); report_tactic_progress(":conflicts", m_num_conflicts); if (r) { - if (m_produce_models) - mc = model2model_converter(mk_model()); + if (m_produce_models) { + g->add(model2model_converter(mk_model())); + } g->reset(); } else { @@ -383,12 +381,9 @@ public: \brief Fix a DL variable in s to 0. If s is not really in the difference logic fragment, then this is a NOOP. */ - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/tactic/arith/elim01_tactic.cpp b/src/tactic/arith/elim01_tactic.cpp deleted file mode 100644 index 46c6f1e1c..000000000 --- a/src/tactic/arith/elim01_tactic.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - elim01_tactic.cpp - -Abstract: - - Replace 0-1 integer variables by Booleans. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-12-7 - -Notes: - ---*/ -#include "tactic/tactical.h" -#include "util/cooperate.h" -#include "tactic/arith/bound_manager.h" -#include "ast/ast_pp.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "ast/arith_decl_plugin.h" -#include "tactic/arith/elim01_tactic.h" -#include "model/model_smt2_pp.h" -#include "ast/rewriter/th_rewriter.h" - -class bool2int_model_converter : public model_converter { - ast_manager& m; - arith_util a; - func_decl_ref_vector m_refs; - obj_hashtable m_bools; - vector > m_nums_as_bool; - ptr_vector m_nums_as_int; -public: - - bool2int_model_converter(ast_manager& m): - m(m), - a(m), - m_refs(m) - {} - - void operator()(model_ref & old_model, unsigned goal_idx) override { - SASSERT(goal_idx == 0); - model * new_model = alloc(model, m); - unsigned num = old_model->get_num_constants(); - for (unsigned i = 0; i < m_nums_as_int.size(); ++i) { - func_decl* f_old = m_nums_as_int[i]; - rational val(0); - rational po(1); - bool is_value = true; - for (unsigned j = 0; is_value && j < m_nums_as_bool[i].size(); ++j) { - func_decl* f = m_nums_as_bool[i][j]; - expr* fi = old_model->get_const_interp(f); - if (!fi) { - is_value = false; - } - else if (m.is_true(fi)) { - val += po; - } - else if (!m.is_false(fi)) { - is_value = false; - } - po *= rational(2); - } - if (is_value) { - expr* fi = a.mk_numeral(val, true); - new_model->register_decl(f_old, fi); - } - } - for (unsigned i = 0; i < num; ++i) { - func_decl* f = old_model->get_constant(i); - expr* fi = old_model->get_const_interp(f); - if (!m_bools.contains(f)) { - new_model->register_decl(f, fi); - } - } - num = old_model->get_num_functions(); - for (unsigned i = 0; i < num; i++) { - func_decl * f = old_model->get_function(i); - func_interp * fi = old_model->get_func_interp(f); - new_model->register_decl(f, fi->copy()); - } - new_model->copy_usort_interps(*old_model); - old_model = new_model; - } - - void insert(func_decl* x_new, func_decl* x_old) { - m_refs.push_back(x_new); - m_refs.push_back(x_old); - m_bools.insert(x_new); - m_nums_as_int.push_back(x_old); - m_nums_as_bool.push_back(ptr_vector()); - m_nums_as_bool.back().push_back(x_new); - } - - void insert(func_decl* x_old, unsigned sz, func_decl * const* x_new) { - m_nums_as_int.push_back(x_old); - m_nums_as_bool.push_back(ptr_vector()); - m_refs.push_back(x_old); - for (unsigned i = 0; i < sz; ++i) { - m_refs.push_back(x_new[i]); - m_nums_as_bool.back().push_back(x_new[i]); - m_bools.insert(x_new[i]); - } - } - - model_converter * translate(ast_translation & translator) override { - bool2int_model_converter* mc = alloc(bool2int_model_converter, translator.to()); - for (unsigned i = 0; i < m_nums_as_int.size(); ++i) { - mc->insert(m_nums_as_int[i], m_nums_as_bool[i].size(), m_nums_as_bool[i].c_ptr()); - } - return mc; - } -}; - - -class elim01_tactic : public tactic { -public: - typedef obj_hashtable expr_set; - ast_manager & m; - arith_util a; - th_rewriter m_rewriter; - params_ref m_params; - unsigned m_max_hi_default; - rational m_max_hi; - - elim01_tactic(ast_manager & _m, params_ref const & p): - m(_m), - a(m), - m_rewriter(m), - m_max_hi_default(8), - m_max_hi(rational(m_max_hi_default)) { - } - - ~elim01_tactic() override { - } - - void updt_params(params_ref const & p) override { - m_max_hi = rational(p.get_uint("max_coefficient", m_max_hi_default)); - m_params = p; - } - - void collect_param_descrs(param_descrs & r) override { - r.insert("max_coefficient", CPK_UINT, "(default: 1) maximal upper bound for finite range -> Bool conversion"); - } - - - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; - - tactic_report report("elim01", *g); - - expr_safe_replace sub(m); - bool2int_model_converter* b2i = alloc(bool2int_model_converter, m); - mc = b2i; - bound_manager bounds(m); - expr_ref_vector axioms(m); - bounds(*g); - - rational zero(0); - bound_manager::iterator bit = bounds.begin(), bend = bounds.end(); - for (; bit != bend; ++bit) { - if (!is_app(*bit)) continue; - app* x = to_app(*bit); - bool s1 = false, s2 = false; - rational lo, hi; - if (a.is_int(x) && - bounds.has_lower(x, lo, s1) && !s1 && zero <= lo && - bounds.has_upper(x, hi, s2) && !s2 && hi <= m_max_hi && lo <= hi) { - add_variable(b2i, sub, x, lo.get_unsigned(), hi.get_unsigned(), axioms); - } - else if (a.is_int(x)) { - TRACE("pb", tout << "Not adding variable " << mk_pp(x, m) << " has lower: " - << bounds.has_lower(x, lo, s1) << " " << lo << " has upper: " - << bounds.has_upper(x, hi, s2) << " " << hi << "\n";); - } - } - - if (sub.empty()) { - result.push_back(g.get()); - return; - } - - expr_ref new_curr(m), tmp_curr(m); - proof_ref new_pr(m); - for (unsigned i = 0; i < g->size(); i++) { - expr * curr = g->form(i); - sub(curr, tmp_curr); - m_rewriter(tmp_curr, new_curr); - if (m.proofs_enabled()) { - new_pr = m.mk_rewrite(curr, new_curr); - new_pr = m.mk_modus_ponens(g->pr(i), new_pr); - } - g->update(i, new_curr, new_pr, g->dep(i)); - } - for (unsigned i = 0; i < axioms.size(); ++i) { - g->assert_expr(axioms[i].get()); - } - g->inc_depth(); - result.push_back(g.get()); - TRACE("pb", g->display(tout);); - SASSERT(g->is_well_sorted()); - - // TBD: support proof conversion (or not..) - } - - tactic * translate(ast_manager & m) override { - return alloc(elim01_tactic, m, m_params); - } - - void cleanup() override {} - - void add_variable(bool2int_model_converter* b2i, - expr_safe_replace& sub, - app* x, - unsigned min_value, - unsigned max_value, - expr_ref_vector& axioms) { - std::string name = x->get_decl()->get_name().str(); - unsigned sh = 0; - app_ref_vector xs(m), ites(m); - func_decl_ref_vector xfs(m); - app_ref zero(m), sum(m); - zero = a.mk_numeral(rational(0), true); - while (max_value >= (1ul << sh)) { - xs.push_back(m.mk_fresh_const(name.c_str(), m.mk_bool_sort())); - xfs.push_back(xs.back()->get_decl()); - ites.push_back(m.mk_ite(xs.back(), a.mk_numeral(rational(1 << sh), true), zero)); - ++sh; - } - switch (ites.size()) { - case 0: - sum = zero; - break; - case 1: - sum = ites[0].get(); - break; - default: - sum = a.mk_add(ites.size(), (expr*const*)ites.c_ptr()); - break; - } - TRACE("pb", tout << mk_pp(x, m) << " " << sum << " max: " << max_value << "\n";); - - sub.insert(x, sum); - b2i->insert(x->get_decl(), xfs.size(), xfs.c_ptr()); - // if max_value+1 is not a power of two: - if ((max_value & (max_value + 1)) != 0) { - axioms.push_back(a.mk_le(sum, a.mk_numeral(rational(max_value), true))); - } - if (min_value > 0) { - axioms.push_back(a.mk_ge(sum, a.mk_numeral(rational(min_value), true))); - } - } - -}; - -tactic * mk_elim01_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(elim01_tactic, m, p)); -} - diff --git a/src/tactic/arith/elim01_tactic.h b/src/tactic/arith/elim01_tactic.h deleted file mode 100644 index d9cd3a2ed..000000000 --- a/src/tactic/arith/elim01_tactic.h +++ /dev/null @@ -1,33 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - elim01_tactic.h - -Abstract: - - Replace 0-1 integer variables by Booleans. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-12-7 - -Notes: - ---*/ -#ifndef ELIM01_TACTIC_H_ -#define ELIM01_TACTIC_H_ - -#include "util/params.h" -class ast_manager; -class tactic; - -tactic * mk_elim01_tactic(ast_manager & m, params_ref const & p = params_ref()); - -/* - ADD_TACTIC("elim01", "eliminate 0-1 integer variables, replace them by Booleans.", "mk_elim01_tactic(m, p)") -*/ - - -#endif diff --git a/src/tactic/arith/eq2bv_tactic.cpp b/src/tactic/arith/eq2bv_tactic.cpp index 2a1af28fc..616ba69c9 100644 --- a/src/tactic/arith/eq2bv_tactic.cpp +++ b/src/tactic/arith/eq2bv_tactic.cpp @@ -107,12 +107,20 @@ class eq2bv_tactic : public tactic { model_converter* translate(ast_translation & translator) override { bvmc* v = alloc(bvmc); - obj_map::iterator it = m_map.begin(), end = m_map.end(); - for (; it != end; ++it) { - v->m_map.insert(translator(it->m_key), translator(it->m_value)); + for (auto const& kv : m_map) { + v->m_map.insert(translator(kv.m_key), translator(kv.m_value)); } return v; } + + void display(std::ostream & out) override { + for (auto const& kv : m_map) { + out << "(model-set " << kv.m_key->get_name() << " " << kv.m_value->get_name() << ")\n"; + } + } + + void get_units(obj_map& units) override { units.reset(); } + }; public: @@ -143,13 +151,8 @@ public: void updt_params(params_ref const & p) override { } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & g, goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; m_trail.reset(); m_fd.reset(); m_max.reset(); @@ -205,7 +208,7 @@ public: } } g->inc_depth(); - mc = mc1.get(); + g->add(mc1.get()); result.push_back(g.get()); TRACE("pb", g->display(tout);); SASSERT(g->is_well_sorted()); diff --git a/src/tactic/arith/factor_tactic.cpp b/src/tactic/arith/factor_tactic.cpp index d65d3abaf..0ad246852 100644 --- a/src/tactic/arith/factor_tactic.cpp +++ b/src/tactic/arith/factor_tactic.cpp @@ -257,12 +257,8 @@ class factor_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("factor", *g); bool produce_proofs = g->proofs_enabled(); @@ -313,12 +309,9 @@ public: } void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (z3_error & ex) { throw ex; diff --git a/src/tactic/arith/fix_dl_var_tactic.cpp b/src/tactic/arith/fix_dl_var_tactic.cpp index a9331c9a2..669ded49d 100644 --- a/src/tactic/arith/fix_dl_var_tactic.cpp +++ b/src/tactic/arith/fix_dl_var_tactic.cpp @@ -23,7 +23,7 @@ Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" @@ -249,12 +249,8 @@ class fix_dl_var_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("fix-dl-var", *g); bool produce_proofs = g->proofs_enabled(); m_produce_models = g->models_enabled(); @@ -270,9 +266,9 @@ class fix_dl_var_tactic : public tactic { m_rw.set_substitution(&subst); if (m_produce_models) { - extension_model_converter * _mc = alloc(extension_model_converter, m); - _mc->insert(var->get_decl(), zero); - mc = _mc; + generic_model_converter * mc = alloc(generic_model_converter, m, "fix_dl"); + mc->add(var, zero); + g->add(mc); } expr_ref new_curr(m); @@ -320,13 +316,10 @@ public: th_rewriter::get_param_descrs(r); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); diff --git a/src/tactic/arith/fm_tactic.cpp b/src/tactic/arith/fm_tactic.cpp index 882836d3f..679adb2dd 100644 --- a/src/tactic/arith/fm_tactic.cpp +++ b/src/tactic/arith/fm_tactic.cpp @@ -180,7 +180,9 @@ class fm_tactic : public tactic { m_clauses.back().swap(c); } - void operator()(model_ref & md, unsigned goal_idx) override { + void get_units(obj_map& units) override { units.reset(); } + + void operator()(model_ref & md) override { TRACE("fm_mc", model_v2_pp(tout, *md); display(tout);); model_evaluator ev(*(md.get())); ev.set_model_completion(true); @@ -1550,12 +1552,8 @@ class fm_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("fm", *g); fail_if_proof_generation("fm", g); m_produce_models = g->models_enabled(); @@ -1603,7 +1601,7 @@ class fm_tactic : public tactic { report_tactic_progress(":fm-cost", m_counter); if (!m_inconsistent) { copy_remaining(); - mc = m_mc.get(); + m_new_goal->add(concat(g->mc(), m_mc.get())); } } reset_constraints(); @@ -1674,12 +1672,9 @@ public: dealloc(d); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } }; diff --git a/src/tactic/arith/lia2card_tactic.cpp b/src/tactic/arith/lia2card_tactic.cpp index 7f005b4b3..ec18ae2bf 100644 --- a/src/tactic/arith/lia2card_tactic.cpp +++ b/src/tactic/arith/lia2card_tactic.cpp @@ -16,35 +16,29 @@ Author: Notes: --*/ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - lia2card_tactic.cpp - -Abstract: - - Convert 0-1 integer variables cardinality constraints to built-in cardinality operator. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-11-5 - -Notes: - ---*/ -#include "tactic/tactical.h" #include "util/cooperate.h" -#include "tactic/arith/bound_manager.h" #include "ast/ast_pp.h" #include "ast/pb_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" +#include "ast/rewriter/expr_safe_replace.h" #include "ast/ast_util.h" #include "ast/ast_pp_util.h" +#include "tactic/tactical.h" +#include "tactic/arith/bound_manager.h" +#include "tactic/generic_model_converter.h" class lia2card_tactic : public tactic { + + struct bound { + unsigned m_lo; + unsigned m_hi; + expr* m_expr; + bound(unsigned lo, unsigned hi, expr* b): + m_lo(lo), m_hi(hi), m_expr(b) {} + bound(): m_lo(0), m_hi(0), m_expr(0) {} + }; + struct lia_rewriter_cfg : public default_rewriter_cfg { ast_manager& m; lia2card_tactic& t; @@ -58,7 +52,7 @@ class lia2card_tactic : public tactic { coeffs.reset(); coeff.reset(); return - t.get_pb_sum(x, rational::one(), args, coeffs, coeff) && + t.get_pb_sum(x, rational::one(), args, coeffs, coeff) && t.get_pb_sum(y, -rational::one(), args, coeffs, coeff); } @@ -91,21 +85,6 @@ class lia2card_tactic : public tactic { } TRACE("pbsum", tout << expr_ref(m.mk_app(f, sz, es), m) << " ==>\n" << result << "\n";); -#if 0 - expr_ref vc(m); - vc = m.mk_not(m.mk_eq(m.mk_app(f, sz, es), result)); - ast_pp_util pp(m); - pp.collect(vc); - std::cout - << "(push)\n" - << "(echo \"" << result << "\")\n" - ; - pp.display_decls(std::cout); - std::cout - << "(assert " << vc << ")\n" - << "(check-sat)\n" - << "(pop)\n"; -#endif return BR_DONE; } @@ -128,15 +107,17 @@ class lia2card_tactic : public tactic { }; public: - typedef obj_hashtable expr_set; + typedef obj_map bounds_map; ast_manager & m; arith_util a; lia_rewriter m_rw; params_ref m_params; pb_util m_pb; mutable ptr_vector* m_todo; - expr_set* m_01s; + bounds_map m_bounds; bool m_compile_equality; + unsigned m_max_ub; + ref m_mc; lia2card_tactic(ast_manager & _m, params_ref const & p): m(_m), @@ -144,107 +125,91 @@ public: m_rw(*this), m_pb(m), m_todo(alloc(ptr_vector)), - m_01s(alloc(expr_set)), - m_compile_equality(false) { + m_compile_equality(true) { + m_max_ub = 100; } ~lia2card_tactic() override { dealloc(m_todo); - dealloc(m_01s); } void updt_params(params_ref const & p) override { m_params = p; - m_compile_equality = p.get_bool("compile_equality", false); + m_compile_equality = p.get_bool("compile_equality", true); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + expr_ref mk_bounded(expr_ref_vector& axioms, app* x, unsigned lo, unsigned hi) { + expr_ref_vector xs(m); + expr_ref last_v(m); + if (!m_mc) m_mc = alloc(generic_model_converter, m, "lia2card"); + if (hi == 0) { + return expr_ref(a.mk_int(0), m); + } + if (lo > 0) { + xs.push_back(a.mk_int(lo)); + } + for (unsigned i = lo; i < hi; ++i) { + std::string name(x->get_decl()->get_name().str()); + expr_ref v(m.mk_fresh_const(name.c_str(), m.mk_bool_sort()), m); + if (last_v) axioms.push_back(m.mk_implies(v, last_v)); + xs.push_back(m.mk_ite(v, a.mk_int(1), a.mk_int(0))); + m_mc->hide(v); + last_v = v; + } + expr* r = a.mk_add(xs.size(), xs.c_ptr()); + m_mc->add(x->get_decl(), r); + return expr_ref(r, m); + } + + void operator()(goal_ref const & g, goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; - m_01s->reset(); + m_bounds.reset(); + m_mc.reset(); + expr_ref_vector axioms(m); + expr_safe_replace rep(m); - tactic_report report("cardinality-intro", *g); + tactic_report report("lia2card", *g); bound_manager bounds(m); bounds(*g); - - bound_manager::iterator bit = bounds.begin(), bend = bounds.end(); - for (; bit != bend; ++bit) { - expr* x = *bit; + for (expr* x : bounds) { bool s1 = false, s2 = false; rational lo, hi; if (a.is_int(x) && - bounds.has_lower(x, lo, s1) && !s1 && lo.is_zero() && - bounds.has_upper(x, hi, s2) && !s2 && hi.is_one()) { - m_01s->insert(x); + is_uninterp_const(x) && + bounds.has_lower(x, lo, s1) && !s1 && lo.is_unsigned() && + bounds.has_upper(x, hi, s2) && !s2 && hi.is_unsigned() && hi.get_unsigned() - lo.get_unsigned() <= m_max_ub) { + expr_ref b = mk_bounded(axioms, to_app(x), lo.get_unsigned(), hi.get_unsigned()); + rep.insert(x, b); + m_bounds.insert(x, bound(lo.get_unsigned(), hi.get_unsigned(), b)); TRACE("pb", tout << "add bound " << mk_pp(x, m) << "\n";); } } - expr_mark subfmls; for (unsigned i = 0; i < g->size(); i++) { - expr_ref new_curr(m); + expr_ref new_curr(m), tmp(m); proof_ref new_pr(m); - m_rw(g->form(i), new_curr, new_pr); + rep(g->form(i), tmp); + m_rw(tmp, new_curr, new_pr); if (m.proofs_enabled() && !new_pr) { new_pr = m.mk_rewrite(g->form(i), new_curr); new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } + // IF_VERBOSE(0, verbose_stream() << mk_pp(g->form(i), m) << "\n--->\n" << new_curr << "\n";); g->update(i, new_curr, new_pr, g->dep(i)); - mark_rec(subfmls, new_curr); + } - expr_set::iterator it = m_01s->begin(), end = m_01s->end(); - for (; it != end; ++it) { - expr* v = *it; - if (subfmls.is_marked(v)) { - g->assert_expr(a.mk_le(v, a.mk_numeral(rational(1), true))); - g->assert_expr(a.mk_le(a.mk_numeral(rational(0), true), v)); - } + for (expr* a : axioms) { + g->assert_expr(a); } + if (m_mc) g->add(m_mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("pb", g->display(tout);); SASSERT(g->is_well_sorted()); - - // TBD: convert models for 0-1 variables. - // TBD: support proof conversion (or not..) + m_bounds.reset(); } - void mark_rec(expr_mark& mark, expr* e) { - ptr_vector todo; - todo.push_back(e); - while (!todo.empty()) { - e = todo.back(); - todo.pop_back(); - if (!mark.is_marked(e)) { - mark.mark(e); - if (is_app(e)) { - for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { - todo.push_back(to_app(e)->get_arg(i)); - } - } - else if (is_quantifier(e)) { - todo.push_back(to_quantifier(e)->get_expr()); - } - } - } - } - - - bool is_01var(expr* x) const { - return m_01s->contains(x); - } - - expr_ref mk_01(expr* x) { - expr* r = m.mk_eq(x, a.mk_numeral(rational(1), m.get_sort(x))); - return expr_ref(r, m); - } - - expr* mk_le(unsigned sz, rational const* weights, expr* const* args, rational const& w) { if (sz == 0) { return w.is_neg()?m.mk_false():m.mk_true(); @@ -332,9 +297,6 @@ public: ok &= get_sum(u, mul, conds, args, coeffs, coeff); conds.pop_back(); } - else if (is_01var(x)) { - insert_arg(mul, conds, mk_01(x), args, coeffs, coeff); - } else if (is_numeral(x, r)) { insert_arg(mul*r, conds, m.mk_true(), args, coeffs, coeff); } @@ -398,13 +360,11 @@ public: "(default:false) compile equalities into pseudo-Boolean equality"); } - void cleanup() override { - expr_set* d = alloc(expr_set); + void cleanup() override { ptr_vector* todo = alloc(ptr_vector); - std::swap(m_01s, d); std::swap(m_todo, todo); - dealloc(d); dealloc(todo); + m_bounds.reset(); } }; diff --git a/src/tactic/arith/lia2pb_tactic.cpp b/src/tactic/arith/lia2pb_tactic.cpp index 48cbb76ef..178ace7fd 100644 --- a/src/tactic/arith/lia2pb_tactic.cpp +++ b/src/tactic/arith/lia2pb_tactic.cpp @@ -20,8 +20,7 @@ Revision History: #include "tactic/arith/bound_manager.h" #include "ast/rewriter/th_rewriter.h" #include "ast/for_each_expr.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" @@ -189,15 +188,12 @@ class lia2pb_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("lia2pb", g); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); tactic_report report("lia2pb", *g); m_bm.reset(); m_rw.reset(); m_new_deps.reset(); @@ -224,12 +220,9 @@ class lia2pb_tactic : public tactic { if (!check_num_bits()) throw tactic_exception("lia2pb failed, number of necessary bits exceeds specified threshold (use option :lia2pb-total-bits to increase threshold)"); - extension_model_converter * mc1 = nullptr; - filter_model_converter * mc2 = nullptr; + ref gmc; if (m_produce_models) { - mc1 = alloc(extension_model_converter, m); - mc2 = alloc(filter_model_converter, m); - mc = concat(mc2, mc1); + gmc = alloc(generic_model_converter, m, "lia2pb"); } expr_ref zero(m); @@ -259,7 +252,7 @@ class lia2pb_tactic : public tactic { else def_args.push_back(m_util.mk_mul(m_util.mk_numeral(a, true), x_prime)); if (m_produce_models) - mc2->insert(x_prime->get_decl()); + gmc->hide(x_prime->get_decl()); a *= rational(2); } SASSERT(def_args.size() > 1); @@ -273,7 +266,7 @@ class lia2pb_tactic : public tactic { TRACE("lia2pb", tout << mk_ismt2_pp(x, m) << " -> " << dep << "\n";); subst.insert(x, def, nullptr, dep); if (m_produce_models) { - mc1->insert(to_app(x)->get_decl(), def); + gmc->add(x, def); } } } @@ -299,6 +292,7 @@ class lia2pb_tactic : public tactic { g->update(idx, new_curr, new_pr, dep); } g->inc_depth(); + g->add(gmc.get()); result.push_back(g.get()); TRACE("lia2pb", g->display(tout);); SASSERT(g->is_well_sorted()); @@ -332,13 +326,10 @@ public: r.insert("lia2pb_total_bits", CPK_UINT, "(default: 2048) total number of bits to be used (per problem) in lia2pb."); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); diff --git a/src/tactic/arith/nla2bv_tactic.cpp b/src/tactic/arith/nla2bv_tactic.cpp index 256fc9b16..06150e18b 100644 --- a/src/tactic/arith/nla2bv_tactic.cpp +++ b/src/tactic/arith/nla2bv_tactic.cpp @@ -26,8 +26,7 @@ Notes: #include "util/optional.h" #include "tactic/arith/bv2int_rewriter.h" #include "tactic/arith/bv2real_rewriter.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "tactic/arith/bound_manager.h" #include "util/obj_pair_hashtable.h" #include "ast/ast_smt2_pp.h" @@ -60,7 +59,7 @@ class nla2bv_tactic : public tactic { expr_ref_vector m_trail; unsigned m_num_bits; unsigned m_default_bv_size; - filter_model_converter_ref m_fmc; + generic_model_converter_ref m_fmc; public: imp(ast_manager & m, params_ref const& p): @@ -86,7 +85,7 @@ class nla2bv_tactic : public tactic { TRACE("nla2bv", g.display(tout); tout << "Muls: " << count_mul(g) << "\n"; ); - m_fmc = alloc(filter_model_converter, m_manager); + m_fmc = alloc(generic_model_converter, m_manager, "nla2bv"); m_bounds(g); collect_power2(g); if(!collect_vars(g)) { @@ -98,13 +97,12 @@ class nla2bv_tactic : public tactic { reduce_bv2int(g); reduce_bv2real(g); TRACE("nla2bv", g.display(tout << "after reduce\n");); - extension_model_converter * evc = alloc(extension_model_converter, m_manager); - mc = concat(m_fmc.get(), evc); + mc = m_fmc.get(); for (unsigned i = 0; i < m_vars.size(); ++i) { - evc->insert(m_vars[i].get(), m_defs[i].get()); + m_fmc->add(m_vars[i].get(), m_defs[i].get()); } for (unsigned i = 0; i < m_bv2real.num_aux_decls(); ++i) { - m_fmc->insert(m_bv2real.get_aux_decl(i)); + m_fmc->hide(m_bv2real.get_aux_decl(i)); } IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(nla->bv :sat-preserving " << m_is_sat_preserving << ")\n";); TRACE("nla2bv_verbose", g.display(tout);); @@ -233,7 +231,7 @@ class nla2bv_tactic : public tactic { bv_sort = m_bv.mk_sort(num_bits); std::string name = n->get_decl()->get_name().str(); s_bv = m_manager.mk_fresh_const(name.c_str(), bv_sort); - m_fmc->insert(to_app(s_bv)->get_decl()); + m_fmc->hide(s_bv); s_bv = m_bv.mk_bv2int(s_bv); if (low) { if (!(*low).is_zero()) { @@ -271,8 +269,8 @@ class nla2bv_tactic : public tactic { s = m_manager.mk_fresh_const(name.c_str(), bv_sort); name += "_r"; t = m_manager.mk_fresh_const(name.c_str(), bv_sort); - m_fmc->insert(to_app(s)->get_decl()); - m_fmc->insert(to_app(t)->get_decl()); + m_fmc->hide(s); + m_fmc->hide(t); s_bv = m_bv2real.mk_bv2real(s, t); m_trail.push_back(s_bv); m_subst.insert(n, s_bv); @@ -442,19 +440,17 @@ public: \return false if transformation is not possible. */ void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); fail_if_proof_generation("nla2bv", g); fail_if_unsat_core_generation("nla2bv", g); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); - + result.reset(); + imp proc(g->m(), m_params); scoped_set_imp setter(*this, proc); + model_converter_ref mc; proc(*(g.get()), mc); - + g->add(mc.get()); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } diff --git a/src/tactic/arith/normalize_bounds_tactic.cpp b/src/tactic/arith/normalize_bounds_tactic.cpp index b7cc09e25..907c7af8c 100644 --- a/src/tactic/arith/normalize_bounds_tactic.cpp +++ b/src/tactic/arith/normalize_bounds_tactic.cpp @@ -21,8 +21,7 @@ Revision History: #include "tactic/tactical.h" #include "tactic/arith/bound_manager.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" @@ -80,12 +79,7 @@ class normalize_bounds_tactic : public tactic { return false; } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = nullptr; pc = nullptr; core = nullptr; + void operator()(goal_ref const & in, goal_ref_buffer & result) { bool produce_models = in->models_enabled(); bool produce_proofs = in->proofs_enabled(); tactic_report report("normalize-bounds", *in); @@ -98,21 +92,16 @@ class normalize_bounds_tactic : public tactic { return; } - extension_model_converter * mc1 = nullptr; - filter_model_converter * mc2 = nullptr; + generic_model_converter * gmc = nullptr; if (produce_models) { - mc1 = alloc(extension_model_converter, m); - mc2 = alloc(filter_model_converter, m); - mc = concat(mc2, mc1); + gmc = alloc(generic_model_converter, m, "normalize_bounds"); + in->add(gmc); } unsigned num_norm_bounds = 0; expr_substitution subst(m); rational val; - bound_manager::iterator it = m_bm.begin(); - bound_manager::iterator end = m_bm.end(); - for (; it != end; ++it) { - expr * x = *it; + for (expr * x : m_bm) { if (is_target(x, val)) { num_norm_bounds++; sort * s = m.get_sort(x); @@ -120,8 +109,8 @@ class normalize_bounds_tactic : public tactic { expr * def = m_util.mk_add(x_prime, m_util.mk_numeral(val, s)); subst.insert(x, def); if (produce_models) { - mc1->insert(to_app(x)->get_decl(), def); - mc2->insert(x_prime->get_decl()); + gmc->add(to_app(x)->get_decl(), def); + gmc->hide(x_prime->get_decl()); } } } @@ -173,13 +162,10 @@ public: r.insert("norm_int_only", CPK_BOOL, "(default: true) normalize only the bounds of integer constants."); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); diff --git a/src/tactic/arith/pb2bv_model_converter.cpp b/src/tactic/arith/pb2bv_model_converter.cpp index 5dc4c1737..772680e6c 100644 --- a/src/tactic/arith/pb2bv_model_converter.cpp +++ b/src/tactic/arith/pb2bv_model_converter.cpp @@ -27,17 +27,12 @@ pb2bv_model_converter::pb2bv_model_converter(ast_manager & _m) : m(_m) { pb2bv_model_converter::pb2bv_model_converter(ast_manager & _m, obj_map const & c2bit, bound_manager const & bm): m(_m) { - obj_map::iterator it = c2bit.begin(); - obj_map::iterator end = c2bit.end(); - for ( ; it != end; it++) { - m_c2bit.push_back(func_decl_pair(it->m_key, to_app(it->m_value)->get_decl())); - m.inc_ref(it->m_key); - m.inc_ref(to_app(it->m_value)->get_decl()); + for (auto const& kv : c2bit) { + m_c2bit.push_back(func_decl_pair(kv.m_key, to_app(kv.m_value)->get_decl())); + m.inc_ref(kv.m_key); + m.inc_ref(to_app(kv.m_value)->get_decl()); } - bound_manager::iterator it2 = bm.begin(); - bound_manager::iterator end2 = bm.end(); - for (; it2 != end2; ++it2) { - expr * c = *it2; + for (expr* c : bm) { SASSERT(is_uninterp_const(c)); func_decl * d = to_app(c)->get_decl(); if (!c2bit.contains(d)) { @@ -49,53 +44,47 @@ pb2bv_model_converter::pb2bv_model_converter(ast_manager & _m, obj_map::const_iterator it = m_c2bit.begin(); - svector::const_iterator end = m_c2bit.end(); - for (; it != end; ++it) { - m.dec_ref(it->first); - m.dec_ref(it->second); + for (auto const& kv : m_c2bit) { + m.dec_ref(kv.first); + m.dec_ref(kv.second); } } -void pb2bv_model_converter::operator()(model_ref & md) { - (*this)(md, 0); +void pb2bv_model_converter::get_units(obj_map& units) { + if (!m_c2bit.empty()) units.reset(); } -void pb2bv_model_converter::operator()(model_ref & md, unsigned goal_idx) { - SASSERT(goal_idx == 0); + +void pb2bv_model_converter::operator()(model_ref & md) { TRACE("pb2bv", tout << "converting model:\n"; model_v2_pp(tout, *md); display(tout);); arith_util a_util(m); - svector::const_iterator it = m_c2bit.begin(); - svector::const_iterator end = m_c2bit.end(); - for (; it != end; ++it) { - if (it->second) { - expr * val = md->get_const_interp(it->second); + for (auto const& kv : m_c2bit) { + if (kv.second) { + expr * val = md->get_const_interp(kv.second); if (val == nullptr || m.is_false(val)) { /* false's and don't cares get the integer 0 solution*/ - md->register_decl(it->first, a_util.mk_numeral(rational(0), true)); + md->register_decl(kv.first, a_util.mk_numeral(rational(0), true)); } else { - md->register_decl(it->first, a_util.mk_numeral(rational(1), true)); + md->register_decl(kv.first, a_util.mk_numeral(rational(1), true)); } } else { - // it->first is a don't care. - md->register_decl(it->first, a_util.mk_numeral(rational(0), true)); + // kv.first is a don't care. + md->register_decl(kv.first, a_util.mk_numeral(rational(0), true)); } } } void pb2bv_model_converter::display(std::ostream & out) { out << "(pb2bv-model-converter"; - svector::const_iterator it = m_c2bit.begin(); - svector::const_iterator end = m_c2bit.end(); - for (; it != end; ++it) { - out << "\n (" << it->first->get_name() << " "; - if (it->second == 0) + for (auto const& kv : m_c2bit) { + out << "\n (" << kv.first->get_name() << " "; + if (kv.second == 0) out << "0"; else - out << it->second->get_name(); + out << kv.second->get_name(); out << ")"; } out << ")\n"; @@ -104,11 +93,9 @@ void pb2bv_model_converter::display(std::ostream & out) { model_converter * pb2bv_model_converter::translate(ast_translation & translator) { ast_manager & to = translator.to(); pb2bv_model_converter * res = alloc(pb2bv_model_converter, to); - svector::iterator it = m_c2bit.begin(); - svector::iterator end = m_c2bit.end(); - for (; it != end; it++) { - func_decl * f1 = translator(it->first); - func_decl * f2 = translator(it->second); + for (auto const& kv : m_c2bit) { + func_decl * f1 = translator(kv.first); + func_decl * f2 = translator(kv.second); res->m_c2bit.push_back(func_decl_pair(f1, f2)); to.inc_ref(f1); to.inc_ref(f2); diff --git a/src/tactic/arith/pb2bv_model_converter.h b/src/tactic/arith/pb2bv_model_converter.h index 7e181b7dc..2d69bf7e4 100644 --- a/src/tactic/arith/pb2bv_model_converter.h +++ b/src/tactic/arith/pb2bv_model_converter.h @@ -32,8 +32,8 @@ public: pb2bv_model_converter(ast_manager & _m, obj_map const & c2bit, bound_manager const & bm); ~pb2bv_model_converter() override; void operator()(model_ref & md) override; - void operator()(model_ref & md, unsigned goal_idx) override; void display(std::ostream & out) override; + void get_units(obj_map& units) override; model_converter * translate(ast_translation & translator) override; }; diff --git a/src/tactic/arith/pb2bv_tactic.cpp b/src/tactic/arith/pb2bv_tactic.cpp index 170a10f91..836808de1 100644 --- a/src/tactic/arith/pb2bv_tactic.cpp +++ b/src/tactic/arith/pb2bv_tactic.cpp @@ -26,7 +26,7 @@ Notes: #include "util/trace.h" #include "ast/ast_smt2_pp.h" #include "ast/expr_substitution.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "tactic/arith/pb2bv_model_converter.h" #include "tactic/arith/pb2bv_tactic.h" #include "ast/ast_pp.h" @@ -886,16 +886,13 @@ private: } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { TRACE("pb2bv", g->display(tout);); SASSERT(g->is_well_sorted()); fail_if_proof_generation("pb2bv", g); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); tactic_report report("pb2bv", *g); m_bm.reset(); m_rw.reset(); m_new_deps.reset(); @@ -949,17 +946,17 @@ private: g->update(idx, new_exprs[idx].get(), nullptr, (m_produce_unsat_cores) ? new_deps[idx].get() : g->dep(idx)); if (m_produce_models) { - filter_model_converter * mc1 = alloc(filter_model_converter, m); - obj_map::iterator it = m_const2bit.begin(); - obj_map::iterator end = m_const2bit.end(); - for (; it != end; ++it) - mc1->insert(to_app(it->m_value)->get_decl()); + model_converter_ref mc; + generic_model_converter * mc1 = alloc(generic_model_converter, m, "pb2bv"); + for (auto const& kv : m_const2bit) + mc1->hide(kv.m_value); // store temp int constants in the filter unsigned num_temps = m_temporary_ints.size(); for (unsigned i = 0; i < num_temps; i++) - mc1->insert(to_app(m_temporary_ints.get(i))->get_decl()); + mc1->hide(m_temporary_ints.get(i)); pb2bv_model_converter * mc2 = alloc(pb2bv_model_converter, m, m_const2bit, m_bm); - mc = concat(mc1, mc2); + mc = concat(mc1, mc2); + g->add(mc.get()); } g->inc_depth(); @@ -1000,12 +997,9 @@ public: m_imp->collect_param_descrs(r); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/tactic/arith/propagate_ineqs_tactic.cpp b/src/tactic/arith/propagate_ineqs_tactic.cpp index 38bb38330..8bde74c8c 100644 --- a/src/tactic/arith/propagate_ineqs_tactic.cpp +++ b/src/tactic/arith/propagate_ineqs_tactic.cpp @@ -52,7 +52,7 @@ public: void updt_params(params_ref const & p) override; void collect_param_descrs(param_descrs & r) override {} - void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) override; + void operator()(goal_ref const & g, goal_ref_buffer & result) override; void cleanup() override; }; @@ -527,14 +527,11 @@ void propagate_ineqs_tactic::updt_params(params_ref const & p) { } void propagate_ineqs_tactic::operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("propagate-ineqs", g); fail_if_unsat_core_generation("propagate-ineqs", g); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); goal_ref r; (*m_imp)(g.get(), r); result.push_back(r.get()); diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index 0774c036d..67dadd34b 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -27,8 +27,7 @@ Revision History: #include "tactic/core/nnf_tactic.h" #include "tactic/core/simplify_tactic.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/filter_model_converter.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/ast_smt2_pp.h" #include "ast/rewriter/expr_replacer.h" @@ -764,25 +763,23 @@ struct purify_arith_proc { m_goal.assert_expr(r.cfg().m_new_cnstrs.get(i), m_produce_proofs ? r.cfg().m_new_cnstr_prs.get(i) : nullptr, nullptr); } - // add filter_model_converter to eliminate auxiliary variables from model + // add generic_model_converter to eliminate auxiliary variables from model if (produce_models) { - filter_model_converter * fmc = alloc(filter_model_converter, m()); + generic_model_converter * fmc = alloc(generic_model_converter, m(), "purify"); mc = fmc; obj_map & f2v = r.cfg().m_app2fresh; - obj_map::iterator it = f2v.begin(); - obj_map::iterator end = f2v.end(); - for (; it != end; ++it) { - app * v = to_app(it->m_value); + for (auto const& kv : f2v) { + app * v = to_app(kv.m_value); SASSERT(is_uninterp_const(v)); - fmc->insert(v->get_decl()); + fmc->hide(v->get_decl()); } } if (produce_models && !m_sin_cos.empty()) { - extension_model_converter* emc = alloc(extension_model_converter, m()); + generic_model_converter* emc = alloc(generic_model_converter, m(), "purify_sin_cos"); mc = concat(mc.get(), emc); obj_map >::iterator it = m_sin_cos.begin(), end = m_sin_cos.end(); for (; it != end; ++it) { - emc->insert(it->m_key->get_decl(), + emc->add(it->m_key->get_decl(), m().mk_ite(u().mk_ge(it->m_value.first, mk_real_zero()), u().mk_acos(it->m_value.second), u().mk_add(u().mk_acos(u().mk_uminus(it->m_value.second)), u().mk_pi()))); } @@ -823,14 +820,10 @@ public: th_rewriter::get_param_descrs(r); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { try { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("purify-arith", *g); TRACE("purify_arith", g->display(tout);); bool produce_proofs = g->proofs_enabled(); @@ -838,10 +831,10 @@ public: bool elim_root_objs = m_params.get_bool("elim_root_objects", true); bool elim_inverses = m_params.get_bool("elim_inverses", true); bool complete = m_params.get_bool("complete", true); - purify_arith_proc proc(*(g.get()), m_util, produce_proofs, elim_root_objs, elim_inverses, complete); - + purify_arith_proc proc(*(g.get()), m_util, produce_proofs, elim_root_objs, elim_inverses, complete); + model_converter_ref mc; proc(mc, produce_models); - + g->add(mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("purify_arith", g->display(tout);); diff --git a/src/tactic/arith/recover_01_tactic.cpp b/src/tactic/arith/recover_01_tactic.cpp index b1666eb7b..015ee1a86 100644 --- a/src/tactic/arith/recover_01_tactic.cpp +++ b/src/tactic/arith/recover_01_tactic.cpp @@ -32,8 +32,7 @@ Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "util/dec_ref_util.h" @@ -112,8 +111,7 @@ class recover_01_tactic : public tactic { } // temporary fields used by operator() and process - extension_model_converter * mc1; - filter_model_converter * mc2; + generic_model_converter * gmc; expr_substitution * subst; goal_ref new_goal; obj_map bool2int; @@ -205,8 +203,8 @@ class recover_01_tactic : public tactic { expr * bool_def = m.mk_eq(var, m_util.mk_numeral(rational(1), true)); subst->insert(atom, bool_def); if (m_produce_models) { - mc2->insert(to_app(var)->get_decl()); - mc1->insert(to_app(atom)->get_decl(), bool_def); + gmc->hide(var); + gmc->add(to_app(atom)->get_decl(), bool_def); } m.inc_ref(atom); m.inc_ref(var); @@ -288,21 +286,18 @@ class recover_01_tactic : public tactic { TRACE("recover_01", tout << x->get_name() << " --> " << mk_ismt2_pp(x_def, m) << "\n";); subst->insert(m.mk_const(x), x_def); if (m_produce_models) { - mc1->insert(x, x_def); + gmc->add(x, x_def); } return true; } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("recover-01", g); fail_if_unsat_core_generation("recover-01", g); m_produce_models = g->models_enabled(); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); tactic_report report("recover-01", *g); bool saved = false; @@ -310,7 +305,9 @@ class recover_01_tactic : public tactic { SASSERT(new_goal->depth() == g->depth()); SASSERT(new_goal->prec() == g->prec()); new_goal->inc_depth(); - + new_goal->add(g->mc()); + new_goal->add(g->pc()); + unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { expr * f = g->form(i); @@ -328,9 +325,8 @@ class recover_01_tactic : public tactic { } if (m_produce_models) { - mc1 = alloc(extension_model_converter, m); - mc2 = alloc(filter_model_converter, m); - mc = concat(mc2, mc1); + gmc = alloc(generic_model_converter, m, "recover_01"); + new_goal->add(gmc); } dec_ref_key_values(m, bool2int); @@ -339,25 +335,20 @@ class recover_01_tactic : public tactic { bool recovered = false; expr_substitution _subst(m); subst = &_subst; - var2clauses::iterator it = m_var2clauses.begin(); - var2clauses::iterator end = m_var2clauses.end(); - for (; it != end; ++it) { - if (process(it->m_key, it->m_value)) { + for (auto& kv : m_var2clauses) { + if (process(kv.m_key, kv.m_value)) { recovered = true; counter++; } else { - ptr_vector::iterator it2 = it->m_value.begin(); - ptr_vector::iterator end2 = it->m_value.end(); - for (; it2 != end2; ++it2) { - new_goal->assert_expr(*it2); + for (app* a : kv.m_value) { + new_goal->assert_expr(a); } } } if (!recovered) { result.push_back(g.get()); - mc = nullptr; return; } @@ -408,13 +399,10 @@ public: r.insert("recover_01_max_bits", CPK_UINT, "(default: 10) maximum number of bits to consider in a clause."); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { try { - (*m_imp)(g, result, mc, pc, core); + (*m_imp)(g, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); diff --git a/src/tactic/bv/bit_blaster_model_converter.cpp b/src/tactic/bv/bit_blaster_model_converter.cpp index 027938752..5474700c3 100644 --- a/src/tactic/bv/bit_blaster_model_converter.cpp +++ b/src/tactic/bv/bit_blaster_model_converter.cpp @@ -21,6 +21,7 @@ Notes: #include "tactic/model_converter.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_smt2_pp.h" +#include "ast/ast_util.h" /** If TO_BOOL == true, then bit-vectors of size n were blasted into n-tuples of Booleans. @@ -30,20 +31,25 @@ template struct bit_blaster_model_converter : public model_converter { func_decl_ref_vector m_vars; expr_ref_vector m_bits; + func_decl_ref_vector m_newbits; ast_manager & m() const { return m_vars.get_manager(); } - bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits):m_vars(m), m_bits(m) { - obj_map::iterator it = const2bits.begin(); - obj_map::iterator end = const2bits.end(); - for (; it != end; ++it) { - func_decl * v = it->m_key; - expr * bits = it->m_value; + bit_blaster_model_converter( + ast_manager & m, + obj_map const & const2bits, + ptr_vector const& newbits): + m_vars(m), m_bits(m), m_newbits(m) { + for (auto const& kv : const2bits) { + func_decl * v = kv.m_key; + expr * bits = kv.m_value; SASSERT(!TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_MKBV)); SASSERT(TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_CONCAT)); m_vars.push_back(v); m_bits.push_back(bits); } + for (func_decl* f : newbits) + m_newbits.push_back(f); } ~bit_blaster_model_converter() override { @@ -117,7 +123,6 @@ struct bit_blaster_model_converter : public model_converter { SASSERT(is_uninterp_const(bit)); func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model->get_const_interp(bit_decl); - // remark: if old_model does not assign bit_val, then assume it is false. if (bit_val != nullptr && m().is_true(bit_val)) val++; } @@ -142,8 +147,30 @@ struct bit_blaster_model_converter : public model_converter { } } - void operator()(model_ref & md, unsigned goal_idx) override { - SASSERT(goal_idx == 0); + app_ref mk_bv(expr* bs, model& old_model) { + bv_util util(m()); + unsigned bv_sz = to_app(bs)->get_num_args(); + expr_ref_vector args(m()); + app_ref result(m()); + for (expr * bit : *to_app(bs)) { + SASSERT(is_uninterp_const(bit)); + func_decl * bit_decl = to_app(bit)->get_decl(); + expr * bit_val = old_model.get_const_interp(bit_decl); + args.push_back(bit_val ? bit_val : bit); + } + + if (TO_BOOL) { + SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_MKBV)); + result = util.mk_bv(bv_sz, args.c_ptr()); + } + else { + SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_CONCAT)); + result = util.mk_concat(bv_sz, args.c_ptr()); + } + return result; + } + + void operator()(model_ref & md) override { model * new_model = alloc(model, m()); obj_hashtable bits; collect_bits(bits); @@ -152,41 +179,58 @@ struct bit_blaster_model_converter : public model_converter { md = new_model; } - void operator()(model_ref & md) override { - operator()(md, 0); + /** + \brief simplisic expansion operator for formulas. + It just adds back bit-vector definitions to the formula whether they are used or not. + + */ + void operator()(expr_ref& fml) override { + unsigned sz = m_vars.size(); + if (sz == 0) return; + expr_ref_vector fmls(m()); + fmls.push_back(fml); + for (unsigned i = 0; i < sz; i++) { + fmls.push_back(m().mk_eq(m().mk_const(m_vars.get(i)), m_bits.get(i))); + } + m_vars.reset(); + m_bits.reset(); + fml = mk_and(fmls); } void display(std::ostream & out) override { - out << "(bit-blaster-model-converter"; + for (func_decl * f : m_newbits) + display_del(out, f); unsigned sz = m_vars.size(); - for (unsigned i = 0; i < sz; i++) { - out << "\n (" << m_vars.get(i)->get_name() << " "; - unsigned indent = m_vars.get(i)->get_name().size() + 4; - out << mk_ismt2_pp(m_bits.get(i), m(), indent) << ")"; - } - out << ")" << std::endl; + for (unsigned i = 0; i < sz; i++) + display_add(out, m(), m_vars.get(i), m_bits.get(i)); + } + + void get_units(obj_map& units) override { + // no-op } protected: - bit_blaster_model_converter(ast_manager & m):m_vars(m), m_bits(m) { } + bit_blaster_model_converter(ast_manager & m):m_vars(m), m_bits(m), m_newbits(m) { } public: model_converter * translate(ast_translation & translator) override { bit_blaster_model_converter * res = alloc(bit_blaster_model_converter, translator.to()); - for (unsigned i = 0; i < m_vars.size(); i++) - res->m_vars.push_back(translator(m_vars[i].get())); - for (unsigned i = 0; i < m_bits.size(); i++) - res->m_bits.push_back(translator(m_bits[i].get())); + for (func_decl * v : m_vars) + res->m_vars.push_back(translator(v)); + for (expr* b : m_bits) + res->m_bits.push_back(translator(b)); + for (func_decl* f : m_newbits) + res->m_newbits.push_back(translator(f)); return res; } }; -model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits) { - return alloc(bit_blaster_model_converter, m, const2bits); +model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits) { + return const2bits.empty() ? nullptr : alloc(bit_blaster_model_converter, m, const2bits, newbits); } -model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits) { - return alloc(bit_blaster_model_converter, m, const2bits); +model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits) { + return const2bits.empty() ? nullptr : alloc(bit_blaster_model_converter, m, const2bits, newbits); } diff --git a/src/tactic/bv/bit_blaster_model_converter.h b/src/tactic/bv/bit_blaster_model_converter.h index f7dd254b4..057891ec6 100644 --- a/src/tactic/bv/bit_blaster_model_converter.h +++ b/src/tactic/bv/bit_blaster_model_converter.h @@ -21,7 +21,7 @@ Notes: #include "tactic/model_converter.h" -model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits); -model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits); +model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits); +model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits); #endif diff --git a/src/tactic/bv/bit_blaster_tactic.cpp b/src/tactic/bv/bit_blaster_tactic.cpp index a8f302752..3018b49a4 100644 --- a/src/tactic/bv/bit_blaster_tactic.cpp +++ b/src/tactic/bv/bit_blaster_tactic.cpp @@ -51,11 +51,7 @@ class bit_blaster_tactic : public tactic { void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = nullptr; pc = nullptr; core = nullptr; + goal_ref_buffer & result) { bool proofs_enabled = g->proofs_enabled(); if (proofs_enabled && m_blast_quant) @@ -66,6 +62,7 @@ class bit_blaster_tactic : public tactic { TRACE("before_bit_blaster", g->display(tout);); m_num_steps = 0; + m_rewriter->start_rewrite(); expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g->size(); @@ -81,19 +78,21 @@ class bit_blaster_tactic : public tactic { new_pr = m().mk_modus_ponens(pr, new_pr); } if (curr != new_curr) { - change = true; - TRACE("bit_blaster", tout << mk_pp(curr, m()) << " -> " << mk_pp(new_curr, m()) << "\n";); + change = true; + TRACE("bit_blaster", tout << mk_pp(curr, m()) << " -> " << new_curr << "\n";); g->update(idx, new_curr, new_pr, g->dep(idx)); } } - if (change && g->models_enabled()) - mc = mk_bit_blaster_model_converter(m(), m_rewriter->const2bits()); - else - mc = nullptr; + if (change && g->models_enabled()) { + obj_map const2bits; + ptr_vector newbits; + m_rewriter->end_rewrite(const2bits, newbits); + g->add(mk_bit_blaster_model_converter(m(), const2bits, newbits)); + } g->inc_depth(); result.push_back(g.get()); - TRACE("after_bit_blaster", g->display(tout); if (mc) mc->display(tout); tout << "\n";); + TRACE("after_bit_blaster", g->display(tout); if (g->mc()) g->mc()->display(tout); tout << "\n";); m_rewriter->cleanup(); } @@ -134,13 +133,10 @@ public: r.insert("blast_full", CPK_BOOL, "(default: false) bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms."); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { try { - (*m_imp)(g, result, mc, pc, core); + (*m_imp)(g, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); @@ -157,6 +153,7 @@ public: return m_imp->get_num_steps(); } + }; @@ -167,3 +164,4 @@ tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p) { tactic * mk_bit_blaster_tactic(ast_manager & m, bit_blaster_rewriter* rw, params_ref const & p) { return clean(alloc(bit_blaster_tactic, m, rw, p)); } + diff --git a/src/tactic/bv/bv1_blaster_tactic.cpp b/src/tactic/bv/bv1_blaster_tactic.cpp index e82948c16..b81bc5687 100644 --- a/src/tactic/bv/bv1_blaster_tactic.cpp +++ b/src/tactic/bv/bv1_blaster_tactic.cpp @@ -36,6 +36,7 @@ class bv1_blaster_tactic : public tactic { ast_manager & m_manager; bv_util m_util; obj_map m_const2bits; + ptr_vector m_newbits; expr_ref_vector m_saved; expr_ref m_bit1; expr_ref m_bit0; @@ -107,6 +108,7 @@ class bv1_blaster_tactic : public tactic { ptr_buffer bits; for (unsigned i = 0; i < bv_size; i++) { bits.push_back(m().mk_fresh_const(nullptr, b)); + m_newbits.push_back(to_app(bits.back())->get_decl()); } r = butil().mk_concat(bits.size(), bits.c_ptr()); m_saved.push_back(r); @@ -379,11 +381,7 @@ class bv1_blaster_tactic : public tactic { void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = nullptr; pc = nullptr; core = nullptr; + goal_ref_buffer & result) { if (!is_target(*g)) throw tactic_exception("bv1 blaster cannot be applied to goal"); @@ -409,7 +407,7 @@ class bv1_blaster_tactic : public tactic { } if (g->models_enabled()) - mc = mk_bv1_blaster_model_converter(m(), m_rw.cfg().m_const2bits); + g->add(mk_bv1_blaster_model_converter(m(), m_rw.cfg().m_const2bits, m_rw.cfg().m_newbits)); g->inc_depth(); result.push_back(g.get()); m_rw.cfg().cleanup(); @@ -454,12 +452,9 @@ public: It also does not support quantifiers. Return a model_converter that converts any model for the updated set into a model for the old set. */ - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(g, result, mc, pc, core); + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { + (*m_imp)(g, result); } void cleanup() override { diff --git a/src/tactic/bv/bv_bound_chk_tactic.cpp b/src/tactic/bv/bv_bound_chk_tactic.cpp index 48201501e..75b772720 100644 --- a/src/tactic/bv/bv_bound_chk_tactic.cpp +++ b/src/tactic/bv/bv_bound_chk_tactic.cpp @@ -135,12 +135,8 @@ class bv_bound_chk_tactic : public tactic { bv_bound_chk_stats m_stats; public: bv_bound_chk_tactic(ast_manager & m, params_ref const & p); + void operator()(goal_ref const & g, goal_ref_buffer & result) override; ~bv_bound_chk_tactic() override; - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override; tactic * translate(ast_manager & m) override; void updt_params(params_ref const & p) override; void cleanup() override; @@ -197,16 +193,12 @@ bv_bound_chk_tactic::~bv_bound_chk_tactic() { dealloc(m_imp); } -void bv_bound_chk_tactic::operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { +void bv_bound_chk_tactic::operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("bv-bound-chk", g); fail_if_unsat_core_generation("bv-bound-chk", g); TRACE("bv-bound-chk", g->display(tout << "before:"); tout << std::endl;); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); m_imp->operator()(g); g->inc_depth(); result.push_back(g.get()); diff --git a/src/tactic/bv/bv_size_reduction_tactic.cpp b/src/tactic/bv/bv_size_reduction_tactic.cpp index 3bcb2e02b..964102825 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.cpp +++ b/src/tactic/bv/bv_size_reduction_tactic.cpp @@ -24,8 +24,7 @@ Notes: #include "tactic/tactical.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/ast_smt2_pp.h" class bv_size_reduction_tactic : public tactic { @@ -40,7 +39,7 @@ public: ~bv_size_reduction_tactic() override; - void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) override; + void operator()(goal_ref const & g, goal_ref_buffer & result) override; void cleanup() override; }; @@ -51,7 +50,7 @@ tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p) { struct bv_size_reduction_tactic::imp { typedef rational numeral; - typedef extension_model_converter bv_size_reduction_mc; + typedef generic_model_converter bv_size_reduction_mc; ast_manager & m; bv_util m_util; @@ -60,7 +59,7 @@ struct bv_size_reduction_tactic::imp { obj_map m_unsigned_lowers; obj_map m_unsigned_uppers; ref m_mc; - filter_model_converter_ref m_fmc; + generic_model_converter_ref m_fmc; scoped_ptr m_replacer; bool m_produce_models; @@ -266,12 +265,12 @@ struct bv_size_reduction_tactic::imp { subst.insert(v, new_def); if (m_produce_models) { if (!m_mc) - m_mc = alloc(bv_size_reduction_mc, m); - m_mc->insert(v->get_decl(), new_def); + m_mc = alloc(bv_size_reduction_mc, m, "bv_size_reduction"); + m_mc->add(v, new_def); if (!m_fmc && new_const) - m_fmc = alloc(filter_model_converter, m); + m_fmc = alloc(generic_model_converter, m, "bv_size_reduction"); if (new_const) - m_fmc->insert(new_const->get_decl()); + m_fmc->hide(new_const); } num_reduced++; } @@ -335,9 +334,9 @@ struct bv_size_reduction_tactic::imp { m_mc = alloc(bv_size_reduction_mc, m); m_mc->insert(v->get_decl(), new_def); if (!m_fmc && new_const) - m_fmc = alloc(filter_model_converter, m); + m_fmc = alloc(generic_model_converter, m, "bv_size_reduction"); if (new_const) - m_fmc->insert(new_const->get_decl()); + m_fmc->hide(new_const); } num_reduced++; TRACE("bv_size_reduction", tout << "New definition = " << mk_ismt2_pp(new_def, m) << "\n";); @@ -383,16 +382,15 @@ bv_size_reduction_tactic::~bv_size_reduction_tactic() { } void bv_size_reduction_tactic::operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("bv-size-reduction", g); fail_if_unsat_core_generation("bv-size-reduction", g); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); + model_converter_ref mc; m_imp->operator()(*(g.get()), mc); g->inc_depth(); + g->add(mc.get()); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } diff --git a/src/tactic/bv/bvarray2uf_rewriter.cpp b/src/tactic/bv/bvarray2uf_rewriter.cpp index a0f80f38b..3ca296eb7 100644 --- a/src/tactic/bv/bvarray2uf_rewriter.cpp +++ b/src/tactic/bv/bvarray2uf_rewriter.cpp @@ -37,7 +37,6 @@ bvarray2uf_rewriter_cfg::bvarray2uf_rewriter_cfg(ast_manager & m, params_ref con m_bindings(m), m_bv_util(m), m_array_util(m), - m_emc(nullptr), m_fmc(nullptr), extra_assertions(m) { updt_params(p); @@ -115,12 +114,11 @@ func_decl_ref bvarray2uf_rewriter_cfg::mk_uf_for_array(expr * e) { bv_f = m_manager.mk_fresh_func_decl("f_t", "", 1, &domain, range); TRACE("bvarray2uf_rw", tout << "for " << mk_ismt2_pp(e, m_manager) << " new func_decl is " << mk_ismt2_pp(bv_f, m_manager) << std::endl; ); if (is_uninterp_const(e)) { - if (m_emc) - m_emc->insert(to_app(e)->get_decl(), - m_array_util.mk_as_array(bv_f)); + if (m_fmc) + m_fmc->add(e, m_array_util.mk_as_array(bv_f)); } else if (m_fmc) - m_fmc->insert(bv_f); + m_fmc->hide(bv_f); m_arrays_fs.insert(e, bv_f); m_manager.inc_ref(e); m_manager.inc_ref(bv_f); @@ -191,12 +189,11 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr bv_f = m_manager.mk_fresh_func_decl("f_t", "", 1, &domain, range); TRACE("bvarray2uf_rw", tout << mk_ismt2_pp(e, m_manager) << " -> " << bv_f->get_name() << std::endl; ); if (is_uninterp_const(e)) { - if (m_emc) - m_emc->insert(e->get_decl(), - m_array_util.mk_as_array(bv_f)); + if (m_fmc) + m_fmc->add(e, m_array_util.mk_as_array(bv_f)); } else if (m_fmc) - m_fmc->insert(bv_f); + m_fmc->hide(bv_f); m_arrays_fs.insert(e, bv_f); m_manager.inc_ref(e); m_manager.inc_ref(bv_f); diff --git a/src/tactic/bv/bvarray2uf_rewriter.h b/src/tactic/bv/bvarray2uf_rewriter.h index bc4014b5b..9984d3469 100644 --- a/src/tactic/bv/bvarray2uf_rewriter.h +++ b/src/tactic/bv/bvarray2uf_rewriter.h @@ -21,8 +21,7 @@ Notes: #define BVARRAY2UF_REWRITER_H_ #include "ast/rewriter/rewriter.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" class bvarray2uf_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; @@ -30,8 +29,7 @@ class bvarray2uf_rewriter_cfg : public default_rewriter_cfg { sort_ref_vector m_bindings; bv_util m_bv_util; array_util m_array_util; - extension_model_converter * m_emc; - filter_model_converter * m_fmc; + generic_model_converter * m_fmc; obj_map m_arrays_fs; @@ -59,7 +57,7 @@ public: expr_ref_vector extra_assertions; - void set_mcs(extension_model_converter * emc, filter_model_converter * fmc) { m_emc = emc; m_fmc = fmc; } + void set_mcs(generic_model_converter * fmc) { m_fmc = fmc; } protected: sort * get_index_sort(expr * e); @@ -79,7 +77,7 @@ struct bvarray2uf_rewriter : public rewriter_tpl { m_cfg(m, p) { } - void set_mcs(extension_model_converter * emc, filter_model_converter * fmc) { m_cfg.set_mcs(emc, fmc); } + void set_mcs(generic_model_converter * fmc) { m_cfg.set_mcs(fmc); } }; #endif diff --git a/src/tactic/bv/bvarray2uf_tactic.cpp b/src/tactic/bv/bvarray2uf_tactic.cpp index 6287181b1..6bda3969b 100644 --- a/src/tactic/bv/bvarray2uf_tactic.cpp +++ b/src/tactic/bv/bvarray2uf_tactic.cpp @@ -20,8 +20,7 @@ Notes: #include "tactic/tactical.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/ast_smt2_pp.h" #include "tactic/bv/bvarray2uf_tactic.h" @@ -54,24 +53,21 @@ class bvarray2uf_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("bvarray2uf", *g); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); fail_if_unsat_core_generation("bvarray2uf", g); TRACE("bvarray2uf", tout << "Before: " << std::endl; g->display(tout); ); m_produce_models = g->models_enabled(); + model_converter_ref mc; if (m_produce_models) { - extension_model_converter * emc = alloc(extension_model_converter, m_manager); - filter_model_converter * fmc = alloc(filter_model_converter, m_manager); - mc = concat(emc, fmc); - m_rw.set_mcs(emc, fmc); + generic_model_converter * fmc = alloc(generic_model_converter, m_manager, "bvarray2uf"); + mc = fmc; + m_rw.set_mcs(fmc); } @@ -95,6 +91,7 @@ class bvarray2uf_tactic : public tactic { g->assert_expr(m_rw.m_cfg.extra_assertions[i].get()); g->inc_depth(); + g->add(mc.get()); result.push_back(g.get()); TRACE("bvarray2uf", tout << "After: " << std::endl; g->display(tout);); SASSERT(g->is_well_sorted()); @@ -131,11 +128,8 @@ public: } void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/tactic/bv/dt2bv_tactic.cpp b/src/tactic/bv/dt2bv_tactic.cpp index 8f164a921..39d8cf5bb 100644 --- a/src/tactic/bv/dt2bv_tactic.cpp +++ b/src/tactic/bv/dt2bv_tactic.cpp @@ -21,12 +21,10 @@ Revision History: #include "tactic/bv/dt2bv_tactic.h" #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/datatype_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/filter_model_converter.h" -#include "tactic/extension_model_converter.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_util.h" #include "ast/rewriter/enum2bv_rewriter.h" @@ -117,12 +115,7 @@ public: void collect_param_descrs(param_descrs & r) override { } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - mc = nullptr; pc = nullptr; core = nullptr; + void operator()(goal_ref const & g, goal_ref_buffer & result) override { bool produce_proofs = g->proofs_enabled(); tactic_report report("dt2bv", *g); unsigned size = g->size(); @@ -131,13 +124,10 @@ public: for (unsigned i = 0; i < size; ++i) { quick_for_each_expr(proc, visited, g->form(i)); } - obj_hashtable::iterator it = m_non_fd_sorts.begin(), end = m_non_fd_sorts.end(); - for (; it != end; ++it) { - m_fd_sorts.remove(*it); - } + for (sort* s : m_non_fd_sorts) + m_fd_sorts.remove(s); if (!m_fd_sorts.empty()) { - ref ext = alloc(extension_model_converter, m); - ref filter = alloc(filter_model_converter, m); + ref filter = alloc(generic_model_converter, m, "dt2bv"); enum2bv_rewriter rw(m, m_params); rw.set_is_fd(&m_is_fd); expr_ref new_curr(m); @@ -152,23 +142,14 @@ public: } expr_ref_vector bounds(m); rw.flush_side_constraints(bounds); - for (unsigned i = 0; i < bounds.size(); ++i) { - g->assert_expr(bounds[i].get()); - } - { - obj_map::iterator it = rw.enum2bv().begin(), end = rw.enum2bv().end(); - for (; it != end; ++it) { - filter->insert(it->m_value); - } - } - { - obj_map::iterator it = rw.enum2def().begin(), end = rw.enum2def().end(); - for (; it != end; ++it) { - ext->insert(it->m_key, it->m_value); - } - } + for (expr* b : bounds) + g->assert_expr(b); + for (auto const& kv : rw.enum2bv()) + filter->hide(kv.m_value); + for (auto const& kv : rw.enum2def()) + filter->add(kv.m_key, kv.m_value); - mc = concat(filter.get(), ext.get()); + g->add(filter.get()); report_tactic_progress(":fd-num-translated", rw.num_translated()); } g->inc_depth(); diff --git a/src/tactic/bv/elim_small_bv_tactic.cpp b/src/tactic/bv/elim_small_bv_tactic.cpp index 17887d495..9959780b7 100644 --- a/src/tactic/bv/elim_small_bv_tactic.cpp +++ b/src/tactic/bv/elim_small_bv_tactic.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "util/cooperate.h" #include "ast/bv_decl_plugin.h" #include "ast/used_vars.h" @@ -35,7 +35,7 @@ class elim_small_bv_tactic : public tactic { params_ref m_params; bv_util m_util; th_rewriter m_simp; - ref m_mc; + ref m_mc; goal * m_goal; unsigned m_max_bits; unsigned long long m_max_steps; @@ -224,13 +224,8 @@ class elim_small_bv_tactic : public tactic { m_rw.cfg().updt_params(p); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("elim-small-bv", *g); bool produce_proofs = g->proofs_enabled(); fail_if_proof_generation("elim-small-bv", g); @@ -250,7 +245,7 @@ class elim_small_bv_tactic : public tactic { } g->update(idx, new_curr, new_pr, g->dep(idx)); } - mc = m_rw.m_cfg.m_mc.get(); + g->add(m_rw.m_cfg.m_mc.get()); report_tactic_progress(":elim-small-bv-num-eliminated", m_rw.m_cfg.m_num_eliminated); g->inc_depth(); @@ -288,11 +283,8 @@ public: } void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/tactic/bv/max_bv_sharing_tactic.cpp b/src/tactic/bv/max_bv_sharing_tactic.cpp index 8054cfdd8..98936ccad 100644 --- a/src/tactic/bv/max_bv_sharing_tactic.cpp +++ b/src/tactic/bv/max_bv_sharing_tactic.cpp @@ -237,12 +237,8 @@ class max_bv_sharing_tactic : public tactic { ast_manager & m() const { return m_rw.m(); } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("max-bv-sharing", *g); bool produce_proofs = g->proofs_enabled(); @@ -298,12 +294,9 @@ public: "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/tactic/converter.h b/src/tactic/converter.h index d50458cf1..5db95c320 100644 --- a/src/tactic/converter.h +++ b/src/tactic/converter.h @@ -29,17 +29,18 @@ public: converter():m_ref_count(0) {} virtual ~converter() {} - void inc_ref() { ++m_ref_count; } + void inc_ref() { ++m_ref_count; } + void dec_ref() { --m_ref_count; - if (m_ref_count == 0) + if (m_ref_count == 0) { dealloc(this); + } } virtual void cancel() {} - // for debugging purposes - virtual void display(std::ostream & out) {} + virtual void display(std::ostream & out) = 0; }; template @@ -68,10 +69,8 @@ public: virtual char const * get_name() const = 0; void display(std::ostream & out) override { - out << "(" << get_name() << "\n"; m_c1->display(out); m_c2->display(out); - out << ")\n"; } }; @@ -86,10 +85,9 @@ protected: T * translate_core(ast_translation & translator) { T * t1 = m_c1 ? m_c1->translate(translator) : nullptr; ptr_buffer t2s; - unsigned num = m_c2s.size(); - for (unsigned i = 0; i < num; i++) - t2s.push_back(m_c2s[i] ? m_c2s[i]->translate(translator) : nullptr); - return alloc(T2, t1, num, t2s.c_ptr(), m_szs.c_ptr()); + for (T* c : m_c2s) + t2s.push_back(c ? c->translate(translator) : nullptr); + return alloc(T2, t1, m_c2s.size(), t2s.c_ptr(), m_szs.c_ptr()); } public: @@ -105,36 +103,24 @@ public: } ~concat_star_converter() override { - unsigned sz = m_c2s.size(); - for (unsigned i = 0; i < sz; i++) { - T * c2 = m_c2s[i]; - if (c2) - c2->dec_ref(); - } + for (T* c : m_c2s) + if (c) c->dec_ref(); } void cancel() override { if (m_c1) m_c1->cancel(); - unsigned num = m_c2s.size(); - for (unsigned i = 0; i < num; i++) { - if (m_c2s[i]) - m_c2s[i]->cancel(); - } + for (T* c : m_c2s) + if (c) c->cancel(); } virtual char const * get_name() const = 0; void display(std::ostream & out) override { - out << "(" << get_name() << "\n"; if (m_c1) m_c1->display(out); - out << "(\n"; - unsigned num = m_c2s.size(); - for (unsigned i = 0; i < num; i++) - if (m_c2s[i]) - m_c2s[i]->display(out); - out << "))\n"; + for (T* c : m_c2s) + if (c) c->display(out); } }; diff --git a/src/tactic/core/blast_term_ite_tactic.cpp b/src/tactic/core/blast_term_ite_tactic.cpp index 4082659df..3c4684cb8 100644 --- a/src/tactic/core/blast_term_ite_tactic.cpp +++ b/src/tactic/core/blast_term_ite_tactic.cpp @@ -19,7 +19,6 @@ Notes: #include "tactic/tactical.h" #include "ast/normal_forms/defined_names.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/filter_model_converter.h" #include "util/cooperate.h" #include "ast/scoped_proof.h" @@ -114,13 +113,8 @@ class blast_term_ite_tactic : public tactic { m_rw.cfg().updt_params(p); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("blast-term-ite", *g); bool produce_proofs = g->proofs_enabled(); @@ -172,12 +166,8 @@ public: "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/tactic/core/cofactor_term_ite_tactic.cpp b/src/tactic/core/cofactor_term_ite_tactic.cpp index 30486358d..4f52599cd 100644 --- a/src/tactic/core/cofactor_term_ite_tactic.cpp +++ b/src/tactic/core/cofactor_term_ite_tactic.cpp @@ -54,16 +54,11 @@ public: void updt_params(params_ref const & p) override { m_params = p; m_elim_ite.updt_params(p); } void collect_param_descrs(param_descrs & r) override { m_elim_ite.collect_param_descrs(r); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & g, goal_ref_buffer& result) override { SASSERT(g->is_well_sorted()); fail_if_proof_generation("cofactor-term-ite", g); fail_if_unsat_core_generation("cofactor-term-ite", g); tactic_report report("cofactor-term-ite", *g); - mc = nullptr; pc = nullptr; core = nullptr; process(*(g.get())); g->inc_depth(); result.push_back(g.get()); diff --git a/src/tactic/core/collect_statistics_tactic.cpp b/src/tactic/core/collect_statistics_tactic.cpp index 0e08395ad..939541957 100644 --- a/src/tactic/core/collect_statistics_tactic.cpp +++ b/src/tactic/core/collect_statistics_tactic.cpp @@ -63,10 +63,7 @@ public: void collect_param_descrs(param_descrs & r) override {} - void operator()(goal_ref const & g, goal_ref_buffer & result, - model_converter_ref & mc, proof_converter_ref & pc, - expr_dependency_ref & core) override { - mc = nullptr; + void operator()(goal_ref const & g, goal_ref_buffer & result) override { tactic_report report("collect-statistics", *g); collect_proc cp(m, m_stats); @@ -76,10 +73,8 @@ public: for_each_expr(cp, visited, g->form(i)); std::cout << "(" << std::endl; - stats_type::iterator it = m_stats.begin(); - stats_type::iterator end = m_stats.end(); - for (; it != end; it++) - std::cout << " :" << it->first << " " << it->second << std::endl; + for (auto const& kv : m_stats) + std::cout << " :" << kv.first << " " << kv.second << std::endl; std::cout << ")" << std::endl; g->inc_depth(); diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 93a0b2e88..b143bf933 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -621,11 +621,7 @@ void ctx_simplify_tactic::get_param_descrs(param_descrs & r) { } void ctx_simplify_tactic::operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = nullptr; pc = nullptr; core = nullptr; + goal_ref_buffer & result) { (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); diff --git a/src/tactic/core/ctx_simplify_tactic.h b/src/tactic/core/ctx_simplify_tactic.h index e84a5fccc..2d476660a 100644 --- a/src/tactic/core/ctx_simplify_tactic.h +++ b/src/tactic/core/ctx_simplify_tactic.h @@ -54,11 +54,7 @@ public: static void get_param_descrs(param_descrs & r); void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override; + void operator()(goal_ref const & in, goal_ref_buffer & result) override; void cleanup() override; }; diff --git a/src/tactic/core/der_tactic.cpp b/src/tactic/core/der_tactic.cpp index f809f2a74..8de00525e 100644 --- a/src/tactic/core/der_tactic.cpp +++ b/src/tactic/core/der_tactic.cpp @@ -73,12 +73,8 @@ public: dealloc(m_imp); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - mc = nullptr; pc = nullptr; core = nullptr; + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); diff --git a/src/tactic/core/distribute_forall_tactic.cpp b/src/tactic/core/distribute_forall_tactic.cpp index d8f613044..98c942432 100644 --- a/src/tactic/core/distribute_forall_tactic.cpp +++ b/src/tactic/core/distribute_forall_tactic.cpp @@ -100,16 +100,13 @@ public: } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); ast_manager & m = g->m(); bool produce_proofs = g->proofs_enabled(); rw r(m, produce_proofs); m_rw = &r; - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); tactic_report report("distribute-forall", *g); expr_ref new_curr(m); diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 47f96a7d9..43da3bc00 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -183,19 +183,11 @@ tactic * dom_simplify_tactic::translate(ast_manager & m) { return alloc(dom_simplify_tactic, m, m_simplifier->translate(m), m_params); } -void dom_simplify_tactic::operator()( - goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = nullptr; pc = nullptr; core = nullptr; - +void dom_simplify_tactic::operator()(goal_ref const & in, goal_ref_buffer & result) { tactic_report report("dom-simplify", *in.get()); simplify_goal(*(in.get())); in->inc_depth(); result.push_back(in.get()); - } void dom_simplify_tactic::cleanup() { diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 9e1a41f8d..eb3500729 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -129,20 +129,13 @@ public: m_trail(m), m_args(m), m_dominators(m), m_depth(0), m_max_depth(1024), m_forward(true) {} - ~dom_simplify_tactic() override; tactic * translate(ast_manager & m) override; void updt_params(params_ref const & p) override {} static void get_param_descrs(param_descrs & r) {} - void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); } - - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override; - + void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); } + void operator()(goal_ref const & in, goal_ref_buffer & result) override; void cleanup() override; }; diff --git a/src/tactic/core/elim_term_ite_tactic.cpp b/src/tactic/core/elim_term_ite_tactic.cpp index 7691d3398..0bb59afcf 100644 --- a/src/tactic/core/elim_term_ite_tactic.cpp +++ b/src/tactic/core/elim_term_ite_tactic.cpp @@ -20,7 +20,7 @@ Notes: #include "tactic/tactical.h" #include "ast/normal_forms/defined_names.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "util/cooperate.h" class elim_term_ite_tactic : public tactic { @@ -28,7 +28,7 @@ class elim_term_ite_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m; defined_names m_defined_names; - ref m_mc; + ref m_mc; goal * m_goal; unsigned long long m_max_memory; // in bytes bool m_produce_models; @@ -55,8 +55,8 @@ class elim_term_ite_tactic : public tactic { m_num_fresh++; if (m_produce_models) { if (!m_mc) - m_mc = alloc(filter_model_converter, m); - m_mc->insert(_result->get_decl()); + m_mc = alloc(generic_model_converter, m, "elim_term_ite"); + m_mc->hide(_result->get_decl()); } } result = _result.get(); @@ -100,12 +100,8 @@ class elim_term_ite_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("elim-term-ite", *g); bool produce_proofs = g->proofs_enabled(); m_rw.cfg().m_produce_models = g->models_enabled(); @@ -124,7 +120,7 @@ class elim_term_ite_tactic : public tactic { } g->update(idx, new_curr, new_pr, g->dep(idx)); } - mc = m_rw.m_cfg.m_mc.get(); + g->add(m_rw.m_cfg.m_mc.get()); report_tactic_progress(":elim-term-ite-consts", m_rw.m_cfg.m_num_fresh); g->inc_depth(); result.push_back(g.get()); @@ -140,15 +136,15 @@ public: m_params(p) { m_imp = alloc(imp, m, p); } - - tactic * translate(ast_manager & m) override { - return alloc(elim_term_ite_tactic, m, m_params); - } ~elim_term_ite_tactic() override { dealloc(m_imp); } + tactic * translate(ast_manager & m) override { + return alloc(elim_term_ite_tactic, m, m_params); + } + void updt_params(params_ref const & p) override { m_params = p; m_imp->m_rw.cfg().updt_params(p); @@ -161,12 +157,9 @@ public: "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 476ea03dd..ce77f89d9 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -17,8 +17,7 @@ Notes: --*/ #include "tactic/tactical.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" @@ -34,7 +33,7 @@ class elim_uncnstr_tactic : public tactic { struct imp { // unconstrained vars collector - typedef extension_model_converter mc; + typedef generic_model_converter mc; struct rw_cfg : public default_rewriter_cfg { bool m_produce_proofs; @@ -99,6 +98,7 @@ class elim_uncnstr_tactic : public tactic { TRACE("elim_uncnstr_bug", tout << "eliminating:\n" << mk_ismt2_pp(t, m()) << "\n";); TRACE("elim_uncnstr_bug_ll", tout << "eliminating:\n" << mk_bounded_pp(t, m()) << "\n";); m_fresh_vars.push_back(v); + if (m_mc) m_mc->hide(v); m_cache_domain.push_back(t); m_cache.insert(t, v); return true; @@ -120,7 +120,7 @@ class elim_uncnstr_tactic : public tactic { SASSERT(uncnstr(v)); SASSERT(to_app(v)->get_num_args() == 0); if (m_mc) - m_mc->insert(to_app(v)->get_decl(), def); + m_mc->add(to_app(v)->get_decl(), def); } void add_defs(unsigned num, expr * const * args, expr * u, expr * identity) { @@ -805,26 +805,19 @@ class elim_uncnstr_tactic : public tactic { ast_manager & m() { return m_manager; } void init_mc(bool produce_models) { - if (!produce_models) { - m_mc = nullptr; - return; + m_mc = nullptr; + if (produce_models) { + m_mc = alloc(mc, m(), "elim_uncstr"); } - m_mc = alloc(mc, m()); } void init_rw(bool produce_proofs) { m_rw = alloc(rw, m(), produce_proofs, m_vars, m_mc.get(), m_max_memory, m_max_steps); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = nullptr; pc = nullptr; core = nullptr; - bool produce_models = g->models_enabled(); + void operator()(goal_ref const & g, goal_ref_buffer & result) { bool produce_proofs = g->proofs_enabled(); - + TRACE("elim_uncnstr_bug", g->display(tout);); tactic_report report("elim-uncnstr-vars", *g); m_vars.reset(); @@ -837,14 +830,9 @@ class elim_uncnstr_tactic : public tactic { } bool modified = true; TRACE("elim_uncnstr", tout << "unconstrained variables...\n"; - obj_hashtable::iterator it = m_vars.begin(); - obj_hashtable::iterator end = m_vars.end(); - for (; it != end; ++it) { - expr * v = *it; - tout << mk_ismt2_pp(v, m()) << " "; - } + for (expr * v : m_vars) tout << mk_ismt2_pp(v, m()) << " "; tout << "\n";); - init_mc(produce_models); + init_mc(g->models_enabled()); init_rw(produce_proofs); expr_ref new_f(m()); @@ -866,25 +854,15 @@ class elim_uncnstr_tactic : public tactic { g->update(idx, new_f, new_pr, g->dep(idx)); } if (!modified) { - if (round == 0) { - mc = nullptr; + if (round == 0) { } else { - app_ref_vector & fresh_vars = m_rw->cfg().m_fresh_vars; - m_num_elim_apps = fresh_vars.size(); - if (produce_models && !fresh_vars.empty()) { - filter_model_converter * fmc = alloc(filter_model_converter, m()); - for (unsigned i = 0; i < fresh_vars.size(); i++) - fmc->insert(fresh_vars.get(i)->get_decl()); - mc = concat(fmc, m_mc.get()); - } - else { - mc = nullptr; - } + m_num_elim_apps = m_rw->cfg().m_fresh_vars.size(); + g->add(m_mc.get()); } + TRACE("elim_uncnstr", if (m_mc) m_mc->display(tout); else tout << "no mc\n";); m_mc = nullptr; - m_rw = nullptr; - TRACE("elim_uncnstr", if (mc) mc->display(tout);); + m_rw = nullptr; result.push_back(g.get()); g->inc_depth(); return; @@ -904,13 +882,13 @@ class elim_uncnstr_tactic : public tactic { idx = 0; } } - + }; imp * m_imp; params_ref m_params; public: - elim_uncnstr_tactic(ast_manager & m, params_ref const & p): + elim_uncnstr_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } @@ -933,12 +911,9 @@ public: insert_max_steps(r); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(g, result, mc, pc, core); + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { + (*m_imp)(g, result); report_tactic_progress(":num-elim-apps", get_num_elim_apps()); } diff --git a/src/tactic/core/injectivity_tactic.cpp b/src/tactic/core/injectivity_tactic.cpp index 342f16212..64947eca0 100644 --- a/src/tactic/core/injectivity_tactic.cpp +++ b/src/tactic/core/injectivity_tactic.cpp @@ -144,12 +144,8 @@ class injectivity_tactic : public tactic { } void operator()(goal_ref const & goal, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(goal->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("injectivity", *goal); fail_if_unsat_core_generation("injectivity", goal); // TODO: Support UNSAT cores fail_if_proof_generation("injectivity", goal); @@ -271,11 +267,8 @@ public: } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_finder)(g, result, mc, pc, core); + goal_ref_buffer & result) override { + (*m_finder)(g, result); for (unsigned i = 0; i < g->size(); ++i) { expr* curr = g->form(i); diff --git a/src/tactic/core/nnf_tactic.cpp b/src/tactic/core/nnf_tactic.cpp index 4641023ad..f482181d2 100644 --- a/src/tactic/core/nnf_tactic.cpp +++ b/src/tactic/core/nnf_tactic.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "ast/normal_forms/nnf.h" #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" class nnf_tactic : public tactic { params_ref m_params; @@ -53,14 +53,9 @@ public: void collect_param_descrs(param_descrs & r) override { nnf::get_param_descrs(r); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & g, goal_ref_buffer & result) override { TRACE("nnf", tout << "params: " << m_params << "\n"; g->display(tout);); SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("nnf", *g); bool produce_proofs = g->proofs_enabled(); @@ -97,10 +92,10 @@ public: result.push_back(g.get()); unsigned num_extra_names = dnames.get_num_names(); if (num_extra_names > 0) { - filter_model_converter * fmc = alloc(filter_model_converter, m); - mc = fmc; + generic_model_converter * fmc = alloc(generic_model_converter, m, "nnf"); + g->add(fmc); for (unsigned i = 0; i < num_extra_names; i++) - fmc->insert(dnames.get_name_decl(i)); + fmc->hide(dnames.get_name_decl(i)); } TRACE("nnf", g->display(tout);); SASSERT(g->is_well_sorted()); diff --git a/src/tactic/core/occf_tactic.cpp b/src/tactic/core/occf_tactic.cpp index 519a4c580..9bd6300ba 100644 --- a/src/tactic/core/occf_tactic.cpp +++ b/src/tactic/core/occf_tactic.cpp @@ -23,13 +23,13 @@ Revision History: --*/ #include "tactic/tactical.h" #include "tactic/core/occf_tactic.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "util/cooperate.h" class occf_tactic : public tactic { struct imp { ast_manager & m; - filter_model_converter * m_mc; + generic_model_converter * m_mc; imp(ast_manager & _m): m(_m) { @@ -115,7 +115,7 @@ class occf_tactic : public tactic { SASSERT(!c2b.contains(cnstr)); expr * bvar = m.mk_fresh_const(nullptr, m.mk_bool_sort()); if (produce_models) - m_mc->insert(to_app(bvar)->get_decl()); + m_mc->hide(to_app(bvar)->get_decl()); c2b.insert(cnstr, bvar_info(bvar, sign)); if (sign) { g->assert_expr(m.mk_or(bvar, m.mk_not(cnstr)), nullptr, nullptr); @@ -128,13 +128,8 @@ class occf_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; - fail_if_proof_generation("occf", g); bool produce_models = g->models_enabled(); @@ -157,8 +152,8 @@ class occf_tactic : public tactic { if (!is_target(cls)) continue; if (produce_models && !m_mc) { - m_mc = alloc(filter_model_converter, m); - mc = m_mc; + m_mc = alloc(generic_model_converter, m, "occf"); + g->add(m_mc); } expr * keep = nullptr; new_lits.reset(); @@ -210,12 +205,9 @@ public: void updt_params(params_ref const & p) override {} void collect_param_descrs(param_descrs & r) override {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/tactic/core/pb_preprocess_tactic.cpp b/src/tactic/core/pb_preprocess_tactic.cpp index 8dfad6fe2..7f17c8dae 100644 --- a/src/tactic/core/pb_preprocess_tactic.cpp +++ b/src/tactic/core/pb_preprocess_tactic.cpp @@ -33,55 +33,13 @@ Notes: --*/ #include "tactic/core/pb_preprocess_tactic.h" #include "tactic/tactical.h" +#include "tactic/generic_model_converter.h" #include "ast/for_each_expr.h" #include "ast/pb_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" #include "ast/expr_substitution.h" #include "ast/ast_pp.h" -class pb_preproc_model_converter : public model_converter { - ast_manager& m; - pb_util pb; - expr_ref_vector m_refs; - svector > m_const; - -public: - pb_preproc_model_converter(ast_manager& m):m(m), pb(m), m_refs(m) {} - - void operator()(model_ref & mdl, unsigned goal_idx) override { - SASSERT(goal_idx == 0); - for (unsigned i = 0; i < m_const.size(); ++i) { - mdl->register_decl(m_const[i].first->get_decl(), m_const[i].second); - } - } - - void set_value(expr* e, bool p) { - while (m.is_not(e, e)) { - p = !p; - } - SASSERT(is_app(e)); - set_value_p(to_app(e), p?m.mk_true():m.mk_false()); - } - - model_converter * translate(ast_translation & translator) override { - pb_preproc_model_converter* mc = alloc(pb_preproc_model_converter, translator.to()); - for (unsigned i = 0; i < m_const.size(); ++i) { - mc->set_value_p(translator(m_const[i].first), translator(m_const[i].second)); - } - return mc; - } - -private: - void set_value_p(app* e, expr* v) { - SASSERT(e->get_num_args() == 0); - SASSERT(is_uninterp_const(e)); - m_const.push_back(std::make_pair(e, v)); - m_refs.push_back(e); - m_refs.push_back(v); - } - -}; - class pb_preprocess_tactic : public tactic { struct rec { unsigned_vector pos, neg; rec() { } }; typedef obj_map var_map; @@ -114,24 +72,31 @@ class pb_preprocess_tactic : public tactic { for (unsigned i = 0; i < m_other.size(); ++i) { out << "ot " << m_other[i] << ": " << mk_pp(g->form(m_other[i]), m) << "\n"; } - - var_map::iterator it = m_vars.begin(); - var_map::iterator end = m_vars.end(); - for (; it != end; ++it) { - app* e = it->m_key; - unsigned_vector const& pos = it->m_value.pos; - unsigned_vector const& neg = it->m_value.neg; + + for (auto const& kv : m_vars) { + app* e = kv.m_key; + unsigned_vector const& pos = kv.m_value.pos; + unsigned_vector const& neg = kv.m_value.neg; out << mk_pp(e, m) << ": "; - for (unsigned i = 0; i < pos.size(); ++i) { - out << "p: " << pos[i] << " "; + for (unsigned p : pos) { + out << "p: " << p << " "; } - for (unsigned i = 0; i < neg.size(); ++i) { - out << "n: " << neg[i] << " "; + for (unsigned n : neg) { + out << "n: " << n << " "; } out << "\n"; } } + void set_value(generic_model_converter& mc, expr* e, bool p) { + while (m.is_not(e, e)) { + p = !p; + } + SASSERT(is_app(e)); + mc.add(to_app(e), p?m.mk_true():m.mk_false()); + } + + public: pb_preprocess_tactic(ast_manager& m, params_ref const& p = params_ref()): m(m), pb(m), m_r(m) {} @@ -142,20 +107,17 @@ public: return alloc(pb_preprocess_tactic, m); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()( + goal_ref const & g, + goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); - pc = nullptr; core = nullptr; if (g->proofs_enabled()) { throw tactic_exception("pb-preprocess does not support proofs"); } - pb_preproc_model_converter* pp = alloc(pb_preproc_model_converter, m); - mc = pp; + generic_model_converter* pp = alloc(generic_model_converter, m, "pb-preprocess"); + g->add(pp); g->inc_depth(); result.push_back(g.get()); @@ -163,7 +125,7 @@ public: // decompose(g); } - bool simplify(goal_ref const& g, pb_preproc_model_converter& mc) { + bool simplify(goal_ref const& g, generic_model_converter& mc) { reset(); normalize(g); if (g->inconsistent()) { @@ -201,11 +163,11 @@ public: TRACE("pb", tout << mk_pp(e, m) << " " << r.pos.size() << " " << r.neg.size() << "\n";); if (r.pos.empty()) { replace(r.neg, e, m.mk_false(), g); - mc.set_value(e, false); + set_value(mc, e, false); } else if (r.neg.empty()) { replace(r.pos, e, m.mk_true(), g); - mc.set_value(e, true); + set_value(mc, e, true); } if (g->inconsistent()) return false; ++it; @@ -507,7 +469,7 @@ private: // Implement very special case of resolution. - void resolve(pb_preproc_model_converter& mc, unsigned idx1, + void resolve(generic_model_converter& mc, unsigned idx1, unsigned_vector const& positions, app* e, bool pos, goal_ref const& g) { if (positions.size() != 1) return; unsigned idx2 = positions[0]; @@ -556,7 +518,7 @@ private: else { args2[j] = m.mk_true(); } - mc.set_value(arg, j != min_index); + set_value(mc, arg, j != min_index); } tmp1 = pb.mk_ge(args2.size(), coeffs2.c_ptr(), args2.c_ptr(), k2); diff --git a/src/tactic/core/propagate_values_tactic.cpp b/src/tactic/core/propagate_values_tactic.cpp index e2a6e18de..41e48d864 100644 --- a/src/tactic/core/propagate_values_tactic.cpp +++ b/src/tactic/core/propagate_values_tactic.cpp @@ -23,6 +23,7 @@ Revision History: #include "ast/ast_smt2_pp.h" #include "ast/expr_substitution.h" #include "tactic/goal_shared_occs.h" +#include "ast/pb_decl_plugin.h" class propagate_values_tactic : public tactic { struct imp { @@ -129,19 +130,28 @@ class propagate_values_tactic : public tactic { } TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m) << "\n---->\n" << mk_ismt2_pp(new_curr, m) << "\n";); - push_result(new_curr, new_pr); - - if (new_curr != curr) + if (new_curr != curr) { m_modified = true; + //if (has_pb(curr)) + // IF_VERBOSE(0, verbose_stream() << mk_ismt2_pp(curr, m) << "\n---->\n" << mk_ismt2_pp(new_curr, m) << "\n"); + } + push_result(new_curr, new_pr); + } + + bool has_pb(expr* e) { + pb_util pb(m); + if (pb.is_ge(e)) return true; + if (m.is_or(e)) { + for (expr* a : *to_app(e)) { + if (pb.is_ge(a)) return true; + } + } + return false; } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("propagate-values", *g); m_goal = g.get(); @@ -240,13 +250,9 @@ public: r.insert("max_rounds", CPK_UINT, "(default: 2) maximum number of rounds."); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); diff --git a/src/tactic/core/reduce_args_tactic.cpp b/src/tactic/core/reduce_args_tactic.cpp index 333e2638c..24898320d 100644 --- a/src/tactic/core/reduce_args_tactic.cpp +++ b/src/tactic/core/reduce_args_tactic.cpp @@ -22,8 +22,7 @@ Notes: #include "ast/has_free_vars.h" #include "util/map.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" /** \brief Reduce the number of arguments in function applications. @@ -74,7 +73,7 @@ public: ~reduce_args_tactic() override; - void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) override; + void operator()(goal_ref const & g, goal_ref_buffer & result) override; void cleanup() override; }; @@ -394,13 +393,10 @@ struct reduce_args_tactic::imp { ptr_buffer new_args; var_ref_vector new_vars(m_manager); ptr_buffer new_eqs; - extension_model_converter * e_mc = alloc(extension_model_converter, m_manager); - filter_model_converter * f_mc = alloc(filter_model_converter, m_manager); - decl2arg2func_map::iterator it = decl2arg2funcs.begin(); - decl2arg2func_map::iterator end = decl2arg2funcs.end(); - for (; it != end; ++it) { - func_decl * f = it->m_key; - arg2func * map = it->m_value; + generic_model_converter * f_mc = alloc(generic_model_converter, m_manager, "reduce_args"); + for (auto const& kv : decl2arg2funcs) { + func_decl * f = kv.m_key; + arg2func * map = kv.m_value; expr * def = nullptr; SASSERT(decl2args.contains(f)); bit_vector & bv = decl2args.find(f); @@ -416,7 +412,7 @@ struct reduce_args_tactic::imp { for (; it2 != end2; ++it2) { app * t = it2->m_key; func_decl * new_def = it2->m_value; - f_mc->insert(new_def); + f_mc->hide(new_def); SASSERT(new_def->get_arity() == new_args.size()); app * new_t = m_manager.mk_app(new_def, new_args.size(), new_args.c_ptr()); if (def == nullptr) { @@ -438,12 +434,12 @@ struct reduce_args_tactic::imp { } } SASSERT(def); - e_mc->insert(f, def); + f_mc->add(f, def); } - return concat(f_mc, e_mc); + return f_mc; } - void operator()(goal & g, model_converter_ref & mc) { + void operator()(goal & g) { if (g.inconsistent()) return; TRACE("reduce_args", g.display(tout);); @@ -472,9 +468,9 @@ struct reduce_args_tactic::imp { report_tactic_progress(":reduced-funcs", decl2args.size()); if (g.models_enabled()) - mc = mk_mc(decl2args, ctx.m_decl2arg2funcs); + g.add(mk_mc(decl2args, ctx.m_decl2arg2funcs)); - TRACE("reduce_args", g.display(tout); if (mc) mc->display(tout);); + TRACE("reduce_args", g.display(tout); if (g.mc()) g.mc()->display(tout);); } }; @@ -487,15 +483,12 @@ reduce_args_tactic::~reduce_args_tactic() { } void reduce_args_tactic::operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("reduce-args", g); fail_if_unsat_core_generation("reduce-args", g); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); - m_imp->operator()(*(g.get()), mc); + result.reset(); + m_imp->operator()(*(g.get())); g->inc_depth(); result.push_back(g.get()); SASSERT(g->is_well_sorted()); diff --git a/src/tactic/core/simplify_tactic.cpp b/src/tactic/core/simplify_tactic.cpp index 842e1127d..7e729714f 100644 --- a/src/tactic/core/simplify_tactic.cpp +++ b/src/tactic/core/simplify_tactic.cpp @@ -93,15 +93,11 @@ void simplify_tactic::get_param_descrs(param_descrs & r) { } void simplify_tactic::operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { try { (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); - mc = nullptr; pc = nullptr; core = nullptr; } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); diff --git a/src/tactic/core/simplify_tactic.h b/src/tactic/core/simplify_tactic.h index c780ceabd..e64408a29 100644 --- a/src/tactic/core/simplify_tactic.h +++ b/src/tactic/core/simplify_tactic.h @@ -31,15 +31,13 @@ public: ~simplify_tactic() override; void updt_params(params_ref const & p) override; + static void get_param_descrs(param_descrs & r); + void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override; - + void operator()(goal_ref const & in, goal_ref_buffer & result) override; + void cleanup() override; unsigned get_num_steps() const; diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index 9253b7172..acd75d663 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -18,15 +18,16 @@ Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/expr_replacer.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/occurs.h" #include "util/cooperate.h" #include "tactic/goal_shared_occs.h" #include "ast/ast_pp.h" +#include "ast/pb_decl_plugin.h" class solve_eqs_tactic : public tactic { struct imp { - typedef extension_model_converter gmc; + typedef generic_model_converter gmc; ast_manager & m_manager; expr_replacer * m_r; @@ -263,8 +264,8 @@ class solve_eqs_tactic : public tactic { bool solve_arith_core(app * lhs, expr * rhs, expr * eq, app_ref & var, expr_ref & def, proof_ref & pr) { SASSERT(m_a_util.is_add(lhs)); bool is_int = m_a_util.is_int(lhs); - expr * a; - expr * v; + expr * a = nullptr; + expr * v = nullptr; rational a_val; unsigned num = lhs->get_num_args(); unsigned i; @@ -583,10 +584,8 @@ class solve_eqs_tactic : public tactic { expr_ref new_def(m()); proof_ref new_pr(m()); expr_dependency_ref new_dep(m()); - unsigned size = m_ordered_vars.size(); - for (unsigned idx = 0; idx < size; idx++) { + for (app * v : m_ordered_vars) { checkpoint(); - expr * v = m_ordered_vars[idx]; expr * def = nullptr; proof * pr = nullptr; expr_dependency * dep = nullptr; @@ -605,8 +604,7 @@ class solve_eqs_tactic : public tactic { m_subst->reset(); TRACE("solve_eqs", tout << "after normalizing variables\n"; - for (unsigned i = 0; i < m_ordered_vars.size(); i++) { - expr * v = m_ordered_vars[i]; + for (expr * v : m_ordered_vars) { expr * def = 0; proof * pr = 0; expr_dependency * dep = 0; @@ -615,16 +613,15 @@ class solve_eqs_tactic : public tactic { }); #if 0 DEBUG_CODE({ - for (unsigned i = 0; i < m_ordered_vars.size(); i++) { - expr * v = m_ordered_vars[i]; - expr * def = 0; - proof * pr = 0; - expr_dependency * dep = 0; - m_norm_subst->find(v, def, pr, dep); - SASSERT(def != 0); - CASSERT("solve_eqs_bug", !occurs(v, def)); - } - }); + for (expr * v : m_ordered_vars) { + expr * def = 0; + proof * pr = 0; + expr_dependency * dep = 0; + m_norm_subst->find(v, def, pr, dep); + SASSERT(def != 0); + CASSERT("solve_eqs_bug", !occurs(v, def)); + } + }); #endif } @@ -651,6 +648,13 @@ class solve_eqs_tactic : public tactic { } m_r->operator()(f, new_f, new_pr, new_dep); +#if 0 + pb_util pb(m()); + if (pb.is_ge(f) && f != new_f) { + IF_VERBOSE(0, verbose_stream() << mk_ismt2_pp(f, m()) << "\n--->\n" << mk_ismt2_pp(new_f, m()) << "\n"); + } +#endif + TRACE("solve_eqs_subst", tout << mk_ismt2_pp(f, m()) << "\n--->\n" << mk_ismt2_pp(new_f, m()) << "\n";); m_num_steps += m_r->get_num_steps() + 1; if (m_produce_proofs) @@ -668,12 +672,11 @@ class solve_eqs_tactic : public tactic { g.display(tout);); #if 0 DEBUG_CODE({ - for (unsigned i = 0; i < m_ordered_vars.size(); i++) { - expr * v = m_ordered_vars[i]; - for (unsigned j = 0; j < g.size(); j++) { - CASSERT("solve_eqs_bug", !occurs(v, g.form(j))); - } - }}); + for (expr* v : m_ordered_vars) { + for (unsigned j = 0; j < g.size(); j++) { + CASSERT("solve_eqs_bug", !occurs(v, g.form(j))); + } + }}); #endif } @@ -681,15 +684,15 @@ class solve_eqs_tactic : public tactic { IF_VERBOSE(100, if (!m_ordered_vars.empty()) verbose_stream() << "num. eliminated vars: " << m_ordered_vars.size() << "\n";); m_num_eliminated_vars += m_ordered_vars.size(); if (m_produce_models) { - if (mc.get() == nullptr) - mc = alloc(gmc, m()); + if (!mc.get()) + mc = alloc(gmc, m(), "solve-eqs"); for (app* v : m_ordered_vars) { expr * def = nullptr; proof * pr; - expr_dependency * dep; + expr_dependency * dep = nullptr; m_norm_subst->find(v, def, pr, dep); - SASSERT(def != 0); - static_cast(mc.get())->insert(v->get_decl(), def); + SASSERT(def); + static_cast(mc.get())->add(v, def); } } } @@ -742,13 +745,9 @@ class solve_eqs_tactic : public tactic { return m_num_eliminated_vars; } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; + model_converter_ref mc; tactic_report report("solve_eqs", *g); m_produce_models = g->models_enabled(); m_produce_proofs = g->proofs_enabled(); @@ -768,7 +767,6 @@ class solve_eqs_tactic : public tactic { normalize(); substitute(*(g.get())); if (g->inconsistent()) { - mc = nullptr; break; } save_elim_vars(mc); @@ -776,6 +774,7 @@ class solve_eqs_tactic : public tactic { } } g->inc_depth(); + g->add(mc.get()); result.push_back(g.get()); TRACE("solve_eqs", g->display(tout);); SASSERT(g->is_well_sorted()); @@ -809,12 +808,9 @@ public: r.insert("ite_solver", CPK_BOOL, "(default: true) use if-then-else solver."); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); report_tactic_progress(":num-elim-vars", m_imp->get_num_eliminated_vars()); } diff --git a/src/tactic/core/split_clause_tactic.cpp b/src/tactic/core/split_clause_tactic.cpp index 37d9e902e..138bc91ec 100644 --- a/src/tactic/core/split_clause_tactic.cpp +++ b/src/tactic/core/split_clause_tactic.cpp @@ -46,21 +46,15 @@ class split_clause_tactic : public tactic { } class split_pc : public proof_converter { - ast_manager & m_manager; - app * m_clause; - proof * m_clause_pr; + app_ref m_clause; + proof_ref m_clause_pr; public: - split_pc(ast_manager & m, app * cls, proof * pr):m_manager(m), m_clause(cls), m_clause_pr(pr) { - m.inc_ref(cls); - m.inc_ref(pr); + split_pc(ast_manager & m, app * cls, proof * pr):m_clause(cls, m), m_clause_pr(pr, m) { } - ~split_pc() override { - m_manager.dec_ref(m_clause); - m_manager.dec_ref(m_clause_pr); - } + ~split_pc() override { } - void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) override { + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { // Let m_clause be of the form (l_0 or ... or l_{num_source - 1}) // Each source[i] proof is a proof for "false" using l_i as a hypothesis // So, I use lemma for producing a proof for (not l_i) that does not contain the hypothesis, @@ -73,12 +67,14 @@ class split_clause_tactic : public tactic { expr * not_li = m.mk_not(m_clause->get_arg(i)); prs.push_back(m.mk_lemma(pr_i, not_li)); } - result = m.mk_unit_resolution(prs.size(), prs.c_ptr()); + return proof_ref(m.mk_unit_resolution(prs.size(), prs.c_ptr()), m); } proof_converter * translate(ast_translation & translator) override { - return alloc(split_pc, translator.to(), translator(m_clause), translator(m_clause_pr)); + return alloc(split_pc, translator.to(), translator(m_clause.get()), translator(m_clause_pr.get())); } + + void display(std::ostream & out) override { out << "(split-clause-pc)\n"; } }; public: @@ -99,19 +95,15 @@ public: m_largest_clause = p.get_bool("split_largest_clause", false); } - void collect_param_descrs(param_descrs & r) override { + void collect_param_descrs(param_descrs & r) override { r.insert("split_largest_clause", CPK_BOOL, "(default: false) split the largest clause in the goal."); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { SASSERT(in->is_well_sorted()); tactic_report report("split-clause", *in); TRACE("before_split_clause", in->display(tout);); - pc = nullptr; mc = nullptr; core = nullptr; ast_manager & m = in->m(); unsigned cls_pos = select_clause(m, in); if (cls_pos == UINT_MAX) { @@ -121,16 +113,11 @@ public: app * cls = to_app(in->form(cls_pos)); expr_dependency * cls_dep = in->dep(cls_pos); if (produce_proofs) - pc = alloc(split_pc, m, cls, in->pr(cls_pos)); - unsigned cls_sz = cls->get_num_args(); - report_tactic_progress(":num-new-branches", cls_sz); - for (unsigned i = 0; i < cls_sz; i++) { - goal * subgoal_i; - if (i == cls_sz - 1) - subgoal_i = in.get(); - else - subgoal_i = alloc(goal, *in); - expr * lit_i = cls->get_arg(i); + in->set(alloc(split_pc, m, cls, in->pr(cls_pos))); + report_tactic_progress(":num-new-branches", cls->get_num_args()); + for (expr* lit_i : *cls) { + goal * subgoal_i = alloc(goal, *in); + subgoal_i->set(in->mc()); proof * pr_i = nullptr; if (produce_proofs) pr_i = m.mk_hypothesis(lit_i); @@ -138,6 +125,8 @@ public: subgoal_i->inc_depth(); result.push_back(subgoal_i); } + in->set(concat(in->pc(), result.size(), result.c_ptr())); + in->add(dependency_converter::concat(result.size(), result.c_ptr())); } void cleanup() override { diff --git a/src/tactic/core/symmetry_reduce_tactic.cpp b/src/tactic/core/symmetry_reduce_tactic.cpp index 81f626f38..d288adbc7 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -38,12 +38,9 @@ public: ~symmetry_reduce_tactic() override; - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override; - void cleanup() override; + void operator()(goal_ref const & g, + goal_ref_buffer & result) override; + virtual void cleanup() override; }; class ac_rewriter { @@ -635,13 +632,10 @@ symmetry_reduce_tactic::~symmetry_reduce_tactic() { } void symmetry_reduce_tactic::operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { fail_if_proof_generation("symmetry_reduce", g); fail_if_unsat_core_generation("symmetry_reduce", g); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); (*m_imp)(*(g.get())); g->inc_depth(); result.push_back(g.get()); diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index 6efce9d4e..2abaae0e6 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -51,7 +51,7 @@ Notes: --*/ #include "tactic/tactical.h" #include "tactic/goal_shared_occs.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/rewriter/bool_rewriter.h" #include "tactic/core/simplify_tactic.h" #include "util/cooperate.h" @@ -80,7 +80,7 @@ class tseitin_cnf_tactic : public tactic { frame(app * n):m_t(n), m_first(true) {} }; - typedef filter_model_converter mc; + typedef generic_model_converter mc; ast_manager & m; svector m_frame_stack; @@ -344,7 +344,7 @@ class tseitin_cnf_tactic : public tactic { app * v = m.mk_fresh_const(nullptr, m.mk_bool_sort()); m_fresh_vars.push_back(v); if (m_mc) - m_mc->insert(v->get_decl()); + m_mc->hide(v->get_decl()); return v; } @@ -799,12 +799,8 @@ class tseitin_cnf_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("tseitin-cnf", *g); fail_if_proof_generation("tseitin-cnf", g); m_produce_models = g->models_enabled(); @@ -817,7 +813,7 @@ class tseitin_cnf_tactic : public tactic { m_frame_stack.reset(); m_clauses.reset(); if (m_produce_models) - m_mc = alloc(filter_model_converter, m); + m_mc = alloc(generic_model_converter, m, "tseitin"); else m_mc = nullptr; @@ -843,9 +839,7 @@ class tseitin_cnf_tactic : public tactic { g->assert_expr(cls); } if (m_produce_models && !m_fresh_vars.empty()) - mc = m_mc.get(); - else - mc = nullptr; + g->add(m_mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("tseitin_cnf", g->display(tout);); @@ -883,12 +877,8 @@ public: r.insert("ite_extra", CPK_BOOL, "(default: true) add redundant clauses (that improve unit propagation) when encoding if-then-else formulas"); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer & result) override { + (*m_imp)(in, result); report_tactic_progress(":cnf-aux-vars", m_imp->m_num_aux_vars); } diff --git a/src/tactic/dependency_converter.cpp b/src/tactic/dependency_converter.cpp new file mode 100644 index 000000000..34c864526 --- /dev/null +++ b/src/tactic/dependency_converter.cpp @@ -0,0 +1,107 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + dependency_converter.cpp + +Abstract: + + Utility for converting dependencies across subgoals. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-11-19 + +Notes: + + +--*/ + +#include "tactic/dependency_converter.h" +#include "tactic/goal.h" + +class unit_dependency_converter : public dependency_converter { + expr_dependency_ref m_dep; +public: + + unit_dependency_converter(expr_dependency_ref& d) : m_dep(d) {} + + virtual expr_dependency_ref operator()() { return m_dep; } + + virtual dependency_converter * translate(ast_translation & translator) { + expr_dependency_translation tr(translator); + expr_dependency_ref d(tr(m_dep), translator.to()); + return alloc(unit_dependency_converter, d); + } + + virtual void display(std::ostream& out) { + out << m_dep.get() << "\n"; + } +}; + +class concat_dependency_converter : public dependency_converter { + dependency_converter_ref m_dc1; + dependency_converter_ref m_dc2; +public: + + concat_dependency_converter(dependency_converter* c1, dependency_converter* c2) : m_dc1(c1), m_dc2(c2) {} + + virtual expr_dependency_ref operator()() { + expr_dependency_ref d1 = (*m_dc1)(); + expr_dependency_ref d2 = (*m_dc2)(); + ast_manager& m = d1.get_manager(); + return expr_dependency_ref(m.mk_join(d1, d2), m); + } + + virtual dependency_converter * translate(ast_translation & translator) { + return alloc(concat_dependency_converter, m_dc1->translate(translator), m_dc2->translate(translator)); + } + + virtual void display(std::ostream& out) { + m_dc1->display(out); + m_dc2->display(out); + } +}; + +class goal_dependency_converter : public dependency_converter { + ast_manager& m; + goal_ref_buffer m_goals; +public: + goal_dependency_converter(unsigned n, goal * const* goals) : + m(goals[0]->m()) { + for (unsigned i = 0; i < n; ++i) m_goals.push_back(goals[i]); + } + + virtual expr_dependency_ref operator()() { + expr_dependency_ref result(m.mk_empty_dependencies(), m); + for (goal_ref g : m_goals) { + dependency_converter_ref dc = g->dc(); + if (dc) result = m.mk_join(result, (*dc)()); + } + return result; + } + virtual dependency_converter * translate(ast_translation & translator) { + goal_ref_buffer goals; + for (goal_ref g : m_goals) goals.push_back(g->translate(translator)); + return alloc(goal_dependency_converter, goals.size(), goals.c_ptr()); + } + + virtual void display(std::ostream& out) { out << "goal-dep\n"; } + +}; + +dependency_converter * dependency_converter::concat(dependency_converter * mc1, dependency_converter * mc2) { + if (!mc1) return mc2; + if (!mc2) return mc1; + return alloc(concat_dependency_converter, mc1, mc2); +} + +dependency_converter* dependency_converter::unit(expr_dependency_ref& d) { + return alloc(unit_dependency_converter, d); +} + +dependency_converter * dependency_converter::concat(unsigned n, goal * const* goals) { + if (n == 0) return nullptr; + return alloc(goal_dependency_converter, n, goals); +} diff --git a/src/tactic/dependency_converter.h b/src/tactic/dependency_converter.h new file mode 100644 index 000000000..4ea0c6672 --- /dev/null +++ b/src/tactic/dependency_converter.h @@ -0,0 +1,47 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + dependency_converter.h + +Abstract: + + Utility for converting dependencies across subgoals. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-11-19 + +Notes: + + +--*/ +#ifndef DEPENDENCY_CONVERTER_H_ +#define DEPENDENCY_CONVERTER_H_ + +#include "util/ref.h" +#include "ast/ast_pp_util.h" +#include "model/model.h" +#include "tactic/converter.h" + +class goal; + +class dependency_converter : public converter { +public: + static dependency_converter* unit(expr_dependency_ref& d); + + static dependency_converter* concat(dependency_converter * dc1, dependency_converter * dc2); + + static dependency_converter* concat(unsigned n, goal * const* goals); + + virtual expr_dependency_ref operator()() = 0; + + virtual dependency_converter * translate(ast_translation & translator) = 0; +}; + +typedef ref dependency_converter_ref; +typedef sref_vector dependency_converter_ref_vector; +typedef sref_buffer dependency_converter_ref_buffer; + +#endif diff --git a/src/tactic/equiv_proof_converter.h b/src/tactic/equiv_proof_converter.h index 12cd26215..5f72b3b07 100644 --- a/src/tactic/equiv_proof_converter.h +++ b/src/tactic/equiv_proof_converter.h @@ -35,8 +35,8 @@ public: ~equiv_proof_converter() override {} - void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) override { - m_replace(m, num_source, source, result); + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { + return m_replace(m, num_source, source); } proof_converter * translate(ast_translation & translator) override { @@ -47,6 +47,7 @@ public: ast_manager& get_manager() { return m; } + void display(std::ostream & out) override {} }; #endif diff --git a/src/tactic/extension_model_converter.cpp b/src/tactic/extension_model_converter.cpp deleted file mode 100644 index 38f0100ee..000000000 --- a/src/tactic/extension_model_converter.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - extension_model_converter.cpp - -Abstract: - - Model converter that introduces eliminated variables in a model. - -Author: - - Leonardo (leonardo) 2011-10-21 - -Notes: - ---*/ -#include "tactic/extension_model_converter.h" -#include "model/model_evaluator.h" -#include "ast/ast_smt2_pp.h" -#include "model/model_v2_pp.h" -#include "ast/ast_pp.h" - -extension_model_converter::~extension_model_converter() { -} - -#ifdef _TRACE -static void display_decls_info(std::ostream & out, model_ref & md) { - ast_manager & m = md->get_manager(); - unsigned sz = md->get_num_decls(); - for (unsigned i = 0; i < sz; i++) { - func_decl * d = md->get_decl(i); - out << d->get_name(); - out << " ("; - for (unsigned j = 0; j < d->get_arity(); j++) - out << mk_pp(d->get_domain(j), m); - out << mk_pp(d->get_range(), m); - out << ") "; - if (d->get_info()) - out << *(d->get_info()); - out << " :id " << d->get_id() << "\n"; - } -} -#endif - -void extension_model_converter::operator()(model_ref & md, unsigned goal_idx) { - SASSERT(goal_idx == 0); - TRACE("extension_mc", model_v2_pp(tout, *md); display_decls_info(tout, md);); - model_evaluator ev(*(md.get())); - ev.set_model_completion(true); - ev.set_expand_array_equalities(false); - expr_ref val(m()); - unsigned i = m_vars.size(); - while (i > 0) { - --i; - expr * def = m_defs.get(i); - ev(def, val); - TRACE("extension_mc", tout << m_vars.get(i)->get_name() << " ->\n" << mk_ismt2_pp(def, m()) << "\n==>\n" << mk_ismt2_pp(val, m()) << "\n";); - func_decl * f = m_vars.get(i); - unsigned arity = f->get_arity(); - if (arity == 0) { - md->register_decl(f, val); - } - else { - func_interp * new_fi = alloc(func_interp, m(), arity); - new_fi->set_else(val); - md->register_decl(f, new_fi); - } - } - TRACE("extension_mc", model_v2_pp(tout, *md); display_decls_info(tout, md);); -} - -void extension_model_converter::insert(func_decl * v, expr * def) { - m_vars.push_back(v); - m_defs.push_back(def); -} - - -void extension_model_converter::display(std::ostream & out) { - out << "(extension-model-converter"; - for (unsigned i = 0; i < m_vars.size(); i++) { - out << "\n (" << m_vars.get(i)->get_name() << " "; - unsigned indent = m_vars.get(i)->get_name().size() + 4; - out << mk_ismt2_pp(m_defs.get(i), m(), indent) << ")"; - } - out << ")" << std::endl; -} - -model_converter * extension_model_converter::translate(ast_translation & translator) { - extension_model_converter * res = alloc(extension_model_converter, translator.to()); - for (unsigned i = 0; i < m_vars.size(); i++) - res->m_vars.push_back(translator(m_vars[i].get())); - for (unsigned i = 0; i < m_defs.size(); i++) - res->m_defs.push_back(translator(m_defs[i].get())); - return res; -} diff --git a/src/tactic/extension_model_converter.h b/src/tactic/extension_model_converter.h index bc1a2699d..0816fdd8b 100644 --- a/src/tactic/extension_model_converter.h +++ b/src/tactic/extension_model_converter.h @@ -35,7 +35,7 @@ public: ast_manager & m() const { return m_vars.get_manager(); } - void operator()(model_ref & md, unsigned goal_idx) override; + void operator()(model_ref & md) override; void display(std::ostream & out) override; diff --git a/src/tactic/filter_model_converter.cpp b/src/tactic/filter_model_converter.cpp deleted file mode 100644 index 46328186d..000000000 --- a/src/tactic/filter_model_converter.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - filter_model_converter.cpp - -Abstract: - - Filter decls from a model - -Author: - - Leonardo (leonardo) 2011-05-06 - -Notes: - ---*/ -#include "tactic/filter_model_converter.h" -#include "model/model_v2_pp.h" - -filter_model_converter::~filter_model_converter() { -} - -void filter_model_converter::operator()(model_ref & old_model, unsigned goal_idx) { - TRACE("filter_mc", tout << "before filter_model_converter\n"; model_v2_pp(tout, *old_model); display(tout);); - ast_fast_mark1 fs; - unsigned num = m_decls.size(); - for (unsigned i = 0; i < num; i++) - fs.mark(m_decls.get(i)); - model * new_model = alloc(model, m()); - num = old_model->get_num_constants(); - for (unsigned i = 0; i < num; i++) { - func_decl * f = old_model->get_constant(i); - if (fs.is_marked(f)) - continue; - expr * fi = old_model->get_const_interp(f); - new_model->register_decl(f, fi); - } - num = old_model->get_num_functions(); - for (unsigned i = 0; i < num; i++) { - func_decl * f = old_model->get_function(i); - if (fs.is_marked(f)) - continue; - func_interp * fi = old_model->get_func_interp(f); - SASSERT(fi); - new_model->register_decl(f, fi->copy()); - } - new_model->copy_usort_interps(*old_model); - old_model = new_model; - TRACE("filter_mc", tout << "after filter_model_converter\n"; model_v2_pp(tout, *old_model);); -} - -void filter_model_converter::operator()(svector & labels, unsigned goal_idx) { -} - -void filter_model_converter::display(std::ostream & out) { - out << "(filter-model-converter"; - for (unsigned i = 0; i < m_decls.size(); i++) { - out << " " << m_decls.get(i)->get_name(); - } - out << ")" << std::endl; -} - -model_converter * filter_model_converter::translate(ast_translation & translator) { - filter_model_converter * res = alloc(filter_model_converter, translator.to()); - for (unsigned i = 0; i < m_decls.size(); i++) - res->m_decls.push_back(translator(m_decls[i].get())); - return res; -} diff --git a/src/tactic/fpa/fpa2bv_model_converter.h b/src/tactic/fpa/fpa2bv_model_converter.h index 9fad9259d..2020657b7 100644 --- a/src/tactic/fpa/fpa2bv_model_converter.h +++ b/src/tactic/fpa/fpa2bv_model_converter.h @@ -37,17 +37,12 @@ public: dealloc(m_bv2fp); } - void operator()(model_ref & md, unsigned goal_idx) override { - SASSERT(goal_idx == 0); + void operator()(model_ref & md) override { model * new_model = alloc(model, m); convert(md.get(), new_model); md = new_model; } - void operator()(model_ref & md) override { - operator()(md, 0); - } - void display(std::ostream & out) override; model_converter * translate(ast_translation & translator) override; diff --git a/src/tactic/fpa/fpa2bv_tactic.cpp b/src/tactic/fpa/fpa2bv_tactic.cpp index 41802c7d3..30babdd33 100644 --- a/src/tactic/fpa/fpa2bv_tactic.cpp +++ b/src/tactic/fpa/fpa2bv_tactic.cpp @@ -47,16 +47,13 @@ class fpa2bv_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); m_proofs_enabled = g->proofs_enabled(); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); tactic_report report("fpa2bv", *g); m_rw.reset(); @@ -100,7 +97,7 @@ class fpa2bv_tactic : public tactic { } if (g->models_enabled()) - mc = mk_fpa2bv_model_converter(m, m_conv); + g->add(mk_fpa2bv_model_converter(m, m_conv)); g->inc_depth(); result.push_back(g.get()); @@ -110,7 +107,7 @@ class fpa2bv_tactic : public tactic { SASSERT(g->is_well_sorted()); TRACE("fpa2bv", tout << "AFTER: " << std::endl; g->display(tout); - if (mc) mc->display(tout); tout << std::endl; ); + if (g->mc()) g->mc()->display(tout); tout << std::endl; ); } }; @@ -140,12 +137,9 @@ public: } void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); diff --git a/src/tactic/generic_model_converter.cpp b/src/tactic/generic_model_converter.cpp new file mode 100644 index 000000000..9e2a7b9b2 --- /dev/null +++ b/src/tactic/generic_model_converter.cpp @@ -0,0 +1,264 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + generic_model_converter.cpp + +Abstract: + + Generic model converter that hides and adds entries. + It subsumes filter_model_converter and extension_model_converter. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-29 + +Notes: + +--*/ +#include "ast/ast_pp.h" +#include "ast/for_each_expr.h" +#include "ast/ast_util.h" +#include "ast/occurs.h" +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/rewriter/th_rewriter.h" +#include "tactic/generic_model_converter.h" +#include "model/model_v2_pp.h" +#include "model/model_evaluator.h" + + +generic_model_converter::~generic_model_converter() { +} + +void generic_model_converter::add(func_decl * d, expr* e) { + VERIFY(e); + entry et(d, e, m, ADD); + VERIFY(d->get_range() == m.get_sort(e)); + m_first_idx.insert_if_not_there(et.m_f, m_entries.size()); + m_entries.push_back(et); +} + +void generic_model_converter::operator()(model_ref & md) { + TRACE("model_converter", tout << "before generic_model_converter\n"; model_v2_pp(tout, *md); display(tout);); + // IF_VERBOSE(0, verbose_stream() << "Apply converter " << m_orig << "\n";); + model_evaluator ev(*(md.get())); + ev.set_model_completion(true); + ev.set_expand_array_equalities(false); + expr_ref val(m); + unsigned arity; + bool reset_ev = false; + for (unsigned i = m_entries.size(); i-- > 0; ) { + entry const& e = m_entries[i]; + switch (e.m_instruction) { + case instruction::HIDE: + md->unregister_decl(e.m_f); + break; + case instruction::ADD: + ev(e.m_def, val); + TRACE("model_converter", tout << e.m_f->get_name() << " ->\n" << e.m_def << "\n==>\n" << val << "\n";); + arity = e.m_f->get_arity(); + reset_ev = false; + if (arity == 0) { + expr* old_val = md->get_const_interp(e.m_f); + if (old_val && old_val == val) { + // skip + } + else { + reset_ev = old_val != nullptr; + md->register_decl(e.m_f, val); + } + } + else { + func_interp * old_val = md->get_func_interp(e.m_f); + if (old_val && old_val->get_else() == val) { + // skip + } + else { + reset_ev = old_val != nullptr; + func_interp * new_fi = alloc(func_interp, m, arity); + new_fi->set_else(val); + md->register_decl(e.m_f, new_fi); + } + } + if (reset_ev) { + ev.reset(); + ev.set_model_completion(true); + ev.set_expand_array_equalities(false); + } + break; + } + } + TRACE("model_converter", tout << "after generic_model_converter\n"; model_v2_pp(tout, *md);); +} + +void generic_model_converter::display(std::ostream & out) { + for (entry const& e : m_entries) { + switch (e.m_instruction) { + case instruction::HIDE: + display_del(out, e.m_f); + break; + case instruction::ADD: + display_add(out, m, e.m_f, e.m_def); + break; + } + } +} + +model_converter * generic_model_converter::translate(ast_translation & translator) { + ast_manager& to = translator.to(); + generic_model_converter * res = alloc(generic_model_converter, to, m_orig.c_str()); + for (entry const& e : m_entries) { + res->m_entries.push_back(entry(translator(e.m_f.get()), translator(e.m_def.get()), to, e.m_instruction)); + } + return res; +} + +void generic_model_converter::collect(ast_pp_util& visitor) { + m_env = &visitor.env(); + for (entry const& e : m_entries) { + visitor.coll.visit_func(e.m_f); + if (e.m_def) visitor.coll.visit(e.m_def); + } +} + +struct min_app_idx_proc { + unsigned m_min; + obj_map& m_idxs; + min_app_idx_proc(obj_map& idxs) : m_min(UINT_MAX), m_idxs(idxs) {} + void operator()(app * n) { + unsigned idx; + if (m_idxs.find(n->get_decl(), idx)) { + m_min = std::min(m_min, idx); + } + } + void operator()(var * n) {} + void operator()(quantifier * n) {} +}; + +void generic_model_converter::operator()(expr_ref& fml) { + min_app_idx_proc min_proc(m_first_idx); + for_each_expr(min_proc, fml); + unsigned min_idx = min_proc.m_min; + if (min_idx == UINT_MAX) return; + expr_ref_vector fmls(m); + fmls.push_back(fml); + for (unsigned i = m_entries.size(); i-- > min_idx;) { + entry const& e = m_entries[i]; + if (e.m_instruction != instruction::ADD) { + continue; + } + unsigned arity = e.m_f->get_arity(); + if (arity == 0) { + fmls.push_back(simplify_def(e)); + } + else { + expr_ref_vector args(m); + sort_ref_vector sorts(m); + svector names; + for (unsigned i = 0; i < arity; ++i) { + sorts.push_back(e.m_f->get_domain(i)); + names.push_back(symbol(i)); + args.push_back(m.mk_var(i, sorts.back())); + } + // TBD: check if order is correct with respect to quantifier binding ordering + expr_ref lhs(m.mk_app(e.m_f, arity, args.c_ptr()), m); + expr_ref body(m.mk_eq(lhs, e.m_def), m); + fmls.push_back(m.mk_forall(arity, sorts.c_ptr(), names.c_ptr(), body)); + } + if (m_first_idx[e.m_f] == i) { + m_first_idx.remove(e.m_f); + } + } + unsigned j = min_idx; + for (unsigned i = min_idx; i < m_entries.size(); ++i) { + entry& e = m_entries[i]; + if (e.m_instruction == instruction::HIDE) { + if (i != j) { + m_entries[j] = e; + } + ++j; + } + } + m_entries.shrink(j); + fml = mk_and(fmls); +} + +void generic_model_converter::get_units(obj_map& units) { + th_rewriter rw(m); + expr_safe_replace rep(m); + expr_ref tmp(m); + for (auto const& kv : units) { + rep.insert(kv.m_key, kv.m_value ? m.mk_true() : m.mk_false()); + } + for (unsigned i = m_entries.size(); i-- > 0;) { + entry const& e = m_entries[i]; + switch (e.m_instruction) { + case HIDE: + tmp = m.mk_const(e.m_f); + if (units.contains(tmp)) { + m.dec_ref(tmp); + units.remove(tmp); + } + break; + case ADD: + if (e.m_f->get_arity() == 0 && m.is_bool(e.m_f->get_range())) { + tmp = m.mk_const(e.m_f); + if (units.contains(tmp)) { + break; + } + tmp = e.m_def; + rep(tmp); + rw(tmp); + if (m.is_true(tmp)) { + tmp = m.mk_const(e.m_f); + m.inc_ref(tmp); + units.insert(tmp, true); + rep.insert(tmp, m.mk_true()); + } + else if (m.is_false(tmp)) { + tmp = m.mk_const(e.m_f); + m.inc_ref(tmp); + units.insert(tmp, false); + rep.insert(tmp, m.mk_false()); + } + } + break; + } + } +} + + +/* + \brief simplify definition expansion from model converter in the case they come from blocked clauses. + In this case the definitions are of the form: + + x <=> x or not (C) + + or dually, + + x <=> not (not x or not C) + + in either case the definitions simplify to + + x or C + + */ +expr_ref generic_model_converter::simplify_def(entry const& e) { + expr_ref result(m); + expr_ref c(m.mk_const(e.m_f), m); + if (m.is_bool(c) && occurs(c, e.m_def)) { + expr_safe_replace rep(m); + expr_ref result1 = e.m_def; + expr_ref result2 = e.m_def; + rep.apply_substitution(c, m.mk_true(), result1); + rep.apply_substitution(c, m.mk_false(), result2); + th_rewriter rw(m); + expr_ref result(m.mk_and(m.mk_implies(result2, c), m.mk_implies(c, result1)), m); + rw(result); + return result; + } + else { + return expr_ref(m.mk_eq(c, e.m_def), m); + } +} diff --git a/src/tactic/generic_model_converter.h b/src/tactic/generic_model_converter.h new file mode 100644 index 000000000..d303fb2e3 --- /dev/null +++ b/src/tactic/generic_model_converter.h @@ -0,0 +1,80 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + generic_model_converter.h + +Abstract: + + Generic model converter that hides and adds entries. + It subsumes filter_model_converter and extension_model_converter. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-29 + +Notes: + +--*/ +#ifndef GENERIC_MODEL_CONVERTER_H_ +#define GENERIC_MODEL_CONVERTER_H_ + +#include "tactic/model_converter.h" + +class generic_model_converter : public model_converter { + enum instruction { HIDE, ADD }; + struct entry { + func_decl_ref m_f; + expr_ref m_def; + instruction m_instruction; + entry(func_decl* f, expr* d, ast_manager& m, instruction i): + m_f(f, m), m_def(d, m), m_instruction(i) {} + + entry& operator=(entry const& other) { + m_f = other.m_f; + m_def = other.m_def; + m_instruction = other.m_instruction; + return *this; + } + }; + ast_manager& m; + std::string m_orig; + vector m_entries; + obj_map m_first_idx; + + expr_ref simplify_def(entry const& e); + +public: + generic_model_converter(ast_manager & m, char const* orig) : m(m), m_orig(orig) {} + + virtual ~generic_model_converter(); + + void hide(expr* e) { SASSERT(is_app(e) && to_app(e)->get_num_args() == 0); hide(to_app(e)->get_decl()); } + + void hide(func_decl * f) { m_entries.push_back(entry(f, 0, m, HIDE)); } + + void add(func_decl * d, expr* e); + + void add(expr * d, expr* e) { SASSERT(is_app(d) && to_app(d)->get_num_args() == 0); add(to_app(d)->get_decl(), e); } + + void operator()(labels_vec & labels) override {} + + void operator()(model_ref & md) override; + + void cancel() override {} + + void display(std::ostream & out) override; + + model_converter * translate(ast_translation & translator) override; + + void collect(ast_pp_util& visitor) override; + + void operator()(expr_ref& fml) override; + + void get_units(obj_map& units) override; +}; + +typedef ref generic_model_converter_ref; + +#endif diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index a8c2452e6..d5d2ee558 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -16,11 +16,11 @@ Author: Revision History: --*/ -#include "tactic/goal.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/for_each_expr.h" #include "ast/well_sorted.h" +#include "tactic/goal.h" goal::precision goal::mk_union(precision p1, precision p2) { if (p1 == PRECISE) return p2; @@ -85,6 +85,9 @@ goal::goal(goal const & src, bool): m_core_enabled(src.unsat_core_enabled()), m_inconsistent(false), m_precision(src.m_precision) { + m_mc = src.m_mc.get(); + m_pc = src.m_pc.get(); + m_dc = src.m_dc.get(); } goal::~goal() { @@ -105,6 +108,9 @@ void goal::copy_to(goal & target) const { SASSERT(target.m_core_enabled == m_core_enabled); target.m_inconsistent = m_inconsistent; target.m_precision = mk_union(prec(), target.prec()); + target.m_mc = m_mc.get(); + target.m_pc = m_pc.get(); + target.m_dc = m_dc.get(); } void goal::push_back(expr * f, proof * pr, expr_dependency * d) { @@ -263,6 +269,13 @@ void goal::get_formulas(ptr_vector & result) { } } +void goal::get_formulas(expr_ref_vector & result) { + unsigned sz = size(); + for (unsigned i = 0; i < sz; i++) { + result.push_back(form(i)); + } +} + void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { // KLM: don't know why this assertion is no longer true // SASSERT(proofs_enabled() == (pr != 0 && !m().is_undef_proof(pr))); @@ -337,10 +350,7 @@ void goal::display_with_dependencies(ast_printer & prn, std::ostream & out) cons out << "\n |-"; deps.reset(); m().linearize(dep(i), deps); - ptr_vector::iterator it = deps.begin(); - ptr_vector::iterator end = deps.end(); - for (; it != end; ++it) { - expr * d = *it; + for (expr * d : deps) { if (is_uninterp_const(d)) { out << " " << mk_ismt2_pp(d, m()); } @@ -354,10 +364,7 @@ void goal::display_with_dependencies(ast_printer & prn, std::ostream & out) cons } if (!to_pp.empty()) { out << "\n :dependencies-definitions ("; - obj_hashtable::iterator it = to_pp.begin(); - obj_hashtable::iterator end = to_pp.end(); - for (; it != end; ++it) { - expr * d = *it; + for (expr* d : to_pp) { out << "\n (#" << d->get_id() << "\n "; prn.display(out, d, 2); out << ")"; @@ -375,10 +382,7 @@ void goal::display_with_dependencies(std::ostream & out) const { out << "\n |-"; deps.reset(); m().linearize(dep(i), deps); - ptr_vector::iterator it = deps.begin(); - ptr_vector::iterator end = deps.end(); - for (; it != end; ++it) { - expr * d = *it; + for (expr * d : deps) { if (is_uninterp_const(d)) { out << " " << mk_ismt2_pp(d, m()); } @@ -481,6 +485,11 @@ void goal::display_dimacs(std::ostream & out) const { } out << "0\n"; } + for (auto const& kv : expr2var) { + expr* key = kv.m_key; + if (is_app(key)) + out << "c " << kv.m_value << " " << to_app(key)->get_decl()->get_name() << "\n"; + } } unsigned goal::num_exprs() const { @@ -498,14 +507,12 @@ void goal::shrink(unsigned j) { unsigned sz = size(); for (unsigned i = j; i < sz; i++) m().pop_back(m_forms); - if (proofs_enabled()) { + if (proofs_enabled()) for (unsigned i = j; i < sz; i++) m().pop_back(m_proofs); - } - if (unsat_core_enabled()) { + if (unsat_core_enabled()) for (unsigned i = j; i < sz; i++) m().pop_back(m_dependencies); - } } /** @@ -650,6 +657,9 @@ goal * goal::translate(ast_translation & translator) const { res->m_inconsistent = m_inconsistent; res->m_depth = m_depth; res->m_precision = m_precision; + res->m_pc = m_pc ? m_pc->translate(translator) : nullptr; + res->m_mc = m_mc ? m_mc->translate(translator) : nullptr; + res->m_dc = m_dc ? m_dc->translate(translator) : nullptr; return res; } diff --git a/src/tactic/goal.h b/src/tactic/goal.h index 529ece72c..2d91bc67f 100644 --- a/src/tactic/goal.h +++ b/src/tactic/goal.h @@ -35,6 +35,9 @@ Revision History: #include "util/ref.h" #include "util/ref_vector.h" #include "util/ref_buffer.h" +#include "tactic/model_converter.h" +#include "tactic/proof_converter.h" +#include "tactic/dependency_converter.h" class goal { public: @@ -49,6 +52,9 @@ public: protected: ast_manager & m_manager; + model_converter_ref m_mc; + proof_converter_ref m_pc; + dependency_converter_ref m_dc; unsigned m_ref_count; expr_array m_forms; expr_array m_proofs; @@ -71,6 +77,7 @@ protected: unsigned get_not_idx(expr * f) const; void shrink(unsigned j); void reset_core(); + public: goal(ast_manager & m, bool models_enabled = true, bool core_enabled = false); @@ -103,7 +110,7 @@ public: void copy_to(goal & target) const; void copy_from(goal const & src) { src.copy_to(*this); } - + void assert_expr(expr * f, proof * pr, expr_dependency * d); void assert_expr(expr * f, expr_dependency * d); void assert_expr(expr * f, expr * d) { assert_expr(f, m().mk_leaf(d)); } @@ -120,6 +127,7 @@ public: void update(unsigned i, expr * f, proof * pr = nullptr, expr_dependency * dep = nullptr); void get_formulas(ptr_vector & result); + void get_formulas(expr_ref_vector & result); void elim_true(); void elim_redundancies(); @@ -141,6 +149,16 @@ public: bool is_decided() const; bool is_well_sorted() const; + dependency_converter* dc() { return m_dc.get(); } + model_converter* mc() const { return m_mc.get(); } + proof_converter* pc() const { return inconsistent() ? proof2proof_converter(m(), pr(0)) : m_pc.get(); } + void add(dependency_converter* d) { m_dc = dependency_converter::concat(m_dc.get(), d); } + void add(model_converter* m) { m_mc = concat(m_mc.get(), m); } + void add(proof_converter* p) { m_pc = concat(m_pc.get(), p); } + void set(dependency_converter* d) { m_dc = d; } + void set(model_converter* m) { m_mc = m; } + void set(proof_converter* p) { m_pc = p; } + goal * translate(ast_translation & translator) const; }; diff --git a/src/tactic/horn_subsume_model_converter.cpp b/src/tactic/horn_subsume_model_converter.cpp index cb56f9b7b..0d49a769e 100644 --- a/src/tactic/horn_subsume_model_converter.cpp +++ b/src/tactic/horn_subsume_model_converter.cpp @@ -170,6 +170,10 @@ void horn_subsume_model_converter::add_default_false_interpretation(expr* e, mod } +void horn_subsume_model_converter::operator()(expr_ref& fml) { + NOT_IMPLEMENTED_YET(); +} + void horn_subsume_model_converter::operator()(model_ref& mr) { func_decl_ref pred(m); @@ -190,11 +194,11 @@ void horn_subsume_model_converter::operator()(model_ref& mr) { add_default_false_interpretation(body, mr); SASSERT(m.is_bool(body)); - TRACE("mc", tout << "eval: " << h->get_name() << "\n" << mk_pp(body, m) << "\n";); + TRACE("mc", tout << "eval: " << h->get_name() << "\n" << body << "\n";); expr_ref tmp(body); mr->eval(tmp, body); - TRACE("mc", tout << "to:\n" << mk_pp(body, m) << "\n";); + TRACE("mc", tout << "to:\n" << body << "\n";); if (arity == 0) { expr* e = mr->get_const_interp(h); diff --git a/src/tactic/horn_subsume_model_converter.h b/src/tactic/horn_subsume_model_converter.h index 5b9c22f17..6d27ceec2 100644 --- a/src/tactic/horn_subsume_model_converter.h +++ b/src/tactic/horn_subsume_model_converter.h @@ -58,9 +58,8 @@ class horn_subsume_model_converter : public model_converter { public: - horn_subsume_model_converter(ast_manager& m): - m(m), m_funcs(m), m_bodies(m), m_rewrite(m), - m_delay_head(m), m_delay_body(m) {} + horn_subsume_model_converter(ast_manager& m): + m(m), m_funcs(m), m_bodies(m), m_rewrite(m), m_delay_head(m), m_delay_body(m) {} bool mk_horn(expr* clause, func_decl_ref& pred, expr_ref& body); @@ -74,10 +73,16 @@ public: void operator()(model_ref& _m) override; + void operator()(expr_ref& fml) override; + model_converter * translate(ast_translation & translator) override; ast_manager& get_manager() { return m; } + void display(std::ostream & out) override {} + + void get_units(obj_map& units) override { units.reset(); } + }; #endif diff --git a/src/tactic/model_converter.cpp b/src/tactic/model_converter.cpp index b295a98b7..b39940366 100644 --- a/src/tactic/model_converter.cpp +++ b/src/tactic/model_converter.cpp @@ -18,32 +18,83 @@ Notes: --*/ #include "tactic/model_converter.h" #include "model/model_v2_pp.h" +#include "ast/ast_smt2_pp.h" + +/* + * Add or overwrite value in model. + */ +void model_converter::display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const { + VERIFY(e); + smt2_pp_environment_dbg env(m); + smt2_pp_environment* _env = m_env ? m_env : &env; + VERIFY(f->get_range() == m.get_sort(e)); + ast_smt2_pp(out, f, e, *_env, params_ref(), 0, "model-add") << "\n"; +} + +/* + * A value is removed from the model. + */ +void model_converter::display_del(std::ostream& out, func_decl* f) const { + if (m_env) { + ast_smt2_pp(out << "(model-del ", f->get_name(), f->is_skolem(), *m_env) << ")\n"; + } + else { + out << "(model-del " << f->get_name() << ")\n"; + } +} + +void model_converter::display_add(std::ostream& out, ast_manager& m) { + // default printer for converter that adds entries + model_ref mdl = alloc(model, m); + (*this)(mdl); + for (unsigned i = 0, sz = mdl->get_num_constants(); i < sz; ++i) { + func_decl* f = mdl->get_constant(i); + display_add(out, m, f, mdl->get_const_interp(f)); + } + for (unsigned i = 0, sz = mdl->get_num_functions(); i < sz; ++i) { + func_decl* f = mdl->get_function(i); + func_interp* fi = mdl->get_func_interp(f); + display_add(out, m, f, fi->get_interp()); + } +} + class concat_model_converter : public concat_converter { public: - concat_model_converter(model_converter * mc1, model_converter * mc2):concat_converter(mc1, mc2) {} + concat_model_converter(model_converter * mc1, model_converter * mc2): concat_converter(mc1, mc2) { + VERIFY(m_c1 && m_c2); + } void operator()(model_ref & m) override { this->m_c2->operator()(m); this->m_c1->operator()(m); } - void operator()(model_ref & m, unsigned goal_idx) override { - this->m_c2->operator()(m, goal_idx); - this->m_c1->operator()(m, 0); + void operator()(expr_ref & fml) override { + this->m_c2->operator()(fml); + this->m_c1->operator()(fml); + } + + void operator()(labels_vec & r) override { + this->m_c2->operator()(r); + this->m_c1->operator()(r); } - void operator()(labels_vec & r, unsigned goal_idx) override { - this->m_c2->operator()(r, goal_idx); - this->m_c1->operator()(r, 0); + void get_units(obj_map& fmls) override { + m_c2->get_units(fmls); + m_c1->get_units(fmls); } - char const * get_name() const override { return "concat-model-converter"; } model_converter * translate(ast_translation & translator) override { return this->translate_core(translator); } + + void collect(ast_pp_util& visitor) override { + this->m_c1->collect(visitor); + this->m_c2->collect(visitor); + } }; model_converter * concat(model_converter * mc1, model_converter * mc2) { @@ -54,83 +105,14 @@ model_converter * concat(model_converter * mc1, model_converter * mc2) { return alloc(concat_model_converter, mc1, mc2); } -class concat_star_model_converter : public concat_star_converter { -public: - concat_star_model_converter(model_converter * mc1, unsigned num, model_converter * const * mc2s, unsigned * szs): - concat_star_converter(mc1, num, mc2s, szs) { - } - - void operator()(model_ref & m) override { - // TODO: delete method after conversion is complete - UNREACHABLE(); - } - - void operator()(model_ref & m, unsigned goal_idx) override { - unsigned num = this->m_c2s.size(); - for (unsigned i = 0; i < num; i++) { - if (goal_idx < this->m_szs[i]) { - // found the model converter that should be used - model_converter * c2 = this->m_c2s[i]; - if (c2) - c2->operator()(m, goal_idx); - if (m_c1) - this->m_c1->operator()(m, i); - return; - } - // invalid goal - goal_idx -= this->m_szs[i]; - } - UNREACHABLE(); - } - - void operator()(labels_vec & r, unsigned goal_idx) override { - unsigned num = this->m_c2s.size(); - for (unsigned i = 0; i < num; i++) { - if (goal_idx < this->m_szs[i]) { - // found the model converter that should be used - model_converter * c2 = this->m_c2s[i]; - if (c2) - c2->operator()(r, goal_idx); - if (m_c1) - this->m_c1->operator()(r, i); - return; - } - // invalid goal - goal_idx -= this->m_szs[i]; - } - UNREACHABLE(); - } - - char const * get_name() const override { return "concat-star-model-converter"; } - - model_converter * translate(ast_translation & translator) override { - return this->translate_core(translator); - } -}; - -model_converter * concat(model_converter * mc1, unsigned num, model_converter * const * mc2s, unsigned * szs) { - SASSERT(num > 0); - if (num == 1) - return concat(mc1, mc2s[0]); - unsigned i; - for (i = 0; i < num; i++) { - if (mc2s[i] != nullptr) - break; - } - if (i == num) { - // all mc2s are 0 - return mc1; - } - return alloc(concat_star_model_converter, mc1, num, mc2s, szs); -} class model2mc : public model_converter { model_ref m_model; - buffer m_labels; + labels_vec m_labels; public: model2mc(model * m):m_model(m) {} - model2mc(model * m, buffer const & r):m_model(m), m_labels(r) {} + model2mc(model * m, labels_vec const & r):m_model(m), m_labels(r) {} ~model2mc() override {} @@ -138,51 +120,56 @@ public: m = m_model; } - void operator()(model_ref & m, unsigned goal_idx) override { - m = m_model; + void operator()(labels_vec & r) override { + r.append(m_labels.size(), m_labels.c_ptr()); } - void operator()(labels_vec & r, unsigned goal_idx) override { - r.append(m_labels.size(), m_labels.c_ptr()); + void operator()(expr_ref& fml) override { + expr_ref r(m_model->get_manager()); + m_model->eval(fml, r, false); + fml = r; } - void cancel() override { + void get_units(obj_map& fmls) override { + // no-op + } + + void cancel() override { } void display(std::ostream & out) override { - out << "(model->model-converter-wrapper\n"; + out << "(rmodel->model-converter-wrapper\n"; model_v2_pp(out, *m_model); out << ")\n"; } model_converter * translate(ast_translation & translator) override { model * m = m_model->translate(translator); - return alloc(model2mc, m); + return alloc(model2mc, m, m_labels); } + }; model_converter * model2model_converter(model * m) { - if (m == nullptr) - return nullptr; + if (!m) return nullptr; return alloc(model2mc, m); } -model_converter * model_and_labels2model_converter(model * m, buffer & r) { - if (m == nullptr) - return nullptr; +model_converter * model_and_labels2model_converter(model * m, labels_vec const & r) { + if (!m) return nullptr; return alloc(model2mc, m, r); } void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m) { if (mc) { m = alloc(model, mng); - (*mc)(m, 0); + (*mc)(m); } } -void apply(model_converter_ref & mc, model_ref & m, unsigned gidx) { +void apply(model_converter_ref & mc, model_ref & m) { if (mc) { - (*mc)(m, gidx); + (*mc)(m); } } diff --git a/src/tactic/model_converter.h b/src/tactic/model_converter.h index 5e549344e..cd8fb5ee3 100644 --- a/src/tactic/model_converter.h +++ b/src/tactic/model_converter.h @@ -15,51 +15,96 @@ Author: Notes: + A model converter, mc, can be used to convert a model for one + of a generated subgoal into a model for an initial goal or solver state. + For a goal or solver state that is decided, a model converter can be + a simple wrapper around a model. + + Logically, given a formula F and subgoal formula F_s a model converter mc + for F_s relative to F has the property: + + m |= F_s iff mc(m) |= F for every model m + + For the evaluator associated with models, m, we expect + + eval(m)(F_s) <=> eval(mc(m))(F) + + This property holds for both eval, that decides on a fixed value + for constants that have no interpretation in m and for 'peval' + (partial eval) that retuns just the constants that are unfixed. + (in the model evaluator one can control this behavior using a + configuration flag) + + and more generally over the eval method have: + + G => F_s iff peval(mc(e))(G) => F for every formula G + + + where e is the empty model (a model that does not evaluate any + + When a model converter supports application to a formula it satisfies + the following property: + + mc(G) & F_s is SAT iff G & F is SAT + + For a model converter that is a sequence of definitions and removals + of functions we can obtain mc(G) by adding back or expanding definitinos + that are required to interpret G fully in the context of F_s. + --*/ #ifndef MODEL_CONVERTER_H_ #define MODEL_CONVERTER_H_ +#include "util/ref.h" +#include "ast/ast_pp_util.h" #include "model/model.h" #include "tactic/converter.h" -#include "util/ref.h" class labels_vec : public svector {}; +class smt2_pp_environment; class model_converter : public converter { +protected: + smt2_pp_environment* m_env; + void display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const; + void display_del(std::ostream& out, func_decl* f) const; + void display_add(std::ostream& out, ast_manager& m); + public: - virtual void operator()(model_ref & m) {} // TODO: delete - virtual void operator()(model_ref & m, unsigned goal_idx) { - // TODO: make it virtual after the transition to goal/tactic/tactical is complete - SASSERT(goal_idx == 0); - operator()(m); - } + model_converter(): m_env(nullptr) {} - virtual void operator()(labels_vec & r, unsigned goal_idx) {} + virtual void operator()(model_ref & m) = 0; + + virtual void operator()(labels_vec & r) {} virtual model_converter * translate(ast_translation & translator) = 0; + + virtual void collect(ast_pp_util& visitor) { m_env = &visitor.env(); } + + /** + \brief we are adding a formula to the context of the model converter. + The operator has as side effect of adding definitions as assertions to the + formula and removing these definitions from the model converter. + */ + virtual void operator()(expr_ref& formula) { UNREACHABLE(); } + + virtual void get_units(obj_map& fmls) { UNREACHABLE(); } }; typedef ref model_converter_ref; - -model_converter * concat(model_converter * mc1, model_converter * mc2); - -/** - \brief \c mc1 is the model converter for a sequence of subgoals of size \c num. - Given an i in [0, num), mc2s[i] is the model converter for subgoal i, - and num_subgoals[i] is the number of subgoals of subgoals[i]. -*/ -model_converter * concat(model_converter * mc1, unsigned num, model_converter * const * mc2s, unsigned * num_subgoals); - -model_converter * model2model_converter(model * m); - -model_converter * model_and_labels2model_converter(model * m, buffer &r); - -void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m); - -void apply(model_converter_ref & mc, model_ref & m, unsigned gidx); - typedef sref_vector model_converter_ref_vector; typedef sref_buffer model_converter_ref_buffer; +model_converter * concat(model_converter * mc1, model_converter * mc2); + +model_converter * model2model_converter(model * m); + +model_converter * model_and_labels2model_converter(model * m, labels_vec const &r); + +void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m); + +void apply(model_converter_ref & mc, model_ref & m); + + #endif diff --git a/src/tactic/nlsat_smt/CMakeLists.txt b/src/tactic/nlsat_smt/CMakeLists.txt deleted file mode 100644 index ccfc0e3ef..000000000 --- a/src/tactic/nlsat_smt/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -z3_add_component(nlsat_smt_tactic - SOURCES - nl_purify_tactic.cpp - COMPONENT_DEPENDENCIES - nlsat_tactic - smt_tactic - TACTIC_HEADERS - nl_purify_tactic.h -) diff --git a/src/tactic/nlsat_smt/nl_purify_tactic.h b/src/tactic/nlsat_smt/nl_purify_tactic.h deleted file mode 100644 index 85d033921..000000000 --- a/src/tactic/nlsat_smt/nl_purify_tactic.h +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - nl_purify_tactic.h - -Abstract: - - Tactic for purifying quantifier-free formulas that mix QF_NRA and other theories. - It is designed to allow cooprating between the nlsat solver and other theories - in a decoubled way. - -Author: - - Nikolaj Bjorner (nbjorner) 2015-5-5. - -Revision History: - ---*/ -#ifndef NL_PURIFY_TACTIC_H_ -#define NL_PURIFY_TACTIC_H_ - -#include "util/params.h" -class ast_manager; -class tactic; - -tactic * mk_nl_purify_tactic(ast_manager & m, params_ref const & p = params_ref()); - -/* - ADD_TACTIC("nl-purify", "Decompose goal into pure NL-sat formula and formula over other theories.", "mk_nl_purify_tactic(m, p)") -*/ - -#endif - diff --git a/src/tactic/portfolio/CMakeLists.txt b/src/tactic/portfolio/CMakeLists.txt index a8a9b2bba..2b714cc2c 100644 --- a/src/tactic/portfolio/CMakeLists.txt +++ b/src/tactic/portfolio/CMakeLists.txt @@ -1,11 +1,12 @@ z3_add_component(portfolio SOURCES + bounded_int2bv_solver.cpp default_tactic.cpp enum2bv_solver.cpp - pb2bv_solver.cpp - bounded_int2bv_solver.cpp fd_solver.cpp + pb2bv_solver.cpp smt_strategic_solver.cpp + solver2lookahead.cpp COMPONENT_DEPENDENCIES aig_tactic fp @@ -18,4 +19,5 @@ z3_add_component(portfolio ufbv_tactic TACTIC_HEADERS default_tactic.h + fd_solver.h ) diff --git a/src/tactic/portfolio/bounded_int2bv_solver.cpp b/src/tactic/portfolio/bounded_int2bv_solver.cpp index 170780ab0..8767644f3 100644 --- a/src/tactic/portfolio/bounded_int2bv_solver.cpp +++ b/src/tactic/portfolio/bounded_int2bv_solver.cpp @@ -21,8 +21,7 @@ Notes: #include "solver/solver_na2as.h" #include "tactic/tactic.h" #include "ast/rewriter/pb2bv_rewriter.h" -#include "tactic/filter_model_converter.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" #include "tactic/arith/bound_manager.h" @@ -72,11 +71,25 @@ public: } } - solver* translate(ast_manager& m, params_ref const& p) override { - return alloc(bounded_int2bv_solver, m, p, m_solver->translate(m, p)); + solver* translate(ast_manager& dst_m, params_ref const& p) override { + flush_assertions(); + bounded_int2bv_solver* result = alloc(bounded_int2bv_solver, dst_m, p, m_solver->translate(dst_m, p)); + ast_translation tr(m, dst_m); + for (auto& kv : m_int2bv) result->m_int2bv.insert(tr(kv.m_key), tr(kv.m_value)); + for (auto& kv : m_bv2int) result->m_bv2int.insert(tr(kv.m_key), tr(kv.m_value)); + for (auto& kv : m_bv2offset) result->m_bv2offset.insert(tr(kv.m_key), kv.m_value); + for (func_decl* f : m_bv_fns) result->m_bv_fns.push_back(tr(f)); + for (func_decl* f : m_int_fns) result->m_int_fns.push_back(tr(f)); + for (bound_manager* b : m_bounds) result->m_bounds.push_back(b->translate(dst_m)); + model_converter_ref mc = external_model_converter(); + if (mc) { + ast_translation tr(m, dst_m); + result->set_model_converter(mc->translate(tr)); + } + return result; } - void assert_expr(expr * t) override { + void assert_expr_core(expr * t) override { unsigned i = m_assertions.size(); m_assertions.push_back(t); while (i < m_assertions.size()) { @@ -136,18 +149,45 @@ public: void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } void collect_statistics(statistics & st) const override { m_solver->collect_statistics(st); } void get_unsat_core(ptr_vector & r) override { m_solver->get_unsat_core(r); } - void get_model(model_ref & mdl) override { + void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { - extend_model(mdl); - filter_model(mdl); + model_converter_ref mc = local_model_converter(); + if (mc) (*mc)(mdl); } } + model_converter* external_model_converter() const { + return concat(mc0(), local_model_converter()); + } + model_converter* local_model_converter() const { + if (m_int2bv.empty() && m_bv_fns.empty()) return nullptr; + generic_model_converter* mc = alloc(generic_model_converter, m, "bounded_int2bv"); + for (func_decl* f : m_bv_fns) + mc->hide(f); + for (auto const& kv : m_int2bv) { + rational offset; + VERIFY (m_bv2offset.find(kv.m_value, offset)); + expr_ref value(m_bv.mk_bv2int(m.mk_const(kv.m_value)), m); + if (!offset.is_zero()) { + value = m_arith.mk_add(value, m_arith.mk_numeral(offset, true)); + } + TRACE("int2bv", tout << mk_pp(kv.m_key, m) << " " << value << "\n";); + mc->add(kv.m_key, value); + } + return mc; + } + + model_converter_ref get_model_converter() const override { + model_converter_ref mc = external_model_converter(); + mc = concat(mc.get(), m_solver->get_model_converter().get()); + return mc; + } proof * get_proof() override { return m_solver->get_proof(); } std::string reason_unknown() const override { return m_solver->reason_unknown(); } void set_reason_unknown(char const* msg) override { m_solver->set_reason_unknown(msg); } void get_labels(svector & r) override { m_solver->get_labels(r); } ast_manager& get_manager() const override { return m; } + expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { flush_assertions(); return m_solver->cube(vars, backtrack_level); } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_solver->find_mutexes(vars, mutexes); } lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { flush_assertions(); @@ -184,38 +224,10 @@ public: } } return r; - } private: - void filter_model(model_ref& mdl) const { - if (m_bv_fns.empty()) { - return; - } - filter_model_converter filter(m); - for (unsigned i = 0; i < m_bv_fns.size(); ++i) { - filter.insert(m_bv_fns[i].get()); - } - filter(mdl, 0); - } - - void extend_model(model_ref& mdl) { - extension_model_converter ext(m); - obj_map::iterator it = m_int2bv.begin(), end = m_int2bv.end(); - for (; it != end; ++it) { - rational offset; - VERIFY (m_bv2offset.find(it->m_value, offset)); - expr_ref value(m_bv.mk_bv2int(m.mk_const(it->m_value)), m); - if (!offset.is_zero()) { - value = m_arith.mk_add(value, m_arith.mk_numeral(offset, true)); - } - TRACE("int2bv", tout << mk_pp(it->m_key, m) << " " << value << "\n";); - ext.insert(it->m_key, value); - } - ext(mdl, 0); - } - void accumulate_sub(expr_safe_replace& sub) const { for (unsigned i = 0; i < m_bounds.size(); ++i) { accumulate_sub(sub, *m_bounds[i]); @@ -292,8 +304,8 @@ private: void flush_assertions() const { if (m_assertions.empty()) return; bound_manager& bm = *m_bounds.back(); - for (unsigned i = 0; i < m_assertions.size(); ++i) { - bm(m_assertions[i].get()); + for (expr* a : m_assertions) { + bm(a); } TRACE("int2bv", bm.display(tout);); expr_safe_replace sub(m); @@ -304,8 +316,8 @@ private: m_solver->assert_expr(m_assertions); } else { - for (unsigned i = 0; i < m_assertions.size(); ++i) { - sub(m_assertions[i].get(), fml1); + for (expr* a : m_assertions) { + sub(a, fml1); m_rewriter(fml1, fml2, proof); if (m.canceled()) { m_rewriter.reset(); diff --git a/src/tactic/portfolio/default_tactic.cpp b/src/tactic/portfolio/default_tactic.cpp index 5cf06ca86..51cda17cd 100644 --- a/src/tactic/portfolio/default_tactic.cpp +++ b/src/tactic/portfolio/default_tactic.cpp @@ -31,7 +31,6 @@ Notes: #include "tactic/fpa/qffplra_tactic.h" #include "tactic/smtlogics/qfaufbv_tactic.h" #include "tactic/smtlogics/qfauflia_tactic.h" -#include "tactic/smtlogics/qfufnra_tactic.h" tactic * mk_default_tactic(ast_manager & m, params_ref const & p) { tactic * st = using_params(and_then(mk_simplify_tactic(m), diff --git a/src/tactic/portfolio/enum2bv_solver.cpp b/src/tactic/portfolio/enum2bv_solver.cpp index 3f4a20774..6b5fe9056 100644 --- a/src/tactic/portfolio/enum2bv_solver.cpp +++ b/src/tactic/portfolio/enum2bv_solver.cpp @@ -20,21 +20,20 @@ Notes: --*/ -#include "solver/solver_na2as.h" -#include "tactic/tactic.h" #include "ast/bv_decl_plugin.h" #include "ast/datatype_decl_plugin.h" -#include "ast/rewriter/enum2bv_rewriter.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" +#include "tactic/tactic.h" +#include "tactic/generic_model_converter.h" #include "tactic/portfolio/enum2bv_solver.h" +#include "solver/solver_na2as.h" +#include "ast/rewriter/enum2bv_rewriter.h" class enum2bv_solver : public solver_na2as { - ast_manager& m; - ref m_solver; - enum2bv_rewriter m_rewriter; + ast_manager& m; + ref m_solver; + enum2bv_rewriter m_rewriter; public: @@ -49,11 +48,17 @@ public: ~enum2bv_solver() override {} - solver* translate(ast_manager& m, params_ref const& p) override { - return alloc(enum2bv_solver, m, p, m_solver->translate(m, p)); + solver* translate(ast_manager& dst_m, params_ref const& p) override { + solver* result = alloc(enum2bv_solver, dst_m, p, m_solver->translate(dst_m, p)); + model_converter_ref mc = external_model_converter(); + if (mc) { + ast_translation tr(m, dst_m); + result->set_model_converter(mc->translate(tr)); + } + return result; } - void assert_expr(expr * t) override { + void assert_expr_core(expr * t) override { expr_ref tmp(t, m); expr_ref_vector bounds(m); proof_ref tmp_proof(m); @@ -74,6 +79,7 @@ public: } lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { + m_solver->updt_params(get_params()); return m_solver->check_sat(num_assumptions, assumptions); } @@ -83,19 +89,47 @@ public: void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } void collect_statistics(statistics & st) const override { m_solver->collect_statistics(st); } void get_unsat_core(ptr_vector & r) override { m_solver->get_unsat_core(r); } - void get_model(model_ref & mdl) override { + void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { - extend_model(mdl); - filter_model(mdl); + model_converter_ref mc = local_model_converter(); + if (mc) (*mc)(mdl); } } + model_converter* local_model_converter() const { + if (m_rewriter.enum2def().empty() && + m_rewriter.enum2bv().empty()) { + return nullptr; + } + generic_model_converter* mc = alloc(generic_model_converter, m, "enum2bv"); + for (auto const& kv : m_rewriter.enum2bv()) + mc->hide(kv.m_value); + for (auto const& kv : m_rewriter.enum2def()) + mc->add(kv.m_key, kv.m_value); + return mc; + } + + model_converter* external_model_converter() const { + return concat(mc0(), local_model_converter()); + } + + model_converter_ref get_model_converter() const override { + model_converter_ref mc = external_model_converter(); + mc = concat(mc.get(), m_solver->get_model_converter().get()); + return mc; + } proof * get_proof() override { return m_solver->get_proof(); } std::string reason_unknown() const override { return m_solver->reason_unknown(); } void set_reason_unknown(char const* msg) override { m_solver->set_reason_unknown(msg); } void get_labels(svector & r) override { m_solver->get_labels(r); } ast_manager& get_manager() const override { return m; } - lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_solver->find_mutexes(vars, mutexes); } + lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { + return m_solver->find_mutexes(vars, mutexes); + } + expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { + return m_solver->cube(vars, backtrack_level); + } + lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { datatype_util dt(m); bv_util bv(m); @@ -104,8 +138,8 @@ public: // ensure that enumeration variables that // don't occur in the constraints // are also internalized. - for (unsigned i = 0; i < vars.size(); ++i) { - expr_ref tmp(m.mk_eq(vars[i], vars[i]), m); + for (expr* v : vars) { + expr_ref tmp(m.mk_eq(v, v), m); proof_ref proof(m); m_rewriter(tmp, tmp, proof); } @@ -113,13 +147,13 @@ public: m_solver->assert_expr(bounds); // translate enumeration constants to bit-vectors. - for (unsigned i = 0; i < vars.size(); ++i) { - func_decl* f = nullptr; - if (is_app(vars[i]) && is_uninterp_const(vars[i]) && m_rewriter.enum2bv().find(to_app(vars[i])->get_decl(), f)) { + for (expr* v : vars) { + func_decl* f = 0; + if (is_app(v) && is_uninterp_const(v) && m_rewriter.enum2bv().find(to_app(v)->get_decl(), f)) { bvars.push_back(m.mk_const(f)); } else { - bvars.push_back(vars[i]); + bvars.push_back(v); } } lbool r = m_solver->get_consequences(asms, bvars, consequences); @@ -144,24 +178,7 @@ public: return r; } - void filter_model(model_ref& mdl) { - filter_model_converter filter(m); - obj_map::iterator it = m_rewriter.enum2bv().begin(), end = m_rewriter.enum2bv().end(); - for (; it != end; ++it) { - filter.insert(it->m_value); - } - filter(mdl, 0); - } - void extend_model(model_ref& mdl) { - extension_model_converter ext(m); - obj_map::iterator it = m_rewriter.enum2def().begin(), end = m_rewriter.enum2def().end(); - for (; it != end; ++it) { - ext.insert(it->m_key, it->m_value); - - } - ext(mdl, 0); - } unsigned get_num_assertions() const override { return m_solver->get_num_assertions(); diff --git a/src/tactic/portfolio/fd_solver.cpp b/src/tactic/portfolio/fd_solver.cpp index b29fb20c1..b0d95baee 100644 --- a/src/tactic/portfolio/fd_solver.cpp +++ b/src/tactic/portfolio/fd_solver.cpp @@ -23,11 +23,23 @@ Notes: #include "tactic/portfolio/enum2bv_solver.h" #include "tactic/portfolio/pb2bv_solver.h" #include "tactic/portfolio/bounded_int2bv_solver.h" +#include "solver/solver2tactic.h" +#include "solver/parallel_tactic.h" +#include "solver/parallel_params.hpp" -solver * mk_fd_solver(ast_manager & m, params_ref const & p) { - solver* s = mk_inc_sat_solver(m, p); +solver * mk_fd_solver(ast_manager & m, params_ref const & p, bool incremental_mode) { + solver* s = mk_inc_sat_solver(m, p, incremental_mode); s = mk_enum2bv_solver(m, p, s); s = mk_pb2bv_solver(m, p, s); s = mk_bounded_int2bv_solver(m, p, s); return s; } + +tactic * mk_fd_tactic(ast_manager & m, params_ref const& p) { + return mk_solver2tactic(mk_fd_solver(m, p, false)); +} + +tactic * mk_parallel_qffd_tactic(ast_manager& m, params_ref const& p) { + solver* s = mk_fd_solver(m, p); + return mk_parallel_tactic(s, p); +} diff --git a/src/tactic/portfolio/fd_solver.h b/src/tactic/portfolio/fd_solver.h index 53cae16d1..e1c5b909c 100644 --- a/src/tactic/portfolio/fd_solver.h +++ b/src/tactic/portfolio/fd_solver.h @@ -23,7 +23,15 @@ Notes: #include "util/params.h" class solver; +class tactic; -solver * mk_fd_solver(ast_manager & m, params_ref const & p); +solver * mk_fd_solver(ast_manager & m, params_ref const & p, bool incremental_mode = true); +tactic * mk_fd_tactic(ast_manager & m, params_ref const & p); +tactic * mk_parallel_qffd_tactic(ast_manager& m, params_ref const& p); + +/* + ADD_TACTIC("qffd", "builtin strategy for solving QF_FD problems.", "mk_fd_tactic(m, p)") + ADD_TACTIC("pqffd", "builtin strategy for solving QF_FD problems in parallel.", "mk_parallel_qffd_tactic(m, p)") +*/ #endif diff --git a/src/tactic/portfolio/pb2bv_solver.cpp b/src/tactic/portfolio/pb2bv_solver.cpp index 8339272b1..f8794ca41 100644 --- a/src/tactic/portfolio/pb2bv_solver.cpp +++ b/src/tactic/portfolio/pb2bv_solver.cpp @@ -16,18 +16,20 @@ Notes: --*/ -#include "tactic/portfolio/pb2bv_solver.h" -#include "solver/solver_na2as.h" -#include "tactic/tactic.h" -#include "ast/rewriter/pb2bv_rewriter.h" -#include "tactic/filter_model_converter.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" +#include "tactic/portfolio/pb2bv_solver.h" +#include "tactic/tactic.h" +#include "tactic/generic_model_converter.h" +#include "solver/solver_na2as.h" +#include "ast/rewriter/pb2bv_rewriter.h" +#include "ast/rewriter/th_rewriter.h" class pb2bv_solver : public solver_na2as { ast_manager& m; mutable expr_ref_vector m_assertions; mutable ref m_solver; + mutable th_rewriter m_th_rewriter; mutable pb2bv_rewriter m_rewriter; public: @@ -37,6 +39,7 @@ public: m(m), m_assertions(m), m_solver(s), + m_th_rewriter(m, p), m_rewriter(m, p) { solver::updt_params(p); @@ -44,11 +47,18 @@ public: ~pb2bv_solver() override {} - solver* translate(ast_manager& m, params_ref const& p) override { - return alloc(pb2bv_solver, m, p, m_solver->translate(m, p)); + solver* translate(ast_manager& dst_m, params_ref const& p) override { + flush_assertions(); + solver* result = alloc(pb2bv_solver, dst_m, p, m_solver->translate(dst_m, p)); + model_converter_ref mc = external_model_converter(); + if (mc.get()) { + ast_translation tr(m, dst_m); + result->set_model_converter(mc->translate(tr)); + } + return result; } - void assert_expr(expr * t) override { + void assert_expr_core(expr * t) override { m_assertions.push_back(t); } @@ -69,41 +79,52 @@ public: return m_solver->check_sat(num_assumptions, assumptions); } - void updt_params(params_ref const & p) override { solver::updt_params(p); m_solver->updt_params(p); } - void collect_param_descrs(param_descrs & r) override { m_solver->collect_param_descrs(r); } + void updt_params(params_ref const & p) override { solver::updt_params(p); m_rewriter.updt_params(p); m_solver->updt_params(p); } + void collect_param_descrs(param_descrs & r) override { m_solver->collect_param_descrs(r); m_rewriter.collect_param_descrs(r);} void set_produce_models(bool f) override { m_solver->set_produce_models(f); } void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } - void collect_statistics(statistics & st) const override { + void collect_statistics(statistics & st) const override { m_rewriter.collect_statistics(st); m_solver->collect_statistics(st); } void get_unsat_core(ptr_vector & r) override { m_solver->get_unsat_core(r); } - void get_model(model_ref & mdl) override { + void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { - filter_model(mdl); + model_converter_ref mc = local_model_converter(); + if (mc) (*mc)(mdl); } } + + model_converter* external_model_converter() const{ + return concat(mc0(), local_model_converter()); + } + model_converter_ref get_model_converter() const override { + model_converter_ref mc = external_model_converter(); + mc = concat(mc.get(), m_solver->get_model_converter().get()); + return mc; + } proof * get_proof() override { return m_solver->get_proof(); } std::string reason_unknown() const override { return m_solver->reason_unknown(); } void set_reason_unknown(char const* msg) override { m_solver->set_reason_unknown(msg); } void get_labels(svector & r) override { m_solver->get_labels(r); } ast_manager& get_manager() const override { return m; } + expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { flush_assertions(); return m_solver->cube(vars, backtrack_level); } lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_solver->find_mutexes(vars, mutexes); } lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { flush_assertions(); return m_solver->get_consequences(asms, vars, consequences); } - void filter_model(model_ref& mdl) { + model_converter* local_model_converter() const { if (m_rewriter.fresh_constants().empty()) { - return; + return nullptr; } - filter_model_converter filter(m); + generic_model_converter* filter = alloc(generic_model_converter, m, "pb2bv"); func_decl_ref_vector const& fns = m_rewriter.fresh_constants(); - for (unsigned i = 0; i < fns.size(); ++i) { - filter.insert(fns[i]); + for (func_decl* f : fns) { + filter->hide(f); } - filter(mdl, 0); + return filter; } unsigned get_num_assertions() const override { @@ -119,11 +140,14 @@ public: private: void flush_assertions() const { + if (m_assertions.empty()) return; + m_rewriter.updt_params(get_params()); proof_ref proof(m); - expr_ref fml(m); + expr_ref fml1(m), fml(m); expr_ref_vector fmls(m); - for (unsigned i = 0; i < m_assertions.size(); ++i) { - m_rewriter(m_assertions[i].get(), fml, proof); + for (expr* a : m_assertions) { + m_th_rewriter(a, fml1, proof); + m_rewriter(false, fml1, fml, proof); m_solver->assert_expr(fml); } m_rewriter.flush_side_constraints(fmls); diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index e436b6143..b8ba2f59d 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -36,7 +36,6 @@ Notes: #include "tactic/portfolio/fd_solver.h" #include "tactic/ufbv/ufbv_tactic.h" #include "tactic/fpa/qffp_tactic.h" -#include "tactic/smtlogics/qfufnra_tactic.h" #include "muz/fp/horn_tactic.h" #include "smt/smt_solver.h" #include "sat/sat_solver/inc_sat_solver.h" @@ -94,9 +93,7 @@ tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const else if (logic=="HORN") return mk_horn_tactic(m, p); else if ((logic == "QF_FD" || logic == "SAT") && !m.proofs_enabled()) - return mk_solver2tactic(mk_fd_solver(m, p)); - //else if (logic=="QF_UFNRA") - // return mk_qfufnra_tactic(m, p); + return mk_fd_tactic(m, p); else return mk_default_tactic(m, p); } diff --git a/src/tactic/portfolio/solver2lookahead.cpp b/src/tactic/portfolio/solver2lookahead.cpp new file mode 100644 index 000000000..0c18ab079 --- /dev/null +++ b/src/tactic/portfolio/solver2lookahead.cpp @@ -0,0 +1,24 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + solver2lookahead.cpp + +Abstract: + + Lookahead wrapper for arbitrary solver. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-9 + +Notes: + +--*/ +#include "sat/sat_solver/inc_sat_solver.h" +#include "solver/solver.h" + +solver * mk_solver2lookahead(solver* s) { + return 0; +} diff --git a/src/tactic/portfolio/solver2lookahead.h b/src/tactic/portfolio/solver2lookahead.h new file mode 100644 index 000000000..80d73ddf3 --- /dev/null +++ b/src/tactic/portfolio/solver2lookahead.h @@ -0,0 +1,26 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + solver2lookahead.h + +Abstract: + + Lookahead wrapper for arbitrary solver. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-9 + +Notes: + +--*/ +#ifndef SOLVER2LOOKAHEAD_H_ +#define SOLVER2LOOKAHEAD_H_ + +class solver; + +solver * mk_solver2lookahead(solver* s); + +#endif diff --git a/src/tactic/proof_converter.cpp b/src/tactic/proof_converter.cpp index 26a87fe04..f1a209487 100644 --- a/src/tactic/proof_converter.cpp +++ b/src/tactic/proof_converter.cpp @@ -17,6 +17,7 @@ Notes: --*/ #include "tactic/proof_converter.h" +#include "tactic/goal.h" #include "ast/ast_smt2_pp.h" class concat_proof_converter : public concat_converter { @@ -25,11 +26,11 @@ public: char const * get_name() const override { return "concat-proof-converter"; } - void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) override { + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { proof_ref tmp(m); - this->m_c2->operator()(m, num_source, source, tmp); + tmp = this->m_c2->operator()(m, num_source, source); proof * new_source = tmp.get(); - this->m_c1->operator()(m, 1, &new_source, result); + return this->m_c1->operator()(m, 1, &new_source); } proof_converter * translate(ast_translation & translator) override { @@ -45,66 +46,40 @@ proof_converter * concat(proof_converter * pc1, proof_converter * pc2) { return alloc(concat_proof_converter, pc1, pc2); } -class concat_star_proof_converter : public concat_star_converter { +class subgoal_proof_converter : public proof_converter { + proof_converter_ref m_pc; + goal_ref_buffer m_goals; public: - concat_star_proof_converter(proof_converter * pc1, unsigned num, proof_converter * const * pc2s, unsigned * szs): - concat_star_converter(pc1, num, pc2s, szs) { + subgoal_proof_converter(proof_converter* pc, unsigned n, goal * const* goals): + m_pc(pc) + { + for (unsigned i = 0; i < n; ++i) m_goals.push_back(goals[i]); } - char const * get_name() const override { return "concat-star-proof-converter"; } + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { + // ignore the proofs from the arguments, instead obtain the proofs fromt he subgoals. + SASSERT(num_source == 0); + proof_converter_ref_buffer pc_buffer; + for (goal_ref g : m_goals) { + pc_buffer.push_back(g->pc()); - void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) override { - unsigned num = this->m_szs.size(); -#ifdef Z3DEBUG - unsigned sum = 0; - for (unsigned i = 0; i < num; i++) { - sum += this->m_szs[i]; - } - SASSERT(sum == num_source); -#endif - proof_ref_buffer tmp_prs(m); - for (unsigned i = 0; i < num; i++) { - unsigned sz = m_szs[i]; - proof_converter * c2 = m_c2s[i]; - proof_ref pr(m); - if (c2) { - (*c2)(m, sz, source, pr); - } - else { - SASSERT(sz == 1); - pr = *source; - } - source += sz; - tmp_prs.push_back(pr.get()); - } - if (m_c1) { - (*m_c1)(m, tmp_prs.size(), tmp_prs.c_ptr(), result); - } - else { - SASSERT(tmp_prs.size() == 1); - result = tmp_prs[0]; } + return apply(m, m_pc, pc_buffer); } - proof_converter * translate(ast_translation & translator) override { - return this->translate_core(translator); + proof_converter* translate(ast_translation& tr) override { + proof_converter_ref pc1 = m_pc->translate(tr); + goal_ref_buffer goals; + for (goal_ref g : m_goals) goals.push_back(g->translate(tr)); + return alloc(subgoal_proof_converter, pc1.get(), goals.size(), goals.c_ptr()); } + + void display(std::ostream& out) override {} + }; -proof_converter * concat(proof_converter * pc1, unsigned num, proof_converter * const * pc2s, unsigned * szs) { - SASSERT(num > 0); - if (num == 1) - return concat(pc1, pc2s[0]); - unsigned i; - for (i = 0; i < num; i++) { - if (pc2s[i] != nullptr) - break; - } - if (i == num) { - // all pc2s are 0 - return pc1; - } - return alloc(concat_star_proof_converter, pc1, num, pc2s, szs); +proof_converter * concat(proof_converter *pc, unsigned n, goal* const* goals) { + return alloc(subgoal_proof_converter, pc, n, goals); } class proof2pc : public proof_converter { @@ -113,9 +88,9 @@ public: proof2pc(ast_manager & m, proof * pr):m_pr(pr, m) {} ~proof2pc() override {} - void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) override { + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { SASSERT(num_source == 0); - result = m_pr; + return m_pr; } proof_converter * translate(ast_translation & translator) override { @@ -136,7 +111,7 @@ proof_converter * proof2proof_converter(ast_manager & m, proof * pr) { void apply(ast_manager & m, proof_converter * pc, proof_ref & pr) { if (pc) { proof * _pr = pr.get(); - (*pc)(m, 1, &_pr, pr); + pr = (*pc)(m, 1, &_pr); } } @@ -148,15 +123,15 @@ void apply(ast_manager & m, proof_converter * pc, proof_ref & pr) { pc1 and pc2s must be different from 0. */ -void apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s, proof_ref & result) { +proof_ref apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s) { SASSERT(pc1); proof_ref_buffer prs(m); unsigned sz = pc2s.size(); for (unsigned i = 0; i < sz; i++) { proof_ref pr(m); SASSERT(pc2s[i]); // proof production is enabled - pc2s[i]->operator()(m, 0, nullptr, pr); + pr = pc2s[i]->operator()(m, 0, 0); prs.push_back(pr); } - (*pc1)(m, sz, prs.c_ptr(), result); + return (*pc1)(m, sz, prs.c_ptr()); } diff --git a/src/tactic/proof_converter.h b/src/tactic/proof_converter.h index c3a841b31..df8462de7 100644 --- a/src/tactic/proof_converter.h +++ b/src/tactic/proof_converter.h @@ -20,33 +20,34 @@ Notes: #define PROOF_CONVERTER_H_ #include "ast/ast.h" -#include "tactic/converter.h" #include "util/ref.h" +#include "tactic/converter.h" +class goal; class proof_converter : public converter { public: ~proof_converter() override { } - virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) = 0; + virtual proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) = 0; virtual proof_converter * translate(ast_translation & translator) = 0; }; typedef ref proof_converter_ref; +typedef sref_vector proof_converter_ref_vector; +typedef sref_buffer proof_converter_ref_buffer; + proof_converter * concat(proof_converter * pc1, proof_converter * pc2); /** - \brief \c pc1 is the proof converter for a sequence of subgoals of size \c num. - Given an i in [0, num), pc2s[i] is the proof converter for subgoal i, - and num_subgoals[i] is the number of subgoals of subgoals[i]. -*/ -proof_converter * concat(proof_converter * pc1, unsigned num, proof_converter * const * pc2s, unsigned * num_subgoals); + \brief create a proof converter that takes a set of subgoals and converts their proofs to a proof of + the goal they were derived from. + */ +proof_converter * concat(proof_converter *pc1, unsigned n, goal* const* goals); proof_converter * proof2proof_converter(ast_manager & m, proof * pr); -typedef sref_vector proof_converter_ref_vector; -typedef sref_buffer proof_converter_ref_buffer; - -void apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s, proof_ref & result); void apply(ast_manager & m, proof_converter * pc, proof_ref & pr); +proof_ref apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s); + #endif diff --git a/src/tactic/replace_proof_converter.cpp b/src/tactic/replace_proof_converter.cpp index ba74452cf..4a98110eb 100644 --- a/src/tactic/replace_proof_converter.cpp +++ b/src/tactic/replace_proof_converter.cpp @@ -53,8 +53,7 @@ public: }; -void replace_proof_converter::operator()(ast_manager & m, unsigned num_source, - proof * const * source, proof_ref & result) { +proof_ref replace_proof_converter::operator()(ast_manager & m, unsigned num_source, proof * const * source) { SASSERT(num_source == 1); replace_map replace(m); proof_ref p(m); @@ -73,14 +72,12 @@ void replace_proof_converter::operator()(ast_manager & m, unsigned num_source, replace.apply(tmp); TRACE("proof_converter", tout << mk_pp(source[0], m) << "\n"; tout << mk_pp(tmp.get(), m) << "\n";); - result = to_app(tmp); + return proof_ref(to_app(tmp), m); } proof_converter * replace_proof_converter::translate(ast_translation & translator) { replace_proof_converter* rp = alloc(replace_proof_converter, m); - for (unsigned i = 0; i < m_proofs.size(); ++i) { - rp->insert(translator(m_proofs[i].get())); - } + for (proof* p : m_proofs) rp->insert(translator(p)); return rp; } diff --git a/src/tactic/replace_proof_converter.h b/src/tactic/replace_proof_converter.h index 44ba2d82d..67cd77a0f 100644 --- a/src/tactic/replace_proof_converter.h +++ b/src/tactic/replace_proof_converter.h @@ -34,7 +34,7 @@ public: ~replace_proof_converter() override {} - void operator()(ast_manager & _m, unsigned num_source, proof * const * source, proof_ref & result) override; + proof_ref operator()(ast_manager & _m, unsigned num_source, proof * const * source) override; proof_converter * translate(ast_translation & translator) override; @@ -45,6 +45,8 @@ public: // run the replacements the inverse direction. void invert() { m_proofs.reverse(); } + void display(std::ostream & out) override {} + }; #endif diff --git a/src/tactic/sine_filter.cpp b/src/tactic/sine_filter.cpp index e67169a8e..0ac726986 100644 --- a/src/tactic/sine_filter.cpp +++ b/src/tactic/sine_filter.cpp @@ -18,11 +18,9 @@ Revision History: #include "tactic/sine_filter.h" #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/datatype_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/filter_model_converter.h" -#include "tactic/extension_model_converter.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_util.h" #include "util/obj_pair_hashtable.h" @@ -38,23 +36,17 @@ public: sine_tactic(ast_manager& m, params_ref const& p): m(m), m_params(p) {} - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(sine_tactic, m, m_params); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; - + void operator()(goal_ref const & g, goal_ref_buffer& result) override { TRACE("sine", g->display(tout);); TRACE("sine", tout << g->size();); ptr_vector new_forms; @@ -69,11 +61,9 @@ public: result.push_back(g.get()); TRACE("sine", result[0]->display(tout);); SASSERT(g->is_well_sorted()); - filter_model_converter * fmc = alloc(filter_model_converter, m); - mc = fmc; } - virtual void cleanup() { + void cleanup() override { } private: diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index fa9ea4e4a..4a26a3716 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -59,19 +59,17 @@ public: sls_params::collect_param_descrs(r); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; result.reset(); + result.reset(); TRACE("sls", g->display(tout);); tactic_report report("sls", *g); + model_converter_ref mc; m_engine->operator()(g, mc); - + g->add(mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("sls", g->display(tout);); diff --git a/src/tactic/smtlogics/CMakeLists.txt b/src/tactic/smtlogics/CMakeLists.txt index c90fd7468..2741334b4 100644 --- a/src/tactic/smtlogics/CMakeLists.txt +++ b/src/tactic/smtlogics/CMakeLists.txt @@ -11,7 +11,6 @@ z3_add_component(smtlogic_tactics qfnra_tactic.cpp qfufbv_ackr_model_converter.cpp qfufbv_tactic.cpp - qfufnra_tactic.cpp qfuf_tactic.cpp quant_tactics.cpp COMPONENT_DEPENDENCIES @@ -22,7 +21,6 @@ z3_add_component(smtlogic_tactics fp muz nlsat_tactic - nlsat_smt_tactic qe sat_solver smt_tactic @@ -40,6 +38,5 @@ z3_add_component(smtlogic_tactics qfnra_tactic.h qfuf_tactic.h qfufbv_tactic.h - qfufnra_tactic.h quant_tactics.h ) diff --git a/src/tactic/smtlogics/nra_tactic.cpp b/src/tactic/smtlogics/nra_tactic.cpp index 381bc4bb6..a9b32e5a8 100644 --- a/src/tactic/smtlogics/nra_tactic.cpp +++ b/src/tactic/smtlogics/nra_tactic.cpp @@ -19,13 +19,13 @@ Notes: #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" -#include "smt/tactic/smt_tactic.h" #include "tactic/core/nnf_tactic.h" +#include "tactic/arith/probe_arith.h" +#include "smt/tactic/smt_tactic.h" #include "qe/qe_tactic.h" #include "qe/nlqsat.h" -#include "nlsat/tactic/qfnra_nlsat_tactic.h" #include "qe/qe_lite.h" -#include "tactic/arith/probe_arith.h" +#include "nlsat/tactic/qfnra_nlsat_tactic.h" tactic * mk_nra_tactic(ast_manager & m, params_ref const& p) { params_ref p1 = p; diff --git a/src/tactic/smtlogics/qfbv_tactic.cpp b/src/tactic/smtlogics/qfbv_tactic.cpp index b69069864..bc93b4e7b 100644 --- a/src/tactic/smtlogics/qfbv_tactic.cpp +++ b/src/tactic/smtlogics/qfbv_tactic.cpp @@ -28,6 +28,7 @@ Notes: #include "tactic/bv/bv_size_reduction_tactic.h" #include "tactic/aig/aig_tactic.h" #include "sat/tactic/sat_tactic.h" +#include "sat/sat_solver/inc_sat_solver.h" #include "ackermannization/ackermannize_bv_tactic.h" #define MEMLIMIT 300 @@ -127,11 +128,10 @@ static tactic * mk_qfbv_tactic(ast_manager& m, params_ref const & p, tactic* sat tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p) { - tactic * new_sat = cond(mk_produce_proofs_probe(), and_then(mk_simplify_tactic(m), mk_smt_tactic()), - mk_sat_tactic(m)); + mk_psat_tactic(m, p)); - return mk_qfbv_tactic(m, p, new_sat, mk_smt_tactic()); + return mk_qfbv_tactic(m, p, new_sat, mk_psmt_tactic(m, p)); } diff --git a/src/tactic/smtlogics/qflia_tactic.cpp b/src/tactic/smtlogics/qflia_tactic.cpp index 628555cae..541a81682 100644 --- a/src/tactic/smtlogics/qflia_tactic.cpp +++ b/src/tactic/smtlogics/qflia_tactic.cpp @@ -89,7 +89,7 @@ static tactic * mk_bv2sat_tactic(ast_manager & m) { mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m), mk_aig_tactic(), - mk_sat_tactic(m)), + mk_sat_tactic(m, solver_p)), solver_p); } @@ -220,7 +220,7 @@ tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p) { using_params(mk_lia2sat_tactic(m), quasi_pb_p), mk_fail_if_undecided_tactic()), mk_bounded_tactic(m), - mk_smt_tactic())), + mk_psmt_tactic(m, p))), main_p); st->updt_params(p); diff --git a/src/tactic/smtlogics/qfnia_tactic.cpp b/src/tactic/smtlogics/qfnia_tactic.cpp index d967660ce..2ef49229a 100644 --- a/src/tactic/smtlogics/qfnia_tactic.cpp +++ b/src/tactic/smtlogics/qfnia_tactic.cpp @@ -26,7 +26,7 @@ Notes: #include "tactic/bv/max_bv_sharing_tactic.h" #include "sat/tactic/sat_tactic.h" #include "tactic/arith/nla2bv_tactic.h" -#include "tactic/arith/elim01_tactic.h" +#include "tactic/arith/lia2card_tactic.h" #include "tactic/core/ctx_simplify_tactic.h" #include "tactic/core/cofactor_term_ite_tactic.h" #include "nlsat/tactic/qfnra_nlsat_tactic.h" @@ -73,7 +73,7 @@ static tactic * mk_qfnia_premable(ast_manager & m, params_ref const & p_ref) { using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), using_params(mk_simplify_tactic(m), pull_ite_p), mk_elim_uncnstr_tactic(m), - mk_elim01_tactic(m), + mk_lia2card_tactic(m), skip_if_failed(using_params(mk_cofactor_term_ite_tactic(m), elim_p))); } diff --git a/src/tactic/smtlogics/qfufbv_tactic.cpp b/src/tactic/smtlogics/qfufbv_tactic.cpp index bc2f55061..3bd28ad6d 100644 --- a/src/tactic/smtlogics/qfufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfufbv_tactic.cpp @@ -40,6 +40,7 @@ Notes: #include "tactic/smtlogics/qfbv_tactic.h" #include "solver/tactic2solver.h" #include "tactic/bv/bv_bound_chk_tactic.h" +#include "ackermannization/ackermannize_bv_tactic.h" /////////////// class qfufbv_ackr_tactic : public tactic { @@ -53,12 +54,7 @@ public: ~qfufbv_ackr_tactic() override { } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - mc = nullptr; + void operator()(goal_ref const & g, goal_ref_buffer & result) override { ast_manager& m(g->m()); tactic_report report("qfufbv_ackr", *g); fail_if_unsat_core_generation("qfufbv_ackr", g); @@ -80,7 +76,7 @@ public: // report model if (g->models_enabled() && (o == l_true)) { model_ref abstr_model = imp.get_model(); - mc = mk_qfufbv_ackr_model_converter(m, imp.get_info(), abstr_model); + g->add(mk_qfufbv_ackr_model_converter(m, imp.get_info(), abstr_model)); } } @@ -162,13 +158,14 @@ static tactic * mk_qfufbv_preamble1(ast_manager & m, params_ref const & p) { static tactic * mk_qfufbv_preamble(ast_manager & m, params_ref const & p) { return and_then(mk_simplify_tactic(m), - mk_propagate_values_tactic(m), - mk_solve_eqs_tactic(m), - mk_elim_uncnstr_tactic(m), - if_no_proofs(if_no_unsat_cores(mk_reduce_args_tactic(m))), - if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), - mk_max_bv_sharing_tactic(m) - ); + mk_propagate_values_tactic(m), + mk_solve_eqs_tactic(m), + mk_elim_uncnstr_tactic(m), + if_no_proofs(if_no_unsat_cores(mk_reduce_args_tactic(m))), + if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), + mk_max_bv_sharing_tactic(m), + if_no_proofs(if_no_unsat_cores(mk_ackermannize_bv_tactic(m,p))) + ); } tactic * mk_qfufbv_tactic(ast_manager & m, params_ref const & p) { diff --git a/src/tactic/smtlogics/qfufnra_tactic.cpp b/src/tactic/smtlogics/qfufnra_tactic.cpp deleted file mode 100644 index e031b0f52..000000000 --- a/src/tactic/smtlogics/qfufnra_tactic.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/*++ -Copyright (c) 2015 Microsoft Corporation - -Module Name: - - qfufnra_tactic.cpp - -Abstract: - - Tactic for QF_UFNRA - -Author: - - Nikolaj (nbjorner) 2015-05-05 - -Notes: - ---*/ -#include "tactic/tactical.h" -#include "tactic/core/simplify_tactic.h" -#include "tactic/core/propagate_values_tactic.h" -#include "tactic/nlsat_smt/nl_purify_tactic.h" -#include "tactic/smtlogics/qfufnra_tactic.h" -#include "tactic/arith/purify_arith_tactic.h" -#include "tactic/core/solve_eqs_tactic.h" -#include "tactic/core/elim_term_ite_tactic.h" -#include "tactic/core/elim_uncnstr_tactic.h" -#include "tactic/core/simplify_tactic.h" -#include "tactic/core/nnf_tactic.h" -#include "tactic/core/tseitin_cnf_tactic.h" - -tactic * mk_qfufnra_tactic(ast_manager & m, params_ref const& p) { - params_ref main_p = p; - main_p.set_bool("elim_and", true); - main_p.set_bool("blast_distinct", true); - - return and_then(and_then(using_params(mk_simplify_tactic(m, p), main_p), - mk_purify_arith_tactic(m, p), - mk_propagate_values_tactic(m, p), - mk_solve_eqs_tactic(m, p), - mk_elim_uncnstr_tactic(m, p)), - and_then(mk_elim_term_ite_tactic(m, p), - mk_solve_eqs_tactic(m, p), - using_params(mk_simplify_tactic(m, p), main_p), - mk_tseitin_cnf_core_tactic(m, p), - using_params(mk_simplify_tactic(m, p), main_p), - mk_nl_purify_tactic(m, p))); -} - - diff --git a/src/tactic/smtlogics/qfufnra_tactic.h b/src/tactic/smtlogics/qfufnra_tactic.h deleted file mode 100644 index 026ab5c5c..000000000 --- a/src/tactic/smtlogics/qfufnra_tactic.h +++ /dev/null @@ -1,31 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - qfufnra_tactic.h - -Abstract: - - Tactic for QF_UFNRA - -Author: - - Leonardo (leonardo) 2012-02-28 - -Notes: - ---*/ -#ifndef QFUFNRA_TACTIC_H_ -#define QFUFNRA_TACTIC_H_ - -#include "util/params.h" -class ast_manager; -class tactic; - -tactic * mk_qfufnra_tactic(ast_manager & m, params_ref const & p = params_ref()); -/* - ADD_TACTIC("qfufnra", "builtin strategy for solving QF_UNFRA problems.", "mk_qfufnra_tactic(m, p)") -*/ - -#endif diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index da063de39..4fa9ca43f 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -67,16 +67,8 @@ void report_tactic_progress(char const * id, unsigned val) { } } -void skip_tactic::operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - result.reset(); +void skip_tactic::operator()(goal_ref const & in, goal_ref_buffer& result) { result.push_back(in.get()); - mc = nullptr; - pc = nullptr; - core = nullptr; } tactic * mk_skip_tactic() { @@ -85,11 +77,7 @@ tactic * mk_skip_tactic() { class fail_tactic : public tactic { public: - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer & result) override { throw tactic_exception("fail tactic"); } @@ -108,13 +96,9 @@ class report_verbose_tactic : public skip_tactic { public: report_verbose_tactic(char const * msg, unsigned lvl) : m_msg(msg), m_lvl(lvl) {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { IF_VERBOSE(m_lvl, verbose_stream() << m_msg << "\n";); - skip_tactic::operator()(in, result, mc, pc, core); + skip_tactic::operator()(in, result); } }; @@ -127,14 +111,10 @@ class trace_tactic : public skip_tactic { public: trace_tactic(char const * tag): m_tag(tag) {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { TRACE(m_tag, in->display(tout);); (void)m_tag; - skip_tactic::operator()(in, result, mc, pc, core); + skip_tactic::operator()(in, result); } }; @@ -146,14 +126,10 @@ class fail_if_undecided_tactic : public skip_tactic { public: fail_if_undecided_tactic() {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (!in->is_decided()) throw tactic_exception("undecided"); - skip_tactic::operator()(in, result, mc, pc, core); + skip_tactic::operator()(in, result); } }; @@ -161,10 +137,10 @@ tactic * mk_fail_if_undecided_tactic() { return alloc(fail_if_undecided_tactic); } -void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { +void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result) { t.reset_statistics(); try { - t(in, result, mc, pc, core); + t(in, result); t.cleanup(); } catch (tactic_exception & ex) { @@ -184,29 +160,26 @@ lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, p core = nullptr; ast_manager & m = g->m(); goal_ref_buffer r; - model_converter_ref mc; - proof_converter_ref pc; try { - exec(t, g, r, mc, pc, core); + exec(t, g, r); } catch (tactic_exception & ex) { reason_unknown = ex.msg(); return l_undef; } - TRACE("tactic_mc", mc->display(tout);); TRACE("tactic_check_sat", tout << "r.size(): " << r.size() << "\n"; for (unsigned i = 0; i < r.size(); i++) r[i]->display(tout);); if (is_decided_sat(r)) { - if (models_enabled) { - if (mc) - (*mc)(labels, 0); + model_converter_ref mc = r[0]->mc(); + if (mc.get()) { + (*mc)(labels); model_converter2model(m, mc.get(), md); - if (!md) { - // create empty model. - md = alloc(model, m); - } + } + if (!md) { + // create empty model. + md = alloc(model, m); } return l_true; } @@ -218,10 +191,11 @@ lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, p return l_false; } else { - if (models_enabled) { - model_converter2model(m, mc.get(), md); - if (mc) - (*mc)(labels, 0); + if (models_enabled && r.size() >= 1) { + model_converter_ref mc = r[0]->mc(); + model_converter2model(m, mc.get(), md); + if (mc) + (*mc)(labels); } reason_unknown = "incomplete"; return l_undef; diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index 096ce367e..c9b5a23fd 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -24,8 +24,6 @@ Notes: #include "tactic/goal.h" #include "util/params.h" #include "util/statistics.h" -#include "tactic/model_converter.h" -#include "tactic/proof_converter.h" #include "tactic/tactic_exception.h" #include "util/lbool.h" @@ -50,19 +48,7 @@ public: The list of resultant subgoals is stored in \c result. The content of \c in may be destroyed during the operation. - - The resultant model converter \c mc can be used to convert a model for one of the returned subgoals - into a model for \in. If mc == 0, then model construction is disabled or any model for a subgoal - of \c in is also a model for \c in. - If \c result is decided_sat (i.e., it contains a single empty subgoal), then - the model converter is just wrapping the model. - - The resultant proof converter \c pc can be used to convert proofs for each subgoal in \c result - into a proof for \c in. If pc == 0, then one of the following conditions should hold: - 1- proof construction is disabled, - 2- result contains a single subgoal, and any proof of unsatisfiability for this subgoal is a proof for \c in. - 3- result is an decided_unsat (i.e., it contains a single unsat subgoal). The actual proof can be extracted from this goal. - + The output parameter \c core is used to accumulate the unsat core of closed subgoals. It must be 0 if dependency tracking is disabled, or the result is decided unsat, or no tagged assertions were used to close any subgoal. @@ -75,11 +61,7 @@ public: Therefore, in most cases, pc == 0 and core == 0 for non-branching tactics. */ - virtual void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) = 0; + virtual void operator()(goal_ref const & in, goal_ref_buffer& result) = 0; virtual void collect_statistics(statistics & st) const {} virtual void reset_statistics() {} @@ -119,9 +101,9 @@ void report_tactic_progress(char const * id, unsigned val); class skip_tactic : public tactic { public: - void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) override; + void operator()(goal_ref const & in, goal_ref_buffer& result) override; void cleanup() override {} - tactic * translate(ast_manager & m) override { return this; } + tactic * translate(ast_manager & m) override { return this; } }; tactic * mk_skip_tactic(); @@ -152,7 +134,7 @@ public: #define MK_SIMPLE_TACTIC_FACTORY(NAME, ST) MK_TACTIC_FACTORY(NAME, return ST;) -void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); +void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result); lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown); // Throws an exception if goal \c in requires proof generation. diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index a9ada3a11..bb04a2be7 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -16,33 +16,28 @@ Author: Notes: --*/ -#include "tactic/tactical.h" #include "util/scoped_timer.h" #include "util/cancel_eh.h" #include "util/cooperate.h" #include "util/scoped_ptr_vector.h" #include "util/z3_omp.h" +#include "tactic/tactical.h" class binary_tactical : public tactic { protected: - tactic * m_t1; - tactic * m_t2; - + tactic_ref m_t1; + tactic_ref m_t2; public: + binary_tactical(tactic * t1, tactic * t2): m_t1(t1), m_t2(t2) { SASSERT(m_t1); SASSERT(m_t2); - m_t1->inc_ref(); - m_t2->inc_ref(); } - ~binary_tactical() override { - m_t1->dec_ref(); - m_t2->dec_ref(); - } + ~binary_tactical() override { } void updt_params(params_ref const & p) override { m_t1->updt_params(p); @@ -59,7 +54,7 @@ public: m_t2->collect_statistics(st); } - void reset_statistics() override { + void reset_statistics() override { m_t1->reset_statistics(); m_t2->reset_statistics(); } @@ -106,108 +101,62 @@ public: and_then_tactical(tactic * t1, tactic * t2):binary_tactical(t1, t2) {} ~and_then_tactical() override {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { - bool models_enabled = in->models_enabled(); bool proofs_enabled = in->proofs_enabled(); bool cores_enabled = in->unsat_core_enabled(); - ast_manager & m = in->m(); - goal_ref_buffer r1; - model_converter_ref mc1; - proof_converter_ref pc1; - expr_dependency_ref core1(m); - result.reset(); - mc = nullptr; - pc = nullptr; - core = nullptr; - m_t1->operator()(in, r1, mc1, pc1, core1); - SASSERT(!is_decided(r1) || (!pc1 && !core1)); // the pc and core of decided goals is 0 + ast_manager & m = in->m(); + goal_ref_buffer r1; + m_t1->operator()(in, r1); unsigned r1_size = r1.size(); - SASSERT(r1_size > 0); + SASSERT(r1_size > 0); if (r1_size == 1) { if (r1[0]->is_decided()) { - result.push_back(r1[0]); - if (models_enabled) mc = mc1; - return; + result.push_back(r1[0]); + return; } goal_ref r1_0 = r1[0]; - m_t2->operator()(r1_0, result, mc, pc, core); - if (models_enabled) mc = concat(mc1.get(), mc.get()); - if (proofs_enabled) pc = concat(pc1.get(), pc.get()); - if (cores_enabled) core = m.mk_join(core1.get(), core); + m_t2->operator()(r1_0, result); } else { - if (cores_enabled) core = core1; - proof_converter_ref_buffer pc_buffer; - model_converter_ref_buffer mc_buffer; - sbuffer sz_buffer; - goal_ref_buffer r2; + goal_ref_buffer r2; for (unsigned i = 0; i < r1_size; i++) { - goal_ref g = r1[i]; - r2.reset(); - model_converter_ref mc2; - proof_converter_ref pc2; - expr_dependency_ref core2(m); - m_t2->operator()(g, r2, mc2, pc2, core2); + goal_ref g = r1[i]; + r2.reset(); + m_t2->operator()(g, r2); if (is_decided(r2)) { SASSERT(r2.size() == 1); if (is_decided_sat(r2)) { - // found solution... + // found solution... + result.reset(); result.push_back(r2[0]); - if (models_enabled) { - // mc2 contains the actual model - model_ref md; - md = alloc(model, m); - apply(mc2, md, 0); - apply(mc1, md, i); - mc = model2model_converter(md.get()); - } - SASSERT(!pc); SASSERT(!core); - return; + return; } else { SASSERT(is_decided_unsat(r2)); - // the proof and unsat core of a decided_unsat goal are stored in the node itself. - // pc2 and core2 must be 0. - SASSERT(!pc2); - SASSERT(!core2); - if (models_enabled) mc_buffer.push_back(nullptr); - if (proofs_enabled) pc_buffer.push_back(proof2proof_converter(m, r2[0]->pr(0))); - if (models_enabled || proofs_enabled) sz_buffer.push_back(0); - if (cores_enabled) core = m.mk_join(core.get(), r2[0]->dep(0)); } } else { result.append(r2.size(), r2.c_ptr()); - if (models_enabled) mc_buffer.push_back(mc2.get()); - if (proofs_enabled) pc_buffer.push_back(pc2.get()); - if (models_enabled || proofs_enabled) sz_buffer.push_back(r2.size()); - if (cores_enabled) core = m.mk_join(core.get(), core2.get()); } } - + if (result.empty()) { // all subgoals were shown to be unsat. // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); - if (proofs_enabled) - apply(m, pc1, pc_buffer, pr); - SASSERT(cores_enabled || core == 0); + expr_dependency_ref core(m); + if (proofs_enabled) { + apply(m, in->pc(), pr); + } + dependency_converter* dc = in->dc(); + if (cores_enabled && dc) { + core = (*dc)(); + } in->assert_expr(m.mk_false(), pr, core); - core = nullptr; result.push_back(in.get()); - SASSERT(!mc); SASSERT(!pc); SASSERT(!core); - } - else { - if (models_enabled) mc = concat(mc1.get(), mc_buffer.size(), mc_buffer.c_ptr(), sz_buffer.c_ptr()); - if (proofs_enabled) pc = concat(pc1.get(), pc_buffer.size(), pc_buffer.c_ptr(), sz_buffer.c_ptr()); - SASSERT(cores_enabled || core == 0); } } } @@ -271,92 +220,58 @@ tactic * and_then(unsigned num, tactic * const * ts) { class nary_tactical : public tactic { protected: - ptr_vector m_ts; + sref_vector m_ts; public: nary_tactical(unsigned num, tactic * const * ts) { for (unsigned i = 0; i < num; i++) { SASSERT(ts[i]); m_ts.push_back(ts[i]); - ts[i]->inc_ref(); } } - ~nary_tactical() override { - unsigned sz = m_ts.size(); - for (unsigned i = 0; i < sz; i++) { - m_ts[i]->dec_ref(); - } - } + ~nary_tactical() override { } void updt_params(params_ref const & p) override { TRACE("nary_tactical_updt_params", tout << "updt_params: " << p << "\n";); - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->updt_params(p); + for (tactic* t : m_ts) t->updt_params(p); } void collect_param_descrs(param_descrs & r) override { - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->collect_param_descrs(r); + for (tactic* t : m_ts) t->collect_param_descrs(r); } void collect_statistics(statistics & st) const override { - ptr_vector::const_iterator it = m_ts.begin(); - ptr_vector::const_iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->collect_statistics(st); + for (tactic const* t : m_ts) t->collect_statistics(st); } - void reset_statistics() override { - ptr_vector::const_iterator it = m_ts.begin(); - ptr_vector::const_iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->reset_statistics(); + void reset_statistics() override { + for (tactic* t : m_ts) t->reset_statistics(); } void cleanup() override { - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->cleanup(); + for (tactic* t : m_ts) t->cleanup(); } void reset() override { - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->reset(); + for (tactic* t : m_ts) t->reset(); } void set_logic(symbol const & l) override { - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->set_logic(l); + for (tactic* t : m_ts) t->set_logic(l); } void set_progress_callback(progress_callback * callback) override { - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->set_progress_callback(callback); + for (tactic* t : m_ts) t->set_progress_callback(callback); } protected: template tactic * translate_core(ast_manager & m) { - ptr_buffer new_ts; - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) { - tactic * curr = *it; - tactic * new_curr = curr->translate(m); - new_ts.push_back(new_curr); + sref_vector new_ts; + for (tactic* curr : m_ts) { + new_ts.push_back(curr->translate(m)); } return alloc(T, new_ts.size(), new_ts.c_ptr()); } @@ -369,31 +284,24 @@ public: ~or_else_tactical() override {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { goal orig(*(in.get())); unsigned sz = m_ts.size(); unsigned i; for (i = 0; i < sz; i++) { tactic * t = m_ts[i]; - result.reset(); - mc = nullptr; - pc = nullptr; - core = nullptr; SASSERT(sz > 0); if (i < sz - 1) { try { - t->operator()(in, result, mc, pc, core); + t->operator()(in, result); return; } catch (tactic_exception &) { + result.reset(); } } else { - t->operator()(in, result, mc, pc, core); + t->operator()(in, result); return; } in->reset_all(); @@ -468,11 +376,7 @@ public: - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { bool use_seq; #ifdef _NO_OMP_ use_seq = true; @@ -481,7 +385,7 @@ public: #endif if (use_seq) { // execute tasks sequentially - or_else_tactical::operator()(in, result, mc, pc, core); + or_else_tactical::operator()(in, result); return; } @@ -509,15 +413,12 @@ public: #pragma omp parallel for for (int i = 0; i < static_cast(sz); i++) { goal_ref_buffer _result; - model_converter_ref _mc; - proof_converter_ref _pc; - expr_dependency_ref _core(*(managers[i])); goal_ref in_copy = in_copies[i]; tactic & t = *(ts.get(i)); try { - t(in_copy, _result, _mc, _pc, _core); + t(in_copy, _result); bool first = false; #pragma omp critical (par_tactical) { @@ -533,13 +434,11 @@ public: } } ast_translation translator(*(managers[i]), m, false); - for (unsigned k = 0; k < _result.size(); k++) { - result.push_back(_result[k]->translate(translator)); + for (goal* g : _result) { + result.push_back(g->translate(translator)); } - mc = _mc ? _mc->translate(translator) : nullptr; - pc = _pc ? _pc->translate(translator) : nullptr; - expr_dependency_translation td(translator); - core = td(_core); + goal_ref in2(in_copy->translate(translator)); + in->copy_from(*(in2.get())); } } catch (tactic_exception & ex) { @@ -562,7 +461,6 @@ public: } } if (finished_id == UINT_MAX) { - mc = nullptr; switch (ex_kind) { case ERROR_EX: throw z3_error(error_code); case TACTIC_EX: throw tactic_exception(ex_msg.c_str()); @@ -599,11 +497,7 @@ public: par_and_then_tactical(tactic * t1, tactic * t2):and_then_tactical(t1, t2) {} ~par_and_then_tactical() override {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { bool use_seq; #ifdef _NO_OMP_ use_seq = true; @@ -612,43 +506,30 @@ public: #endif if (use_seq) { // execute tasks sequentially - and_then_tactical::operator()(in, result, mc, pc, core); + and_then_tactical::operator()(in, result); return; } - bool models_enabled = in->models_enabled(); + // enabling proofs is possible, but requires translating subgoals back. + fail_if_proof_generation("par_and_then", in); bool proofs_enabled = in->proofs_enabled(); bool cores_enabled = in->unsat_core_enabled(); ast_manager & m = in->m(); - goal_ref_buffer r1; - model_converter_ref mc1; - proof_converter_ref pc1; - expr_dependency_ref core1(m); - result.reset(); - mc = nullptr; - pc = nullptr; - core = nullptr; - m_t1->operator()(in, r1, mc1, pc1, core1); - SASSERT(!is_decided(r1) || (!pc1 && !core1)); // the pc and core of decided goals is 0 - unsigned r1_size = r1.size(); + goal_ref_buffer r1; + m_t1->operator()(in, r1); + unsigned r1_size = r1.size(); SASSERT(r1_size > 0); if (r1_size == 1) { // Only one subgoal created... no need for parallelism if (r1[0]->is_decided()) { - result.push_back(r1[0]); - if (models_enabled) mc = mc1; - SASSERT(!pc); SASSERT(!core); - return; + result.push_back(r1[0]); + return; } goal_ref r1_0 = r1[0]; - m_t2->operator()(r1_0, result, mc, pc, core); - if (models_enabled) mc = concat(mc1.get(), mc.get()); - if (proofs_enabled) pc = concat(pc1.get(), pc.get()); - if (cores_enabled) core = m.mk_join(core1.get(), core); + m_t2->operator()(r1_0, result); } else { - if (cores_enabled) core = core1; scoped_ptr_vector managers; tactic_ref_vector ts2; @@ -662,13 +543,9 @@ public: ts2.push_back(m_t2->translate(*new_m)); } - proof_converter_ref_buffer pc_buffer; - model_converter_ref_buffer mc_buffer; scoped_ptr_vector core_buffer; scoped_ptr_vector goals_vect; - pc_buffer.resize(r1_size); - mc_buffer.resize(r1_size); core_buffer.resize(r1_size); goals_vect.resize(r1_size); @@ -684,14 +561,11 @@ public: goal_ref new_g = g_copies[i]; goal_ref_buffer r2; - model_converter_ref mc2; - proof_converter_ref pc2; - expr_dependency_ref core2(new_m); bool curr_failed = false; try { - ts2[i]->operator()(new_g, r2, mc2, pc2, core2); + ts2[i]->operator()(new_g, r2); } catch (tactic_exception & ex) { #pragma omp critical (par_and_then_tactical) @@ -756,32 +630,13 @@ public: } ast_translation translator(new_m, m, false); SASSERT(r2.size() == 1); - result.push_back(r2[0]->translate(translator)); - if (models_enabled) { - // mc2 contains the actual model - mc2 = mc2 ? mc2->translate(translator) : nullptr; - model_ref md; - md = alloc(model, m); - apply(mc2, md, 0); - apply(mc1, md, i); - mc = model2model_converter(md.get()); - } - SASSERT(!pc); SASSERT(!core); + result.push_back(r2[0]->translate(translator)); } } else { SASSERT(is_decided_unsat(r2)); - // the proof and unsat core of a decided_unsat goal are stored in the node itself. - // pc2 and core2 must be 0. - SASSERT(!pc2); - SASSERT(!core2); - if (models_enabled) mc_buffer.set(i, nullptr); - if (proofs_enabled) { - proof * pr = r2[0]->pr(0); - pc_buffer.push_back(proof2proof_converter(m, pr)); - } - if (cores_enabled && r2[0]->dep(0) != nullptr) { + if (cores_enabled && r2[0]->dep(0) != 0) { expr_dependency_ref * new_dep = alloc(expr_dependency_ref, new_m); *new_dep = r2[0]->dep(0); core_buffer.set(i, new_dep); @@ -792,11 +647,10 @@ public: goal_ref_buffer * new_r2 = alloc(goal_ref_buffer); goals_vect.set(i, new_r2); new_r2->append(r2.size(), r2.c_ptr()); - mc_buffer.set(i, mc2.get()); - pc_buffer.set(i, pc2.get()); - if (cores_enabled && core2 != 0) { + dependency_converter* dc = r1[i]->dc(); + if (cores_enabled && dc) { expr_dependency_ref * new_dep = alloc(expr_dependency_ref, new_m); - *new_dep = core2; + *new_dep = (*dc)(); core_buffer.set(i, new_dep); } } @@ -814,25 +668,21 @@ public: if (found_solution) return; - - core = nullptr; - sbuffer sz_buffer; + + expr_dependency_ref core(m); for (unsigned i = 0; i < r1_size; i++) { ast_translation translator(*(managers[i]), m, false); goal_ref_buffer * r = goals_vect[i]; - if (r != nullptr) { + unsigned j = result.size(); + if (r != 0) { for (unsigned k = 0; k < r->size(); k++) { result.push_back((*r)[k]->translate(translator)); } - sz_buffer.push_back(r->size()); } - else { - sz_buffer.push_back(0); + if (proofs_enabled) { + // update proof converter of r1[i] + r1[i]->set(concat(r1[i]->pc(), result.size() - j, result.c_ptr() + j)); } - if (mc_buffer[i] != nullptr) - mc_buffer.set(i, mc_buffer[i]->translate(translator)); - if (pc_buffer[i] != nullptr) - pc_buffer.set(i, pc_buffer[i]->translate(translator)); expr_dependency_translation td(translator); if (core_buffer[i] != nullptr) { expr_dependency_ref curr_core(m); @@ -840,24 +690,24 @@ public: core = m.mk_join(curr_core, core); } } + if (core) { + in->add(dependency_converter::unit(core)); + } if (result.empty()) { // all subgoals were shown to be unsat. // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); - if (proofs_enabled) - apply(m, pc1, pc_buffer, pr); - SASSERT(cores_enabled || core == 0); + if (proofs_enabled) { + apply(m, in->pc(), pr); + } + dependency_converter* dc = in->dc(); + if (cores_enabled && dc) { + core = (*dc)(); + } in->assert_expr(m.mk_false(), pr, core); - core = nullptr; result.push_back(in.get()); - SASSERT(!mc); SASSERT(!pc); SASSERT(!core); - } - else { - if (models_enabled) mc = concat(mc1.get(), mc_buffer.size(), mc_buffer.c_ptr(), sz_buffer.c_ptr()); - if (proofs_enabled) pc = concat(pc1.get(), pc_buffer.size(), pc_buffer.c_ptr(), sz_buffer.c_ptr()); - SASSERT(cores_enabled || core == 0); } } } @@ -885,35 +735,28 @@ tactic * par_and_then(unsigned num, tactic * const * ts) { class unary_tactical : public tactic { protected: - tactic * m_t; + tactic_ref m_t; public: unary_tactical(tactic * t): m_t(t) { - SASSERT(t); - t->inc_ref(); + SASSERT(t); } - ~unary_tactical() override { - m_t->dec_ref(); - } + virtual ~unary_tactical() { } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - m_t->operator()(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer& result) override { + m_t->operator()(in, result); } - void cleanup() override { m_t->cleanup(); } + void cleanup(void) override { m_t->cleanup(); } void collect_statistics(statistics & st) const override { m_t->collect_statistics(st); } - void reset_statistics() override { m_t->reset_statistics(); } + void reset_statistics() override { m_t->reset_statistics(); } void updt_params(params_ref const & p) override { m_t->updt_params(p); } void collect_param_descrs(param_descrs & r) override { m_t->collect_param_descrs(r); } void reset() override { m_t->reset(); } - void set_logic(symbol const& l) override { m_t->set_logic(l); } + void set_logic(symbol const& l) override { m_t->set_logic(l); } void set_progress_callback(progress_callback * callback) override { m_t->set_progress_callback(callback); } protected: @@ -929,16 +772,10 @@ class repeat_tactical : public unary_tactical { void operator()(unsigned depth, goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer& result) { // TODO: implement a non-recursive version. if (depth > m_max_depth) { result.push_back(in.get()); - mc = nullptr; - pc = nullptr; - core = nullptr; return; } @@ -947,23 +784,14 @@ class repeat_tactical : public unary_tactical { bool cores_enabled = in->unsat_core_enabled(); ast_manager & m = in->m(); - goal_ref_buffer r1; - model_converter_ref mc1; - proof_converter_ref pc1; - expr_dependency_ref core1(m); - result.reset(); - mc = nullptr; - pc = nullptr; - core = nullptr; + goal_ref_buffer r1; + result.reset(); { goal orig_in(in->m(), proofs_enabled, models_enabled, cores_enabled); orig_in.copy_from(*(in.get())); - m_t->operator()(in, r1, mc1, pc1, core1); - if (is_equal(orig_in, *(in.get()))) { - result.push_back(r1[0]); - if (models_enabled) mc = mc1; - if (proofs_enabled) pc = pc1; - if (cores_enabled) core = core1; + m_t->operator()(in, r1); + if (r1.size() == 1 && is_equal(orig_in, *(r1[0]))) { + result.push_back(r1[0]); return; } } @@ -971,61 +799,31 @@ class repeat_tactical : public unary_tactical { SASSERT(r1_size > 0); if (r1_size == 1) { if (r1[0]->is_decided()) { - result.push_back(r1[0]); - if (models_enabled) mc = mc1; - SASSERT(!pc); SASSERT(!core); + result.push_back(r1[0]); return; } goal_ref r1_0 = r1[0]; - operator()(depth+1, r1_0, result, mc, pc, core); - if (models_enabled) mc = concat(mc.get(), mc1.get()); - if (proofs_enabled) pc = concat(pc.get(), pc1.get()); - if (cores_enabled) core = m.mk_join(core1.get(), core); + operator()(depth+1, r1_0, result); } else { - if (cores_enabled) core = core1; - proof_converter_ref_buffer pc_buffer; - model_converter_ref_buffer mc_buffer; - sbuffer sz_buffer; goal_ref_buffer r2; for (unsigned i = 0; i < r1_size; i++) { goal_ref g = r1[i]; - r2.reset(); - model_converter_ref mc2; - proof_converter_ref pc2; - expr_dependency_ref core2(m); - operator()(depth+1, g, r2, mc2, pc2, core2); + r2.reset(); + operator()(depth+1, g, r2); if (is_decided(r2)) { SASSERT(r2.size() == 1); if (is_decided_sat(r2)) { // found solution... result.push_back(r2[0]); - if (models_enabled) { - // mc2 contains the actual model - model_ref md; - if (mc2) (*mc2)(md, 0); - if (mc1) (*mc1)(md, i); - mc = model2model_converter(md.get()); - } - SASSERT(!pc); SASSERT(!core); return; } else { SASSERT(is_decided_unsat(r2)); - SASSERT(!pc2); - SASSERT(!core2); - if (models_enabled) mc_buffer.push_back(nullptr); - if (proofs_enabled) pc_buffer.push_back(proof2proof_converter(m, r2[0]->pr(0))); - if (models_enabled || proofs_enabled) sz_buffer.push_back(0); - if (cores_enabled) core = m.mk_join(core.get(), r2[0]->dep(0)); } } else { - result.append(r2.size(), r2.c_ptr()); - if (models_enabled) mc_buffer.push_back(mc2.get()); - if (proofs_enabled) pc_buffer.push_back(pc2.get()); - if (models_enabled || proofs_enabled) sz_buffer.push_back(r2.size()); - if (cores_enabled) core = m.mk_join(core.get(), core2.get()); + result.append(r2.size(), r2.c_ptr()); } } @@ -1034,18 +832,15 @@ class repeat_tactical : public unary_tactical { // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); - if (proofs_enabled) - apply(m, pc1, pc_buffer, pr); - SASSERT(cores_enabled || core == 0); + expr_dependency_ref core(m); + if (proofs_enabled) { + apply(m, in->pc(), pr); + } + if (cores_enabled && in->dc()) { + core = (*in->dc())(); + } in->assert_expr(m.mk_false(), pr, core); - core = nullptr; result.push_back(in.get()); - SASSERT(!mc); SASSERT(!pc); SASSERT(!core); - } - else { - if (models_enabled) mc = concat(mc1.get(), mc_buffer.size(), mc_buffer.c_ptr(), sz_buffer.c_ptr()); - if (proofs_enabled) pc = concat(pc1.get(), pc_buffer.size(), pc_buffer.c_ptr(), sz_buffer.c_ptr()); - SASSERT(cores_enabled || core == 0); } } } @@ -1056,15 +851,11 @@ public: m_max_depth(max_depth) { } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - operator()(0, in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer& result) override { + operator()(0, in, result); } - tactic * translate(ast_manager & m) override { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(repeat_tactical, new_t, m_max_depth); } @@ -1079,22 +870,15 @@ class fail_if_branching_tactical : public unary_tactical { public: fail_if_branching_tactical(tactic * t, unsigned threshold):unary_tactical(t), m_threshold(threshold) {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - m_t->operator()(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer& result) override { + m_t->operator()(in, result); if (result.size() > m_threshold) { - result.reset(); - mc = nullptr; - pc = nullptr; - core = nullptr; + result.reset(); // assumes in is not strenthened to one of the branches throw tactic_exception("failed-if-branching tactical"); } }; - tactic * translate(ast_manager & m) override { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(fail_if_branching_tactical, new_t, m_threshold); } @@ -1108,16 +892,12 @@ class cleanup_tactical : public unary_tactical { public: cleanup_tactical(tactic * t):unary_tactical(t) {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - m_t->operator()(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer& result) override { + m_t->operator()(in, result); m_t->cleanup(); } - tactic * translate(ast_manager & m) override { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(cleanup_tactical, new_t); } @@ -1132,20 +912,16 @@ class try_for_tactical : public unary_tactical { public: try_for_tactical(tactic * t, unsigned ts):unary_tactical(t), m_timeout(ts) {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { cancel_eh eh(in->m().limit()); { // Warning: scoped_timer is not thread safe in Linux. scoped_timer timer(m_timeout, &eh); - m_t->operator()(in, result, mc, pc, core); + m_t->operator()(in, result); } } - tactic * translate(ast_manager & m) override { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(try_for_tactical, new_t, m_timeout); } @@ -1177,7 +953,7 @@ public: tout << "new_p: " << new_p << "\n";); } - tactic * translate(ast_manager & m) override { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(using_params_tactical, new_t, m_params); } @@ -1202,16 +978,12 @@ public: annotate_tactical(char const* name, tactic* t): unary_tactical(t), m_name(name) {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { scope _scope(m_name); - m_t->operator()(in, result, mc, pc, core); + m_t->operator()(in, result); } - tactic * translate(ast_manager & m) override { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(annotate_tactical, m_name.c_str(), new_t); } @@ -1223,34 +995,27 @@ tactic * annotate_tactic(char const* name, tactic * t) { } class cond_tactical : public binary_tactical { - probe * m_p; + probe_ref m_p; public: cond_tactical(probe * p, tactic * t1, tactic * t2): binary_tactical(t1, t2), m_p(p) { SASSERT(m_p); - m_p->inc_ref(); } - ~cond_tactical() override { - m_p->dec_ref(); - } + virtual ~cond_tactical() {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer & result) override { if (m_p->operator()(*(in.get())).is_true()) - m_t1->operator()(in, result, mc, pc, core); + m_t1->operator()(in, result); else - m_t2->operator()(in, result, mc, pc, core); + m_t2->operator()(in, result); } tactic * translate(ast_manager & m) override { tactic * new_t1 = m_t1->translate(m); tactic * new_t2 = m_t2->translate(m); - return alloc(cond_tactical, m_p, new_t1, new_t2); + return alloc(cond_tactical, m_p.get(), new_t1, new_t2); } }; @@ -1263,28 +1028,18 @@ tactic * when(probe * p, tactic * t) { } class fail_if_tactic : public tactic { - probe * m_p; + probe_ref m_p; public: fail_if_tactic(probe * p): m_p(p) { SASSERT(m_p); - m_p->inc_ref(); } - ~fail_if_tactic() override { - m_p->dec_ref(); - } + virtual ~fail_if_tactic() {} void cleanup() override {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - mc = nullptr; - pc = nullptr; - core = nullptr; + void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (m_p->operator()(*(in.get())).is_true()) { throw tactic_exception("fail-if tactic"); } @@ -1308,18 +1063,12 @@ class if_no_proofs_tactical : public unary_tactical { public: if_no_proofs_tactical(tactic * t):unary_tactical(t) {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer & result) override { if (in->proofs_enabled()) { - mc = nullptr; pc = nullptr; core = nullptr; - result.reset(); result.push_back(in.get()); } else { - m_t->operator()(in, result, mc, pc, core); + m_t->operator()(in, result); } } @@ -1330,18 +1079,12 @@ class if_no_unsat_cores_tactical : public unary_tactical { public: if_no_unsat_cores_tactical(tactic * t):unary_tactical(t) {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (in->unsat_core_enabled()) { - mc = nullptr; pc = nullptr; core = nullptr; - result.reset(); result.push_back(in.get()); } else { - m_t->operator()(in, result, mc, pc, core); + m_t->operator()(in, result); } } @@ -1352,22 +1095,19 @@ class if_no_models_tactical : public unary_tactical { public: if_no_models_tactical(tactic * t):unary_tactical(t) {} - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (in->models_enabled()) { - mc = nullptr; pc = nullptr; core = nullptr; - result.reset(); result.push_back(in.get()); } else { - m_t->operator()(in, result, mc, pc, core); + m_t->operator()(in, result); } } - tactic * translate(ast_manager & m) override { return translate_core(m); } + tactic * translate(ast_manager & m) override { + return translate_core(m); + } + }; tactic * if_no_proofs(tactic * t) { diff --git a/src/tactic/ufbv/macro_finder_tactic.cpp b/src/tactic/ufbv/macro_finder_tactic.cpp index 5917136b6..3c981302e 100644 --- a/src/tactic/ufbv/macro_finder_tactic.cpp +++ b/src/tactic/ufbv/macro_finder_tactic.cpp @@ -19,7 +19,7 @@ Notes: #include "tactic/tactical.h" #include "ast/macros/macro_manager.h" #include "ast/macros/macro_finder.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "tactic/ufbv/macro_finder_tactic.h" class macro_finder_tactic : public tactic { @@ -38,12 +38,8 @@ class macro_finder_tactic : public tactic { void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("macro-finder", *g); bool produce_proofs = g->proofs_enabled(); @@ -69,15 +65,14 @@ class macro_finder_tactic : public tactic { produce_proofs ? new_proofs.get(i) : nullptr, unsat_core_enabled ? new_deps.get(i) : nullptr); - extension_model_converter * evmc = alloc(extension_model_converter, mm.get_manager()); + generic_model_converter * evmc = alloc(generic_model_converter, mm.get_manager(), "macro_finder"); unsigned num = mm.get_num_macros(); for (unsigned i = 0; i < num; i++) { expr_ref f_interp(mm.get_manager()); func_decl * f = mm.get_macro_interpretation(i, f_interp); - evmc->insert(f, f_interp); + evmc->add(f, f_interp); } - mc = evmc; - + g->add(evmc); g->inc_depth(); result.push_back(g.get()); TRACE("macro-finder", g->display(tout);); @@ -118,11 +113,8 @@ public: } void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/tactic/ufbv/quasi_macros_tactic.cpp b/src/tactic/ufbv/quasi_macros_tactic.cpp index 7fbc3c7a5..9b39308f3 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.cpp +++ b/src/tactic/ufbv/quasi_macros_tactic.cpp @@ -17,9 +17,9 @@ Notes: --*/ #include "tactic/tactical.h" +#include "tactic/generic_model_converter.h" #include "ast/macros/macro_manager.h" #include "ast/macros/macro_finder.h" -#include "tactic/extension_model_converter.h" #include "ast/macros/quasi_macros.h" #include "tactic/ufbv/quasi_macros_tactic.h" @@ -36,12 +36,8 @@ class quasi_macros_tactic : public tactic { void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("quasi-macros", *g); bool produce_proofs = g->proofs_enabled(); @@ -81,15 +77,14 @@ class quasi_macros_tactic : public tactic { produce_proofs ? proofs.get(i) : nullptr, produce_unsat_cores ? deps.get(i) : nullptr); - extension_model_converter * evmc = alloc(extension_model_converter, mm.get_manager()); + generic_model_converter * evmc = alloc(generic_model_converter, mm.get_manager(), "quasi_macros"); unsigned num = mm.get_num_macros(); for (unsigned i = 0; i < num; i++) { expr_ref f_interp(mm.get_manager()); func_decl * f = mm.get_macro_interpretation(i, f_interp); - evmc->insert(f, f_interp); + evmc->add(f, f_interp); } - mc = evmc; - + g->add(evmc); g->inc_depth(); result.push_back(g.get()); TRACE("quasi-macros", g->display(tout);); @@ -129,11 +124,8 @@ public: } void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp index eed72a71e..e9db976f9 100644 --- a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp +++ b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp @@ -32,12 +32,8 @@ class ufbv_rewriter_tactic : public tactic { ast_manager & m() const { return m_manager; } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = nullptr; pc = nullptr; core = nullptr; tactic_report report("ufbv-rewriter", *g); fail_if_unsat_core_generation("ufbv-rewriter", g); @@ -60,7 +56,8 @@ class ufbv_rewriter_tactic : public tactic { for (unsigned i = 0; i < new_forms.size(); i++) g->assert_expr(new_forms.get(i), produce_proofs ? new_proofs.get(i) : nullptr, nullptr); - mc = nullptr; // CMW: Remark: The demodulator could potentially remove all references to a variable. + // CMW: Remark: The demodulator could potentially + // remove all references to a variable. g->inc_depth(); result.push_back(g.get()); @@ -100,12 +97,8 @@ public: insert_produce_proofs(r); } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) override { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer & result) override { + (*m_imp)(in, result); } void cleanup() override { diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index fbcaec5ef..b487fe9ba 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable(test-z3 arith_rewriter.cpp arith_simplifier_plugin.cpp ast.cpp + bdd.cpp bit_blaster.cpp bits.cpp bit_vector.cpp @@ -92,6 +93,8 @@ add_executable(test-z3 rational.cpp rcf.cpp region.cpp + sat_local_search.cpp + sat_lookahead.cpp sat_user_scope.cpp simple_parser.cpp simplex.cpp @@ -116,7 +119,6 @@ add_executable(test-z3 upolynomial.cpp var_subst.cpp vector.cpp - lp.cpp ${z3_test_extra_object_files} ) z3_add_install_tactic_rule(${z3_test_deps}) diff --git a/src/test/bdd.cpp b/src/test/bdd.cpp new file mode 100644 index 000000000..ea5a0bc34 --- /dev/null +++ b/src/test/bdd.cpp @@ -0,0 +1,83 @@ +#include "sat/sat_bdd.h" + +namespace sat { + static void test1() { + bdd_manager m(20); + bdd v0 = m.mk_var(0); + bdd v1 = m.mk_var(1); + bdd v2 = m.mk_var(2); + bdd c1 = v0 && v1 && v2; + bdd c2 = v2 && v0 && v1; + std::cout << c1 << "\n"; + SASSERT(c1 == c2); + std::cout << "cnf size: " << c1.cnf_size() << "\n"; + + c1 = v0 || v1 || v2; + c2 = v2 || v1 || v0; + std::cout << c1 << "\n"; + SASSERT(c1 == c2); + std::cout << "cnf size: " << c1.cnf_size() << "\n"; + } + + static void test2() { + bdd_manager m(20); + bdd v0 = m.mk_var(0); + bdd v1 = m.mk_var(1); + bdd v2 = m.mk_var(2); + SASSERT(m.mk_ite(v0,v0,v1) == (v0 || v1)); + SASSERT(m.mk_ite(v0,v1,v1) == v1); + SASSERT(m.mk_ite(v1,v0,v1) == (v0 && v1)); + SASSERT(m.mk_ite(v1,v0,v0) == v0); + SASSERT(m.mk_ite(v0,!v0,v1) == (!v0 && v1)); + SASSERT(m.mk_ite(v0,v1,!v0) == (!v0 || v1)); + SASSERT(!(v0 && v1) == (!v0 || !v1)); + SASSERT(!(v0 || v1) == (!v0 && !v1)); + } + + static void test3() { + bdd_manager m(20); + bdd v0 = m.mk_var(0); + bdd v1 = m.mk_var(1); + bdd v2 = m.mk_var(2); + bdd c1 = (v0 && v1) || v2; + bdd c2 = m.mk_exists(0, c1); + std::cout << c1 << "\n"; + std::cout << c2 << "\n"; + SASSERT(c2 == (v1 || v2)); + c2 = m.mk_exists(1, c1); + SASSERT(c2 == (v0 || v2)); + c2 = m.mk_exists(2, c1); + SASSERT(c2.is_true()); + SASSERT(m.mk_exists(3, c1) == c1); + c1 = (v0 && v1) || (v1 && v2) || (!v0 && !v2); + c2 = m.mk_exists(0, c1); + SASSERT(c2 == (v1 || (v1 && v2) || !v2)); + c2 = m.mk_exists(1, c1); + SASSERT(c2 == (v0 || v2 || (!v0 && !v2))); + c2 = m.mk_exists(2, c1); + SASSERT(c2 == ((v0 && v1) || v1 || !v0)); + } + + void test4() { + bdd_manager m(3); + bdd v0 = m.mk_var(0); + bdd v1 = m.mk_var(1); + bdd v2 = m.mk_var(2); + bdd c1 = (v0 && v2) || v1; + std::cout << "before reorder:\n"; + std::cout << c1 << "\n"; + std::cout << c1.bdd_size() << "\n"; + m.gc(); + m.try_reorder(); + std::cout << "after reorder:\n"; + std::cout << c1 << "\n"; + std::cout << c1.bdd_size() << "\n"; + } +} + +void tst_bdd() { + sat::test1(); + sat::test2(); + sat::test3(); + sat::test4(); +} diff --git a/src/test/cnf_backbones.cpp b/src/test/cnf_backbones.cpp index 27d175298..50584c90c 100644 --- a/src/test/cnf_backbones.cpp +++ b/src/test/cnf_backbones.cpp @@ -228,8 +228,8 @@ static void cnf_backbones(bool use_chunk, char const* file_name) { params_ref p = gparams::get_module("sat"); p.set_bool("produce_models", true); reslimit limit; - sat::solver solver(p, limit, nullptr); - sat::solver solver2(p, limit, nullptr); + sat::solver solver(p, limit); + sat::solver solver2(p, limit); g_solver = &solver; if (file_name) { diff --git a/src/test/get_implied_equalities.cpp b/src/test/get_implied_equalities.cpp index 952fb121a..dae182a34 100644 --- a/src/test/get_implied_equalities.cpp +++ b/src/test/get_implied_equalities.cpp @@ -75,9 +75,9 @@ static void tst_get_implied_equalities1() { } static void tst_get_implied_equalities2() { - enable_trace("after_search"); - enable_trace("get_implied_equalities"); - enable_trace("implied_equalities"); + //enable_trace("after_search"); + //enable_trace("get_implied_equalities"); + //enable_trace("implied_equalities"); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); diff --git a/src/test/horn_subsume_model_converter.cpp b/src/test/horn_subsume_model_converter.cpp index a361cfb2a..3e493d408 100644 --- a/src/test/horn_subsume_model_converter.cpp +++ b/src/test/horn_subsume_model_converter.cpp @@ -31,17 +31,17 @@ void tst_horn_subsume_model_converter() { mc->insert(p, m.mk_app(q, a.mk_numeral(rational(1), true), a.mk_numeral(rational(2), true))); model_converter_ref mcr = mc.get(); - apply(mcr, mr, 0); + apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); mc->insert(p, m.mk_app(q, a.mk_numeral(rational(3), true), a.mk_numeral(rational(5), true))); - apply(mcr, mr, 0); + apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); mc->insert(p, m.mk_app(r, m.mk_var(0,a.mk_int()), m.mk_var(1, a.mk_int()))); - apply(mcr, mr, 0); + apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); @@ -52,7 +52,7 @@ void tst_horn_subsume_model_converter() { body1 = m.mk_app(q, m.mk_var(1, a.mk_int()), m.mk_var(2, a.mk_int())); VERIFY(mc->mk_horn(head1, body1, pred, body2)); mc->insert(pred, body2); - apply(mcr, mr, 0); + apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); @@ -60,7 +60,7 @@ void tst_horn_subsume_model_converter() { body1 = m.mk_app(q, m.mk_var(1, a.mk_int()), m.mk_var(0, a.mk_int())); VERIFY(mc->mk_horn(head1, body1, pred, body2)); mc->insert(pred, body2); - apply(mcr, mr, 0); + apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); diff --git a/src/test/main.cpp b/src/test/main.cpp index ccdc77ec9..64f754667 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -77,49 +77,50 @@ void display_usage() { void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage, bool& test_all) { int i = 1; while (i < argc) { - char * arg = argv[i], *eq_pos = nullptr; - - if (arg[0] == '-' || arg[0] == '/') { - char * opt_name = arg + 1; - char * opt_arg = nullptr; - char * colon = strchr(arg, ':'); - if (colon) { - opt_arg = colon + 1; - *colon = 0; - } - if (strcmp(opt_name, "h") == 0 || + char * arg = argv[i]; + char * eq_pos = 0; + + if (arg[0] == '-' || arg[0] == '/') { + char * opt_name = arg + 1; + char * opt_arg = 0; + char * colon = strchr(arg, ':'); + if (colon) { + opt_arg = colon + 1; + *colon = 0; + } + if (strcmp(opt_name, "h") == 0 || strcmp(opt_name, "?") == 0) { - display_usage(); + display_usage(); do_display_usage = true; return; - } - else if (strcmp(opt_name, "v") == 0) { - if (!opt_arg) - error("option argument (/v:level) is missing."); - long lvl = strtol(opt_arg, nullptr, 10); - set_verbosity_level(lvl); - } - else if (strcmp(opt_name, "w") == 0) { + } + else if (strcmp(opt_name, "v") == 0) { + if (!opt_arg) + error("option argument (/v:level) is missing."); + long lvl = strtol(opt_arg, 0, 10); + set_verbosity_level(lvl); + } + else if (strcmp(opt_name, "w") == 0) { enable_warning_messages(true); - } - else if (strcmp(opt_name, "a") == 0) { + } + else if (strcmp(opt_name, "a") == 0) { test_all = true; - } + } #ifdef _TRACE - else if (strcmp(opt_name, "tr") == 0) { - if (!opt_arg) - error("option argument (/tr:tag) is missing."); - enable_trace(opt_arg); - } + else if (strcmp(opt_name, "tr") == 0) { + if (!opt_arg) + error("option argument (/tr:tag) is missing."); + enable_trace(opt_arg); + } #endif #ifdef Z3DEBUG - else if (strcmp(opt_name, "dbg") == 0) { - if (!opt_arg) - error("option argument (/dbg:tag) is missing."); - enable_debug(opt_arg); - } + else if (strcmp(opt_name, "dbg") == 0) { + if (!opt_arg) + error("option argument (/dbg:tag) is missing."); + enable_debug(opt_arg); + } #endif - } + } else if (arg[0] != '"' && (eq_pos = strchr(arg, '='))) { char * key = arg; *eq_pos = 0; @@ -241,10 +242,12 @@ int main(int argc, char ** argv) { TST_ARGV(ddnf); TST(ddnf1); TST(model_evaluator); - TST_ARGV(lp); TST(get_consequences); TST(pb2bv); + TST_ARGV(sat_lookahead); + TST_ARGV(sat_local_search); TST_ARGV(cnf_backbones); + TST(bdd); //TST_ARGV(hs); } diff --git a/src/test/mpff.cpp b/src/test/mpff.cpp index dd934831c..c78489f21 100644 --- a/src/test/mpff.cpp +++ b/src/test/mpff.cpp @@ -35,7 +35,7 @@ static void tst1() { std::cout << i << ": " << a << "\n"; } } - catch (z3_exception & ex) { + catch (const z3_exception & ex) { std::cout << ex.msg() << "\n"; } } @@ -432,7 +432,7 @@ static void tst_limits(unsigned prec) { m.round_to_plus_inf(); bool overflow = false; try { m.inc(a); } - catch (mpff_manager::overflow_exception) { overflow = true; } + catch (const mpff_manager::overflow_exception &) { overflow = true; } VERIFY(overflow); m.set_max(a); m.dec(a); @@ -446,7 +446,7 @@ static void tst_limits(unsigned prec) { ENSURE(m.eq(a, b)); overflow = true; try { m.dec(a); } - catch (mpff_manager::overflow_exception) { overflow = true; } + catch (const mpff_manager::overflow_exception &) { overflow = true; } ENSURE(overflow); m.round_to_plus_inf(); m.set_min(a); diff --git a/src/test/pb2bv.cpp b/src/test/pb2bv.cpp index 35c444bae..d58bf61ee 100644 --- a/src/test/pb2bv.cpp +++ b/src/test/pb2bv.cpp @@ -37,7 +37,7 @@ static void test1() { expr_ref fml(m), result(m); proof_ref proof(m); fml = pb.mk_at_least_k(vars.size(), vars.c_ptr(), k); - rw(fml, result, proof); + rw(true, fml, result, proof); std::cout << fml << " |-> " << result << "\n"; } expr_ref_vector lemmas(m); @@ -60,9 +60,10 @@ static void test_semantics(ast_manager& m, expr_ref_vector const& vars, vector(1 << N); ++values) { smt_params fp; smt::kernel solver(m, fp); @@ -86,6 +87,12 @@ static void test_semantics(ast_manager& m, expr_ref_vector const& vars, vector> cur_term; + while (cur_term != 0) { + coefficients.push_back(cur_term); + infile >> cur_term; + } + + // read variables + infile >> cur_term; + while (cur_term != 0) { + lits.push_back(sat::literal(abs(cur_term), cur_term < 0)); + infile >> cur_term; + } + + if (lits.size() != coefficients.size()) { + std::cout << "Objective function format error. They have different lenghts.\n"; + return false; + } + + for (unsigned i = 0; i < lits.size(); ++i) { + local_search.add_soft(lits[i].var(), coefficients[i]); + } + + // read the constraints, one at a time + int k; + for (int c = 0; c < num_constraints; ++c) { + lits.reset(); + infile >> cur_term; + while (cur_term != 0) { + lits.push_back(sat::literal(abs(cur_term), cur_term > 0)); + infile >> cur_term; + } + infile >> k; + //local_search.add_cardinality(lits.size(), lits.c_ptr(), static_cast(lits.size() - k)); + local_search.add_cardinality(lits.size(), lits.c_ptr(), static_cast(k)); + } + + infile.close(); + return true; +#else + return false; +#endif +} + +void tst_sat_local_search(char ** argv, int argc, int& i) { + if (argc < i + 2) { + std::cout << "require dimacs file name\n"; + return; + } + reslimit limit; + params_ref params; + sat::solver solver(params, limit); + sat::local_search local_search; + + local_search.import(solver, true); + char const* file_name = argv[i + 1]; + ++i; + + int cutoff_time = 1; + + int v; + while (i + 1 < argc) { + std::cout << argv[i + 1] << "\n"; + // set other ad hoc parameters. + if (argv[i + 1][0] == '-' && i + 2 < argc) { + switch (argv[i + 1][1]) { + case 's': // seed + v = atoi(argv[i + 2]); + local_search.config().set_random_seed(v); + break; + case 't': // cutoff_time + v = atoi(argv[i + 2]); + cutoff_time = v; + break; + case 'b': // best_known_value + v = atoi(argv[i + 2]); + local_search.config().set_best_known_value(v); + break; + default: + ++i; + v = -1; + break; + } + } + ++i; + } + + if (!build_instance(file_name, solver, local_search)) { + return; + } + + //std::cout << "local instance built\n"; + + + // set up cancellation/timeout environment. + + cancel_eh eh(local_search.rlimit()); + scoped_ctrl_c ctrlc(eh, false, true); + scoped_timer timer(cutoff_time*1000, &eh); + local_search.check(); + +} diff --git a/src/test/sat_lookahead.cpp b/src/test/sat_lookahead.cpp new file mode 100644 index 000000000..fccbe8eed --- /dev/null +++ b/src/test/sat_lookahead.cpp @@ -0,0 +1,53 @@ +#include "sat/sat_solver.h" +#include "sat/sat_watched.h" +#include "util/statistics.h" +#include "sat/sat_lookahead.h" +#include "sat/dimacs.h" + +static void display_model(sat::model const & m) { + for (unsigned i = 1; i < m.size(); i++) { + switch (m[i]) { + case l_false: std::cout << "-" << i << " "; break; + case l_undef: break; + case l_true: std::cout << i << " "; break; + } + } + std::cout << "\n"; +} + + +void tst_sat_lookahead(char ** argv, int argc, int& i) { + if (argc != i + 2) { + std::cout << "require dimacs file name\n"; + return; + } +// enable_trace("sat"); + reslimit limit; + params_ref params; + sat::solver solver(params, limit); + char const* file_name = argv[i + 1]; + ++i; + + { + std::ifstream in(file_name); + if (in.bad() || in.fail()) { + std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; + exit(ERR_OPEN_FILE); + } + parse_dimacs(in, solver); + } + + sat::lookahead lh(solver); + + IF_VERBOSE(20, solver.display_status(verbose_stream());); + + lbool is_sat = lh.check(); + std::cout << is_sat << "\n"; + + statistics st; + lh.collect_statistics(st); + st.display(std::cout); + if (is_sat == l_true) { + display_model(lh.get_model()); + } +} diff --git a/src/test/sat_user_scope.cpp b/src/test/sat_user_scope.cpp index 703cf7e3a..51af0f7c3 100644 --- a/src/test/sat_user_scope.cpp +++ b/src/test/sat_user_scope.cpp @@ -55,7 +55,7 @@ static void init_vars(sat::solver& s) { static void check_coherence(sat::solver& s1, trail_t& t) { params_ref p; reslimit rlim; - sat::solver s2(p, rlim, nullptr); + sat::solver s2(p, rlim); init_vars(s2); sat::literal_vector cls; for (unsigned i = 0; i < t.size(); ++i) { @@ -81,7 +81,7 @@ void tst_sat_user_scope() { trail_t trail; params_ref p; reslimit rlim; - sat::solver s(p, rlim, nullptr); // incremental solver + sat::solver s(p, rlim); // incremental solver init_vars(s); while (true) { for (unsigned i = 0; i < s_num_frames; ++i) { diff --git a/src/test/smt2print_parse.cpp b/src/test/smt2print_parse.cpp index 9a8d7b788..99ffb726c 100644 --- a/src/test/smt2print_parse.cpp +++ b/src/test/smt2print_parse.cpp @@ -10,13 +10,21 @@ Copyright (c) 2015 Microsoft Corporation #include "api/z3.h" #include -void test_print(Z3_context ctx, Z3_ast a) { +void test_print(Z3_context ctx, Z3_ast_vector av) { Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT); - char const* spec1 = Z3_benchmark_to_smtlib_string(ctx, "test", nullptr, nullptr, nullptr, 0, nullptr, a); + Z3_ast* args = new Z3_ast[Z3_ast_vector_size(ctx, av)]; + for (unsigned i = 0; i < Z3_ast_vector_size(ctx, av); ++i) { + args[i] = Z3_ast_vector_get(ctx, av, i); + } + Z3_ast a = Z3_mk_and(ctx, Z3_ast_vector_size(ctx, av), args); + Z3_inc_ref(ctx, a); + delete[] args; + char const* spec1 = Z3_benchmark_to_smtlib_string(ctx, "test", 0, 0, 0, 0, 0, a); + Z3_dec_ref(ctx, a); std::cout << "spec1: benchmark->string\n" << spec1 << "\n"; std::cout << "attempting to parse spec1...\n"; - Z3_ast b = + Z3_ast_vector b = Z3_parse_smtlib2_string(ctx, spec1, 0, @@ -26,15 +34,17 @@ void test_print(Z3_context ctx, Z3_ast a) { nullptr, nullptr); std::cout << "parse successful, converting ast->string\n"; - char const* spec2 = Z3_ast_to_string(ctx, b); + Z3_ast_vector_inc_ref(ctx, b); + char const* spec2 = Z3_ast_vector_to_string(ctx, b); std::cout << "spec2: string->ast->string\n" << spec2 << "\n"; + Z3_ast_vector_dec_ref(ctx, b); } void test_parseprint(char const* spec) { Z3_context ctx = Z3_mk_context(nullptr); std::cout << "spec:\n" << spec << "\n"; - Z3_ast a = + Z3_ast_vector a = Z3_parse_smtlib2_string(ctx, spec, 0, @@ -45,11 +55,12 @@ void test_parseprint(char const* spec) { nullptr); std::cout << "done parsing\n"; - + Z3_ast_vector_inc_ref(ctx, a); test_print(ctx, a); std::cout << "done printing\n"; + Z3_ast_vector_dec_ref(ctx, a); Z3_del_context(ctx); } diff --git a/src/test/sorting_network.cpp b/src/test/sorting_network.cpp index 12964c192..f5c415c04 100644 --- a/src/test/sorting_network.cpp +++ b/src/test/sorting_network.cpp @@ -152,36 +152,81 @@ struct ast_ext2 { expr_ref_vector m_clauses; expr_ref_vector m_trail; ast_ext2(ast_manager& m):m(m), m_clauses(m), m_trail(m) {} - typedef expr* literal; - typedef ptr_vector literal_vector; + typedef expr* pliteral; + typedef ptr_vector pliteral_vector; expr* trail(expr* e) { m_trail.push_back(e); return e; } - literal mk_false() { return m.mk_false(); } - literal mk_true() { return m.mk_true(); } - literal mk_max(literal a, literal b) { + pliteral mk_false() { return m.mk_false(); } + pliteral mk_true() { return m.mk_true(); } + pliteral mk_max(pliteral a, pliteral b) { return trail(m.mk_or(a, b)); } - literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } - literal mk_not(literal a) { if (m.is_not(a,a)) return a; + pliteral mk_min(pliteral a, pliteral b) { return trail(m.mk_and(a, b)); } + pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } - std::ostream& pp(std::ostream& out, literal lit) { + std::ostream& pp(std::ostream& out, pliteral lit) { return out << mk_pp(lit, m); } - literal fresh() { - return trail(m.mk_fresh_const("x", m.mk_bool_sort())); + pliteral fresh(char const* n) { + return trail(m.mk_fresh_const(n, m.mk_bool_sort())); } - void mk_clause(unsigned n, literal const* lits) { + void mk_clause(unsigned n, pliteral const* lits) { m_clauses.push_back(mk_or(m, n, lits)); } }; +static void test_eq1(unsigned n, sorting_network_encoding enc) { + //std::cout << "test eq1 " << n << " for encoding: " << enc << "\n"; + ast_manager m; + reg_decl_plugins(m); + ast_ext2 ext(m); + expr_ref_vector in(m), out(m); + for (unsigned i = 0; i < n; ++i) { + in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); + } + smt_params fp; + smt::kernel solver(m, fp); + psort_nw sn(ext); + sn.cfg().m_encoding = enc; -static void test_sorting_eq(unsigned n, unsigned k) { + expr_ref result1(m), result2(m); + + // equality: + solver.push(); + result1 = sn.eq(true, 1, in.size(), in.c_ptr()); + for (expr* cl : ext.m_clauses) { + solver.assert_expr(cl); + } + expr_ref_vector ors(m); + for (unsigned i = 0; i < n; ++i) { + expr_ref_vector ands(m); + for (unsigned j = 0; j < n; ++j) { + ands.push_back(j == i ? in[j].get() : m.mk_not(in[j].get())); + } + ors.push_back(mk_and(ands)); + } + result2 = mk_or(ors); + solver.assert_expr(m.mk_not(m.mk_eq(result1, result2))); + //std::cout << ext.m_clauses << "\n"; + //std::cout << result1 << "\n"; + //std::cout << result2 << "\n"; + lbool res = solver.check(); + if (res == l_true) { + model_ref model; + solver.get_model(model); + model_smt2_pp(std::cout, m, *model, 0); + TRACE("pb", model_smt2_pp(tout, m, *model, 0);); + } + ENSURE(l_false == res); + ext.m_clauses.reset(); +} + +static void test_sorting_eq(unsigned n, unsigned k, sorting_network_encoding enc) { ENSURE(k < n); ast_manager m; reg_decl_plugins(m); @@ -193,15 +238,17 @@ static void test_sorting_eq(unsigned n, unsigned k) { smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); + sn.cfg().m_encoding = enc; expr_ref result(m); // equality: - std::cout << "eq " << k << "\n"; + std::cout << "eq " << k << " out of " << n << " for encoding " << enc << "\n"; solver.push(); - result = sn.eq(true, k, in.size(), in.c_ptr()); + result = sn.eq(false, k, in.size(), in.c_ptr()); + std::cout << result << "\n" << ext.m_clauses << "\n"; solver.assert_expr(result); - for (unsigned i = 0; i < ext.m_clauses.size(); ++i) { - solver.assert_expr(ext.m_clauses[i].get()); + for (expr* cl : ext.m_clauses) { + solver.assert_expr(cl); } lbool res = solver.check(); ENSURE(res == l_true); @@ -230,7 +277,7 @@ static void test_sorting_eq(unsigned n, unsigned k) { ext.m_clauses.reset(); } -static void test_sorting_le(unsigned n, unsigned k) { +static void test_sorting_le(unsigned n, unsigned k, sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); ast_ext2 ext(m); @@ -241,6 +288,7 @@ static void test_sorting_le(unsigned n, unsigned k) { smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); + sn.cfg().m_encoding = enc; expr_ref result(m); // B <= k std::cout << "le " << k << "\n"; @@ -277,7 +325,7 @@ static void test_sorting_le(unsigned n, unsigned k) { } -void test_sorting_ge(unsigned n, unsigned k) { +void test_sorting_ge(unsigned n, unsigned k, sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); ast_ext2 ext(m); @@ -288,6 +336,7 @@ void test_sorting_ge(unsigned n, unsigned k) { smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); + sn.cfg().m_encoding = enc; expr_ref result(m); // k <= B std::cout << "ge " << k << "\n"; @@ -323,11 +372,11 @@ void test_sorting_ge(unsigned n, unsigned k) { solver.pop(1); } -void test_sorting5(unsigned n, unsigned k) { +void test_sorting5(unsigned n, unsigned k, sorting_network_encoding enc) { std::cout << "n: " << n << " k: " << k << "\n"; - test_sorting_le(n, k); - test_sorting_eq(n, k); - test_sorting_ge(n, k); + test_sorting_le(n, k, enc); + test_sorting_eq(n, k, enc); + test_sorting_ge(n, k, enc); } expr_ref naive_at_most1(expr_ref_vector const& xs) { @@ -341,7 +390,7 @@ expr_ref naive_at_most1(expr_ref_vector const& xs) { return mk_and(clauses); } -void test_at_most_1(unsigned n, bool full) { +void test_at_most_1(unsigned n, bool full, sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); expr_ref_vector in(m), out(m); @@ -351,11 +400,15 @@ void test_at_most_1(unsigned n, bool full) { ast_ext2 ext(m); psort_nw sn(ext); + sn.cfg().m_encoding = enc; expr_ref result1(m), result2(m); result1 = sn.le(full, 1, in.size(), in.c_ptr()); result2 = naive_at_most1(in); + std::cout << "clauses: " << ext.m_clauses << "\n-----\n"; + std::cout << "encoded: " << result1 << "\n"; + std::cout << "naive: " << result2 << "\n"; smt_params fp; smt::kernel solver(m, fp); @@ -367,15 +420,27 @@ void test_at_most_1(unsigned n, bool full) { solver.assert_expr(m.mk_not(m.mk_eq(result1, result2))); std::cout << result1 << "\n"; + lbool res = solver.check(); + if (res == l_true) { + model_ref model; + solver.get_model(model); + model_smt2_pp(std::cout, m, *model, 0); + } - VERIFY(l_false == solver.check()); + VERIFY(l_false == res); solver.pop(1); } if (n >= 9) return; + if (n <= 1) return; for (unsigned i = 0; i < static_cast(1 << n); ++i) { - std::cout << "checking: " << n << ": " << i << "\n"; + std::cout << "checking n: " << n << " bits: "; + for (unsigned j = 0; j < n; ++j) { + bool is_true = (i & (1 << j)) != 0; + std::cout << (is_true?"1":"0"); + } + std::cout << "\n"; solver.push(); unsigned k = 0; for (unsigned j = 0; j < n; ++j) { @@ -402,7 +467,7 @@ void test_at_most_1(unsigned n, bool full) { } -static void test_at_most1() { +static void test_at_most1(sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); expr_ref_vector in(m), out(m); @@ -413,33 +478,43 @@ static void test_at_most1() { ast_ext2 ext(m); psort_nw sn(ext); + sn.cfg().m_encoding = enc; expr_ref result(m); result = sn.le(true, 1, in.size(), in.c_ptr()); std::cout << result << "\n"; std::cout << ext.m_clauses << "\n"; } -void tst_sorting_network() { - for (unsigned i = 1; i < 17; ++i) { - test_at_most_1(i, true); - test_at_most_1(i, false); - } - - for (unsigned n = 2; n < 20; ++n) { - std::cout << "verify eq-1 out of " << n << "\n"; - test_sorting_eq(n, 1); - } - - test_at_most1(); - - test_sorting_eq(11,7); +static void test_sorting5(sorting_network_encoding enc) { + test_sorting_eq(11,7, enc); for (unsigned n = 3; n < 20; n += 2) { for (unsigned k = 1; k < n; ++k) { - test_sorting5(n, k); + test_sorting5(n, k, enc); } } +} + +static void tst_sorting_network(sorting_network_encoding enc) { + for (unsigned i = 1; i < 17; ++i) { + test_at_most_1(i, true, enc); + test_at_most_1(i, false, enc); + } + for (unsigned n = 2; n < 20; ++n) { + std::cout << "verify eq-1 out of " << n << "\n"; + test_sorting_eq(n, 1, enc); + test_eq1(n, enc); + } + test_at_most1(enc); + test_sorting5(enc); +} + +void tst_sorting_network() { + tst_sorting_network(sorting_network_encoding::ordered_at_most_1); + tst_sorting_network(sorting_network_encoding::grouped_at_most_1); + tst_sorting_network(sorting_network_encoding::bimander_at_most_1); test_sorting1(); test_sorting2(); test_sorting3(); test_sorting4(); } + diff --git a/src/util/ema.h b/src/util/ema.h new file mode 100644 index 000000000..5a32e021c --- /dev/null +++ b/src/util/ema.h @@ -0,0 +1,56 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + ema.h + +Abstract: + + Exponential moving average based on CaDiCal. + The exponential scheme used to adjust beta to alpha is + described in Biere & Froelich, POS (Pragmatics of SAT) 2016. + +Author: + + Nikolaj Bjorner (nbjorner) 2018-05-03 + +Revision History: + +--*/ +#ifndef EMA_H_ +#define EMA_H_ + +class ema { + double m_alpha, m_beta, m_value; + unsigned m_period, m_wait; + bool invariant() const { return 0 <= m_alpha && m_alpha <= m_beta && m_beta <= 1; } + public: + ema(): m_alpha(0), m_beta(1), m_value(0), m_period(0), m_wait(0) { + SASSERT(invariant()); + } + + ema(double alpha): + m_alpha(alpha), m_beta(1), m_value(0), + m_period(0), m_wait(0) { + SASSERT(invariant()); + } + + void set_alpha(double alpha) { + m_alpha = alpha; + SASSERT(invariant()); + } + + operator double () const { return m_value; } + + void update(double x) { + SASSERT(invariant()); + m_value += m_beta * (x - m_value); + if (m_beta <= m_alpha || m_wait--) return; + m_wait = m_period = 2*(m_period + 1) - 1; + m_beta *= 0.5; + if (m_beta < m_alpha) m_beta = m_alpha; + } +}; + +#endif diff --git a/src/util/env_params.cpp b/src/util/env_params.cpp index c2b5f7974..3ba6df735 100644 --- a/src/util/env_params.cpp +++ b/src/util/env_params.cpp @@ -23,7 +23,7 @@ Notes: #include "util/memory_manager.h" void env_params::updt_params() { - params_ref p = gparams::get(); + params_ref const& p = gparams::get_ref(); set_verbosity_level(p.get_uint("verbose", get_verbosity_level())); enable_warning_messages(p.get_bool("warning", true)); memory::set_max_size(megabytes_to_bytes(p.get_uint("memory_max_size", 0))); diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 12c335cdf..5ee49ef16 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -104,10 +104,8 @@ public: ~imp() { reset(); - dictionary::iterator it = m_module_param_descrs.begin(); - dictionary::iterator end = m_module_param_descrs.end(); - for (; it != end; ++it) { - dealloc(it->m_value); + for (auto & kv : m_module_param_descrs) { + dealloc(kv.m_value); } } @@ -115,10 +113,8 @@ public: #pragma omp critical (gparams) { m_params.reset(); - dictionary::iterator it = m_module_params.begin(); - dictionary::iterator end = m_module_params.end(); - for (; it != end; ++it) { - dealloc(it->m_value); + for (auto & kv : m_module_params) { + dealloc(kv.m_value); } m_module_params.reset(); } @@ -425,26 +421,22 @@ public: return r; } + // unfortunately, params_ref is not thread safe + // so better create a local copy of the parameters. params_ref get_module(symbol const & module_name) { params_ref result; params_ref * ps = nullptr; #pragma omp critical (gparams) { if (m_module_params.find(module_name, ps)) { - result = *ps; + result.copy(*ps); } } return result; } - params_ref get() { - params_ref result; - TRACE("gparams", tout << "get() m_params: " << m_params << "\n";); - #pragma omp critical (gparams) - { - result = m_params; - } - return result; + params_ref const& get_ref() { + return m_params; } // ----------------------------------------------- @@ -464,16 +456,14 @@ public: out << "Example: pp.decimal=true\n"; out << "\n"; } - dictionary::iterator it = get_module_param_descrs().begin(); - dictionary::iterator end = get_module_param_descrs().end(); - for (; it != end; ++it) { - out << "[module] " << it->m_key; + for (auto & kv : get_module_param_descrs()) { + out << "[module] " << kv.m_key; char const * descr = nullptr; - if (get_module_descrs().find(it->m_key, descr)) { + if (get_module_descrs().find(kv.m_key, descr)) { out << ", description: " << descr; } out << "\n"; - it->m_value->display(out, indent + 4, smt2_style, include_descr); + kv.m_value->display(out, indent + 4, smt2_style, include_descr); } } } @@ -481,12 +471,10 @@ public: void display_modules(std::ostream & out) { #pragma omp critical (gparams) { - dictionary::iterator it = get_module_param_descrs().begin(); - dictionary::iterator end = get_module_param_descrs().end(); - for (; it != end; ++it) { - out << "[module] " << it->m_key; + for (auto & kv : get_module_param_descrs()) { + out << "[module] " << kv.m_key; char const * descr = nullptr; - if (get_module_descrs().find(it->m_key, descr)) { + if (get_module_descrs().find(kv.m_key, descr)) { out << ", description: " << descr; } out << "\n"; @@ -618,10 +606,10 @@ params_ref gparams::get_module(symbol const & module_name) { return g_imp->get_module(module_name); } -params_ref gparams::get() { - TRACE("gparams", tout << "gparams::get()\n";); +params_ref const& gparams::get_ref() { + TRACE("gparams", tout << "gparams::get_ref()\n";); SASSERT(g_imp != 0); - return g_imp->get(); + return g_imp->get_ref(); } void gparams::display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) { diff --git a/src/util/gparams.h b/src/util/gparams.h index 7ddaf0ccb..5334b28d0 100644 --- a/src/util/gparams.h +++ b/src/util/gparams.h @@ -106,7 +106,8 @@ public: /** \brief Return the global parameter set (i.e., parameters that are not associated with any particular module). */ - static params_ref get(); + + static params_ref const& get_ref(); /** \brief Dump information about available parameters in the given output stream. diff --git a/src/util/hashtable.h b/src/util/hashtable.h index cea74f466..420e48949 100644 --- a/src/util/hashtable.h +++ b/src/util/hashtable.h @@ -24,11 +24,12 @@ Revision History: #include #include "util/memory_manager.h" #include "util/hash.h" +#include "util/vector.h" #define DEFAULT_HASHTABLE_INITIAL_CAPACITY 8 #define SMALL_TABLE_CAPACITY 64 -// #define HASHTABLE_STATISTICS +// #define HASHTABLE_STATISTICS #ifdef HASHTABLE_STATISTICS #define HS_CODE(CODE) { CODE } @@ -375,8 +376,7 @@ public: } ((void) 0) void insert(data && e) { - if ((m_size + m_num_deleted) << 2 > (m_capacity * 3)) { - // if ((m_size + m_num_deleted) * 2 > (m_capacity)) { + if (((m_size + m_num_deleted) << 2) > (m_capacity * 3)) { expand_table(); } unsigned hash = get_hash(e); @@ -488,7 +488,9 @@ public: else if (curr->is_free()) { \ return 0; \ } \ - HS_CODE(const_cast(this)->m_st_collision++;); \ + else { \ + HS_CODE(const_cast(this)->m_st_collision++;); \ + } \ } ((void) 0) entry * find_core(data const & e) const { @@ -652,7 +654,33 @@ public: unsigned long long get_num_collision() const { return 0; } #endif - +#define COLL_LOOP_BODY() { \ + if (curr->is_used()) { \ + if (curr->get_hash() == hash && equals(curr->get_data(), e)) return; \ + collisions.push_back(curr->get_data()); \ + continue; \ + } \ + else if (curr->is_free()) { \ + continue; \ + } \ + collisions.push_back(curr->get_data()); \ + } ((void) 0); + + void get_collisions(data const& e, vector& collisions) { + unsigned hash = get_hash(e); + unsigned mask = m_capacity - 1; + unsigned idx = hash & mask; + entry * begin = m_table + idx; + entry * end = m_table + m_capacity; + entry * curr = begin; + for (; curr != end; ++curr) { + COLL_LOOP_BODY(); + } + for (curr = m_table; curr != begin; ++curr) { + COLL_LOOP_BODY(); + } + + } }; template diff --git a/src/util/heap.h b/src/util/heap.h index 02e30bf04..182932fd0 100644 --- a/src/util/heap.h +++ b/src/util/heap.h @@ -27,10 +27,6 @@ class heap : private LT { int_vector m_values; int_vector m_value2indices; - bool less_than(int v1, int v2) const { - return LT::operator()(v1, v2); - } - static int left(int i) { return i << 1; } @@ -126,6 +122,10 @@ public: CASSERT("heap", check_invariant()); } + bool less_than(int v1, int v2) const { + return LT::operator()(v1, v2); + } + bool empty() const { return m_values.size() == 1; } diff --git a/src/util/lp/disjoint_intervals.h b/src/util/lp/disjoint_intervals.h new file mode 100644 index 000000000..5f4f31af6 --- /dev/null +++ b/src/util/lp/disjoint_intervals.h @@ -0,0 +1,334 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Lev Nachmanson +*/ +#pragma once +#include +namespace lp { +// represents the set of disjoint intervals of integer number +struct disjoint_intervals { + std::map m_endpoints; // 0 means start, 1 means end, 2 means both - for a point interval + bool m_empty; + // constructors create an interval containing all integer numbers or an empty interval + disjoint_intervals() : m_empty(false) {} + disjoint_intervals(bool is_empty) : m_empty(is_empty) {} + + bool is_start(short x) const { return x == 0 || x == 2; } + bool is_start(const std::map::iterator & it) const { + return is_start(it->second); + } + bool is_start(const std::map::reverse_iterator & it) const { + return is_start(it->second); + } + bool is_end(short x) const { return x == 1 || x == 2; } + bool is_end(const std::map::iterator & it) const { + return is_end(it->second); + } + bool is_end(const std::map::reverse_iterator & it) const { + return is_end(it->second); + } + + int pos(const std::map::iterator & it) const { + return it->first; + } + int pos(const std::map::reverse_iterator & it) const { + return it->first; + } + + int bound_kind(const std::map::iterator & it) const { + return it->second; + } + + int bound_kind(const std::map::reverse_iterator & it) const { + return it->second; + } + + bool is_proper_start(short x) const { return x == 0; } + bool is_proper_end(short x) const { return x == 1; } + bool is_proper_end(const std::map::iterator & it) const { + return is_proper_end(it->second); + } + bool is_proper_end(const std::map::reverse_iterator & it) const { + return is_proper_end(it->second); + } + + bool is_one_point_interval(short x) const { return x == 2; } + bool is_one_point_interval(const std::map::iterator & it) const { + return is_one_point_interval(it->second); + } + bool is_one_point_interval(const std::map::reverse_iterator & it) const { + return is_one_point_interval(it->second); + } + + + void erase(int x) { + m_endpoints.erase(x); + } + + void set_one_point_segment(int x) { + m_endpoints[x] = 2; + } + + void set_start(int x) { + m_endpoints[x] = 0; + } + + void set_end(int x) { + m_endpoints[x] = 1; + } + + void remove_all_endpoints_below(int x) { + while (m_endpoints.begin() != m_endpoints.end() && m_endpoints.begin()->first < x) + m_endpoints.erase(m_endpoints.begin()); + } + // we intersect the existing set with the half open to the right interval + void intersect_with_lower_bound(int x) { + if (m_empty) + return; + if (m_endpoints.empty()) { + set_start(x); + return; + } + bool pos_inf = has_pos_inf(); + auto it = m_endpoints.begin(); + while (it != m_endpoints.end() && pos(it) < x) { + m_endpoints.erase(it); + it = m_endpoints.begin(); + } + if (m_endpoints.empty()) { + if (!pos_inf) { + m_empty = true; + return; + } + set_start(x); + return; + } + lp_assert(pos(it) >= x); + if (pos(it) == x) { + if (is_proper_end(it)) + set_one_point_segment(x); + } + else { // x(it) > x + if (is_proper_end(it)) { + set_start(x); + } + } + + lp_assert(is_correct()); + } + + // we intersect the existing set with the half open interval + void intersect_with_upper_bound(int x) { + if (m_empty) + return; + if (m_endpoints.empty()) { + set_end(x); + return; + } + bool neg_inf = has_neg_inf(); + auto it = m_endpoints.rbegin(); + + while (!m_endpoints.empty() && pos(it) > x) { + m_endpoints.erase(std::prev(m_endpoints.end())); + it = m_endpoints.rbegin(); + } + if (m_endpoints.empty()) { + if (!neg_inf) { + m_empty = true; + return; + } + set_end(x); + } + lp_assert(pos(it) <= x); + if (pos(it) == x) { + if (is_one_point_interval(it)) {} + else if (is_proper_end(it)) {} + else {// is_proper_start(it->second) + set_one_point_segment(x); + } + } + else { // pos(it) < x} + if (is_start(it)) + set_end(x); + } + lp_assert(is_correct()); + } + + bool has_pos_inf() const { + if (m_empty) + return false; + + if (m_endpoints.empty()) + return true; + + lp_assert(m_endpoints.rbegin() != m_endpoints.rend()); + return m_endpoints.rbegin()->second == 0; + } + + bool has_neg_inf() const { + if (m_empty) + return false; + + if (m_endpoints.empty()) + return true; + auto it = m_endpoints.begin(); + return is_proper_end(it->second);//m_endpoints.begin()); + } + + // we are intersecting + void intersect_with_interval(int x, int y) { + if (m_empty) + return; + lp_assert(x <= y); + intersect_with_lower_bound(x); + intersect_with_upper_bound(y); + } + + // add an intervar [x, inf] + void unite_with_interval_x_pos_inf(int x) { + if (m_empty) { + set_start(x); + m_empty = false; + return; + } + + while (!m_endpoints.empty() && pos(m_endpoints.rbegin()) > x) { + m_endpoints.erase(std::prev(m_endpoints.end())); + } + + if (m_endpoints.empty()) { + set_start(x); + return; + } + auto it = m_endpoints.rbegin(); + lp_assert(pos(it) <= x); + if (pos(it) == x) { + if (is_end(it)) { + m_endpoints.erase(x); + } else { + set_start(x); + } + } else if (pos(it) == x - 1 && is_end(it)) { + m_endpoints.erase(x - 1); // closing the gap + } else { + if (!has_pos_inf()) + set_start(x); + } + } + + // add an interval [-inf, x] + void unite_with_interval_neg_inf_x(int x) { + if (m_empty) { + set_end(x); + m_empty = false; + return; + } + auto it = m_endpoints.upper_bound(x); + + if (it == m_endpoints.end()) { + bool pos_inf = has_pos_inf(); + m_endpoints.clear(); + // it could be the case where x is inside of the last infinite interval with pos inf + if (!pos_inf) + set_end(x); + return; + } + lp_assert(pos(it) > x); + if (is_one_point_interval(pos(it))) { + set_end(it->second); + } else { + if (is_start(it->second)) { + set_end(x); + } + } + + while (!m_endpoints.empty() && m_endpoints.begin()->first < x) { + m_endpoints.erase(m_endpoints.begin()); + } + lp_assert(is_correct()); + } + + void unite_with_interval(int x, int y) { + lp_assert(false); // not implemented + } + + bool is_correct() const { + if (m_empty) { + if (m_endpoints.size() > 0) { + std::cout << "is empty is true but m_endpoints.size() = " << m_endpoints.size() << std::endl; + return false; + } + return true; + } + bool expect_end; + bool prev = false; + int prev_x; + for (auto t : m_endpoints) { + if (prev && (expect_end != t.second > 0)) { + std::cout << "x = " << t.first << "\n"; + if (expect_end) { + std::cout << "expecting an interval end\n"; + } else { + std::cout << "expecting an interval start\n"; + } + return false; + } + + if (t.second == 2) { + expect_end = false; // swallow a point interval + } else { + if (prev) + expect_end = !expect_end; + else + expect_end = is_start(t.second); + } + if (prev) { + if (t.first - prev_x <= 1) { + std::cout << "the sequence is not increasing or the gap is too small: " << prev_x << ", " << t.first << std::endl; + return false; + } + } + prev = true; + prev_x = t.first; + } + + return true; + } + + void print(std::ostream & out) const { + if (m_empty) { + out << "empty\n"; + return; + } + if (m_endpoints.empty()){ + out << "[-oo,oo]\n"; + return; + } + bool first = true; + for (auto t : m_endpoints) { + if (first) { + if (t.second == 1) { + out << "[-oo," << t.first << "]"; + } + else if (t.second == 0) + out << "[" << t.first << ","; + else if (t.second == 2) + out << "[" << t.first << "]"; + first = false; + } else { + if (t.second==0) + out << "[" << t.first << ","; + else if (t.second == 1) + out << t.first << "]"; + else if (t.second == 2) + out << "[" << t.first << "]"; + } + } + if (has_pos_inf()) + out << "oo]"; + out << "\n"; + } + + +}; +} diff --git a/src/util/lp/lar_constraints.h b/src/util/lp/lar_constraints.h index 9d3a0e0aa..5c33db8c6 100644 --- a/src/util/lp/lar_constraints.h +++ b/src/util/lp/lar_constraints.h @@ -84,7 +84,6 @@ struct lar_term_constraint: public lar_base_constraint { class lar_constraint : public lar_base_constraint { public: vector> m_coeffs; - lar_constraint() {} lar_constraint(const vector> & left_side, lconstraint_kind kind, const mpq & right_side) : lar_base_constraint(kind, right_side), m_coeffs(left_side) {} diff --git a/src/util/lp/lar_solver.hpp b/src/util/lp/lar_solver.hpp new file mode 100644 index 000000000..6846717af --- /dev/null +++ b/src/util/lp/lar_solver.hpp @@ -0,0 +1,2089 @@ +#include "util/lp/lar_solver.h" +/* + Copyright (c) 2017 Microsoft Corporation + Author: Nikolaj Bjorner, Lev Nachmanson +*/ + +namespace lp { + +unsigned lar_solver::constraint_count() const { + return m_constraints.size(); +} +const lar_base_constraint& lar_solver::get_constraint(unsigned ci) const { + return *(m_constraints[ci]); +} + +////////////////// methods //////////////////////////////// +static_matrix> & lar_solver::A_r() { return m_mpq_lar_core_solver.m_r_A;} +static_matrix> const & lar_solver::A_r() const { return m_mpq_lar_core_solver.m_r_A;} +static_matrix & lar_solver::A_d() { return m_mpq_lar_core_solver.m_d_A;} +static_matrix const & lar_solver::A_d() const { return m_mpq_lar_core_solver.m_d_A;} + +lp_settings & lar_solver::settings() { return m_settings;} + +lp_settings const & lar_solver::settings() const { return m_settings;} + +void clear() {lp_assert(false); // not implemented +} + + +lar_solver::lar_solver() : m_status(lp_status::OPTIMAL), + m_infeasible_column_index(-1), + m_terms_start_index(1000000), + m_mpq_lar_core_solver(m_settings, *this), + m_tracker_of_x_change([&](unsigned j) { + call_assignment_tracker(j); + } + ), + m_int_solver(nullptr) +{} + +void lar_solver::set_track_pivoted_rows(bool v) { + m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows = v? (& m_rows_with_changed_bounds) : nullptr; +} + +bool lar_solver::get_track_pivoted_rows() const { + return m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows != nullptr; +} + + +lar_solver::~lar_solver(){ + for (auto c : m_constraints) + delete c; + for (auto t : m_terms) + delete t; +} + +bool lar_solver::is_term(var_index j) const { + return j >= m_terms_start_index && j - m_terms_start_index < m_terms.size(); +} + +unsigned lar_solver::adjust_term_index(unsigned j) const { + lp_assert(is_term(j)); + return j - m_terms_start_index; +} + + +bool lar_solver::use_lu() const { return m_settings.simplex_strategy() == simplex_strategy_enum::lu; } + +bool lar_solver::sizes_are_correct() const { + lp_assert(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_x.size()); + return true; +} + + +void lar_solver::print_implied_bound(const implied_bound& be, std::ostream & out) const { + out << "implied bound\n"; + unsigned v = be.m_j; + if (is_term(v)) { + out << "it is a term number " << be.m_j << std::endl; + print_term(*m_terms[be.m_j - m_terms_start_index], out); + } + else { + out << get_column_name(v); + } + out << " " << lconstraint_kind_string(be.kind()) << " " << be.m_bound << std::endl; + out << "end of implied bound" << std::endl; +} + +bool lar_solver::implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const { + std::unordered_map coeff_map; + auto rs_of_evidence = zero_of_type(); + unsigned n_of_G = 0, n_of_L = 0; + bool strict = false; + for (auto & it : explanation) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + const auto & constr = *m_constraints[con_ind]; + lconstraint_kind kind = coeff.is_pos() ? constr.m_kind : flip_kind(constr.m_kind); + register_in_map(coeff_map, constr, coeff); + if (kind == GT || kind == LT) + strict = true; + if (kind == GE || kind == GT) n_of_G++; + else if (kind == LE || kind == LT) n_of_L++; + rs_of_evidence += coeff*constr.m_right_side; + } + lp_assert(n_of_G == 0 || n_of_L == 0); + lconstraint_kind kind = n_of_G ? GE : (n_of_L ? LE : EQ); + if (strict) + kind = static_cast((static_cast(kind) / 2)); + + if (!is_term(be.m_j)) { + if (coeff_map.size() != 1) + return false; + auto it = coeff_map.find(be.m_j); + if (it == coeff_map.end()) return false; + mpq ratio = it->second; + if (ratio < zero_of_type()) { + kind = static_cast(-kind); + } + rs_of_evidence /= ratio; + } else { + const lar_term * t = m_terms[adjust_term_index(be.m_j)]; + const auto first_coeff = *t->m_coeffs.begin(); + unsigned j = first_coeff.first; + auto it = coeff_map.find(j); + if (it == coeff_map.end()) + return false; + mpq ratio = it->second / first_coeff.second; + for (auto & p : t->m_coeffs) { + it = coeff_map.find(p.first); + if (it == coeff_map.end()) + return false; + if (p.second * ratio != it->second) + return false; + } + if (ratio < zero_of_type()) { + kind = static_cast(-kind); + } + rs_of_evidence /= ratio; + rs_of_evidence += t->m_v * ratio; + } + + return kind == be.kind() && rs_of_evidence == be.m_bound; +} + + +void lar_solver::analyze_new_bounds_on_row( + unsigned row_index, + bound_propagator & bp) { + lp_assert(!use_tableau()); + iterator_on_pivot_row it(m_mpq_lar_core_solver.get_pivot_row(), m_mpq_lar_core_solver.m_r_basis[row_index]); + + bound_analyzer_on_row ra_pos(it, + zero_of_type>(), + row_index, + bp + ); + ra_pos.analyze(); +} + +void lar_solver::analyze_new_bounds_on_row_tableau( + unsigned row_index, + bound_propagator & bp + ) { + + if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation) + return; + iterator_on_row it(A_r().m_rows[row_index]); + lp_assert(use_tableau()); + bound_analyzer_on_row::analyze_row(it, + zero_of_type>(), + row_index, + bp + ); +} + + +void lar_solver::substitute_basis_var_in_terms_for_row(unsigned i) { + // todo : create a map from term basic vars to the rows where they are used + unsigned basis_j = m_mpq_lar_core_solver.m_r_solver.m_basis[i]; + for (unsigned k = 0; k < m_terms.size(); k++) { + if (term_is_used_as_row(k)) + continue; + if (!m_terms[k]->contains(basis_j)) + continue; + m_terms[k]->subst(basis_j, m_mpq_lar_core_solver.m_r_solver.m_pivot_row); + } +} + +void lar_solver::calculate_implied_bounds_for_row(unsigned i, bound_propagator & bp) { + if(use_tableau()) { + analyze_new_bounds_on_row_tableau(i, bp); + } else { + m_mpq_lar_core_solver.calculate_pivot_row(i); + substitute_basis_var_in_terms_for_row(i); + analyze_new_bounds_on_row(i, bp); + } +} + + +linear_combination_iterator * lar_solver::create_new_iter_from_term(unsigned term_index) const { + lp_assert(false); // not implemented + return nullptr; + // new linear_combination_iterator_on_vector(m_terms[adjust_term_index(term_index)]->coeffs_as_vector()); +} + +unsigned lar_solver::adjust_column_index_to_term_index(unsigned j) const { + unsigned ext_var_or_term = m_columns_to_ext_vars_or_term_indices[j]; + return ext_var_or_term < m_terms_start_index ? j : ext_var_or_term; +} + +void lar_solver::propagate_bounds_on_a_term(const lar_term& t, bound_propagator & bp, unsigned term_offset) { + lp_assert(false); // not implemented +} + + +void lar_solver::explain_implied_bound(implied_bound & ib, bound_propagator & bp) { + unsigned i = ib.m_row_or_term_index; + int bound_sign = ib.m_is_low_bound? 1: -1; + int j_sign = (ib.m_coeff_before_j_is_pos ? 1 :-1) * bound_sign; + unsigned m_j = ib.m_j; + if (is_term(m_j)) { + auto it = m_ext_vars_to_columns.find(m_j); + lp_assert(it != m_ext_vars_to_columns.end()); + m_j = it->second.ext_j(); + } + for (auto const& r : A_r().m_rows[i]) { + unsigned j = r.m_j; + mpq const& a = r.get_val(); + if (j == m_j) continue; + if (is_term(j)) { + auto it = m_ext_vars_to_columns.find(j); + lp_assert(it != m_ext_vars_to_columns.end()); + j = it->second.ext_j(); + } + int a_sign = is_pos(a)? 1: -1; + int sign = j_sign * a_sign; + const ul_pair & ul = m_columns_to_ul_pairs[j]; + auto witness = sign > 0? ul.upper_bound_witness(): ul.low_bound_witness(); + lp_assert(is_valid(witness)); + bp.consume(a, witness); + } + // lp_assert(implied_bound_is_correctly_explained(ib, explanation)); +} + + +bool lar_solver::term_is_used_as_row(unsigned term) const { + lp_assert(is_term(term)); + return contains(m_ext_vars_to_columns, term); +} + +void lar_solver::propagate_bounds_on_terms(bound_propagator & bp) { + for (unsigned i = 0; i < m_terms.size(); i++) { + if (term_is_used_as_row(i + m_terms_start_index)) + continue; // this term is used a left side of a constraint, + // it was processed as a touched row if needed + propagate_bounds_on_a_term(*m_terms[i], bp, i); + } +} + + +// goes over touched rows and tries to induce bounds +void lar_solver::propagate_bounds_for_touched_rows(bound_propagator & bp) { + if (!use_tableau()) + return; // todo: consider to remove the restriction + + for (unsigned i : m_rows_with_changed_bounds.m_index) { + calculate_implied_bounds_for_row(i, bp); + } + m_rows_with_changed_bounds.clear(); + if (!use_tableau()) { + propagate_bounds_on_terms(bp); + } +} + +lp_status lar_solver::get_status() const { return m_status;} + +void lar_solver::set_status(lp_status s) {m_status = s;} + +bool lar_solver::has_int_var() const { + return m_mpq_lar_core_solver.m_r_solver.m_tracker_of_x_change != nullptr; +} + +lp_status lar_solver::find_feasible_solution() { + lp_assert(inf_int_set_is_correct()); + m_settings.st().m_make_feasible++; + if (A_r().column_count() > m_settings.st().m_max_cols) + m_settings.st().m_max_cols = A_r().column_count(); + if (A_r().row_count() > m_settings.st().m_max_rows) + m_settings.st().m_max_rows = A_r().row_count(); + if (strategy_is_undecided()) + decide_on_strategy_and_adjust_initial_state(); + + if (has_int_var()) { + m_inf_int_set.resize(A_r().column_count()); + } + + m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true; + auto ret = solve(); + TRACE("intinf", m_int_solver->display_inf_or_int_inf_columns(tout);); + lp_assert(inf_int_set_is_correct()); + return ret; +} + +lp_status lar_solver::solve() { + lp_assert(inf_int_set_is_correct()); + if (m_status == lp_status::INFEASIBLE) { + return m_status; + } + solve_with_core_solver(); + if (m_status != lp_status::INFEASIBLE) { + if (m_settings.bound_propagation()) + detect_rows_with_changed_bounds(); + } + + m_columns_with_changed_bound.clear(); + lp_assert(inf_int_set_is_correct()); + return m_status; +} + +void lar_solver::fill_explanation_from_infeasible_column(vector> & evidence) const{ + + // this is the case when the lower bound is in conflict with the upper one + const ul_pair & ul = m_columns_to_ul_pairs[m_infeasible_column_index]; + evidence.push_back(std::make_pair(numeric_traits::one(), ul.upper_bound_witness())); + evidence.push_back(std::make_pair(-numeric_traits::one(), ul.low_bound_witness())); +} + + +unsigned lar_solver::get_total_iterations() const { return m_mpq_lar_core_solver.m_r_solver.total_iterations(); } +vector lar_solver::get_list_of_all_var_indices() const { + vector ret; + for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_heading.size(); j++) + ret.push_back(j); + return ret; +} +void lar_solver::push() { + m_simplex_strategy = m_settings.simplex_strategy(); + m_simplex_strategy.push(); + m_columns_to_ul_pairs.push(); + m_infeasible_column_index.push(); + m_mpq_lar_core_solver.push(); + m_term_count = m_terms.size(); + m_term_count.push(); + m_constraint_count = m_constraints.size(); + m_constraint_count.push(); +} + +void lar_solver::clean_popped_elements(unsigned n, int_set& set) { + vector to_remove; + for (unsigned j: set.m_index) + if (j >= n) + to_remove.push_back(j); + for (unsigned j : to_remove) + set.erase(j); +} + +void lar_solver::shrink_inf_set_after_pop(unsigned n, int_set & set) { + clean_popped_elements(n, set); + set.resize(n); +} + + +void lar_solver::pop(unsigned k) { + TRACE("arith_int", tout << "pop" << std::endl;); + lp_assert(inf_int_set_is_correct()); + TRACE("lar_solver", tout << "k = " << k << std::endl;); + + int n_was = static_cast(m_ext_vars_to_columns.size()); + m_infeasible_column_index.pop(k); + unsigned n = m_columns_to_ul_pairs.peek_size(k); + for (unsigned j = n_was; j-- > n;) + m_ext_vars_to_columns.erase(m_columns_to_ext_vars_or_term_indices[j]); + m_columns_to_ext_vars_or_term_indices.resize(n); + TRACE("arith_int", tout << "pop" << std::endl;); + if (m_settings.use_tableau()) { + pop_tableau(); + } + lp_assert(A_r().column_count() == n); + m_columns_to_ul_pairs.pop(k); + + m_mpq_lar_core_solver.pop(k); + clean_popped_elements(n, m_columns_with_changed_bound); + clean_popped_elements(n, m_inf_int_set); + unsigned m = A_r().row_count(); + lp_assert(inf_int_set_is_correct()); + clean_popped_elements(m, m_rows_with_changed_bounds); + lp_assert(inf_int_set_is_correct()); + clean_inf_set_of_r_solver_after_pop(); + lp_assert(m_settings.simplex_strategy() == simplex_strategy_enum::undecided || + (!use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + + + lp_assert(ax_is_correct()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.inf_set_is_correct()); + m_constraint_count.pop(k); + for (unsigned i = m_constraint_count; i < m_constraints.size(); i++) + delete m_constraints[i]; + + m_constraints.resize(m_constraint_count); + m_term_count.pop(k); + for (unsigned i = m_term_count; i < m_terms.size(); i++) { + delete m_terms[i]; + } + m_terms.resize(m_term_count); + m_simplex_strategy.pop(k); + m_settings.simplex_strategy() = m_simplex_strategy; + lp_assert(sizes_are_correct()); + lp_assert((!m_settings.use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + m_status = m_mpq_lar_core_solver.m_r_solver.current_x_is_feasible()? lp_status::OPTIMAL: lp_status::UNKNOWN; + +} + +vector lar_solver::get_all_constraint_indices() const { + vector ret; + constraint_index i = 0; + while ( i < m_constraints.size()) + ret.push_back(i++); + return ret; +} + +bool lar_solver::maximize_term_on_tableau(const vector> & term, + impq &term_max) { + if (settings().simplex_strategy() == simplex_strategy_enum::undecided) + decide_on_strategy_and_adjust_initial_state(); + + m_mpq_lar_core_solver.solve(); + if (m_mpq_lar_core_solver.m_r_solver.get_status() == lp_status::UNBOUNDED) + return false; + + term_max = 0; + for (auto & p : term) + term_max += p.first * m_mpq_lar_core_solver.m_r_x[p.second]; + + return true; +} + +bool lar_solver::costs_are_zeros_for_r_solver() const { + for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_solver.m_costs.size(); j++) { + lp_assert(is_zero(m_mpq_lar_core_solver.m_r_solver.m_costs[j])); + } + return true; +} +bool lar_solver::reduced_costs_are_zeroes_for_r_solver() const { + for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_solver.m_d.size(); j++) { + lp_assert(is_zero(m_mpq_lar_core_solver.m_r_solver.m_d[j])); + } + return true; +} + +void lar_solver::set_costs_to_zero(const vector> & term) { + auto & rslv = m_mpq_lar_core_solver.m_r_solver; + auto & jset = m_mpq_lar_core_solver.m_r_solver.m_inf_set; // hijack this set that should be empty right now + lp_assert(jset.m_index.size()==0); + + for (auto & p : term) { + unsigned j = p.second; + rslv.m_costs[j] = zero_of_type(); + int i = rslv.m_basis_heading[j]; + if (i < 0) + jset.insert(j); + else { + for (auto & rc : A_r().m_rows[i]) + jset.insert(rc.m_j); + } + } + + for (unsigned j : jset.m_index) + rslv.m_d[j] = zero_of_type(); + + jset.clear(); + + lp_assert(reduced_costs_are_zeroes_for_r_solver()); + lp_assert(costs_are_zeros_for_r_solver()); +} + +void lar_solver::prepare_costs_for_r_solver(const vector> & term) { + + auto & rslv = m_mpq_lar_core_solver.m_r_solver; + rslv.m_using_infeas_costs = false; + lp_assert(costs_are_zeros_for_r_solver()); + lp_assert(reduced_costs_are_zeroes_for_r_solver()); + rslv.m_costs.resize(A_r().column_count(), zero_of_type()); + for (auto & p : term) { + unsigned j = p.second; + rslv.m_costs[j] = p.first; + if (rslv.m_basis_heading[j] < 0) + rslv.m_d[j] += p.first; + else + rslv.update_reduced_cost_for_basic_column_cost_change(- p.first, j); + } + lp_assert(rslv.reduced_costs_are_correct_tableau()); +} + +bool lar_solver::maximize_term_on_corrected_r_solver(const vector> & term, + impq &term_max) { + settings().backup_costs = false; + switch (settings().simplex_strategy()) { + case simplex_strategy_enum::tableau_rows: + prepare_costs_for_r_solver(term); + settings().simplex_strategy() = simplex_strategy_enum::tableau_costs; + { + bool ret = maximize_term_on_tableau(term, term_max); + settings().simplex_strategy() = simplex_strategy_enum::tableau_rows; + set_costs_to_zero(term); + m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); + return ret; + } + case simplex_strategy_enum::tableau_costs: + prepare_costs_for_r_solver(term); + { + bool ret = maximize_term_on_tableau(term, term_max); + set_costs_to_zero(term); + m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); + return ret; + } + + case simplex_strategy_enum::lu: + lp_assert(false); // not implemented + return false; + default: + lp_unreachable(); // wrong mode + } + return false; +} +// starting from a given feasible state look for the maximum of the term +// return true if found and false if unbounded +bool lar_solver::maximize_term(const vector> & term, + impq &term_max) { + lp_assert(m_mpq_lar_core_solver.m_r_solver.current_x_is_feasible()); + m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = false; + return maximize_term_on_corrected_r_solver(term, term_max); +} + + + +const lar_term & lar_solver::get_term(unsigned j) const { + lp_assert(j >= m_terms_start_index); + return *m_terms[j - m_terms_start_index]; +} + +void lar_solver::pop_core_solver_params() { + pop_core_solver_params(1); +} + +void lar_solver::pop_core_solver_params(unsigned k) { + A_r().pop(k); + A_d().pop(k); +} + + +void lar_solver::set_upper_bound_witness(var_index j, constraint_index ci) { + ul_pair ul = m_columns_to_ul_pairs[j]; + ul.upper_bound_witness() = ci; + m_columns_to_ul_pairs[j] = ul; +} + +void lar_solver::set_low_bound_witness(var_index j, constraint_index ci) { + ul_pair ul = m_columns_to_ul_pairs[j]; + ul.low_bound_witness() = ci; + m_columns_to_ul_pairs[j] = ul; +} + +void lar_solver::register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j) { + auto it = coeffs.find(j); + if (it == coeffs.end()) { + coeffs[j] = a; + } else { + it->second += a; + } +} + + +void lar_solver::substitute_terms_in_linear_expression(const vector>& left_side_with_terms, + vector> &left_side, mpq & free_coeff) const { + std::unordered_map coeffs; + for (auto & t : left_side_with_terms) { + unsigned j = t.second; + if (!is_term(j)) { + register_monoid_in_map(coeffs, t.first, j); + } else { + const lar_term & term = * m_terms[adjust_term_index(t.second)]; + for (auto & p : term.coeffs()){ + register_monoid_in_map(coeffs, t.first * p.second , p.first); + } + free_coeff += t.first * term.m_v; + } + } + + for (auto & p : coeffs) + left_side.push_back(std::make_pair(p.second, p.first)); +} + + +void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) { + if (A_r().row_count() != m_column_buffer.data_size()) + m_column_buffer.resize(A_r().row_count()); + else + m_column_buffer.clear(); + lp_assert(m_column_buffer.size() == 0 && m_column_buffer.is_OK()); + + m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); + for (unsigned i : m_column_buffer.m_index) + m_rows_with_changed_bounds.insert(i); +} + + + +void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) { + for (auto & rc : m_mpq_lar_core_solver.m_r_A.m_columns[j]) + m_rows_with_changed_bounds.insert(rc.m_i); +} + +bool lar_solver::use_tableau() const { return m_settings.use_tableau(); } + +bool lar_solver::use_tableau_costs() const { + return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; +} + +void lar_solver::detect_rows_of_column_with_bound_change(unsigned j) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { // it is a basic column + // just mark the row at touched and exit + m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]); + return; + } + + if (use_tableau()) + detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); + else + detect_rows_of_bound_change_column_for_nbasic_column(j); +} + +void lar_solver::adjust_x_of_column(unsigned j) { + lp_assert(false); +} + +bool lar_solver::row_is_correct(unsigned i) const { + numeric_pair r = zero_of_type>(); + for (const auto & c : A_r().m_rows[i]) + r += c.m_value * m_mpq_lar_core_solver.m_r_x[c.m_j]; + return is_zero(r); +} + +bool lar_solver::ax_is_correct() const { + for (unsigned i = 0; i < A_r().row_count(); i++) { + if (!row_is_correct(i)) + return false; + } + return true; +} + +bool lar_solver::tableau_with_costs() const { + return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; +} + +bool lar_solver::costs_are_used() const { + return m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows; +} + +void lar_solver::change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta) { + lp_assert(inf_int_set_is_correct()); + if (use_tableau()) { + for (const auto & c : A_r().m_columns[j]) { + unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.m_i]; + if (tableau_with_costs()) { + m_basic_columns_with_changed_cost.insert(bj); + } + m_mpq_lar_core_solver.m_r_solver.update_x_with_delta_and_track_feasibility(bj, - A_r().get_val(c) * delta); + TRACE("change_x_del", + tout << "changed basis column " << bj << ", it is " << + ( m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj)? "feas":"inf") << std::endl;); + + } + } else { + m_column_buffer.clear(); + m_column_buffer.resize(A_r().row_count()); + m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); + for (unsigned i : m_column_buffer.m_index) { + unsigned bj = m_mpq_lar_core_solver.m_r_basis[i]; + m_mpq_lar_core_solver.m_r_solver.update_x_with_delta_and_track_feasibility(bj, -m_column_buffer[i] * delta); + } + } + lp_assert(inf_int_set_is_correct()); +} + +void lar_solver::update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { + if (costs_are_used()) { + bool was_infeas = m_mpq_lar_core_solver.m_r_solver.m_inf_set.contains(j); + m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); + if (was_infeas != m_mpq_lar_core_solver.m_r_solver.m_inf_set.contains(j)) + m_basic_columns_with_changed_cost.insert(j); + } else { + m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); + } + } else { + numeric_pair delta; + if (m_mpq_lar_core_solver.m_r_solver.make_column_feasible(j, delta)) + change_basic_columns_dependend_on_a_given_nb_column(j, delta); + } +} + + +void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { + m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]); + return; + } + + if (use_tableau()) + detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); + else + detect_rows_of_bound_change_column_for_nbasic_column(j); +} + +void lar_solver::detect_rows_with_changed_bounds() { + for (auto j : m_columns_with_changed_bound.m_index) + detect_rows_with_changed_bounds_for_column(j); +} + +void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds() { + for (auto j : m_columns_with_changed_bound.m_index) + update_x_and_inf_costs_for_column_with_changed_bounds(j); +} + +void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { + for (auto j : m_columns_with_changed_bound.m_index) + update_x_and_inf_costs_for_column_with_changed_bounds(j); + + if (tableau_with_costs()) { + for (unsigned j : m_basic_columns_with_changed_cost.m_index) + m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); + lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + } +} + + +void lar_solver::solve_with_core_solver() { + if (!use_tableau()) + add_last_rows_to_lu(m_mpq_lar_core_solver.m_r_solver); + if (m_mpq_lar_core_solver.need_to_presolve_with_double_solver()) { + add_last_rows_to_lu(m_mpq_lar_core_solver.m_d_solver); + } + m_mpq_lar_core_solver.prefix_r(); + if (costs_are_used()) { + m_basic_columns_with_changed_cost.clear(); + m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); + } + if (use_tableau()) + update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); + else + update_x_and_inf_costs_for_columns_with_changed_bounds(); + TRACE("intinf", m_int_solver->display_inf_or_int_inf_columns(tout);); + m_mpq_lar_core_solver.solve(); + set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); + lp_assert(inf_int_set_is_correct()); + lp_assert(m_status != lp_status::OPTIMAL || all_constraints_hold()); +} + + +numeric_pair lar_solver::get_basic_var_value_from_row_directly(unsigned i) { + numeric_pair r = zero_of_type>(); + + unsigned bj = m_mpq_lar_core_solver.m_r_solver.m_basis[i]; + for (const auto & c: A_r().m_rows[i]) { + if (c.m_j == bj) continue; + const auto & x = m_mpq_lar_core_solver.m_r_x[c.m_j]; + if (!is_zero(x)) + r -= c.m_value * x; + } + return r; +} + +numeric_pair lar_solver::get_basic_var_value_from_row(unsigned i) { + if (settings().use_tableau()) { + return get_basic_var_value_from_row_directly(i); + } + + numeric_pair r = zero_of_type>(); + m_mpq_lar_core_solver.calculate_pivot_row(i); + for (unsigned j : m_mpq_lar_core_solver.m_r_solver.m_pivot_row.m_index) { + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis_heading[j] < 0); + r -= m_mpq_lar_core_solver.m_r_solver.m_pivot_row.m_data[j] * m_mpq_lar_core_solver.m_r_x[j]; + } + return r; +} + +template +void lar_solver::add_last_rows_to_lu(lp_primal_core_solver & s) { + auto & f = s.m_factorization; + if (f != nullptr) { + auto columns_to_replace = f->get_set_of_columns_to_replace_for_add_last_rows(s.m_basis_heading); + if (f->m_refactor_counter + columns_to_replace.size() >= 200 || f->has_dense_submatrix()) { + delete f; + f = nullptr; + } else { + f->add_last_rows_to_B(s.m_basis_heading, columns_to_replace); + } + } + if (f == nullptr) { + init_factorization(f, s.m_A, s.m_basis, m_settings); + if (f->get_status() != LU_status::OK) { + delete f; + f = nullptr; + } + } + +} + +bool lar_solver::x_is_correct() const { + if (m_mpq_lar_core_solver.m_r_x.size() != A_r().column_count()) { + // std::cout << "the size is off " << m_r_solver.m_x.size() << ", " << A().column_count() << std::endl; + return false; + } + for (unsigned i = 0; i < A_r().row_count(); i++) { + numeric_pair delta = A_r().dot_product_with_row(i, m_mpq_lar_core_solver.m_r_x); + if (!delta.is_zero()) { + // std::cout << "x is off ("; + // std::cout << "m_b[" << i << "] = " << m_b[i] << " "; + // std::cout << "left side = " << A().dot_product_with_row(i, m_r_solver.m_x) << ' '; + // std::cout << "delta = " << delta << ' '; + // std::cout << "iters = " << total_iterations() << ")" << std::endl; + // std::cout << "row " << i << " is off" << std::endl; + return false; + } + } + return true;; + +} + +bool lar_solver::var_is_registered(var_index vj) const { + if (vj >= m_terms_start_index) { + if (vj - m_terms_start_index >= m_terms.size()) + return false; + } else if ( vj >= A_r().column_count()) { + return false; + } + return true; +} + +unsigned lar_solver::constraint_stack_size() const { + return m_constraint_count.stack_size(); +} + +void lar_solver::fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls) { + lp_assert(A.row_count() > 0); + lp_assert(A.column_count() > 0); + unsigned last_row = A.row_count() - 1; + lp_assert(A.m_rows[last_row].size() == 0); + for (auto & t : ls->m_coeffs) { + lp_assert(!is_zero(t.second)); + var_index j = t.first; + A.set(last_row, j, - t.second); + } + unsigned basis_j = A.column_count() - 1; + A.set(last_row, basis_j, mpq(1)); +} + +template +void lar_solver::create_matrix_A(static_matrix & matr) { + lp_assert(false); // not implemented + /* + unsigned m = number_or_nontrivial_left_sides(); + unsigned n = m_vec_of_canonic_left_sides.size(); + if (matr.row_count() == m && matr.column_count() == n) + return; + matr.init_empty_matrix(m, n); + copy_from_mpq_matrix(matr); + */ +} + +template +void lar_solver::copy_from_mpq_matrix(static_matrix & matr) { + matr.m_rows.resize(A_r().row_count()); + matr.m_columns.resize(A_r().column_count()); + for (unsigned i = 0; i < matr.row_count(); i++) { + for (auto & it : A_r().m_rows[i]) { + matr.set(i, it.m_j, convert_struct::convert(it.get_val())); + } + } +} + + +bool lar_solver::try_to_set_fixed(column_info & ci) { + if (ci.upper_bound_is_set() && ci.low_bound_is_set() && ci.get_upper_bound() == ci.get_low_bound() && !ci.is_fixed()) { + ci.set_fixed_value(ci.get_upper_bound()); + return true; + } + return false; +} + +column_type lar_solver::get_column_type(const column_info & ci) { + auto ret = ci.get_column_type_no_flipping(); + if (ret == column_type::boxed) { // changing boxed to fixed because of the no span + if (ci.get_low_bound() == ci.get_upper_bound()) + ret = column_type::fixed; + } + return ret; +} + +std::string lar_solver::get_column_name(unsigned j) const { + if (j >= m_terms_start_index) + return std::string("_t") + T_to_string(j); + if (j >= m_columns_to_ext_vars_or_term_indices.size()) + return std::string("_s") + T_to_string(j); + + return std::string("v") + T_to_string(m_columns_to_ext_vars_or_term_indices[j]); +} + +bool lar_solver::all_constrained_variables_are_registered(const vector>& left_side) { + for (auto it : left_side) { + if (! var_is_registered(it.second)) + return false; + } + return true; +} + +bool lar_solver::all_constraints_hold() const { + if (m_settings.get_cancel_flag()) + return true; + std::unordered_map var_map; + get_model_do_not_care_about_diff_vars(var_map); + + for (unsigned i = 0; i < m_constraints.size(); i++) { + if (!constraint_holds(*m_constraints[i], var_map)) { + print_constraint(i, std::cout); + return false; + } + } + return true; +} + +bool lar_solver::constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const { + return true; + mpq left_side_val = get_left_side_val(constr, var_map); + switch (constr.m_kind) { + case LE: return left_side_val <= constr.m_right_side; + case LT: return left_side_val < constr.m_right_side; + case GE: return left_side_val >= constr.m_right_side; + case GT: return left_side_val > constr.m_right_side; + case EQ: return left_side_val == constr.m_right_side; + default: + lp_unreachable(); + } + return false; // it is unreachable +} + +bool lar_solver::the_relations_are_of_same_type(const vector> & evidence, lconstraint_kind & the_kind_of_sum) const { + unsigned n_of_G = 0, n_of_L = 0; + bool strict = false; + for (auto & it : evidence) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + lconstraint_kind kind = coeff.is_pos() ? + m_constraints[con_ind]->m_kind : + flip_kind(m_constraints[con_ind]->m_kind); + if (kind == GT || kind == LT) + strict = true; + if (kind == GE || kind == GT) n_of_G++; + else if (kind == LE || kind == LT) n_of_L++; + } + the_kind_of_sum = n_of_G ? GE : (n_of_L ? LE : EQ); + if (strict) + the_kind_of_sum = static_cast((static_cast(the_kind_of_sum) / 2)); + + return n_of_G == 0 || n_of_L == 0; +} + +void lar_solver::register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a) { + for (auto & it : cn.get_left_side_coefficients()) { + unsigned j = it.second; + auto p = coeffs.find(j); + if (p == coeffs.end()) + coeffs[j] = it.first * a; + else { + p->second += it.first * a; + if (p->second.is_zero()) + coeffs.erase(p); + } + } +} + +bool lar_solver::the_left_sides_sum_to_zero(const vector> & evidence) const { + std::unordered_map coeff_map; + for (auto & it : evidence) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + lp_assert(con_ind < m_constraints.size()); + register_in_map(coeff_map, *m_constraints[con_ind], coeff); + } + + if (!coeff_map.empty()) { + std::cout << "left side = "; + vector> t; + for (auto & it : coeff_map) { + t.push_back(std::make_pair(it.second, it.first)); + } + print_linear_combination_of_column_indices(t, std::cout); + std::cout << std::endl; + return false; + } + + return true; +} + +bool lar_solver::the_right_sides_do_not_sum_to_zero(const vector> & evidence) { + mpq ret = numeric_traits::zero(); + for (auto & it : evidence) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + lp_assert(con_ind < m_constraints.size()); + const lar_constraint & constr = *m_constraints[con_ind]; + ret += constr.m_right_side * coeff; + } + return !numeric_traits::is_zero(ret); +} + +bool lar_solver::explanation_is_correct(const vector>& explanation) const { +#ifdef LEAN_DEBUG + lconstraint_kind kind; + lp_assert(the_relations_are_of_same_type(explanation, kind)); + lp_assert(the_left_sides_sum_to_zero(explanation)); + mpq rs = sum_of_right_sides_of_explanation(explanation); + switch (kind) { + case LE: lp_assert(rs < zero_of_type()); + break; + case LT: lp_assert(rs <= zero_of_type()); + break; + case GE: lp_assert(rs > zero_of_type()); + break; + case GT: lp_assert(rs >= zero_of_type()); + break; + case EQ: lp_assert(rs != zero_of_type()); + break; + default: + lp_assert(false); + return false; + } +#endif + return true; +} + +bool lar_solver::inf_explanation_is_correct() const { +#ifdef LEAN_DEBUG + vector> explanation; + get_infeasibility_explanation(explanation); + return explanation_is_correct(explanation); +#endif + return true; +} + +mpq lar_solver::sum_of_right_sides_of_explanation(const vector> & explanation) const { + mpq ret = numeric_traits::zero(); + for (auto & it : explanation) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + lp_assert(con_ind < m_constraints.size()); + ret += (m_constraints[con_ind]->m_right_side - m_constraints[con_ind]->get_free_coeff_of_left_side()) * coeff; + } + return ret; +} + +bool lar_solver::has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) { + + if (var >= m_columns_to_ul_pairs.size()) { + // TBD: bounds on terms could also be used, caller may have to track these. + return false; + } + const ul_pair & ul = m_columns_to_ul_pairs[var]; + ci = ul.low_bound_witness(); + if (ci != static_cast(-1)) { + auto& p = m_mpq_lar_core_solver.m_r_low_bounds()[var]; + value = p.x; + is_strict = p.y.is_pos(); + return true; + } + else { + return false; + } +} + +bool lar_solver::has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) { + + if (var >= m_columns_to_ul_pairs.size()) { + // TBD: bounds on terms could also be used, caller may have to track these. + return false; + } + const ul_pair & ul = m_columns_to_ul_pairs[var]; + ci = ul.upper_bound_witness(); + if (ci != static_cast(-1)) { + auto& p = m_mpq_lar_core_solver.m_r_upper_bounds()[var]; + value = p.x; + is_strict = p.y.is_neg(); + return true; + } + else { + return false; + } +} + +void lar_solver::get_infeasibility_explanation(vector> & explanation) const { + explanation.clear(); + if (m_infeasible_column_index != -1) { + fill_explanation_from_infeasible_column(explanation); + return; + } + if (m_mpq_lar_core_solver.get_infeasible_sum_sign() == 0) { + return; + } + // the infeasibility sign + int inf_sign; + auto inf_row = m_mpq_lar_core_solver.get_infeasibility_info(inf_sign); + get_infeasibility_explanation_for_inf_sign(explanation, inf_row, inf_sign); + lp_assert(explanation_is_correct(explanation)); +} + + + +void lar_solver::get_infeasibility_explanation_for_inf_sign( + vector> & explanation, + const vector> & inf_row, + int inf_sign) const { + + for (auto & it : inf_row) { + mpq coeff = it.first; + unsigned j = it.second; + + int adj_sign = coeff.is_pos() ? inf_sign : -inf_sign; + const ul_pair & ul = m_columns_to_ul_pairs[j]; + + constraint_index bound_constr_i = adj_sign < 0 ? ul.upper_bound_witness() : ul.low_bound_witness(); + lp_assert(bound_constr_i < m_constraints.size()); + explanation.push_back(std::make_pair(coeff, bound_constr_i)); + } +} + +void lar_solver::get_model(std::unordered_map & variable_values) const { + mpq delta = mpq(1, 2); // start from 0.5 to have less clashes + lp_assert(m_status == lp_status::OPTIMAL); + unsigned i; + do { + // different pairs have to produce different singleton values + std::unordered_set set_of_different_pairs; + std::unordered_set set_of_different_singles; + delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(delta); + for (i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++ ) { + const numeric_pair & rp = m_mpq_lar_core_solver.m_r_x[i]; + set_of_different_pairs.insert(rp); + mpq x = rp.x + delta * rp.y; + set_of_different_singles.insert(x); + if (set_of_different_pairs.size() + != set_of_different_singles.size()) { + delta /= mpq(2); + break; + } + + variable_values[i] = x; + } + } while (i != m_mpq_lar_core_solver.m_r_x.size()); +} + +void lar_solver::get_model_do_not_care_about_diff_vars(std::unordered_map & variable_values) const { + mpq delta = mpq(1); + delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(delta); + for (unsigned i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++ ) { + const impq & rp = m_mpq_lar_core_solver.m_r_x[i]; + variable_values[i] = rp.x + delta * rp.y; + } +} + + +std::string lar_solver::get_variable_name(var_index vi) const { + return get_column_name(vi); +} + +// ********** print region start +void lar_solver::print_constraint(constraint_index ci, std::ostream & out) const { + if (ci >= m_constraints.size()) { + out << "constraint " << T_to_string(ci) << " is not found"; + out << std::endl; + return; + } + + print_constraint(m_constraints[ci], out); +} + +void lar_solver::print_constraints(std::ostream& out) const { + for (auto c : m_constraints) { + print_constraint(c, out); + } +} + +void lar_solver::print_terms(std::ostream& out) const { + for (auto it : m_terms) { + print_term(*it, out); + out << "\n"; + } +} + +void lar_solver::print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const { + print_linear_combination_of_column_indices(c->get_left_side_coefficients(), out); + mpq free_coeff = c->get_free_coeff_of_left_side(); + if (!is_zero(free_coeff)) + out << " + " << free_coeff; + +} + +void lar_solver::print_term(lar_term const& term, std::ostream & out) const { + if (!numeric_traits::is_zero(term.m_v)) { + out << term.m_v << " + "; + } + print_linear_combination_of_column_indices(term.coeffs_as_vector(), out); +} + +void lar_solver::print_term_as_indices(lar_term const& term, std::ostream & out) const { + if (!numeric_traits::is_zero(term.m_v)) { + out << term.m_v << " + "; + } + print_linear_combination_of_column_indices_only(term.coeffs_as_vector(), out); +} + +mpq lar_solver::get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const { + mpq ret = cns.get_free_coeff_of_left_side(); + for (auto & it : cns.get_left_side_coefficients()) { + var_index j = it.second; + auto vi = var_map.find(j); + lp_assert(vi != var_map.end()); + ret += it.first * vi->second; + } + return ret; +} + +void lar_solver::print_constraint(const lar_base_constraint * c, std::ostream & out) const { + print_left_side_of_constraint(c, out); + out << " " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side << std::endl; +} + +void lar_solver::fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list) { + for (unsigned i = 0; i < sz; i++) { + var_index var = vars[i]; + if (var >= m_terms_start_index) { // handle the term + for (auto & it : m_terms[var - m_terms_start_index]->m_coeffs) { + column_list.push_back(it.first); + } + } else { + column_list.push_back(var); + } + } +} + +void lar_solver::random_update(unsigned sz, var_index const * vars) { + vector column_list; + fill_var_set_for_random_update(sz, vars, column_list); + random_updater ru(*this, column_list); + ru.update(); + lp_assert(inf_int_set_is_correct()); +} + + +void lar_solver::pivot_fixed_vars_from_basis() { + m_mpq_lar_core_solver.m_r_solver.pivot_fixed_vars_from_basis(); +} + +void lar_solver::pop() { + pop(1); +} + +bool lar_solver::column_represents_row_in_tableau(unsigned j) { + return m_columns_to_ul_pairs()[j].m_i != static_cast(-1); +} + +void lar_solver::make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j) { + // i, j - is the indices of the bottom-right element of the tableau + lp_assert(A_r().row_count() == i + 1 && A_r().column_count() == j + 1); + auto & last_column = A_r().m_columns[j]; + int non_zero_column_cell_index = -1; + for (unsigned k = last_column.size(); k-- > 0;){ + auto & cc = last_column[k]; + if (cc.m_i == i) + return; + non_zero_column_cell_index = k; + } + + lp_assert(non_zero_column_cell_index != -1); + lp_assert(static_cast(non_zero_column_cell_index) != i); + m_mpq_lar_core_solver.m_r_solver.transpose_rows_tableau(last_column[non_zero_column_cell_index].m_i, i); +} + +void lar_solver::remove_last_row_and_column_from_tableau(unsigned j) { + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); + auto & slv = m_mpq_lar_core_solver.m_r_solver; + unsigned i = A_r().row_count() - 1; //last row index + make_sure_that_the_bottom_right_elem_not_zero_in_tableau(i, j); + if (slv.m_basis_heading[j] < 0) { + slv.pivot_column_tableau(j, i); + } + + auto & last_row = A_r().m_rows[i]; + mpq &cost_j = m_mpq_lar_core_solver.m_r_solver.m_costs[j]; + bool cost_is_nz = !is_zero(cost_j); + for (unsigned k = last_row.size(); k-- > 0;) { + auto &rc = last_row[k]; + if (cost_is_nz) { + m_mpq_lar_core_solver.m_r_solver.m_d[rc.m_j] += cost_j*rc.get_val(); + } + + A_r().remove_element(last_row, rc); + } + lp_assert(last_row.size() == 0); + lp_assert(A_r().m_columns[j].size() == 0); + A_r().m_rows.pop_back(); + A_r().m_columns.pop_back(); + slv.m_b.pop_back(); +} + +void lar_solver::remove_last_column_from_A() { + // the last column has to be empty + lp_assert(A_r().m_columns.back().size() == 0); + A_r().m_columns.pop_back(); +} + +void lar_solver::remove_last_column_from_basis_tableau(unsigned j) { + auto& rslv = m_mpq_lar_core_solver.m_r_solver; + int i = rslv.m_basis_heading[j]; + if (i >= 0) { // j is a basic var + int last_pos = static_cast(rslv.m_basis.size()) - 1; + lp_assert(last_pos >= 0); + if (i != last_pos) { + unsigned j_at_last_pos = rslv.m_basis[last_pos]; + rslv.m_basis[i] = j_at_last_pos; + rslv.m_basis_heading[j_at_last_pos] = i; + } + rslv.m_basis.pop_back(); // remove j from the basis + } else { + int last_pos = static_cast(rslv.m_nbasis.size()) - 1; + lp_assert(last_pos >= 0); + i = - 1 - i; + if (i != last_pos) { + unsigned j_at_last_pos = rslv.m_nbasis[last_pos]; + rslv.m_nbasis[i] = j_at_last_pos; + rslv.m_basis_heading[j_at_last_pos] = - i - 1; + } + rslv.m_nbasis.pop_back(); // remove j from the basis + } + rslv.m_basis_heading.pop_back(); + lp_assert(rslv.m_basis.size() == A_r().row_count()); + lp_assert(rslv.basis_heading_is_correct()); +} + +void lar_solver::remove_last_column_from_tableau() { + auto& rslv = m_mpq_lar_core_solver.m_r_solver; + unsigned j = A_r().column_count() - 1; + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); + if (column_represents_row_in_tableau(j)) { + remove_last_row_and_column_from_tableau(j); + if (rslv.m_basis_heading[j] < 0) + rslv.change_basis_unconditionally(j, rslv.m_basis[A_r().row_count()]); // A_r().row_count() is the index of the last row in the basis still + } + else { + remove_last_column_from_A(); + } + rslv.m_x.pop_back(); + rslv.m_d.pop_back(); + rslv.m_costs.pop_back(); + + remove_last_column_from_basis_tableau(j); + lp_assert(m_mpq_lar_core_solver.r_basis_is_OK()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); +} + +void lar_solver::pop_tableau() { + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); + + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); + // We remove last variables starting from m_column_names.size() to m_vec_of_canonic_left_sides.size(). + // At this moment m_column_names is already popped + while (A_r().column_count() > m_columns_to_ext_vars_or_term_indices.size()) + remove_last_column_from_tableau(); + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); +} + +void lar_solver::clean_inf_set_of_r_solver_after_pop() { + lp_assert(inf_int_set_is_correct()); + vector became_feas; + clean_popped_elements(A_r().column_count(), m_mpq_lar_core_solver.m_r_solver.m_inf_set); + std::unordered_set basic_columns_with_changed_cost; + auto inf_index_copy = m_mpq_lar_core_solver.m_r_solver.m_inf_set.m_index; + for (auto j: inf_index_copy) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { + continue; + } + // some basic columns might become non-basic - these columns need to be made feasible + numeric_pair delta; + if (m_mpq_lar_core_solver.m_r_solver.make_column_feasible(j, delta)) { + + change_basic_columns_dependend_on_a_given_nb_column(j, delta); + } + became_feas.push_back(j); + } + + for (unsigned j : became_feas) { + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis_heading[j] < 0); + m_mpq_lar_core_solver.m_r_solver.m_d[j] -= m_mpq_lar_core_solver.m_r_solver.m_costs[j]; + m_mpq_lar_core_solver.m_r_solver.m_costs[j] = zero_of_type(); + m_mpq_lar_core_solver.m_r_solver.m_inf_set.erase(j); + } + became_feas.clear(); + for (unsigned j : m_mpq_lar_core_solver.m_r_solver.m_inf_set.m_index) { + lp_assert(m_mpq_lar_core_solver.m_r_heading[j] >= 0); + if (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(j)) + became_feas.push_back(j); + } + for (unsigned j : became_feas) + m_mpq_lar_core_solver.m_r_solver.m_inf_set.erase(j); + + + if (use_tableau_costs()) { + for (unsigned j : became_feas) + m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); + for (unsigned j : basic_columns_with_changed_cost) + m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); + lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + } +} + +void lar_solver::shrink_explanation_to_minimum(vector> & explanation) const { + // implementing quickXplain + quick_xplain::run(explanation, *this); + lp_assert(this->explanation_is_correct(explanation)); +} + +bool lar_solver::model_is_int_feasible() const { + unsigned n = A_r().column_count(); + for (unsigned j = 0; j < n; j++) { + if (column_is_int(j) && !column_value_is_integer(j)) + return false; + } + return true; +} + +bool lar_solver::term_is_int(const lar_term * t) const { + for (auto const & p : t->m_coeffs) + if (! (column_is_int(p.first) && p.second.is_int())) + return false; + return t->m_v.is_int(); +} + +bool lar_solver::var_is_int(var_index v) const { + if (is_term(v)) { + lar_term const& t = get_term(v); + return term_is_int(&t); + } + else { + return column_is_int(v); + } +} + +bool lar_solver::column_is_int(unsigned j) const { + unsigned ext_var = m_columns_to_ext_vars_or_term_indices[j]; + lp_assert(contains(m_ext_vars_to_columns, ext_var)); + return m_ext_vars_to_columns.find(ext_var)->second.is_integer(); +} + +bool lar_solver::column_is_fixed(unsigned j) const { + return m_mpq_lar_core_solver.column_is_fixed(j); +} + + +bool lar_solver::ext_var_is_int(var_index ext_var) const { + auto it = m_ext_vars_to_columns.find(ext_var); + lp_assert(it != m_ext_vars_to_columns.end()); + return it == m_ext_vars_to_columns.end() || it->second.is_integer(); +} + +// below is the initialization functionality of lar_solver + +bool lar_solver::strategy_is_undecided() const { + return m_settings.simplex_strategy() == simplex_strategy_enum::undecided; +} + +var_index lar_solver::add_var(unsigned ext_j, bool is_int) { + TRACE("add_var", tout << "adding var " << ext_j << (is_int? " int" : " nonint") << std::endl;); + var_index i; + lp_assert(ext_j < m_terms_start_index); + + if (ext_j >= m_terms_start_index) + throw 0; // todo : what is the right way to exit? + auto it = m_ext_vars_to_columns.find(ext_j); + if (it != m_ext_vars_to_columns.end()) { + return it->second.ext_j(); + } + lp_assert(m_columns_to_ul_pairs.size() == A_r().column_count()); + i = A_r().column_count(); + m_columns_to_ul_pairs.push_back(ul_pair(static_cast(-1))); + add_non_basic_var_to_core_fields(ext_j, is_int); + lp_assert(sizes_are_correct()); + if (is_int) { + m_mpq_lar_core_solver.m_r_solver.set_tracker_of_x(& m_tracker_of_x_change); + } + lp_assert(inf_int_set_is_correct()); + return i; +} + +void lar_solver::register_new_ext_var_index(unsigned ext_v, bool is_int) { + lp_assert(!contains(m_ext_vars_to_columns, ext_v)); + unsigned j = static_cast(m_ext_vars_to_columns.size()); + m_ext_vars_to_columns.insert(std::make_pair(ext_v, ext_var_info(j, is_int))); + lp_assert(m_columns_to_ext_vars_or_term_indices.size() == j); + m_columns_to_ext_vars_or_term_indices.push_back(ext_v); +} + +void lar_solver::add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int) { + register_new_ext_var_index(ext_j, is_int); + m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); + m_columns_with_changed_bound.increase_size_by_one(); + m_inf_int_set.increase_size_by_one(); + add_new_var_to_core_fields_for_mpq(false); + if (use_lu()) + add_new_var_to_core_fields_for_doubles(false); +} + +void lar_solver::add_new_var_to_core_fields_for_doubles(bool register_in_basis) { + unsigned j = A_d().column_count(); + A_d().add_column(); + lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); + // lp_assert(m_mpq_lar_core_solver.m_d_low_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later + m_mpq_lar_core_solver.m_d_x.resize(j + 1); + m_mpq_lar_core_solver.m_d_low_bounds.resize(j + 1); + m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); + lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method + if (register_in_basis) { + A_d().add_row(); + m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); + m_mpq_lar_core_solver.m_d_basis.push_back(j); + } + else { + m_mpq_lar_core_solver.m_d_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); + m_mpq_lar_core_solver.m_d_nbasis.push_back(j); + } +} + +void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { + unsigned j = A_r().column_count(); + A_r().add_column(); + lp_assert(m_mpq_lar_core_solver.m_r_x.size() == j); + // lp_assert(m_mpq_lar_core_solver.m_r_low_bounds.size() == j && m_mpq_lar_core_solver.m_r_upper_bounds.size() == j); // restore later + m_mpq_lar_core_solver.m_r_x.resize(j + 1); + m_mpq_lar_core_solver.m_r_low_bounds.increase_size_by_one(); + m_mpq_lar_core_solver.m_r_upper_bounds.increase_size_by_one(); + m_mpq_lar_core_solver.m_r_solver.m_inf_set.increase_size_by_one(); + m_mpq_lar_core_solver.m_r_solver.m_costs.resize(j + 1); + m_mpq_lar_core_solver.m_r_solver.m_d.resize(j + 1); + lp_assert(m_mpq_lar_core_solver.m_r_heading.size() == j); // as A().column_count() on the entry to the method + if (register_in_basis) { + A_r().add_row(); + m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size()); + m_mpq_lar_core_solver.m_r_basis.push_back(j); + if (m_settings.bound_propagation()) + m_rows_with_changed_bounds.insert(A_r().row_count() - 1); + } + else { + m_mpq_lar_core_solver.m_r_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1); + m_mpq_lar_core_solver.m_r_nbasis.push_back(j); + } +} + + +var_index lar_solver::add_term_undecided(const vector> & coeffs, + const mpq &m_v) { + m_terms.push_back(new lar_term(coeffs, m_v)); + return m_terms_start_index + m_terms.size() - 1; +} + +// terms +var_index lar_solver::add_term(const vector> & coeffs, + const mpq &m_v) { + if (strategy_is_undecided()) + return add_term_undecided(coeffs, m_v); + + m_terms.push_back(new lar_term(coeffs, m_v)); + unsigned adjusted_term_index = m_terms.size() - 1; + var_index ret = m_terms_start_index + adjusted_term_index; + if (use_tableau() && !coeffs.empty()) { + add_row_from_term_no_constraint(m_terms.back(), ret); + if (m_settings.bound_propagation()) + m_rows_with_changed_bounds.insert(A_r().row_count() - 1); + } + lp_assert(m_ext_vars_to_columns.size() == A_r().column_count()); + return ret; +} + +void lar_solver::add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) { + register_new_ext_var_index(term_ext_index, term_is_int(term)); + // j will be a new variable + unsigned j = A_r().column_count(); + ul_pair ul(j); + m_columns_to_ul_pairs.push_back(ul); + add_basic_var_to_core_fields(); + if (use_tableau()) { + auto it = iterator_on_term_with_basis_var(*term, j); + A_r().fill_last_row_with_pivoting(it, + m_mpq_lar_core_solver.m_r_solver.m_basis_heading); + m_mpq_lar_core_solver.m_r_solver.m_b.resize(A_r().column_count(), zero_of_type()); + } + else { + fill_last_row_of_A_r(A_r(), term); + } + m_mpq_lar_core_solver.m_r_solver.update_x_and_call_tracker(j, get_basic_var_value_from_row_directly(A_r().row_count() - 1)); + if (use_lu()) + fill_last_row_of_A_d(A_d(), term); + lp_assert(inf_int_set_is_correct()); +} + +void lar_solver::add_basic_var_to_core_fields() { + bool use_lu = m_mpq_lar_core_solver.need_to_presolve_with_double_solver(); + lp_assert(!use_lu || A_r().column_count() == A_d().column_count()); + m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); + m_columns_with_changed_bound.increase_size_by_one(); + m_rows_with_changed_bounds.increase_size_by_one(); + m_inf_int_set.increase_size_by_one(); + add_new_var_to_core_fields_for_mpq(true); + if (use_lu) + add_new_var_to_core_fields_for_doubles(true); +} + +bool lar_solver::bound_is_integer_if_needed(unsigned j, const mpq & right_side) const { + if (!column_is_int(j)) + return true; + return right_side.is_int(); +} + +constraint_index lar_solver::add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) { + TRACE("lar_solver", tout << "j = " << j << std::endl;); + constraint_index ci = m_constraints.size(); + if (!is_term(j)) { // j is a var + lp_assert(bound_is_integer_if_needed(j, right_side)); + auto vc = new lar_var_constraint(j, kind, right_side); + m_constraints.push_back(vc); + update_column_type_and_bound(j, kind, right_side, ci); + } + else { + add_var_bound_on_constraint_for_term(j, kind, right_side, ci); + } + lp_assert(sizes_are_correct()); + lp_assert(inf_int_set_is_correct()); + return ci; +} + +void lar_solver::update_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index) { + switch (m_mpq_lar_core_solver.m_column_types[j]) { + case column_type::free_column: + update_free_column_type_and_bound(j, kind, right_side, constr_index); + break; + case column_type::boxed: + update_boxed_column_type_and_bound(j, kind, right_side, constr_index); + break; + case column_type::low_bound: + update_low_bound_column_type_and_bound(j, kind, right_side, constr_index); + break; + case column_type::upper_bound: + update_upper_bound_column_type_and_bound(j, kind, right_side, constr_index); + break; + case column_type::fixed: + update_fixed_column_type_and_bound(j, kind, right_side, constr_index); + break; + default: + lp_assert(false); // cannot be here + } +} + +void lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(is_term(j)); + unsigned adjusted_term_index = adjust_term_index(j); + lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int()); + auto it = m_ext_vars_to_columns.find(j); + if (it != m_ext_vars_to_columns.end()) { + unsigned term_j = it->second.ext_j(); + mpq rs = right_side - m_terms[adjusted_term_index]->m_v; + m_constraints.push_back(new lar_term_constraint(m_terms[adjusted_term_index], kind, right_side)); + update_column_type_and_bound(term_j, kind, rs, ci); + } + else { + add_constraint_from_term_and_create_new_column_row(j, m_terms[adjusted_term_index], kind, right_side); + } +} + +constraint_index lar_solver::add_constraint(const vector>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm) { + vector> left_side; + mpq rs = -right_side_parm; + substitute_terms_in_linear_expression(left_side_with_terms, left_side, rs); + unsigned term_index = add_term(left_side, zero_of_type()); + constraint_index ci = m_constraints.size(); + add_var_bound_on_constraint_for_term(term_index, kind_par, -rs, ci); + return ci; +} + +void lar_solver::add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term, + lconstraint_kind kind, const mpq & right_side) { + + add_row_from_term_no_constraint(term, term_j); + unsigned j = A_r().column_count() - 1; + update_column_type_and_bound(j, kind, right_side - term->m_v, m_constraints.size()); + m_constraints.push_back(new lar_term_constraint(term, kind, right_side)); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); +} + +void lar_solver::decide_on_strategy_and_adjust_initial_state() { + lp_assert(strategy_is_undecided()); + if (m_columns_to_ul_pairs.size() > m_settings.column_number_threshold_for_using_lu_in_lar_solver) { + m_settings.simplex_strategy() = simplex_strategy_enum::lu; + } + else { + m_settings.simplex_strategy() = simplex_strategy_enum::tableau_rows; // todo: when to switch to tableau_costs? + } + adjust_initial_state(); +} + +void lar_solver::adjust_initial_state() { + switch (m_settings.simplex_strategy()) { + case simplex_strategy_enum::lu: + adjust_initial_state_for_lu(); + break; + case simplex_strategy_enum::tableau_rows: + adjust_initial_state_for_tableau_rows(); + break; + case simplex_strategy_enum::tableau_costs: + lp_assert(false); // not implemented + case simplex_strategy_enum::undecided: + adjust_initial_state_for_tableau_rows(); + break; + } +} + +void lar_solver::adjust_initial_state_for_lu() { + copy_from_mpq_matrix(A_d()); + unsigned n = A_d().column_count(); + m_mpq_lar_core_solver.m_d_x.resize(n); + m_mpq_lar_core_solver.m_d_low_bounds.resize(n); + m_mpq_lar_core_solver.m_d_upper_bounds.resize(n); + m_mpq_lar_core_solver.m_d_heading = m_mpq_lar_core_solver.m_r_heading; + m_mpq_lar_core_solver.m_d_basis = m_mpq_lar_core_solver.m_r_basis; + + /* + unsigned j = A_d().column_count(); + A_d().add_column(); + lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); + // lp_assert(m_mpq_lar_core_solver.m_d_low_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later + m_mpq_lar_core_solver.m_d_x.resize(j + 1 ); + m_mpq_lar_core_solver.m_d_low_bounds.resize(j + 1); + m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); + lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method + if (register_in_basis) { + A_d().add_row(); + m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); + m_mpq_lar_core_solver.m_d_basis.push_back(j); + }else { + m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); + m_mpq_lar_core_solver.m_d_nbasis.push_back(j); + }*/ +} + +void lar_solver::adjust_initial_state_for_tableau_rows() { + for (unsigned j = 0; j < m_terms.size(); j++) { + if (contains(m_ext_vars_to_columns, j + m_terms_start_index)) + continue; + add_row_from_term_no_constraint(m_terms[j], j + m_terms_start_index); + } +} + +// this fills the last row of A_d and sets the basis column: -1 in the last column of the row +void lar_solver::fill_last_row_of_A_d(static_matrix & A, const lar_term* ls) { + lp_assert(A.row_count() > 0); + lp_assert(A.column_count() > 0); + unsigned last_row = A.row_count() - 1; + lp_assert(A.m_rows[last_row].empty()); + + for (auto & t : ls->m_coeffs) { + lp_assert(!is_zero(t.second)); + var_index j = t.first; + A.set(last_row, j, -t.second.get_double()); + } + + unsigned basis_j = A.column_count() - 1; + A.set(last_row, basis_j, -1); +} + +void lar_solver::update_free_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_ind) { + mpq y_of_bound(0); + switch (kind) { + case LT: + y_of_bound = -1; + case LE: + m_mpq_lar_core_solver.m_column_types[j] = column_type::upper_bound; + lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound); + lp_assert(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j); + { + auto up = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + } + set_upper_bound_witness(j, constr_ind); + break; + case GT: + y_of_bound = 1; + case GE: + m_mpq_lar_core_solver.m_column_types[j] = column_type::low_bound; + lp_assert(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j); + { + auto low = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_low_bounds[j] = low; + } + set_low_bound_witness(j, constr_ind); + break; + case EQ: + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = numeric_pair(right_side, zero_of_type()); + set_upper_bound_witness(j, constr_ind); + set_low_bound_witness(j, constr_ind); + break; + + default: + lp_unreachable(); + + } + m_columns_with_changed_bound.insert(j); +} + +void lar_solver::update_upper_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound); + mpq y_of_bound(0); + switch (kind) { + case LT: + y_of_bound = -1; + case LE: + { + auto up = numeric_pair(right_side, y_of_bound); + if (up < m_mpq_lar_core_solver.m_r_upper_bounds()[j]) { + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + m_columns_with_changed_bound.insert(j); + } + } + break; + case GT: + y_of_bound = 1; + case GE: + m_mpq_lar_core_solver.m_column_types[j] = column_type::boxed; + { + auto low = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_low_bounds[j] = low; + set_low_bound_witness(j, ci); + m_columns_with_changed_bound.insert(j); + if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + } + else { + m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; + } + } + break; + case EQ: + { + auto v = numeric_pair(right_side, zero_of_type()); + if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + set_low_bound_witness(j, ci); + m_infeasible_column_index = j; + } + else { + m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; + m_columns_with_changed_bound.insert(j); + set_low_bound_witness(j, ci); + set_upper_bound_witness(j, ci); + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + } + break; + } + break; + + default: + lp_unreachable(); + + } +} + +void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::boxed && m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j])); + mpq y_of_bound(0); + switch (kind) { + case LT: + y_of_bound = -1; + case LE: + { + auto up = numeric_pair(right_side, y_of_bound); + if (up < m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + m_columns_with_changed_bound.insert(j); + } + + if (up < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + lp_assert(false); + m_infeasible_column_index = j; + } + else { + if (m_mpq_lar_core_solver.m_r_low_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j]) + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + } + } + break; + case GT: + y_of_bound = 1; + case GE: + { + auto low = numeric_pair(right_side, y_of_bound); + if (low > m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_mpq_lar_core_solver.m_r_low_bounds[j] = low; + m_columns_with_changed_bound.insert(j); + set_low_bound_witness(j, ci); + } + if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + } + else if (low == m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + } + } + break; + case EQ: + { + auto v = numeric_pair(right_side, zero_of_type()); + if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_upper_bound_witness(j, ci); + } + else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_low_bound_witness(j, ci); + } + else { + m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; + set_low_bound_witness(j, ci); + set_upper_bound_witness(j, ci); + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + m_columns_with_changed_bound.insert(j); + } + + break; + } + + default: + lp_unreachable(); + + } +} +void lar_solver::update_low_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::low_bound); + mpq y_of_bound(0); + switch (kind) { + case LT: + y_of_bound = -1; + case LE: + { + auto up = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + m_columns_with_changed_bound.insert(j); + + if (up < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + } + else { + m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; + } + } + break; + case GT: + y_of_bound = 1; + case GE: + { + auto low = numeric_pair(right_side, y_of_bound); + if (low > m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_mpq_lar_core_solver.m_r_low_bounds[j] = low; + m_columns_with_changed_bound.insert(j); + set_low_bound_witness(j, ci); + } + } + break; + case EQ: + { + auto v = numeric_pair(right_side, zero_of_type()); + if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_upper_bound_witness(j, ci); + } + else { + m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; + set_low_bound_witness(j, ci); + set_upper_bound_witness(j, ci); + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + } + m_columns_with_changed_bound.insert(j); + break; + } + + default: + lp_unreachable(); + + } +} + +void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::fixed && m_mpq_lar_core_solver.m_r_low_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j])); + lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_r_low_bounds()[j].y.is_zero() && m_mpq_lar_core_solver.m_r_upper_bounds()[j].y.is_zero())); + auto v = numeric_pair(right_side, mpq(0)); + + mpq y_of_bound(0); + switch (kind) { + case LT: + if (v <= m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_upper_bound_witness(j, ci); + } + break; + case LE: + { + if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_upper_bound_witness(j, ci); + } + } + break; + case GT: + { + if (v >= m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_low_bound_witness(j, ci); + } + } + break; + case GE: + { + if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_low_bound_witness(j, ci); + } + } + break; + case EQ: + { + if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_upper_bound_witness(j, ci); + } + else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_low_bound_witness(j, ci); + } + break; + } + + default: + lp_unreachable(); + + } +} + + +} // namespace lp + + diff --git a/src/util/lp/permutation_matrix.h b/src/util/lp/permutation_matrix.h index 3c89b3646..8e664bba0 100644 --- a/src/util/lp/permutation_matrix.h +++ b/src/util/lp/permutation_matrix.h @@ -132,7 +132,7 @@ class permutation_matrix : public tail_matrix { unsigned size() const { return static_cast(m_rev.size()); } - unsigned * values() const { return m_permutation; } + unsigned * values() const { return m_permutation.c_ptr(); } void resize(unsigned size) { unsigned old_size = m_permutation.size(); diff --git a/src/util/max_cliques.h b/src/util/max_cliques.h index 340d3fee7..0bf67592c 100644 --- a/src/util/max_cliques.h +++ b/src/util/max_cliques.h @@ -128,7 +128,12 @@ public: turn = !turn; } if (clique.size() > 1) { - cliques.push_back(clique); + if (clique.size() == 2 && clique[0] == negate(clique[1])) { + // no op + } + else { + cliques.push_back(clique); + } } } } diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index 833ac8cbd..59a6cb009 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -11,6 +11,7 @@ Copyright (c) 2015 Microsoft Corporation #include "util/memory_manager.h" #include "util/error_codes.h" #include "util/z3_omp.h" +#include "util/debug.h" // The following two function are automatically generated by the mk_make.py script. // The script collects ADD_INITIALIZER and ADD_FINALIZER commands in the .h files. // For example, rational.h contains @@ -57,6 +58,7 @@ static void throw_out_of_memory() { { g_memory_out_of_memory = true; } + if (g_exit_when_out_of_memory) { std::cerr << g_out_of_memory_msg << "\n"; exit(ERR_MEMOUT); @@ -179,6 +181,22 @@ unsigned long long memory::get_max_used_memory() { return r; } +#if defined(_WINDOWS) +#include +#endif + +unsigned long long memory::get_max_memory_size() { +#if defined(_WINDOWS) + MEMORYSTATUSEX statex; + statex.dwLength = sizeof (statex); + GlobalMemoryStatusEx (&statex); + return statex.ullTotalPhys; +#else + // 16 GB + return 1ull << 34ull; +#endif +} + unsigned long long memory::get_allocation_count() { return g_memory_alloc_count; } @@ -282,6 +300,7 @@ void * memory::allocate(size_t s) { if (g_memory_thread_alloc_size > SYNCH_THRESHOLD) { synchronize_counters(true); } + return static_cast(r) + 1; // we return a pointer to the location after the extra field } diff --git a/src/util/memory_manager.h b/src/util/memory_manager.h index 273fadea9..5cb7dc467 100644 --- a/src/util/memory_manager.h +++ b/src/util/memory_manager.h @@ -67,6 +67,7 @@ public: static unsigned long long get_allocation_size(); static unsigned long long get_max_used_memory(); static unsigned long long get_allocation_count(); + static unsigned long long get_max_memory_size(); // temporary hack to avoid out-of-memory crash in z3.exe static void exit_when_out_of_memory(bool flag, char const * msg); }; diff --git a/src/util/mpq.h b/src/util/mpq.h index f1b261278..010bb2c8a 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -501,6 +501,8 @@ public: void machine_div(mpz const & a, mpz const & b, mpz & c) { mpz_manager::machine_div(a, b, c); } + void machine_div_rem(mpz const & a, mpz const & b, mpz & c, mpz& d) { mpz_manager::machine_div_rem(a, b, c, d); } + void div(mpz const & a, mpz const & b, mpz & c) { mpz_manager::div(a, b, c); } void rat_div(mpz const & a, mpz const & b, mpq & c) { @@ -515,6 +517,13 @@ public: reset_denominator(c); } + void machine_idiv_rem(mpq const & a, mpq const & b, mpq & c, mpq & d) { + SASSERT(is_int(a) && is_int(b)); + machine_div_rem(a.m_num, b.m_num, c.m_num, d.m_num); + reset_denominator(c); + reset_denominator(d); + } + void machine_idiv(mpq const & a, mpq const & b, mpz & c) { SASSERT(is_int(a) && is_int(b)); machine_div(a.m_num, b.m_num, c); diff --git a/src/util/obj_hashtable.h b/src/util/obj_hashtable.h index 8826e8b76..c5cb0c319 100644 --- a/src/util/obj_hashtable.h +++ b/src/util/obj_hashtable.h @@ -204,6 +204,14 @@ public: unsigned long long get_num_collision() const { return m_table.get_num_collision(); } + void get_collisions(Key * k, vector& collisions) { + vector cs; + m_table.get_collisions(key_data(k), cs); + for (key_data const& kd : cs) { + collisions.push_back(kd.m_key); + } + } + void swap(obj_map & other) { m_table.swap(other.m_table); } diff --git a/src/util/params.cpp b/src/util/params.cpp index 5e1e517c1..d7e05cb00 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -323,7 +323,6 @@ class params { typedef std::pair entry; svector m_entries; unsigned m_ref_count; - void del_value(entry & e); void del_values(); @@ -334,7 +333,10 @@ public: } void inc_ref() { m_ref_count++; } - void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } + void dec_ref() { + SASSERT(m_ref_count > 0); + if (--m_ref_count == 0) dealloc(this); + } bool empty() const { return m_entries.empty(); } bool contains(symbol const & k) const; @@ -344,7 +346,6 @@ public: void reset(symbol const & k); void reset(char const * k); - void validate(param_descrs const & p) { svector::iterator it = m_entries.begin(); svector::iterator end = m_entries.end(); @@ -565,27 +566,25 @@ void params_ref::copy(params_ref const & src) { void params_ref::copy_core(params const * src) { if (src == nullptr) return; - svector::const_iterator it = src->m_entries.begin(); - svector::const_iterator end = src->m_entries.end(); - for (; it != end; ++it) { - switch (it->second.m_kind) { + for (auto const& p : src->m_entries) { + switch (p.second.m_kind) { case CPK_BOOL: - m_params->set_bool(it->first, it->second.m_bool_value); + m_params->set_bool(p.first, p.second.m_bool_value); break; case CPK_UINT: - m_params->set_uint(it->first, it->second.m_uint_value); + m_params->set_uint(p.first, p.second.m_uint_value); break; case CPK_DOUBLE: - m_params->set_double(it->first, it->second.m_double_value); + m_params->set_double(p.first, p.second.m_double_value); break; case CPK_NUMERAL: - m_params->set_rat(it->first, *(it->second.m_rat_value)); + m_params->set_rat(p.first, *(p.second.m_rat_value)); break; case CPK_SYMBOL: - m_params->set_sym(it->first, symbol::mk_symbol_from_c_ptr(it->second.m_sym_value)); + m_params->set_sym(p.first, symbol::mk_symbol_from_c_ptr(p.second.m_sym_value)); break; case CPK_STRING: - m_params->set_str(it->first, it->second.m_str_value); + m_params->set_str(p.first, p.second.m_str_value); break; default: UNREACHABLE(); diff --git a/src/util/queue.h b/src/util/queue.h new file mode 100644 index 000000000..a517efc24 --- /dev/null +++ b/src/util/queue.h @@ -0,0 +1,73 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + queue.h + +Abstract: + + Generic queue. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-4-17 + +Notes: + +--*/ +#ifndef _QUEUE_H_ +#define _QUEUE_H_ + +#include "vector.h" + +template +class queue { + vector m_elems; + unsigned m_head; + unsigned m_capacity; + +public: + + queue(): m_head(0), m_capacity(0) {} + + void push(T const& t) { m_elems.push_back(t); } + + bool empty() const { + return m_head == m_elems.size(); + } + + T top() const { + return m_elems[m_head]; + } + + T pop_front() { + SASSERT(!empty()); + m_capacity = std::max(m_capacity, m_elems.size()); + SASSERT(m_head < m_elems.size()); + if (2 * m_head > m_capacity && m_capacity > 10) { + for (unsigned i = 0; i < m_elems.size() - m_head; ++i) { + m_elems[i] = m_elems[i + m_head]; + } + m_elems.shrink(m_elems.size() - m_head); + m_head = 0; + } + return m_elems[m_head++]; + } + + + T back() const { + return m_elems[m_elems.size() - 1]; + } + + T pop_back() { + SASSERT(!empty()); + SASSERT(m_head < m_elems.size()); + T result = back(); + m_elems.shrink(m_elems.size() - 1); + return result; + } +}; + +#endif + diff --git a/src/util/ref_buffer.h b/src/util/ref_buffer.h index 612dde2da..4768c3f28 100644 --- a/src/util/ref_buffer.h +++ b/src/util/ref_buffer.h @@ -82,6 +82,9 @@ public: return m_buffer[idx]; } + T* const* begin() const { return c_ptr(); } + T* const* end() const { return c_ptr() + size(); } + void set(unsigned idx, T * n) { inc_ref(n); dec_ref(m_buffer[idx]); diff --git a/src/util/smt2_util.cpp b/src/util/smt2_util.cpp index 8358c67ac..365d8fe70 100644 --- a/src/util/smt2_util.cpp +++ b/src/util/smt2_util.cpp @@ -34,6 +34,16 @@ bool is_smt2_quoted_symbol(char const * s) { if ('0' <= s[0] && s[0] <= '9') return true; unsigned len = static_cast(strlen(s)); + if (len >= 2 && s[0] == '|' && s[len-1] == '|') { + for (unsigned i = 1; i + 1 < len; i++) { + if (s[i] == '\\' && i + 2 < len && (s[i+1] == '\\' || s[i+1] == '|')) { + i++; + } + else if (s[i] == '\\' || s[i] == '|') + return true; + } + return false; + } for (unsigned i = 0; i < len; i++) if (!is_smt2_simple_symbol_char(s[i])) return true; diff --git a/src/util/sorting_network.h b/src/util/sorting_network.h index 17808ea48..f2906b00c 100644 --- a/src/util/sorting_network.h +++ b/src/util/sorting_network.h @@ -24,6 +24,28 @@ Notes: #ifndef SORTING_NETWORK_H_ #define SORTING_NETWORK_H_ + enum sorting_network_encoding { + grouped_at_most_1, + bimander_at_most_1, + ordered_at_most_1 + }; + + inline std::ostream& operator<<(std::ostream& out, sorting_network_encoding enc) { + switch (enc) { + case grouped_at_most_1: return out << "grouped"; + case bimander_at_most_1: return out << "bimander"; + case ordered_at_most_1: return out << "ordered"; + } + return out << "???"; + } + + struct sorting_network_config { + sorting_network_encoding m_encoding; + sorting_network_config() { + m_encoding = grouped_at_most_1; + } + }; + template class sorting_network { typedef typename Ext::vector vect; @@ -88,7 +110,7 @@ Notes: } public: - sorting_network(Ext& ext): + sorting_network(Ext& ext): m_ext(ext), m_current(&m_currentv), m_next(&m_nextv) @@ -119,8 +141,9 @@ Notes: // Described in Abio et.al. CP 2013. template class psort_nw { - typedef typename psort_expr::literal literal; - typedef typename psort_expr::literal_vector literal_vector; + typedef typename psort_expr::pliteral literal; + typedef typename psort_expr::pliteral_vector literal_vector; + sorting_network_config m_cfg; class vc { unsigned v; // number of vertices @@ -187,6 +210,8 @@ Notes: psort_nw(psort_expr& c): ctx(c) {} + sorting_network_config& cfg() { return m_cfg; } + literal ge(bool full, unsigned k, unsigned n, literal const* xs) { if (k > n) { return ctx.mk_false(); @@ -220,7 +245,17 @@ Notes: else if (k == 1) { literal_vector ors; // scoped_stats _ss(m_stats, k, n); - return mk_at_most_1(full, n, xs, ors, false); + switch (m_cfg.m_encoding) { + case grouped_at_most_1: + return mk_at_most_1(full, n, xs, ors, false); + case bimander_at_most_1: + return mk_at_most_1_bimander(full, n, xs, ors); + case ordered_at_most_1: + return mk_ordered_atmost_1(full, n, xs); + default: + UNREACHABLE(); + return xs[0]; + } } else { SASSERT(2*k <= n); @@ -278,7 +313,7 @@ Notes: if (n == 1) { return ors[0]; } - literal result = fresh(); + literal result = fresh("or"); add_implies_or(result, n, ors); add_or_implies(result, n, ors); return result; @@ -293,15 +328,15 @@ Notes: } void add_implies_and(literal l, literal_vector const& xs) { - for (unsigned j = 0; j < xs.size(); ++j) { - add_clause(ctx.mk_not(l), xs[j]); + for (literal const& x : xs) { + add_clause(ctx.mk_not(l), x); } } void add_and_implies(literal l, literal_vector const& xs) { literal_vector lits; - for (unsigned j = 0; j < xs.size(); ++j) { - lits.push_back(ctx.mk_not(xs[j])); + for (literal const& x : xs) { + lits.push_back(ctx.mk_not(x)); } lits.push_back(l); add_clause(lits); @@ -317,15 +352,29 @@ Notes: if (ands.size() == 1) { return ands[0]; } - literal result = fresh(); + literal result = fresh("and"); add_implies_and(result, ands); add_and_implies(result, ands); return result; } literal mk_exactly_1(bool full, unsigned n, literal const* xs) { + TRACE("pb", tout << "exactly 1 with " << n << " arguments " << (full?"full":"not full") << "\n";); literal_vector ors; - literal r1 = mk_at_most_1(full, n, xs, ors, true); + literal r1; + switch (m_cfg.m_encoding) { + case grouped_at_most_1: + r1 = mk_at_most_1(full, n, xs, ors, true); + break; + case bimander_at_most_1: + r1 = mk_at_most_1_bimander(full, n, xs, ors); + break; + case ordered_at_most_1: + return mk_ordered_exactly_1(full, n, xs); + default: + UNREACHABLE(); + return mk_ordered_exactly_1(full, n, xs); + } if (full) { r1 = mk_and(r1, mk_or(ors)); @@ -337,15 +386,11 @@ Notes: } literal mk_at_most_1(bool full, unsigned n, literal const* xs, literal_vector& ors, bool use_ors) { - TRACE("pb", tout << (full?"full":"partial") << " "; + TRACE("pb_verbose", tout << (full?"full":"partial") << " "; for (unsigned i = 0; i < n; ++i) tout << xs[i] << " "; tout << "\n";); - - if (n >= 4 && false) { - return mk_at_most_1_bimander(full, n, xs, ors); - } literal_vector in(n, xs); - literal result = fresh(); + literal result = fresh("at-most-1"); unsigned inc_size = 4; literal_vector ands; ands.push_back(result); @@ -387,7 +432,7 @@ Notes: // xs[0] + ... + xs[n-1] <= 1 => and_x if (full) { - literal and_i = fresh(); + literal and_i = fresh("and"); for (unsigned i = 0; i < n; ++i) { literal_vector lits; lits.push_back(and_i); @@ -407,29 +452,6 @@ Notes: } -#if 0 - literal result = fresh(); - - // result => xs[0] + ... + xs[n-1] <= 1 - for (unsigned i = 0; i < n; ++i) { - for (unsigned j = i + 1; j < n; ++j) { - add_clause(ctx.mk_not(result), ctx.mk_not(xs[i]), ctx.mk_not(xs[j])); - } - } - - // xs[0] + ... + xs[n-1] <= 1 => result - for (unsigned i = 0; i < n; ++i) { - literal_vector lits; - lits.push_back(result); - for (unsigned j = 0; j < n; ++j) { - if (j != i) lits.push_back(xs[j]); - } - add_clause(lits); - } - - return result; -#endif -#if 1 // r <=> and( or(!xi,!xj)) // literal_vector ands; @@ -439,30 +461,105 @@ Notes: } } return mk_and(ands); -#else - // r <=> or (and !x_{j != i}) - - literal_vector ors; - for (unsigned i = 0; i < n; ++i) { - literal_vector ands; - for (unsigned j = 0; j < n; ++j) { - if (j != i) { - ands.push_back(ctx.mk_not(xs[j])); - } - } - ors.push_back(mk_and(ands)); - } - return mk_or(ors); - -#endif } + literal mk_ordered_exactly_1(bool full, unsigned n, literal const* xs) { + return mk_ordered_1(full, true, n, xs); + } + + literal mk_ordered_atmost_1(bool full, unsigned n, literal const* xs) { + return mk_ordered_1(full, false, n, xs); + } + + literal mk_ordered_1(bool full, bool is_eq, unsigned n, literal const* xs) { + if (n <= 1 && !is_eq) { + return ctx.mk_true(); + } + if (n == 0) { + return ctx.mk_false(); + } + if (n == 1) { + return xs[0]; + } + + SASSERT(n > 1); + + // y0 -> y1 + // x0 -> y0 + // x1 -> y1 + // r, y0 -> ~x1 + // r, y1 -> ~x2 + // r -> x3 | y1 + // r -> ~x3 | ~y1 + + // x0,x1,x2, .., x_{n-1}, x_n + // y0,y1,y2, .., y_{n-1} + // y_i -> y_{i+1} i = 0, ..., n - 2 + // x_i -> y_i i = 0, ..., n - 1 + // r, y_i -> ~x_{i+1} i = 0, ..., n - 1 + // exactly 1: + // r -> x_n | y_{n-1} + // full (exactly 1): + // two_i -> y_i & x_{i+1} + // zero -> ~x_n + // zero -> ~y_{n-1} + // r | zero | two_0 | ... | two_{n-1} + // full atmost 1: + // r | two | two_0 | ... | two_{n-1} + + literal r = fresh("ordered"); + literal_vector ys; + for (unsigned i = 0; i + 1 < n; ++i) { + ys.push_back(fresh("y")); + } + for (unsigned i = 0; i + 2 < n; ++i) { + add_clause(ctx.mk_not(ys[i]), ys[i + 1]); + } + for (unsigned i = 0; i + 1 < n; ++i) { + add_clause(ctx.mk_not(xs[i]), ys[i]); + add_clause(ctx.mk_not(r), ctx.mk_not(ys[i]), ctx.mk_not(xs[i + 1])); + } + + if (is_eq) { + add_clause(ctx.mk_not(r), ys[n-2], xs[n-1]); + } + for (unsigned i = 1; i < n - 1; ++i) { + add_clause(ctx.mk_not(ys[i]), xs[i], ys[i-1]); + } + + add_clause(ctx.mk_not(ys[0]), xs[0]); + if (full) { + literal_vector twos; + for (unsigned i = 0; i < n - 1; ++i) { + twos.push_back(fresh("two")); + } + add_clause(ctx.mk_not(twos[0]), ys[0]); + add_clause(ctx.mk_not(twos[0]), xs[1]); + for (unsigned i = 1; i < n - 1; ++i) { + add_clause(ctx.mk_not(twos[i]), ys[i], twos[i-1]); + add_clause(ctx.mk_not(twos[i]), xs[i + 1], twos[i-1]); + } + if (is_eq) { + literal zero = fresh("zero"); + add_clause(ctx.mk_not(zero), ctx.mk_not(xs[n-1])); + add_clause(ctx.mk_not(zero), ctx.mk_not(ys[n-2])); + add_clause(r, zero, twos.back()); + } + else { + add_clause(r, twos.back()); + } + } + return r; + } // literal mk_at_most_1_bimander(bool full, unsigned n, literal const* xs, literal_vector& ors) { + if (full) { + return mk_at_most_1(full, n, xs, ors, true); + } literal_vector in(n, xs); - literal result = fresh(); + literal result = fresh("bimander"); unsigned inc_size = 2; literal_vector ands; for (unsigned i = 0; i < n; i += inc_size) { @@ -477,7 +574,7 @@ Notes: } literal_vector bits; for (unsigned k = 0; k < nbits; ++k) { - bits.push_back(fresh()); + bits.push_back(fresh("bit")); } for (unsigned i = 0; i < ors.size(); ++i) { for (unsigned k = 0; k < nbits; ++k) { @@ -494,7 +591,7 @@ Notes: } std::ostream& pp(std::ostream& out, literal_vector const& lits) { - for (unsigned i = 0; i < lits.size(); ++i) ctx.pp(out, lits[i]) << " "; + for (literal const& l : lits) ctx.pp(out, l) << " "; return out; } @@ -513,7 +610,7 @@ Notes: for (unsigned i = 0; i < N; ++i) { in.push_back(ctx.mk_not(xs[i])); } - TRACE("pb", + TRACE("pb_verbose", pp(tout << N << ": ", in); tout << " ~ " << k << "\n";); return true; @@ -539,9 +636,9 @@ Notes: return ctx.mk_min(a, b); } - literal fresh() { + literal fresh(char const* n) { m_stats.m_num_compiled_vars++; - return ctx.fresh(); + return ctx.fresh(n); } void add_clause(literal l1, literal l2, literal l3) { literal lits[3] = { l1, l2, l3 }; @@ -558,7 +655,6 @@ Notes: m_stats.m_num_compiled_clauses++; m_stats.m_num_clause_vars += n; literal_vector tmp(n, ls); - TRACE("pb", for (unsigned i = 0; i < n; ++i) tout << ls[i] << " "; tout << "\n";); ctx.mk_clause(n, tmp.c_ptr()); } @@ -595,7 +691,7 @@ Notes: } void card(unsigned k, unsigned n, literal const* xs, literal_vector& out) { - TRACE("pb", tout << "card k: " << k << " n: " << n << "\n";); + TRACE("pb_verbose", tout << "card k: " << k << " n: " << n << "\n";); if (n <= k) { psort_nw::sorting(n, xs, out); } @@ -609,7 +705,7 @@ Notes: card(k, n-l, xs + l, out2); smerge(k, out1.size(), out1.c_ptr(), out2.size(), out2.c_ptr(), out); } - TRACE("pb", tout << "card k: " << k << " n: " << n << "\n"; + TRACE("pb_verbose", tout << "card k: " << k << " n: " << n << "\n"; pp(tout << "in:", n, xs) << "\n"; pp(tout << "out:", out) << "\n";); @@ -637,7 +733,7 @@ Notes: void merge(unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { - TRACE("pb", tout << "merge a: " << a << " b: " << b << "\n";); + TRACE("pb_verbose", tout << "merge a: " << a << " b: " << b << "\n";); if (a == 1 && b == 1) { literal y1 = mk_max(as[0], bs[0]); literal y2 = mk_min(as[0], bs[0]); @@ -672,7 +768,7 @@ Notes: odd_b.size(), odd_b.c_ptr(), out2); interleave(out1, out2, out); } - TRACE("pb", tout << "merge a: " << a << " b: " << b << "\n"; + TRACE("pb_verbose", tout << "merge a: " << a << " b: " << b << "\n"; pp(tout << "a:", a, as) << "\n"; pp(tout << "b:", b, bs) << "\n"; pp(tout << "out:", out) << "\n";); @@ -709,7 +805,7 @@ Notes: void interleave(literal_vector const& as, literal_vector const& bs, literal_vector& out) { - TRACE("pb", tout << "interleave: " << as.size() << " " << bs.size() << "\n";); + TRACE("pb_verbose", tout << "interleave: " << as.size() << " " << bs.size() << "\n";); SASSERT(as.size() >= bs.size()); SASSERT(as.size() <= bs.size() + 2); SASSERT(!as.empty()); @@ -729,7 +825,7 @@ Notes: out.push_back(as[sz+1]); } SASSERT(out.size() == as.size() + bs.size()); - TRACE("pb", tout << "interleave: " << as.size() << " " << bs.size() << "\n"; + TRACE("pb_verbose", tout << "interleave: " << as.size() << " " << bs.size() << "\n"; pp(tout << "a: ", as) << "\n"; pp(tout << "b: ", bs) << "\n"; pp(tout << "out: ", out) << "\n";); @@ -741,7 +837,7 @@ Notes: public: void sorting(unsigned n, literal const* xs, literal_vector& out) { - TRACE("pb", tout << "sorting: " << n << "\n";); + TRACE("pb_verbose", tout << "sorting: " << n << "\n";); switch(n) { case 0: break; @@ -766,7 +862,7 @@ Notes: } break; } - TRACE("pb", tout << "sorting: " << n << "\n"; + TRACE("pb_verbose", tout << "sorting: " << n << "\n"; pp(tout << "in:", n, xs) << "\n"; pp(tout << "out:", out) << "\n";); } @@ -802,7 +898,7 @@ Notes: unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { - TRACE("pb", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n";); + TRACE("pb_verbose", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n";); if (a == 1 && b == 1 && c == 1) { literal y = mk_max(as[0], bs[0]); if (m_t != GE) { @@ -876,7 +972,7 @@ Notes: out.push_back(y); } } - TRACE("pb", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n"; + TRACE("pb_verbose", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n"; pp(tout << "a:", a, as) << "\n"; pp(tout << "b:", b, bs) << "\n"; pp(tout << "out:", out) << "\n"; @@ -920,12 +1016,12 @@ Notes: unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { - TRACE("pb", tout << "dsmerge: c:" << c << " a:" << a << " b:" << b << "\n";); + TRACE("pb_verbose", tout << "dsmerge: c:" << c << " a:" << a << " b:" << b << "\n";); SASSERT(a <= c); SASSERT(b <= c); SASSERT(a + b >= c); for (unsigned i = 0; i < c; ++i) { - out.push_back(fresh()); + out.push_back(fresh("dsmerge")); } if (m_t != GE) { for (unsigned i = 0; i < a; ++i) { @@ -979,11 +1075,11 @@ Notes: void dsorting(unsigned m, unsigned n, literal const* xs, literal_vector& out) { - TRACE("pb", tout << "dsorting m: " << m << " n: " << n << "\n";); + TRACE("pb_verbose", tout << "dsorting m: " << m << " n: " << n << "\n";); SASSERT(m <= n); literal_vector lits; for (unsigned i = 0; i < m; ++i) { - out.push_back(fresh()); + out.push_back(fresh("dsort")); } if (m_t != GE) { for (unsigned k = 1; k <= m; ++k) { @@ -1014,7 +1110,7 @@ Notes: void add_subset(bool polarity, unsigned k, unsigned offset, literal_vector& lits, unsigned n, literal const* xs) { - TRACE("pb", tout << "k:" << k << " offset: " << offset << " n: " << n << " "; + TRACE("pb_verbose", tout << "k:" << k << " offset: " << offset << " n: " << n << " "; pp(tout, lits) << "\n";); SASSERT(k + offset <= n); if (k == 0) { diff --git a/src/util/uint_set.h b/src/util/uint_set.h index 352189ef1..0f3715cb1 100644 --- a/src/util/uint_set.h +++ b/src/util/uint_set.h @@ -24,9 +24,9 @@ Revision History: class uint_set : unsigned_vector { - + public: - + typedef unsigned data; uint_set() {} @@ -252,5 +252,128 @@ inline std::ostream & operator<<(std::ostream & target, const uint_set & s) { return target; } + +class tracked_uint_set { + svector m_in_set; + svector m_set; +public: + typedef svector::const_iterator iterator; + void insert(unsigned v) { + m_in_set.reserve(v+1, false); + if (m_in_set[v]) + return; + m_in_set[v] = true; + m_set.push_back(v); + } + + void remove(unsigned v) { + if (contains(v)) { + m_in_set[v] = false; + unsigned i = 0; + for (i = 0; i < m_set.size() && m_set[i] != v; ++i) + ; + SASSERT(i < m_set.size()); + m_set[i] = m_set.back(); + m_set.pop_back(); + } + } + + tracked_uint_set& operator=(tracked_uint_set const& other) { + m_in_set = other.m_in_set; + m_set = other.m_set; + return *this; + } + + bool contains(unsigned v) const { + return v < m_in_set.size() && m_in_set[v] != 0; + } + + bool empty() const { + return m_set.empty(); + } + + // erase some variable from the set + unsigned erase() { + SASSERT(!empty()); + unsigned v = m_set.back(); + m_set.pop_back(); + m_in_set[v] = false; + return v; + } + unsigned size() const { return m_set.size(); } + iterator begin() const { return m_set.begin(); } + iterator end() const { return m_set.end(); } + // void reset() { m_set.reset(); m_in_set.reset(); } + void reset() { + unsigned sz = m_set.size(); + for (unsigned i = 0; i < sz; ++i) m_in_set[m_set[i]] = false; + m_set.reset(); + } + void finalize() { m_set.finalize(); m_in_set.finalize(); } + tracked_uint_set& operator&=(tracked_uint_set const& other) { + unsigned j = 0; + for (unsigned i = 0; i < m_set.size(); ++i) { + if (other.contains(m_set[i])) { + m_set[j] = m_set[i]; + ++j; + } + else { + m_in_set[m_set[i]] = false; + } + } + m_set.resize(j); + return *this; + } + tracked_uint_set& operator|=(tracked_uint_set const& other) { + for (unsigned i = 0; i < other.m_set.size(); ++i) { + insert(other.m_set[i]); + } + return *this; + } +}; + +class indexed_uint_set { + unsigned m_size; + unsigned_vector m_elems; + unsigned_vector m_index; +public: + indexed_uint_set(): + m_size(0) + {} + + void insert(unsigned x) { + SASSERT(!contains(x)); + m_index.reserve(x + 1, UINT_MAX); + m_elems.reserve(m_size + 1); + m_index[x] = m_size; + m_elems[m_size] = x; + m_size++; + SASSERT(contains(x)); + } + + void remove(unsigned x) { + SASSERT(contains(x)); + unsigned y = m_elems[--m_size]; + if (x != y) { + unsigned idx = m_index[x]; + m_index[y] = idx; + m_elems[idx] = y; + m_index[x] = m_size; + m_elems[m_size] = x; + } + SASSERT(!contains(x)); + } + + bool contains(unsigned x) const { return x < m_index.size() && m_index[x] < m_size && m_elems[m_index[x]] == x; } + void reset() { m_size = 0; } + bool empty() const { return m_size == 0; } + unsigned size() const { return m_size; } + unsigned max_var() const { return m_index.size(); } + typedef unsigned_vector::const_iterator iterator; + iterator begin() const { return m_elems.begin(); } + iterator end() const { return m_elems.begin() + m_size; } + +}; + #endif /* UINT_SET_H_ */ diff --git a/src/util/util.cpp b/src/util/util.cpp index 8ffa89877..59d6d752c 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -17,6 +17,10 @@ Revision History: --*/ +#ifdef _WINDOWS +#include +#endif +#include #include "util/util.h" static unsigned g_verbosity_level = 0; @@ -35,6 +39,25 @@ void set_verbose_stream(std::ostream& str) { g_verbose_stream = &str; } +#ifdef _WINDOWS +static int g_thread_id = 0; +#else +static std::thread::id g_thread_id = std::this_thread::get_id(); +#endif +static bool g_is_threaded = false; + +bool is_threaded() { + if (g_is_threaded) return true; +#ifdef _WINDOWS + int thid = GetCurrentThreadId(); + g_is_threaded = g_thread_id != thid && g_thread_id != 0; + g_thread_id = thid; +#else + g_is_threaded = std::this_thread::get_id() != g_thread_id; +#endif + return g_is_threaded; +} + std::ostream& verbose_stream() { return *g_verbose_stream; } diff --git a/src/util/util.h b/src/util/util.h index 2b11a6ce8..6e7ee5ce5 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -21,6 +21,7 @@ Revision History: #include "util/debug.h" #include "util/memory_manager.h" +#include "util/z3_omp.h" #include #include #include @@ -177,16 +178,36 @@ void set_verbosity_level(unsigned lvl); unsigned get_verbosity_level(); std::ostream& verbose_stream(); void set_verbose_stream(std::ostream& str); +bool is_threaded(); -#define IF_VERBOSE(LVL, CODE) { if (get_verbosity_level() >= LVL) { CODE } } ((void) 0) + +#define IF_VERBOSE(LVL, CODE) { \ + if (get_verbosity_level() >= LVL) { \ + if (is_threaded()) { \ + LOCK_CODE(CODE); \ + } \ + else { \ + CODE; \ + } \ + } } ((void) 0) -#ifdef _EXTERNAL_RELEASE -#define IF_IVERBOSE(LVL, CODE) ((void) 0) +#ifdef _MSC_VER +#define DO_PRAGMA(x) __pragma(x) #else -#define IF_IVERBOSE(LVL, CODE) { if (get_verbosity_level() >= LVL) { CODE } } ((void) 0) +#define DO_PRAGMA(x) _Pragma(#x) #endif - +#ifdef _NO_OMP_ +#define LOCK_CODE(CODE) CODE; +#else +#define LOCK_CODE(CODE) \ + { \ + DO_PRAGMA(omp critical (verbose_lock)) \ + { \ + CODE; \ + } \ + } +#endif template struct default_eq { diff --git a/src/util/vector.h b/src/util/vector.h index 85aeab3d3..c4443255a 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -471,7 +471,7 @@ public: void fill(unsigned sz, T const & elem) { resize(sz); - fill(sz, elem); + fill(elem); } bool contains(T const & elem) const { @@ -548,6 +548,11 @@ typedef svector char_vector; typedef svector signed_char_vector; typedef svector double_vector; +inline std::ostream& operator<<(std::ostream& out, unsigned_vector const& v) { + for (unsigned u : v) out << u << " "; + return out; +} + template struct vector_hash_tpl { Hash m_hash;