diff --git a/CMakeLists.txt b/CMakeLists.txt index afea250cb..a086afd71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,8 +33,8 @@ endif() # Project version ################################################################################ set(Z3_VERSION_MAJOR 4) -set(Z3_VERSION_MINOR 7) -set(Z3_VERSION_PATCH 1) +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}") set(Z3_FULL_VERSION_STR "${Z3_VERSION}") # Note this might be modified 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/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..6761b3f8d 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); @@ -2576,13 +2580,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); } 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/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 af4c92658..0590a8693 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, 1, 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') @@ -69,10 +69,9 @@ def init_project_def(): 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('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') diff --git a/scripts/mk_util.py b/scripts/mk_util.py index aab4bcb21..1391eee87 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2809,6 +2809,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..d8a773747 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,10 +36,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,12 +56,12 @@ 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) @@ -92,7 +92,6 @@ 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) 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/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 index 1000a6d3b..2b461e991 100644 --- a/src/api/api_interp.cpp +++ b/src/api/api_interp.cpp @@ -506,10 +506,11 @@ extern "C" { 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){ + static Z3_ast_vector iZ3_parse(Z3_context ctx, const char *filename, const char ** error){ + return nullptr; +#if 0 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); @@ -521,47 +522,42 @@ extern "C" { read_error << "SMTLIB parse error: " << Z3_get_parser_error(ctx); read_msg = read_error.str(); *error = read_msg.c_str(); - return false; + return nullptr; } Z3_set_error_handler(ctx, nullptr); - return true; + return nullptr; +#endif } - 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[]){ + int Z3_read_interpolation_problem(Z3_context ctx, Z3_ast_vector cnsts, unsigned* _num, unsigned* parents[], const char *filename, Z3_string_ptr error, Z3_ast_vector theory){ hash_map file_params; get_file_params(filename, file_params); unsigned num_theory = 0; - if (file_params.find("THEORY") != file_params.end()) + 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)) + Z3_ast_vector assertions = iZ3_parse(ctx, filename, error); + if (assertions == 0) return false; - if (num_theory > assertions.size()) - num_theory = assertions.size(); - unsigned num = assertions.size() - num_theory; + if (num_theory > Z3_ast_vector_size(ctx, assertions)) + num_theory = Z3_ast_vector_size(ctx, assertions); + unsigned num = Z3_ast_vector_size(ctx, assertions) - 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; theory && j < num_theory; j++) + Z3_ast_vector_push(ctx, theory, Z3_ast_vector_get(ctx, 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]; - + Z3_ast_vector_push(ctx, cnsts, Z3_ast_vector_get(ctx, assertions, j + num_theory)); + if (!parents){ - *_num = num; - *cnsts = &read_cnsts[0]; + Z3_ast_vector_dec_ref(ctx, assertions); return true; } @@ -571,7 +567,7 @@ extern "C" { hash_map pred_map; for (unsigned j = 0; j < num; j++){ - Z3_ast lhs = nullptr, rhs = read_cnsts[j]; + Z3_ast lhs = nullptr, rhs = Z3_ast_vector_get(ctx, 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); @@ -612,7 +608,7 @@ extern "C" { read_error << "formula " << j + 1 << ": should be (implies {children} fmla parent)"; goto fail; } - read_cnsts[j] = lhs; + Z3_ast_vector_set(ctx, cnsts, j, lhs); Z3_ast name = rhs; if (pred_map.find(name) != pred_map.end()){ read_error << "formula " << j + 1 << ": duplicate symbol"; @@ -631,11 +627,12 @@ extern "C" { } *_num = num; - *cnsts = &read_cnsts[0]; *parents = &read_parents[0]; + Z3_ast_vector_dec_ref(ctx, assertions); return true; fail: + Z3_ast_vector_dec_ref(ctx, assertions); read_msg = read_error.str(); *error = read_msg.c_str(); return false; 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 7b8866b59..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 { @@ -143,6 +144,12 @@ extern "C" { 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(); } @@ -298,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 88ce8b144..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]))) { @@ -554,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 903086daa..8d7825335 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -329,11 +329,11 @@ 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 @@ -440,6 +440,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 +845,6 @@ namespace z3 { */ friend expr operator!(expr const & a); - /** \brief Return an expression representing a and b. @@ -901,6 +901,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 +1925,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 +2043,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 +2157,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 +2167,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 +2191,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 +2216,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; } @@ -2880,18 +2973,19 @@ namespace z3 { return expr(a.ctx(), Z3_mk_interpolant(a.ctx(), a)); } - 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_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 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_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, 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); @@ -2902,12 +2996,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); @@ -2918,9 +3013,9 @@ 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); } 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/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/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 index 3f2feb5a6..e2b814983 100644 --- a/src/api/dotnet/InterpolationContext.cs +++ b/src/api/dotnet/InterpolationContext.cs @@ -133,19 +133,16 @@ namespace Microsoft.Z3 /// 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; + uint num = 0; 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); + ASTVector _cnsts = new ASTVector(this); + ASTVector _theory = new ASTVector(this); + + int r = Native.Z3_read_interpolation_problem(nCtx, _cnsts.NativeObject, ref num, out parents, filename, out n_err_str, _theory.NativeObject); error = Marshal.PtrToStringAnsi(n_err_str); - cnsts = new Expr[num]; + cnsts = _cnsts.ToExprArray(); 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]); + theory = _theory.ToExprArray(); return r; } 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/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 index 99a63821f..bce789807 100644 --- a/src/api/java/InterpolationContext.java +++ b/src/api/java/InterpolationContext.java @@ -176,31 +176,25 @@ public class InterpolationContext extends Context /// 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) + public ReadInterpolationProblemResult ReadInterpolationProblem(String filename) { - 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; + ReadInterpolationProblemResult res = new ReadInterpolationProblemResult(); + Native.UIntArrayPtr n_parents = new Native.UIntArrayPtr(); + ASTVector _cnsts = new ASTVector(this); + ASTVector _theory = new ASTVector(this); + Native.StringPtr n_err_str = new Native.StringPtr(); + Native.IntPtr n_num = new Native.IntPtr(); + res.return_value = Native.readInterpolationProblem(nCtx(), _cnsts.getNativeObject(), n_num, + n_parents, filename, n_err_str, _theory.getNativeObject()); + res.error = n_err_str.value; + res.theory = _theory.ToExprArray(); + res.cnsts = _cnsts.ToExprArray(); + int num = n_num.value; + res.parents = new int[num]; + for (int i = 0; i < num; i++) { + res.parents[i] = n_parents.value[i]; + } + return res; } /// 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/python/z3/z3.py b/src/api/python/z3/z3.py index 3eaf20763..978141de9 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. @@ -5032,6 +5043,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 +5079,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 +6139,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 +6446,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 +6491,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 +7264,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 +8240,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,8 +8264,8 @@ 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) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index ac6c39137..0825f27a0 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -5175,9 +5175,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 +5189,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 +5450,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 +5466,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 +5825,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 +5924,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 +6031,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 +6045,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 +6126,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 index 2441d4339..765870bd5 100644 --- a/src/api/z3_interp.h +++ b/src/api/z3_interp.h @@ -207,18 +207,17 @@ extern "C" { 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))) + def_API('Z3_read_interpolation_problem', INT, (_in(CONTEXT), _in(AST_VECTOR), _out(UINT), _out_managed_array(2, UINT), _in(STRING), _out(STRING), _in(AST_VECTOR))) */ int Z3_API Z3_read_interpolation_problem(Z3_context ctx, - unsigned *num, - Z3_ast *cnsts[], - unsigned *parents[], + Z3_ast_vector cnsts, + unsigned* num, + unsigned* parents[], Z3_string filename, Z3_string_ptr error, - unsigned *num_theory, - Z3_ast *theory[]); + Z3_ast_vector theory); diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 12ca136c5..a07be0b22 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1348,6 +1348,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 +1510,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 +2225,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..2db0fde04 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(); } + }; // ----------------------------------- @@ -1451,6 +1454,8 @@ public: void show_id_gen(); + void update_fresh_id(ast_manager const& other); + protected: reslimit m_limit; small_object_allocator m_alloc; 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 ade12de62..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,13 +915,12 @@ 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); @@ -919,6 +933,10 @@ class smt2_printer { 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..47a8a044a 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,6 +39,8 @@ 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; @@ -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. + The procedure is described in "Translating Pseudo-Boolean Constraints into SAT " +         Niklas Een, Niklas Sörensson, 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(expr* e, expr_ref& r) { + app* a; + return (is_app(e) && (a = to_app(e), mk_app(false, 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,29 +802,62 @@ 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); } + 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 @@ -387,15 +873,22 @@ struct pb2bv_rewriter::imp { m_trail.push_back(l); return l; } - literal fresh() { - expr_ref fr(m.mk_fresh_const("sn", m.mk_bool_sort()), m); + literal 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)); + 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 +900,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 +912,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(expr* e, expr_ref& r, proof_ref& p) { + expr_ref ee(e, m()); + if (m_cfg.m_r.mk_app(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); + // m_rw(e, result, result_proof); + m_rw.rewrite(e, result, result_proof); } void push() { m_fresh_lim.push_back(m_fresh.size()); @@ -453,6 +1005,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,6 +1017,8 @@ 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); } diff --git a/src/ast/rewriter/pb2bv_rewriter.h b/src/ast/rewriter/pb2bv_rewriter.h index a4176922a..9e3785649 100644 --- a/src/ast/rewriter/pb2bv_rewriter.h +++ b/src/ast/rewriter/pb2bv_rewriter.h @@ -31,6 +31,7 @@ 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(); 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/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/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 56a354ee8..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; @@ -620,7 +619,7 @@ public: try { ctx.regular_stream() << gparams::get_value(opt) << std::endl; } - catch (const 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..e42f884d6 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -43,6 +43,7 @@ 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" @@ -396,7 +397,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 +407,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); @@ -498,6 +499,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 +872,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 +1275,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 +1431,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); @@ -1505,7 +1525,12 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions 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 +1542,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 +1549,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 +1623,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 +1715,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 +1781,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 +1826,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 +1912,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 +1945,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/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/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/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_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; iis_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/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..e5f4bc133 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,7 @@ 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_objective_refs(m), m_enable_sat(false), m_is_clausal(false), @@ -146,9 +147,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 +275,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 +308,23 @@ 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 { + m_pareto1 = (pri == symbol("pareto")); + is_sat = execute(m_objectives[0], true, false); + } } - 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); } @@ -335,14 +341,12 @@ namespace opt { void context::fix_model(model_ref& mdl) { if (mdl) { - if (m_model_converter) { - (*m_model_converter)(mdl, 0); - } - m_fm(mdl, 0); + (*m_fm)(mdl); + apply(m_model_converter, mdl); } } - void context::get_model(model_ref& mdl) { + void context::get_model_core(model_ref& mdl) { mdl = m_model; fix_model(mdl); } @@ -546,6 +550,7 @@ namespace opt { } void context::yield() { + SASSERT (m_pareto); m_pareto->get_model(m_model, m_labels); update_bound(true); update_bound(false); @@ -565,6 +570,7 @@ namespace opt { return is_sat; } + std::string context::reason_unknown() const { if (m.canceled()) { return Z3_CANCELED_MSG; @@ -594,7 +600,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 +752,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 +765,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 +823,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 +955,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 +1016,7 @@ namespace opt { } } + void context::model_updated(model* md) { opt_params optp(m_params); symbol prefix = optp.solution_prefix(); @@ -1060,12 +1064,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 +1099,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 +1116,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 +1153,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); @@ -1246,6 +1247,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 +1259,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,7 +1394,7 @@ namespace opt { } void context::clear_state() { - set_pareto(nullptr); + m_pareto = nullptr; m_box_index = UINT_MAX; m_model.reset(); } @@ -1405,9 +1411,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 +1430,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 +1445,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 +1474,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 +1518,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 +1624,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..b642f1d7a 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; @@ -157,7 +158,7 @@ namespace opt { vector m_objectives; model_ref m_model; model_converter_ref m_model_converter; - filter_model_converter m_fm; + generic_model_converter_ref m_fm; unsigned m_model_counter; obj_map m_objective_fns; obj_map m_objective_orig; @@ -187,7 +188,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 +201,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 +223,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 +261,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 +298,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_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..6ea7feaad 100644 --- a/src/opt/sortmax.cpp +++ b/src/opt/sortmax.cpp @@ -24,7 +24,7 @@ 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 { @@ -35,7 +35,7 @@ namespace opt { 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); @@ -138,11 +138,11 @@ namespace opt { m_trail.push_back(l); return l; } - literal fresh() { - expr_ref fr(m.mk_fresh_const("sn", m.mk_bool_sort()), m); + literal 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); } 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..ae969a99e 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -2555,12 +2555,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 +2622,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_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..14482eabb --- /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 typename sat::literal literal; + typedef typename sat::literal_vector literal_vector; + + ba_solver& s; + literal m_true; + literal_vector m_lits; + + + ba_sort(ba_solver& s): s(s), m_true(null_literal) {} + literal mk_false(); + literal mk_true(); + literal mk_not(literal l); + literal fresh(char const*); + literal mk_max(literal l1, literal l2); + literal mk_min(literal l1, literal l2); + void mk_clause(unsigned n, literal const* lits); + std::ostream& pp(std::ostream& out, literal 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..70378d9c6 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,23 @@ namespace sat { return false; } + clause_offset clause::get_new_offset() const { + unsigned o1 = m_lits[0].index(); + if (sizeof(clause_offset) == 8) { + unsigned o2 = m_lits[1].index(); + return (clause_offset)o1 + (((clause_offset)o2) << 32); + } + return (clause_offset)o1; + } + + void clause::set_new_offset(clause_offset offset) { + m_lits[0] = to_literal(static_cast(offset)); + if (sizeof(offset) == 8) { + m_lits[1] = to_literal(static_cast(offset >> 32)); + } + } + + 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 +149,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 +208,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 +231,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..2c05cc52c 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(__GLUC__) || 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,10 @@ 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) { - v = it->var(); - } - } - } + for (clause* cp : clauses) + for (literal l : *cp) + if (l.var() > v) + v = l.var(); return v; } @@ -2693,8 +3254,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 +3329,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 +3350,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 +3360,7 @@ namespace sat { m_simplifier.reset_statistics(); m_asymm_branch.reset_statistics(); m_probing.reset_statistics(); + m_aux_stats.reset(); } // ----------------------- @@ -2802,33 +3370,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 +3424,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 +3441,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 +3474,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 +3552,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 +3581,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 +3607,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 +3627,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 +3661,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 +3677,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 +3702,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 +3713,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 +3763,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 +3775,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 +3873,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 +3935,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 +3978,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 +4016,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 +4025,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 +4063,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 +4076,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 +4097,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 +4136,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 +4151,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 +4170,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 +4196,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 +4215,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 +4225,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 +4254,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 +4295,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/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/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..605c447bd 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(); } @@ -525,6 +526,7 @@ void asserted_formulas::update_substitution(expr* n, proof* pr) { } } + /** \brief implement a Knuth-Bendix ordering on expressions. */ @@ -630,6 +632,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 3f7966a73..14a19f824 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -1799,6 +1799,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. @@ -1959,6 +1968,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. */ @@ -3374,6 +3392,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; @@ -3471,6 +3490,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 @@ -3504,7 +3524,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) { @@ -3513,9 +3533,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()); @@ -3532,6 +3553,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(); @@ -3837,6 +3863,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()); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index abfc8c499..32379c353 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -319,6 +319,7 @@ namespace smt { } #endif + clause_vector const& get_lemmas() const { return m_lemmas; } literal get_literal(expr * n) const; @@ -621,8 +622,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); @@ -647,6 +646,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 @@ -889,6 +896,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; @@ -1030,6 +1039,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 615cd2512..a6b881d10 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -583,7 +583,7 @@ namespace smt { case b_justification::CLAUSE: { clause * cls = j.get_clause(); out << "clause "; - if (cls) display_literals_verbose(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_internalizer.cpp b/src/smt/smt_internalizer.cpp index 3d999c3b7..c6672c2b8 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1345,6 +1345,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 a5f9e0c34..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,6 +88,10 @@ namespace smt { smt_solver * result = alloc(smt_solver, m, p, m_logic); smt::kernel::copy(m_context, result->m_context); + + 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); @@ -68,11 +99,13 @@ namespace smt { 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 { @@ -102,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); } @@ -180,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); } @@ -205,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); } @@ -218,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 944efe6ce..992a87dab 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -1079,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..1e5b35fbf 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()); @@ -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 == 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_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 6469ab065..a47302f5d 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -167,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); @@ -175,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); @@ -207,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()); @@ -278,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); @@ -301,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..fb1001f32 --- /dev/null +++ b/src/solver/parallel_tactic.cpp @@ -0,0 +1,758 @@ +/*++ +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 "tactic/portfolio/fd_solver.h" +#include "solver/parallel_tactic.h" +#include "solver/parallel_params.hpp" +#include "smt/tactic/smt_tactic.h" +#include "smt/smt_solver.h" +#include "sat/sat_solver/inc_sat_solver.h" +#include "sat/tactic/sat_tactic.h" + +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 f6e12275f..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; } 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..63d034374 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); @@ -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/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.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 c95a79ff7..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; @@ -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..0f2311233 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(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/sat_local_search.cpp b/src/test/sat_local_search.cpp new file mode 100644 index 000000000..1116c5420 --- /dev/null +++ b/src/test/sat_local_search.cpp @@ -0,0 +1,132 @@ +#include "sat/sat_local_search.h" +#include "sat/sat_solver.h" +#include "util/cancel_eh.h" +#include "util/scoped_ctrl_c.h" +#include "util/scoped_timer.h" + +static bool build_instance(char const * filename, sat::solver& s, sat::local_search& local_search) +{ + char line[16383]; + // for temperally storage + + std::ifstream infile(filename); + //if (infile == NULL) //linux + if (!infile) { + std::cout << "File not found " << filename << "\n"; + return false; + } + infile.getline(line, 16383); +#ifdef _WINDOWS + int cur_term; + int num_vars = 0, num_constraints = 0; + sscanf_s(line, "%d %d", &num_vars, &num_constraints); + //std::cout << "number of variables: " << num_vars << '\n'; + //std::cout << "number of constraints: " << num_constraints << '\n'; + + + unsigned_vector coefficients; + sat::literal_vector lits; + + // process objective function: + // read coefficents + infile >> 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..e3bf2d653 100644 --- a/src/test/sorting_network.cpp +++ b/src/test/sorting_network.cpp @@ -172,16 +172,61 @@ struct ast_ext2 { std::ostream& pp(std::ostream& out, literal lit) { return out << mk_pp(lit, m); } - literal fresh() { - return trail(m.mk_fresh_const("x", m.mk_bool_sort())); + literal fresh(char const* n) { + return trail(m.mk_fresh_const(n, m.mk_bool_sort())); } void mk_clause(unsigned n, literal 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/gparams.cpp b/src/util/gparams.cpp index 183525320..5ee49ef16 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -421,13 +421,15 @@ 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; 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/bound_propagator.h b/src/util/lp/bound_propagator.h new file mode 100644 index 000000000..128973c12 --- /dev/null +++ b/src/util/lp/bound_propagator.h @@ -0,0 +1,27 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Lev Nachmanson +*/ +#pragma once +#include "util/lp/lp_settings.h" +namespace lp { +class lar_solver; +class bound_propagator { + std::unordered_map m_improved_low_bounds; // these maps map a column index to the corresponding index in ibounds + std::unordered_map m_improved_upper_bounds; + lar_solver & m_lar_solver; +public: + vector m_ibounds; +public: + bound_propagator(lar_solver & ls); + column_type get_column_type(unsigned) const; + const impq & get_low_bound(unsigned) const; + const impq & get_upper_bound(unsigned) const; + void try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict); + virtual bool bound_is_interesting(unsigned vi, + lp::lconstraint_kind kind, + const rational & bval) {return true;} + unsigned number_of_found_bounds() const { return m_ibounds.size(); } + virtual void consume(mpq const& v, unsigned j) { std::cout << "doh\n"; } +}; +} diff --git a/src/util/lp/cut_solver.h b/src/util/lp/cut_solver.h new file mode 100644 index 000000000..18da0b88b --- /dev/null +++ b/src/util/lp/cut_solver.h @@ -0,0 +1,201 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Nikolaj Bjorner, Lev Nachmanson +*/ +#pragma once +#include "util/vector.h" +#include "util/trace.h" +#include "util/lp/lp_settings.h" +namespace lp { +template +class cut_solver { + + struct ineq { // we only have less or equal, which is enough for integral variables + mpq m_bound; + vector> m_term; + ineq(vector>& term, mpq bound):m_bound(bound),m_term(term) { + } + }; + + vector m_ineqs; + + enum class lbool { + l_false, // false + l_true, // true + l_undef // undef + }; + + enum class literal_type { + BOOL, + INEQ, + BOUND + }; + + struct literal { + literal_type m_tag; + bool m_sign; // true means the pointed inequality is negated, or bound is negated, or boolean value is negated + + unsigned m_id; + unsigned m_index_of_ineq; // index into m_ineqs + bool m_bool_val; // used if m_tag is equal to BOOL + mpq m_bound; // used if m_tag is BOUND + literal(bool sign, bool val): m_tag(literal_type::BOOL), + m_bool_val(val){ + } + literal(bool sign, unsigned index_of_ineq) : m_tag(literal_type::INEQ), m_index_of_ineq(index_of_ineq) {} + }; + + bool lhs_is_int(const vector> & lhs) const { + for (auto & p : lhs) + if (p.first.is_int() == false) return false; + return true; + } + + public: + void add_ineq(vector> & lhs, mpq rhs) { + lp_assert(lhs_is_int(lhs)); + lp_assert(rhs.is_int()); + m_ineqs.push_back(ineq(lhs, rhs)); + } + + + bool m_inconsistent; // tracks if state is consistent + unsigned m_scope_lvl; // tracks the number of case splits + + svector m_trail; + // backtracking state from the SAT solver: + struct scope { + unsigned m_trail_lim; // pointer into assignment stack + unsigned m_clauses_to_reinit_lim; // ignore for now + bool m_inconsistent; // really needed? + }; + + svector m_scopes; + + bool at_base_lvl() const { return m_scope_lvl == 0; } + + lbool check() { + init_search(); + propagate(); + while (true) { + lbool r = bounded_search(); + if (r != lbool::l_undef) + return r; + + restart(); + simplify_problem(); + if (check_inconsistent()) return lbool::l_false; + gc(); + } + } + + cut_solver() { + } + + void init_search() { + // TBD + // initialize data-structures + } + + void simplify_problem() { + // no-op + } + + void gc() { + // no-op + } + + void restart() { + // no-op for now + } + + bool check_inconsistent() { + // TBD + return false; + } + + lbool bounded_search() { + while (true) { + checkpoint(); + bool done = false; + while (!done) { + lbool is_sat = propagate_and_backjump_step(done); + if (is_sat != lbool::l_true) return is_sat; + } + + gc(); + + if (!decide()) { + lbool is_sat = final_check(); + if (is_sat != lbool::l_undef) { + return is_sat; + } + } + } + } + + void checkpoint() { + // check for cancelation + } + + void cleanup() { + } + + lbool propagate_and_backjump_step(bool& done) { + done = true; + propagate(); + if (!inconsistent()) + return lbool::l_true; + if (!resolve_conflict()) + return lbool::l_false; + if (at_base_lvl()) { + cleanup(); // cleaner may propagate frozen clauses + if (inconsistent()) { + TRACE("sat", tout << "conflict at level 0\n";); + return lbool::l_false; + } + gc(); + } + done = false; + return lbool::l_true; + } + + lbool final_check() { + // there are no more case splits, and all clauses are satisfied. + // prepare the model for external consumption. + return lbool::l_true; + } + + + bool resolve_conflict() { + while (true) { + bool r = resolve_conflict_core(); + // after pop, clauses are reinitialized, + // this may trigger another conflict. + if (!r) + return false; + if (!inconsistent()) + return true; + } + } + + bool resolve_conflict_core() { + // this is where the main action is. + return true; + } + + void propagate() { + // this is where the main action is. + } + + bool decide() { + // this is where the main action is. + // pick the next variable and bound or value on the variable. + // return false if all variables have been assigned. + return false; + } + + bool inconsistent() const { return m_inconsistent; } + +}; +} 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/int_solver.h b/src/util/lp/int_solver.h new file mode 100644 index 000000000..56ddf16fd --- /dev/null +++ b/src/util/lp/int_solver.h @@ -0,0 +1,157 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Lev Nachmanson +*/ +#pragma once +#include "util/lp/lp_settings.h" +#include "util/lp/static_matrix.h" +#include "util/lp/iterator_on_row.h" +#include "util/lp/int_set.h" +#include "util/lp/lar_term.h" +#include "util/lp/cut_solver.h" +#include "util/lp/lar_constraints.h" + +namespace lp { +class lar_solver; +template +struct lp_constraint; +enum class lia_move { + ok, + branch, + cut, + conflict, + continue_with_check, + give_up +}; + +struct explanation { + vector> m_explanation; + void push_justification(constraint_index j, const mpq& v) { + m_explanation.push_back(std::make_pair(v, j)); + } +}; + +class int_solver { +public: + // fields + lar_solver *m_lar_solver; + int_set m_old_values_set; + vector m_old_values_data; + unsigned m_branch_cut_counter; + + // methods + int_solver(lar_solver* lp); + int_set& inf_int_set(); + const int_set& inf_int_set() const; + // main function to check that solution provided by lar_solver is valid for integral values, + // or provide a way of how it can be adjusted. + lia_move check(lar_term& t, mpq& k, explanation& ex); + bool move_non_basic_column_to_bounds(unsigned j); + lia_move check_wrapper(lar_term& t, mpq& k, explanation& ex); +private: + + // how to tighten bounds for integer variables. + + bool gcd_test_for_row(static_matrix> & A, unsigned i, explanation &); + + // gcd test + // 5*x + 3*y + 6*z = 5 + // suppose x is fixed at 2. + // so we have 10 + 3(y + 2z) = 5 + // 5 = -3(y + 2z) + // this is unsolvable because 5/3 is not an integer. + // so we create a lemma that rules out this condition. + // + bool gcd_test(explanation & ); // returns false in case of failure. Creates a theory lemma in case of failure. + + // create goromy cuts + // either creates a conflict or a bound. + + // branch and bound: + // decide what to branch and bound on + // creates a fresh inequality. + + bool branch(const lp_constraint & new_inequality); + bool ext_gcd_test(iterator_on_row & it, + mpq const & least_coeff, + mpq const & lcm_den, + mpq const & consts, + explanation & ex); + void fill_explanation_from_fixed_columns(iterator_on_row & it, explanation &); + void add_to_explanation_from_fixed_or_boxed_column(unsigned j, explanation &); + void patch_int_infeasible_non_basic_column(unsigned j); + void patch_int_infeasible_nbasic_columns(); + bool get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m); + linear_combination_iterator * get_column_iterator(unsigned j); + const impq & low_bound(unsigned j) const; + const impq & upper_bound(unsigned j) const; + bool is_int(unsigned j) const; + bool is_real(unsigned j) const; + bool is_base(unsigned j) const; + bool is_boxed(unsigned j) const; + bool is_fixed(unsigned j) const; + bool is_free(unsigned j) const; + bool value_is_int(unsigned j) const; + void set_value_for_nbasic_column(unsigned j, const impq & new_val); + void set_value_for_nbasic_column_ignore_old_values(unsigned j, const impq & new_val); + bool non_basic_columns_are_at_bounds() const; + void failed(); + bool is_feasible() const; + const impq & get_value(unsigned j) const; + void display_column(std::ostream & out, unsigned j) const; + bool inf_int_set_is_correct() const; + void update_column_in_int_inf_set(unsigned j); + bool column_is_int_inf(unsigned j) const; + void trace_inf_rows() const; + int find_inf_int_base_column(); + int find_inf_int_boxed_base_column_with_smallest_range(); + lp_settings& settings(); + bool move_non_basic_columns_to_bounds(); + void branch_infeasible_int_var(unsigned); + lia_move mk_gomory_cut(lar_term& t, mpq& k,explanation & ex, unsigned inf_col, linear_combination_iterator& iter); + lia_move report_conflict_from_gomory_cut(mpq & k); + void adjust_term_and_k_for_some_ints_case_gomory(lar_term& t, mpq& k, mpq& lcm_den); + void init_check_data(); + bool constrain_free_vars(linear_combination_iterator * r); + lia_move proceed_with_gomory_cut(lar_term& t, mpq& k, explanation& ex, unsigned j); + int find_free_var_in_gomory_row(linear_combination_iterator& iter); + bool is_gomory_cut_target(linear_combination_iterator &iter); + bool at_bound(unsigned j) const; + bool at_low(unsigned j) const; + bool at_upper(unsigned j) const; + bool has_low(unsigned j) const; + bool has_upper(unsigned j) const; + unsigned row_of_basic_column(unsigned j) const; + inline static bool is_rational(const impq & n) { + return is_zero(n.y); + } + + inline static + mpq fractional_part(const impq & n) { + lp_assert(is_rational(n)); + return n.x - floor(n.x); + } + void real_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, lar_term& t, explanation & ex, unsigned inf_column); + void int_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, lar_term& t, explanation& ex, mpq & lcm_den, unsigned inf_column); + constraint_index column_upper_bound_constraint(unsigned j) const; + constraint_index column_low_bound_constraint(unsigned j) const; + void display_row_info(std::ostream & out, unsigned row_index) const; + void gomory_cut_adjust_t_and_k(vector> & pol, lar_term & t, mpq &k, bool num_ints, mpq &lcm_den); + bool current_solution_is_inf_on_cut(const lar_term& t, const mpq& k) const; +public: + bool shift_var(unsigned j, unsigned range); +private: + unsigned random(); + bool has_inf_int() const; + lia_move create_branch_on_column(int j, lar_term& t, mpq& k, bool free_column) const; +public: + void display_inf_or_int_inf_columns(std::ostream & out) const; + template + void fill_cut_solver(cut_solver & cs); + template + void fill_cut_solver_for_constraint(const lar_base_constraint*, cut_solver& ); + template + void get_int_coeffs_from_constraint(const lar_base_constraint* c, vector>& coeff, T & rs); + +}; +} 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/nra_solver.cpp2 b/src/util/lp/nra_solver.cpp2 new file mode 100644 index 000000000..327d2b70f --- /dev/null +++ b/src/util/lp/nra_solver.cpp2 @@ -0,0 +1,264 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Nikolaj Bjorner +*/ + +#include "util/lp/lar_solver.h" +#include "util/lp/nra_solver.h" +#include "nlsat/nlsat_solver.h" +#include "math/polynomial/polynomial.h" +#include "math/polynomial/algebraic_numbers.h" +#include "util/map.h" + + +namespace nra { + + struct mon_eq { + mon_eq(lp::var_index v, unsigned sz, lp::var_index const* vs): + m_v(v), m_vs(sz, vs) {} + lp::var_index m_v; + svector m_vs; + }; + + struct solver::imp { + lp::lar_solver& s; + reslimit& m_limit; + params_ref m_params; + u_map m_lp2nl; // map from lar_solver variables to nlsat::solver variables + scoped_ptr m_nlsat; + vector m_monomials; + unsigned_vector m_monomials_lim; + mutable std::unordered_map m_variable_values; // current model + + imp(lp::lar_solver& s, reslimit& lim, params_ref const& p): + s(s), + m_limit(lim), + m_params(p) { + } + + bool need_check() { + return !m_monomials.empty() && !check_assignments(); + } + + void add(lp::var_index v, unsigned sz, lp::var_index const* vs) { + m_monomials.push_back(mon_eq(v, sz, vs)); + } + + void push() { + m_monomials_lim.push_back(m_monomials.size()); + } + + void pop(unsigned n) { + if (n == 0) return; + m_monomials.shrink(m_monomials_lim[m_monomials_lim.size() - n]); + m_monomials_lim.shrink(m_monomials_lim.size() - n); + } + + /* + \brief Check if polynomials are well defined. + multiply values for vs and check if they are equal to value for v. + epsilon has been computed. + */ + bool check_assignment(mon_eq const& m) const { + rational r1 = m_variable_values[m.m_v]; + rational r2(1); + for (auto w : m.m_vs) { + r2 *= m_variable_values[w]; + } + return r1 == r2; + } + + bool check_assignments() const { + s.get_model(m_variable_values); + for (auto const& m : m_monomials) { + if (!check_assignment(m)) return false; + } + return true; + } + + /** + \brief one-shot nlsat check. + A one shot checker is the least functionality that can + enable non-linear reasoning. + In addition to checking satisfiability we would also need + to identify equalities in the model that should be assumed + with the remaining solver. + + TBD: use partial model from lra_solver to prime the state of nlsat_solver. + TBD: explore more incremental ways of applying nlsat (using assumptions) + */ + lbool check(lp::explanation_t& ex) { + SASSERT(need_check()); + m_nlsat = alloc(nlsat::solver, m_limit, m_params); + m_lp2nl.reset(); + vector core; + + // add linear inequalities from lra_solver + for (unsigned i = 0; i < s.constraint_count(); ++i) { + add_constraint(i); + } + + // add polynomial definitions. + for (auto const& m : m_monomials) { + add_monomial_eq(m); + } + // TBD: add variable bounds? + + lbool r = m_nlsat->check(); + TRACE("arith", m_nlsat->display(tout << r << "\n");); + switch (r) { + case l_true: + break; + case l_false: + ex.reset(); + m_nlsat->get_core(core); + for (auto c : core) { + unsigned idx = static_cast(static_cast(c) - this); + ex.push_back(std::pair(rational(1), idx)); + TRACE("arith", tout << "ex: " << idx << "\n";); + } + break; + + case l_undef: + break; + } + return r; + } + + void add_monomial_eq(mon_eq const& m) { + polynomial::manager& pm = m_nlsat->pm(); + svector vars; + for (auto v : m.m_vs) { + vars.push_back(lp2nl(v)); + } + polynomial::monomial_ref m1(pm.mk_monomial(vars.size(), vars.c_ptr()), pm); + polynomial::monomial_ref m2(pm.mk_monomial(lp2nl(m.m_v), 1), pm); + polynomial::monomial* mls[2] = { m1, m2 }; + polynomial::scoped_numeral_vector coeffs(pm.m()); + coeffs.push_back(mpz(1)); + coeffs.push_back(mpz(-1)); + polynomial::polynomial_ref p(pm.mk_polynomial(2, coeffs.c_ptr(), mls), pm); + polynomial::polynomial* ps[1] = { p }; + bool even[1] = { false }; + nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, even); + m_nlsat->mk_clause(1, &lit, 0); + } + + void add_constraint(unsigned idx) { + auto& c = s.get_constraint(idx); + auto& pm = m_nlsat->pm(); + auto k = c.m_kind; + auto rhs = c.m_right_side; + auto lhs = c.get_left_side_coefficients(); + auto sz = lhs.size(); + svector vars; + rational den = denominator(rhs); + for (auto kv : lhs) { + vars.push_back(lp2nl(kv.second)); + den = lcm(den, denominator(kv.first)); + } + vector coeffs; + for (auto kv : lhs) { + coeffs.push_back(den * kv.first); + } + rhs *= den; + polynomial::polynomial_ref p(pm.mk_linear(sz, coeffs.c_ptr(), vars.c_ptr(), -rhs), pm); + polynomial::polynomial* ps[1] = { p }; + bool is_even[1] = { false }; + nlsat::literal lit; + nlsat::assumption a = this + idx; + switch (k) { + case lp::lconstraint_kind::LE: + lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); + break; + case lp::lconstraint_kind::GE: + lit = ~m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); + break; + case lp::lconstraint_kind::LT: + lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::LT, 1, ps, is_even); + break; + case lp::lconstraint_kind::GT: + lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::GT, 1, ps, is_even); + break; + case lp::lconstraint_kind::EQ: + lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, is_even); + break; + } + m_nlsat->mk_clause(1, &lit, a); + } + + bool is_int(lp::var_index v) { + return s.var_is_int(v); + } + + + polynomial::var lp2nl(lp::var_index v) { + polynomial::var r; + if (!m_lp2nl.find(v, r)) { + r = m_nlsat->mk_var(is_int(v)); + m_lp2nl.insert(v, r); + } + return r; + } + + nlsat::anum const& value(lp::var_index v) const { + return m_nlsat->value(m_lp2nl.find(v)); + } + + nlsat::anum_manager& am() { + return m_nlsat->am(); + } + + std::ostream& display(std::ostream& out) const { + for (auto m : m_monomials) { + out << "v" << m.m_v << " = "; + for (auto v : m.m_vs) { + out << "v" << v << " "; + } + out << "\n"; + } + return out; + } + }; + + solver::solver(lp::lar_solver& s, reslimit& lim, params_ref const& p) { + m_imp = alloc(imp, s, lim, p); + } + + solver::~solver() { + dealloc(m_imp); + } + + void solver::add_monomial(lp::var_index v, unsigned sz, lp::var_index const* vs) { + m_imp->add(v, sz, vs); + } + + lbool solver::check(lp::explanation_t& ex) { + return m_imp->check(ex); + } + + bool solver::need_check() { + return m_imp->need_check(); + } + + void solver::push() { + m_imp->push(); + } + + void solver::pop(unsigned n) { + m_imp->pop(n); + } + + std::ostream& solver::display(std::ostream& out) const { + return m_imp->display(out); + } + + nlsat::anum const& solver::value(lp::var_index v) const { + return m_imp->value(v); + } + + nlsat::anum_manager& solver::am() { + return m_imp->am(); + } + +} diff --git a/src/util/lp/nra_solver.h b/src/util/lp/nra_solver.h new file mode 100644 index 000000000..70e614e91 --- /dev/null +++ b/src/util/lp/nra_solver.h @@ -0,0 +1,70 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Nikolaj Bjorner +*/ + +#pragma once +#include "util/vector.h" +#include "util/lp/lp_settings.h" +#include "util/rlimit.h" +#include "util/params.h" +#include "nlsat/nlsat_solver.h" + +namespace lp { + class lar_solver; +} + + +namespace nra { + + + + class solver { + struct imp; + imp* m_imp; + + public: + + solver(lp::lar_solver& s, reslimit& lim, params_ref const& p = params_ref()); + + ~solver(); + + /* + \brief Add a definition v = vs[0]*vs[1]*...*vs[sz-1] + The variable v is equal to the product of variables vs. + */ + void add_monomial(lp::var_index v, unsigned sz, lp::var_index const* vs); + + /* + \brief Check feasiblity of linear constraints augmented by polynomial definitions + that are added. + */ + lbool check(lp::explanation_t& ex); + + /* + \brief determine whether nra check is needed. + */ + bool need_check(); + + /* + \brief Access model. + */ + nlsat::anum const& value(lp::var_index v) const; + + nlsat::anum_manager& am(); + + /* + \brief push and pop scope. + Monomial definitions are retraced when popping scope. + */ + void push(); + + void pop(unsigned n); + + /* + \brief display state + */ + std::ostream& display(std::ostream& out) const; + + }; +} 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 6bb6678cc..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(); @@ -347,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(); 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/sorting_network.h b/src/util/sorting_network.h index 17808ea48..38c595125 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) @@ -121,6 +143,7 @@ Notes: class psort_nw { typedef typename psort_expr::literal literal; typedef typename psort_expr::literal_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;