diff --git a/RELEASE_NOTES b/RELEASE_NOTES index e55b329db..3337f098b 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -27,6 +27,24 @@ Version 4.8.0 Use sat.acce=true to enable the full repertoire of inprocessing methods. By default, clauses that are "eliminated" by acce are tagged as lemmas (redundant) and are garbage collected if their glue level is high. - Substantial overhaul of the spacer horn clause engine. + - Added basic features to support Lambda bindings. + - Added model compression to eliminate local function definitions in models when + inlining them does not incur substantial overhead. The old behavior, where models are left + uncompressed can be replayed by setting the top-level parameter model_compress=false. + - Integration of a new solver for linear integer arithmetic and mixed linear integer arithmetic by Lev Nachmanson. + It incorporates several improvements to QF_LIA solving based on + . using a better LP engine, which is already the foundation for QF_LRA + . including cuts based on Hermite Normal Form (thanks to approaches described + in "cuts from proofs" and "cutting the mix"). + . extracting integer solutions from LP solutions by tightening bounds selectively. + We use a generalization of Bromberger and Weidenbach that allows avoiding selected + bounds tighthenings (https://easychair.org/publications/paper/qGfG). + It solves significantly more problems in the QF_LIA category and may at this point also + be the best solver for your problem as well. + The new solver is enabled only for select SMT-LIB logics. These include QF_LIA, QF_IDL, and QF_UFLIA. + Other theories (still) use the legacy solver for arithmetic. You can enable the new solver by setting + the parameter smt.arith.solver=6 to give it a spin. + - Removed features: - interpolation API diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index 640e3a10b..704756072 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -115,7 +115,6 @@ void prove_example1() { */ void prove_example2() { std::cout << "prove_example1\n"; - context c; expr x = c.int_const("x"); expr y = c.int_const("y"); @@ -139,6 +138,7 @@ void prove_example2() { s.reset(); s.add(!conjecture2); std::cout << "conjecture 2:\n" << conjecture2 << "\n"; + if (s.check() == unsat) { std::cout << "proved" << "\n"; } diff --git a/examples/c/test_capi.c b/examples/c/test_capi.c index a6937f293..14e403826 100644 --- a/examples/c/test_capi.c +++ b/examples/c/test_capi.c @@ -1754,7 +1754,6 @@ void parser_example5() { err: printf("Z3 error: %s.\n", Z3_get_error_msg(ctx, e)); if (ctx != NULL) { - printf("Error message: '%s'.\n",Z3_get_parser_error(ctx)); del_solver(ctx, s); Z3_del_context(ctx); } diff --git a/examples/dotnet/Program.cs b/examples/dotnet/Program.cs index 06cc66150..230aacf6f 100644 --- a/examples/dotnet/Program.cs +++ b/examples/dotnet/Program.cs @@ -2106,6 +2106,7 @@ namespace test_mapi Console.WriteLine("OK, model: {0}", s.Model.ToString()); } + public static void TranslationExample() { Context ctx1 = new Context(); diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 0ad9a8d10..d5515fbc5 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -1482,7 +1482,7 @@ class JavaExample BoolExpr ca = commAxiom(ctx, g); BoolExpr thm = ctx.parseSMTLIB2String( - "(assert (forall ((x Int) (y Int)) (=> (= x y) (= (gg x 0) (gg 0 y)))))", + "(declare-fun (Int Int) Int) (assert (forall ((x Int) (y Int)) (=> (= x y) (= (gg x 0) (gg 0 y)))))", null, null, new Symbol[] { ctx.mkSymbol("gg") }, new FuncDecl[] { g })[0]; System.out.println("formula: " + thm); @@ -2303,7 +2303,7 @@ class JavaExample p.simplifierExample(ctx); p.finiteDomainExample(ctx); p.floatingPointExample1(ctx); - p.floatingPointExample2(ctx); + // core dumps: p.floatingPointExample2(ctx); } { // These examples need proof generation turned on. @@ -2314,7 +2314,7 @@ class JavaExample p.proveExample2(ctx); p.arrayExample2(ctx); p.tupleExample(ctx); - p.parserExample3(ctx); + // throws p.parserExample3(ctx); p.enumExample(ctx); p.listExample(ctx); p.treeExample(ctx); diff --git a/scripts/mk_genfile_common.py b/scripts/mk_genfile_common.py index 2e0bbe2ca..7fa6d4041 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -718,17 +718,11 @@ def mk_install_tactic_cpp_internal(h_files_full_path, path): fullname, line)) raise e # First pass will just generate the tactic factories - idx = 0 - for data in ADD_TACTIC_DATA: - fout.write('MK_SIMPLE_TACTIC_FACTORY(__Z3_local_factory_%s, %s);\n' % (idx, data[2])) - idx = idx + 1 - fout.write('#define ADD_TACTIC_CMD(NAME, DESCR, FACTORY) ctx.insert(alloc(tactic_cmd, symbol(NAME), DESCR, alloc(FACTORY)))\n') + fout.write('#define ADD_TACTIC_CMD(NAME, DESCR, CODE) ctx.insert(alloc(tactic_cmd, symbol(NAME), DESCR, [](ast_manager &m, const params_ref &p) { return CODE; }))\n') fout.write('#define ADD_PROBE(NAME, DESCR, PROBE) ctx.insert(alloc(probe_info, symbol(NAME), DESCR, PROBE))\n') fout.write('void install_tactics(tactic_manager & ctx) {\n') - idx = 0 for data in ADD_TACTIC_DATA: - fout.write(' ADD_TACTIC_CMD("%s", "%s", __Z3_local_factory_%s);\n' % (data[0], data[1], idx)) - idx = idx + 1 + fout.write(' ADD_TACTIC_CMD("%s", "%s", %s);\n' % data) for data in ADD_PROBE_DATA: fout.write(' ADD_PROBE("%s", "%s", %s);\n' % data) fout.write('}\n') diff --git a/scripts/update_api.py b/scripts/update_api.py index 78fad45be..b082c96e1 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -73,7 +73,7 @@ Type2PyStr = { VOID_PTR : 'ctypes.c_void_p', INT : 'ctypes.c_int', UINT : 'ctype # Mapping to .NET types Type2Dotnet = { VOID : 'void', VOID_PTR : 'IntPtr', INT : 'int', UINT : 'uint', INT64 : 'Int64', UINT64 : 'UInt64', DOUBLE : 'double', - FLOAT : 'float', STRING : 'string', STRING_PTR : 'byte**', BOOL : 'int', SYMBOL : 'IntPtr', + FLOAT : 'float', STRING : 'string', STRING_PTR : 'byte**', BOOL : 'byte', SYMBOL : 'IntPtr', PRINT_MODE : 'uint', ERROR_CODE : 'uint' } # Mapping to Java types diff --git a/src/ackermannization/ackermannize_bv_tactic.cpp b/src/ackermannization/ackermannize_bv_tactic.cpp index 3ffee518e..98417cc7b 100644 --- a/src/ackermannization/ackermannize_bv_tactic.cpp +++ b/src/ackermannization/ackermannize_bv_tactic.cpp @@ -35,7 +35,7 @@ public: fail_if_proof_generation("ackermannize", g); TRACE("ackermannize", g->display(tout << "in\n");); - expr_ref_vector flas(m); + ptr_vector flas; const unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) flas.push_back(g->form(i)); lackr lackr(m, m_p, m_st, flas, nullptr); diff --git a/src/ackermannization/ackr_model_converter.cpp b/src/ackermannization/ackr_model_converter.cpp index 9e87a1a69..c5e04ab23 100644 --- a/src/ackermannization/ackr_model_converter.cpp +++ b/src/ackermannization/ackr_model_converter.cpp @@ -134,7 +134,7 @@ void ackr_model_converter::add_entry(model_evaluator & evaluator, info->abstract(arg, aarg); expr_ref arg_value(m); evaluator(aarg, arg_value); - args.push_back(arg_value); + args.push_back(std::move(arg_value)); } if (fi->get_entry(args.c_ptr()) == nullptr) { TRACE("ackr_model", diff --git a/src/ackermannization/lackr.cpp b/src/ackermannization/lackr.cpp index b739af9d3..8c18df7b2 100644 --- a/src/ackermannization/lackr.cpp +++ b/src/ackermannization/lackr.cpp @@ -23,8 +23,8 @@ #include "ast/for_each_expr.h" #include "model/model_smt2_pp.h" -lackr::lackr(ast_manager& m, params_ref p, lackr_stats& st, expr_ref_vector& formulas, - solver * uffree_solver) +lackr::lackr(ast_manager& m, const params_ref& p, lackr_stats& st, + const ptr_vector& formulas, solver * uffree_solver) : m_m(m) , m_p(p) , m_formulas(formulas) @@ -173,11 +173,10 @@ void lackr::abstract() { } m_info->seal(); // perform abstraction of the formulas - const unsigned sz = m_formulas.size(); - for (unsigned i = 0; i < sz; ++i) { + for (expr * f : m_formulas) { expr_ref a(m_m); - m_info->abstract(m_formulas.get(i), a); - m_abstr.push_back(a); + m_info->abstract(f, a); + m_abstr.push_back(std::move(a)); } } @@ -249,13 +248,9 @@ lbool lackr::lazy() { // Collect all uninterpreted terms, skipping 0-arity. // bool lackr::collect_terms() { - ptr_vector stack; + ptr_vector stack = m_formulas; expr * curr; expr_mark visited; - for(unsigned i = 0; i < m_formulas.size(); ++i) { - stack.push_back(m_formulas.get(i)); - TRACE("lackr", tout << "infla: " <& formulas, solver * uffree_solver); ~lackr(); void updt_params(params_ref const & _p); @@ -82,7 +82,7 @@ class lackr { typedef ackr_helper::app_set app_set; ast_manager& m_m; params_ref m_p; - expr_ref_vector m_formulas; + const ptr_vector& m_formulas; expr_ref_vector m_abstr; fun2terms_map m_fun2terms; ackr_info_ref m_info; diff --git a/src/api/api_algebraic.cpp b/src/api/api_algebraic.cpp index 47d91209e..1bb1b6a51 100644 --- a/src/api/api_algebraic.cpp +++ b/src/api/api_algebraic.cpp @@ -29,14 +29,14 @@ Notes: #define CHECK_IS_ALGEBRAIC(ARG, RET) { \ if (!Z3_algebraic_is_value_core(c, ARG)) { \ - SET_ERROR_CODE(Z3_INVALID_ARG); \ + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); \ return RET; \ } \ } #define CHECK_IS_ALGEBRAIC_X(ARG, RET) { \ if (!Z3_algebraic_is_value_core(c, ARG)) { \ - SET_ERROR_CODE(Z3_INVALID_ARG); \ + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); \ RETURN_Z3(RET); \ } \ } @@ -196,7 +196,7 @@ extern "C" { CHECK_IS_ALGEBRAIC_X(b, nullptr); if ((is_rational(c, b) && get_rational(c, b).is_zero()) || (!is_rational(c, b) && am(c).is_zero(get_irrational(c, b)))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } BIN_OP(/,div); @@ -211,7 +211,7 @@ extern "C" { if (k % 2 == 0) { if ((is_rational(c, a) && get_rational(c, a).is_neg()) || (!is_rational(c, a) && am(c).is_neg(get_irrational(c, a)))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } @@ -360,13 +360,13 @@ extern "C" { expr2polynomial converter(mk_c(c)->m(), pm, nullptr, true); if (!converter.to_polynomial(to_expr(p), _p, d) || static_cast(max_var(_p)) >= n + 1) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } algebraic_numbers::manager & _am = am(c); scoped_anum_vector as(_am); if (!to_anum_vector(c, n, a, as)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } scoped_anum_vector roots(_am); @@ -396,13 +396,13 @@ extern "C" { expr2polynomial converter(mk_c(c)->m(), pm, nullptr, true); if (!converter.to_polynomial(to_expr(p), _p, d) || static_cast(max_var(_p)) >= n) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } algebraic_numbers::manager & _am = am(c); scoped_anum_vector as(_am); if (!to_anum_vector(c, n, a, as)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } { diff --git a/src/api/api_arith.cpp b/src/api/api_arith.cpp index 9bb236fe2..f46f56ef2 100644 --- a/src/api/api_arith.cpp +++ b/src/api/api_arith.cpp @@ -51,7 +51,7 @@ extern "C" { LOG_Z3_mk_real(c, num, den); RESET_ERROR_CODE(); if (den == 0) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } sort* s = mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT); @@ -97,7 +97,7 @@ extern "C" { LOG_Z3_mk_sub(c, num_args, args); RESET_ERROR_CODE(); if (num_args == 0) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } expr* r = to_expr(args[0]); @@ -120,12 +120,8 @@ extern "C" { } Z3_bool Z3_API Z3_is_algebraic_number(Z3_context c, Z3_ast a) { - Z3_TRY; LOG_Z3_is_algebraic_number(c, a); - RESET_ERROR_CODE(); - expr * e = to_expr(a); - return mk_c(c)->autil().is_irrational_algebraic_numeral(e); - Z3_CATCH_RETURN(Z3_FALSE); + return mk_c(c)->autil().is_irrational_algebraic_numeral(to_expr(a)) ? Z3_TRUE : Z3_FALSE; } Z3_ast Z3_API Z3_get_algebraic_number_lower(Z3_context c, Z3_ast a, unsigned precision) { @@ -133,7 +129,7 @@ extern "C" { LOG_Z3_get_algebraic_number_lower(c, a, precision); RESET_ERROR_CODE(); if (!Z3_is_algebraic_number(c, a)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } expr * e = to_expr(a); @@ -151,7 +147,7 @@ extern "C" { LOG_Z3_get_algebraic_number_upper(c, a, precision); RESET_ERROR_CODE(); if (!Z3_is_algebraic_number(c, a)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } expr * e = to_expr(a); @@ -171,7 +167,7 @@ extern "C" { rational val; ast * _a = to_ast(a); if (!is_expr(_a) || !mk_c(c)->autil().is_numeral(to_expr(_a), val)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } expr * r = mk_c(c)->autil().mk_numeral(numerator(val), true); @@ -187,7 +183,7 @@ extern "C" { rational val; ast * _a = to_ast(a); if (!is_expr(_a) || !mk_c(c)->autil().is_numeral(to_expr(_a), val)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } expr * r = mk_c(c)->autil().mk_numeral(denominator(val), true); diff --git a/src/api/api_array.cpp b/src/api/api_array.cpp index 61c37ec3e..a9f5d7d70 100644 --- a/src/api/api_array.cpp +++ b/src/api/api_array.cpp @@ -47,6 +47,7 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i) { Z3_TRY; LOG_Z3_mk_select(c, a, i); @@ -57,7 +58,7 @@ extern "C" { sort * a_ty = m.get_sort(_a); sort * i_ty = m.get_sort(_i); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } sort * domain[2] = {a_ty, i_ty}; @@ -80,7 +81,7 @@ extern "C" { sort * a_ty = m.get_sort(_a); // sort * i_ty = m.get_sort(_i); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } ptr_vector domain; @@ -99,6 +100,7 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v) { Z3_TRY; LOG_Z3_mk_store(c, a, i, v); @@ -111,7 +113,7 @@ extern "C" { sort * i_ty = m.get_sort(_i); sort * v_ty = m.get_sort(_v); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } sort * domain[3] = {a_ty, i_ty, v_ty}; @@ -134,7 +136,7 @@ extern "C" { sort * a_ty = m.get_sort(_a); sort * v_ty = m.get_sort(_v); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } ptr_vector domain; @@ -155,12 +157,13 @@ extern "C" { Z3_CATCH_RETURN(nullptr); } + Z3_ast Z3_API Z3_mk_map(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast const* args) { Z3_TRY; LOG_Z3_mk_map(c, f, n, args); RESET_ERROR_CODE(); if (n == 0) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ast_manager & m = mk_c(c)->m(); @@ -295,7 +298,7 @@ extern "C" { Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(0).get_ast()); RETURN_Z3(r); } - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); Z3_CATCH_RETURN(nullptr); } @@ -311,7 +314,7 @@ extern "C" { Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(n-1).get_ast()); RETURN_Z3(r); } - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); Z3_CATCH_RETURN(nullptr); } diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 34168f1f4..5828b0fcd 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -48,7 +48,7 @@ extern "C" { LOG_Z3_mk_int_symbol(c, i); RESET_ERROR_CODE(); if (i < 0 || (size_t)i >= (SIZE_MAX >> PTR_ALIGNMENT)) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return nullptr; } Z3_symbol result = of_symbol(symbol(i)); @@ -281,7 +281,7 @@ extern "C" { if (_s.is_numerical()) { return _s.get_num(); } - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return -1; Z3_CATCH_RETURN(-1); } @@ -355,7 +355,7 @@ extern "C" { LOG_Z3_get_app_decl(c, a); RESET_ERROR_CODE(); if (!is_app(reinterpret_cast(a))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(of_func_decl(to_app(a)->get_decl())); @@ -371,11 +371,11 @@ extern "C" { LOG_Z3_get_app_arg(c, a, i); RESET_ERROR_CODE(); if (!is_app(reinterpret_cast(a))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } if (i >= to_app(a)->get_num_args()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(of_ast(to_app(a)->get_arg(i))); @@ -398,7 +398,7 @@ extern "C" { LOG_Z3_get_decl_parameter_kind(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return Z3_PARAMETER_INT; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; @@ -430,12 +430,12 @@ extern "C" { LOG_Z3_get_decl_int_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return 0; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_int()) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return p.get_int(); @@ -447,12 +447,12 @@ extern "C" { LOG_Z3_get_decl_double_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return 0; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_double()) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return p.get_double(); @@ -464,12 +464,12 @@ extern "C" { LOG_Z3_get_decl_symbol_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return nullptr; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_symbol()) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } return of_symbol(p.get_symbol()); @@ -481,12 +481,12 @@ extern "C" { LOG_Z3_get_decl_sort_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast() || !is_sort(p.get_ast())) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(of_sort(to_sort(p.get_ast()))); @@ -498,12 +498,12 @@ extern "C" { LOG_Z3_get_decl_ast_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast()) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(of_ast(p.get_ast())); @@ -515,12 +515,12 @@ extern "C" { LOG_Z3_get_decl_func_decl_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast() || !is_func_decl(p.get_ast())) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(of_func_decl(to_func_decl(p.get_ast()))); @@ -532,12 +532,12 @@ extern "C" { LOG_Z3_get_decl_rational_parameter(c, d, idx); RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return ""; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_rational()) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return ""; } return mk_c(c)->mk_external_string(p.get_rational().to_string()); @@ -584,7 +584,7 @@ extern "C" { LOG_Z3_get_domain(c, d, i); RESET_ERROR_CODE(); if (i >= to_func_decl(d)->get_arity()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } Z3_sort r = of_sort(to_func_decl(d)->get_domain(i)); @@ -740,7 +740,7 @@ extern "C" { case AST_APP: { app* e = to_app(a); if (e->get_num_args() != num_args) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); } else { a = m.mk_app(e->get_decl(), num_args, args); @@ -749,7 +749,7 @@ extern "C" { } case AST_QUANTIFIER: { if (num_args != 1) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); } else { a = m.update_quantifier(to_quantifier(a), args[0]); @@ -779,7 +779,7 @@ extern "C" { expr * r = nullptr; for (unsigned i = 0; i < num_exprs; i++) { if (m.get_sort(from[i]) != m.get_sort(to[i])) { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(of_expr(nullptr)); } SASSERT(from[i]->get_ref_count() > 0); @@ -808,8 +808,7 @@ extern "C" { expr * a = to_expr(_a); expr * const * to = to_exprs(_to); var_subst subst(m, false); - expr_ref new_a(m); - subst(a, num_exprs, to, new_a); + expr_ref new_a = subst(a, num_exprs, to); mk_c(c)->save_ast_trail(new_a); RETURN_Z3(of_expr(new_a.get())); Z3_CATCH_RETURN(nullptr); @@ -910,6 +909,7 @@ extern "C" { case PR_TRANSITIVITY_STAR: return Z3_OP_PR_TRANSITIVITY_STAR; case PR_MONOTONICITY: return Z3_OP_PR_MONOTONICITY; case PR_QUANT_INTRO: return Z3_OP_PR_QUANT_INTRO; + case PR_BIND: return Z3_OP_PR_BIND; case PR_DISTRIBUTIVITY: return Z3_OP_PR_DISTRIBUTIVITY; case PR_AND_ELIM: return Z3_OP_PR_AND_ELIM; case PR_NOT_OR_ELIM: return Z3_OP_PR_NOT_OR_ELIM; @@ -1212,14 +1212,14 @@ extern "C" { RESET_ERROR_CODE(); ast* _a = reinterpret_cast(a); if (!_a || _a->get_kind() != AST_VAR) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } var* va = to_var(_a); if (va) { return va->get_idx(); } - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; Z3_CATCH_RETURN(0); } @@ -1230,7 +1230,7 @@ extern "C" { RESET_ERROR_CODE(); CHECK_VALID_AST(a, nullptr); if (c == target) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } SASSERT(mk_c(c)->m().contains(to_ast(a))); diff --git a/src/api/api_ast_map.cpp b/src/api/api_ast_map.cpp index 17dd086b5..44cadc691 100644 --- a/src/api/api_ast_map.cpp +++ b/src/api/api_ast_map.cpp @@ -71,7 +71,7 @@ extern "C" { RESET_ERROR_CODE(); obj_map::obj_map_entry * entry = to_ast_map_ref(m).find_core(to_ast(k)); if (entry == nullptr) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } else { diff --git a/src/api/api_ast_vector.cpp b/src/api/api_ast_vector.cpp index ae5adecea..5fe19a7d5 100644 --- a/src/api/api_ast_vector.cpp +++ b/src/api/api_ast_vector.cpp @@ -65,7 +65,7 @@ extern "C" { LOG_Z3_ast_vector_get(c, v, i); RESET_ERROR_CODE(); if (i >= to_ast_vector_ref(v).size()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } // Remark: Don't need to invoke save_object. @@ -79,7 +79,7 @@ extern "C" { LOG_Z3_ast_vector_set(c, v, i, a); RESET_ERROR_CODE(); if (i >= to_ast_vector_ref(v).size()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return; } to_ast_vector_ref(v).set(i, to_ast(a)); @@ -107,8 +107,7 @@ extern "C" { LOG_Z3_ast_vector_translate(c, v, t); RESET_ERROR_CODE(); if (c == t) { - SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(nullptr); + RETURN_Z3(v); } ast_translation translator(mk_c(c)->m(), mk_c(t)->m()); Z3_ast_vector_ref * new_v = alloc(Z3_ast_vector_ref, *mk_c(t), mk_c(t)->m()); diff --git a/src/api/api_bv.cpp b/src/api/api_bv.cpp index 1876d930d..bd603aa6d 100644 --- a/src/api/api_bv.cpp +++ b/src/api/api_bv.cpp @@ -27,9 +27,6 @@ extern "C" { Z3_TRY; LOG_Z3_mk_bv_sort(c, sz); RESET_ERROR_CODE(); - if (sz == 0) { - SET_ERROR_CODE(Z3_INVALID_ARG); - } parameter p(sz); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_bv_fid(), BV_SORT, 1, &p)); RETURN_Z3(r); @@ -163,7 +160,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ // Not logging this one, since it is just syntax sugar. unsigned sz = Z3_get_bv_sort_size(c, s); if (sz == 0) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "zero length bit-vector supplied"); return nullptr; } Z3_ast x = Z3_mk_int64(c, 1, s); @@ -393,7 +390,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ if (to_sort(t)->get_family_id() == mk_c(c)->get_bv_fid() && to_sort(t)->get_decl_kind() == BV_SORT) { return to_sort(t)->get_parameter(0).get_int(); } - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "sort is not a bit-vector"); return 0; Z3_CATCH_RETURN(0); } diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 5993e9fdd..673d0b1c9 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -144,9 +144,11 @@ namespace api { } } - void context::set_error_code(Z3_error_code err) { + void context::set_error_code(Z3_error_code err, char const* opt_msg) { m_error_code = err; if (err != Z3_OK) { + m_exception_msg.clear(); + if (opt_msg) m_exception_msg = opt_msg; invoke_error_handler(err); } } @@ -159,7 +161,7 @@ namespace api { void context::check_searching() { if (m_searching) { - set_error_code(Z3_INVALID_USAGE); // TBD: error code could be fixed. + set_error_code(Z3_INVALID_USAGE, "cannot use function while searching"); // TBD: error code could be fixed. } } @@ -218,10 +220,9 @@ namespace api { // Corner case bug: n may be in m_last_result, and this is the only reference to n. // When, we execute reset() it is deleted // To avoid this bug, I bump the reference counter before reseting m_last_result - m().inc_ref(n); + ast_ref node(n, m()); m_last_result.reset(); - m_last_result.push_back(n); - m().dec_ref(n); + m_last_result.push_back(std::move(node)); } else { m_ast_trail.push_back(n); @@ -249,25 +250,24 @@ namespace api { if (ex.has_error_code()) { switch(ex.error_code()) { case ERR_MEMOUT: - set_error_code(Z3_MEMOUT_FAIL); + set_error_code(Z3_MEMOUT_FAIL, nullptr); break; case ERR_PARSER: - set_error_code(Z3_PARSER_ERROR); + set_error_code(Z3_PARSER_ERROR, ex.msg()); break; case ERR_INI_FILE: - set_error_code(Z3_INVALID_ARG); + set_error_code(Z3_INVALID_ARG, nullptr); break; case ERR_OPEN_FILE: - set_error_code(Z3_FILE_ACCESS_ERROR); + set_error_code(Z3_FILE_ACCESS_ERROR, nullptr); break; default: - set_error_code(Z3_INTERNAL_FATAL); + set_error_code(Z3_INTERNAL_FATAL, nullptr); break; } } else { - m_exception_msg = ex.msg(); - set_error_code(Z3_EXCEPTION); + set_error_code(Z3_EXCEPTION, ex.msg()); } } @@ -302,7 +302,7 @@ namespace api { case AST_FUNC_DECL: break; } - set_error_code(Z3_SORT_ERROR); + set_error_code(Z3_SORT_ERROR, nullptr); } } @@ -380,7 +380,7 @@ extern "C" { LOG_Z3_dec_ref(c, a); RESET_ERROR_CODE(); if (to_ast(a)->get_ref_count() == 0) { - SET_ERROR_CODE(Z3_DEC_REF_ERROR); + SET_ERROR_CODE(Z3_DEC_REF_ERROR, nullptr); return; } mk_c(c)->m().dec_ref(to_ast(a)); @@ -441,10 +441,14 @@ extern "C" { } void Z3_API Z3_set_error(Z3_context c, Z3_error_code e) { - SET_ERROR_CODE(e); + SET_ERROR_CODE(e, nullptr); } static char const * _get_error_msg(Z3_context c, Z3_error_code err) { + if (c) { + char const* msg = mk_c(c)->get_exception_msg(); + if (msg && *msg) return msg; + } switch(err) { case Z3_OK: return "ok"; case Z3_SORT_ERROR: return "type error"; @@ -458,7 +462,7 @@ extern "C" { case Z3_INTERNAL_FATAL: return "internal error"; case Z3_INVALID_USAGE: return "invalid usage"; case Z3_DEC_REF_ERROR: return "invalid dec_ref command"; - case Z3_EXCEPTION: return c == nullptr ? "Z3 exception" : mk_c(c)->get_exception_msg(); + case Z3_EXCEPTION: return "Z3 exception"; default: return "unknown"; } } diff --git a/src/api/api_context.h b/src/api/api_context.h index 50e89113d..a6f55d1aa 100644 --- a/src/api/api_context.h +++ b/src/api/api_context.h @@ -141,7 +141,7 @@ namespace api { Z3_error_code get_error_code() const { return m_error_code; } void reset_error_code(); - void set_error_code(Z3_error_code err); + void set_error_code(Z3_error_code err, char const* opt_msg); void set_error_handler(Z3_error_handler h) { m_error_handler = h; } // Sign an error if solver is searching void check_searching(); @@ -219,14 +219,6 @@ namespace api { // // ------------------------ smt_params & fparams() { return m_fparams; } - - // ------------------------ - // - // Parser interface - // - // ------------------------ - - std::string m_parser_error_buffer; }; @@ -234,14 +226,14 @@ namespace api { inline api::context * mk_c(Z3_context c) { return reinterpret_cast(c); } #define RESET_ERROR_CODE() { mk_c(c)->reset_error_code(); } -#define SET_ERROR_CODE(ERR) { mk_c(c)->set_error_code(ERR); } -#define CHECK_NON_NULL(_p_,_ret_) { if (_p_ == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); return _ret_; } } -#define CHECK_VALID_AST(_a_, _ret_) { if (_a_ == 0 || !CHECK_REF_COUNT(_a_)) { SET_ERROR_CODE(Z3_INVALID_ARG); return _ret_; } } +#define SET_ERROR_CODE(ERR, MSG) { mk_c(c)->set_error_code(ERR, MSG); } +#define CHECK_NON_NULL(_p_,_ret_) { if (_p_ == 0) { SET_ERROR_CODE(Z3_INVALID_ARG, "ast is null"); return _ret_; } } +#define CHECK_VALID_AST(_a_, _ret_) { if (_a_ == 0 || !CHECK_REF_COUNT(_a_)) { SET_ERROR_CODE(Z3_INVALID_ARG, "not a valid ast"); return _ret_; } } #define CHECK_SEARCHING(c) mk_c(c)->check_searching(); inline bool is_expr(Z3_ast a) { return is_expr(to_ast(a)); } -#define CHECK_IS_EXPR(_p_, _ret_) { if (_p_ == 0 || !is_expr(_p_)) { SET_ERROR_CODE(Z3_INVALID_ARG); return _ret_; } } +#define CHECK_IS_EXPR(_p_, _ret_) { if (_p_ == 0 || !is_expr(_p_)) { SET_ERROR_CODE(Z3_INVALID_ARG, "ast is not an expression"); return _ret_; } } inline bool is_bool_expr(Z3_context c, Z3_ast a) { return is_expr(a) && mk_c(c)->m().is_bool(to_expr(a)); } -#define CHECK_FORMULA(_a_, _ret_) { if (_a_ == 0 || !CHECK_REF_COUNT(_a_) || !is_bool_expr(c, _a_)) { SET_ERROR_CODE(Z3_INVALID_ARG); return _ret_; } } +#define CHECK_FORMULA(_a_, _ret_) { if (_a_ == 0 || !CHECK_REF_COUNT(_a_) || !is_bool_expr(c, _a_)) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return _ret_; } } inline void check_sorts(Z3_context c, ast * n) { mk_c(c)->check_sorts(n); } #endif diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index fd31a65f8..b0a4def55 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -157,7 +157,7 @@ extern "C" { RESET_ERROR_CODE(); sort * r = to_sort(s); if (Z3_get_sort_kind(c, s) != Z3_RELATION_SORT) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "sort should be a relation"); return 0; } return r->get_num_parameters(); @@ -170,18 +170,18 @@ extern "C" { RESET_ERROR_CODE(); sort * r = to_sort(s); if (Z3_get_sort_kind(c, s) != Z3_RELATION_SORT) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "sort should be a relation"); RETURN_Z3(nullptr); } if (col >= r->get_num_parameters()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } parameter const& p = r->get_parameter(col); if (!p.is_ast() || !is_sort(p.get_ast())) { UNREACHABLE(); warning_msg("Sort parameter expected at %d", col); - SET_ERROR_CODE(Z3_INTERNAL_FATAL); + SET_ERROR_CODE(Z3_INTERNAL_FATAL, "sort parameter expected"); RETURN_Z3(nullptr); } Z3_sort res = of_sort(to_sort(p.get_ast())); @@ -364,7 +364,7 @@ extern "C" { install_dl_collect_cmds(coll, ctx); ctx.set_ignore_check(true); if (!parse_smt2_commands(ctx, s)) { - SET_ERROR_CODE(Z3_PARSER_ERROR); + SET_ERROR_CODE(Z3_PARSER_ERROR, nullptr); return nullptr; } @@ -408,7 +408,7 @@ extern "C" { LOG_Z3_fixedpoint_from_file(c, d, s); std::ifstream is(s); if (!is) { - SET_ERROR_CODE(Z3_PARSER_ERROR); + SET_ERROR_CODE(Z3_PARSER_ERROR, nullptr); RETURN_Z3(nullptr); } RETURN_Z3(Z3_fixedpoint_from_stream(c, d, is)); diff --git a/src/api/api_datatype.cpp b/src/api/api_datatype.cpp index 799e537ea..0c2544643 100644 --- a/src/api/api_datatype.cpp +++ b/src/api/api_datatype.cpp @@ -56,7 +56,7 @@ extern "C" { del_datatype_decl(dt); if (!is_ok) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } @@ -118,7 +118,7 @@ extern "C" { del_datatype_decl(dt); if (!is_ok) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } @@ -180,7 +180,7 @@ extern "C" { del_datatype_decl(decl); if (!is_ok) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } @@ -274,7 +274,7 @@ extern "C" { RESET_ERROR_CODE(); mk_c(c)->reset_last_result(); if (!constr) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return; } ast_manager& m = mk_c(c)->m(); @@ -282,7 +282,7 @@ extern "C" { func_decl* f = reinterpret_cast(constr)->m_constructor.get(); if (!f) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return; } if (constructor_decl) { @@ -353,7 +353,7 @@ extern "C" { del_datatype_decl(data); if (!is_ok) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } @@ -416,7 +416,7 @@ extern "C" { del_datatype_decls(datas.size(), datas.c_ptr()); if (!ok) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return; } @@ -445,7 +445,7 @@ extern "C" { datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return dt_util.get_datatype_constructors(_t)->size(); @@ -458,12 +458,12 @@ extern "C" { sort * _t = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } ptr_vector const & decls = *dt_util.get_datatype_constructors(_t); if (idx >= decls.size()) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } func_decl* decl = (decls)[idx]; @@ -488,12 +488,12 @@ extern "C" { datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & decls = *dt_util.get_datatype_constructors(_t); if (idx >= decls.size()) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } func_decl* decl = (decls)[idx]; @@ -511,23 +511,23 @@ extern "C" { datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & decls = *dt_util.get_datatype_constructors(_t); if (idx_c >= decls.size()) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } func_decl* decl = (decls)[idx_c]; if (decl->get_arity() <= idx_a) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & accs = *dt_util.get_constructor_accessors(decl); SASSERT(accs.size() == decl->get_arity()); if (accs.size() <= idx_a) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } decl = (accs)[idx_a]; @@ -543,7 +543,7 @@ extern "C" { sort * tuple = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(tuple) || dt_util.is_recursive(tuple) || dt_util.get_datatype_num_constructors(tuple) != 1) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } Z3_func_decl r = get_datatype_sort_constructor_core(c, t, 0); @@ -558,12 +558,12 @@ extern "C" { sort * tuple = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(tuple) || dt_util.is_recursive(tuple) || dt_util.get_datatype_num_constructors(tuple) != 1) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } ptr_vector const & decls = *dt_util.get_datatype_constructors(tuple); if (decls.size() != 1) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } ptr_vector const & accs = *dt_util.get_constructor_accessors(decls[0]); @@ -578,17 +578,17 @@ extern "C" { sort * tuple = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(tuple) || dt_util.is_recursive(tuple) || dt_util.get_datatype_num_constructors(tuple) != 1) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & decls = *dt_util.get_datatype_constructors(tuple); if (decls.size() != 1) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & accs = *dt_util.get_constructor_accessors((decls)[0]); if (accs.size() <= i) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } func_decl* acc = (accs)[i]; diff --git a/src/api/api_fpa.cpp b/src/api/api_fpa.cpp index 261198354..cdc592527 100644 --- a/src/api/api_fpa.cpp +++ b/src/api/api_fpa.cpp @@ -175,7 +175,7 @@ extern "C" { LOG_Z3_mk_fpa_sort(c, ebits, sbits); RESET_ERROR_CODE(); if (ebits < 2 || sbits < 3) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "ebits should be at least 2, sbits at least 3"); } api::context * ctx = mk_c(c); sort * s = ctx->fpautil().mk_float_sort(ebits, sbits); @@ -222,7 +222,7 @@ extern "C" { RESET_ERROR_CODE(); CHECK_VALID_AST(s, nullptr); if (!is_fp_sort(c, s)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -238,7 +238,7 @@ extern "C" { RESET_ERROR_CODE(); CHECK_VALID_AST(s, nullptr); if (!is_fp_sort(c, s)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -255,7 +255,7 @@ extern "C" { RESET_ERROR_CODE(); CHECK_VALID_AST(s, nullptr); if (!is_fp_sort(c, s)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -271,7 +271,7 @@ extern "C" { LOG_Z3_mk_fpa_fp(c, sgn, exp, sig); RESET_ERROR_CODE(); if (!is_bv(c, sgn) || !is_bv(c, exp) || !is_bv(c, sig)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "bv sorts expected for arguments"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -286,7 +286,7 @@ extern "C" { LOG_Z3_mk_fpa_numeral_float(c, v, ty); RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG,"fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -306,7 +306,7 @@ extern "C" { LOG_Z3_mk_fpa_numeral_double(c, v, ty); RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -323,7 +323,7 @@ extern "C" { LOG_Z3_mk_fpa_numeral_int(c, v, ty); RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -343,7 +343,7 @@ extern "C" { LOG_Z3_mk_fpa_numeral_int64_uint64(c, sgn, exp, sig, ty); RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -363,7 +363,7 @@ extern "C" { LOG_Z3_mk_fpa_numeral_int64_uint64(c, sgn, exp, sig, ty); RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -383,7 +383,7 @@ extern "C" { LOG_Z3_mk_fpa_abs(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -398,7 +398,7 @@ extern "C" { LOG_Z3_mk_fpa_neg(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -413,7 +413,7 @@ extern "C" { LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -428,7 +428,7 @@ extern "C" { LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -443,7 +443,7 @@ extern "C" { LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -458,7 +458,7 @@ extern "C" { LOG_Z3_mk_fpa_add(c, rm, t1, t2); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -473,7 +473,7 @@ extern "C" { LOG_Z3_mk_fpa_fma(c, rm, t1, t2, t3); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2) || !is_fp(c, t3)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -488,7 +488,7 @@ extern "C" { LOG_Z3_mk_fpa_sqrt(c, rm, t); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -503,7 +503,7 @@ extern "C" { LOG_Z3_mk_fpa_rem(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -518,7 +518,7 @@ extern "C" { LOG_Z3_mk_fpa_round_to_integral(c, rm, t); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -533,7 +533,7 @@ extern "C" { LOG_Z3_mk_fpa_min(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -548,7 +548,7 @@ extern "C" { LOG_Z3_mk_fpa_max(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -563,7 +563,7 @@ extern "C" { LOG_Z3_mk_fpa_leq(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -578,7 +578,7 @@ extern "C" { LOG_Z3_mk_fpa_lt(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -593,7 +593,7 @@ extern "C" { LOG_Z3_mk_fpa_geq(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -608,7 +608,7 @@ extern "C" { LOG_Z3_mk_fpa_gt(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -623,7 +623,7 @@ extern "C" { LOG_Z3_mk_fpa_eq(c, t1, t2); RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -638,7 +638,7 @@ extern "C" { LOG_Z3_mk_fpa_is_normal(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -653,7 +653,7 @@ extern "C" { LOG_Z3_mk_fpa_is_subnormal(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -668,7 +668,7 @@ extern "C" { LOG_Z3_mk_fpa_is_zero(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -683,7 +683,7 @@ extern "C" { LOG_Z3_mk_fpa_is_infinite(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -698,7 +698,7 @@ extern "C" { LOG_Z3_mk_fpa_is_nan(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -713,7 +713,7 @@ extern "C" { LOG_Z3_mk_fpa_is_negative(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -728,7 +728,7 @@ extern "C" { LOG_Z3_mk_fpa_is_positive(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -744,14 +744,14 @@ extern "C" { LOG_Z3_mk_fpa_to_fp_bv(c, bv, s); RESET_ERROR_CODE(); if (!is_bv(c, bv) || !is_fp_sort(c, s)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "bv then fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!ctx->bvutil().is_bv(to_expr(bv)) || !fu.is_float(to_sort(s))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "bv sort the flaot sort expected"); return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(bv)); @@ -769,7 +769,7 @@ extern "C" { if (!fu.is_rm(to_expr(rm)) || !fu.is_float(to_expr(t)) || !fu.is_float(to_sort(s))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); @@ -787,7 +787,7 @@ extern "C" { if (!fu.is_rm(to_expr(rm)) || !ctx->autil().is_real(to_expr(t)) || !fu.is_float(to_sort(s))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); @@ -805,7 +805,7 @@ extern "C" { if (!fu.is_rm(to_expr(rm)) || !ctx->bvutil().is_bv(to_expr(t)) || !fu.is_float(to_sort(s))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); @@ -823,7 +823,7 @@ extern "C" { if (!fu.is_rm(to_expr(rm)) || !ctx->bvutil().is_bv(to_expr(t)) || !fu.is_float(to_sort(s))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); return nullptr; } expr * a = fu.mk_to_fp_unsigned(to_sort(s), to_expr(rm), to_expr(t)); @@ -837,7 +837,7 @@ extern "C" { LOG_Z3_mk_fpa_to_ubv(c, rm, t, sz); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -852,7 +852,7 @@ extern "C" { LOG_Z3_mk_fpa_to_sbv(c, rm, t, sz); RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "rm and float sorts expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -867,7 +867,7 @@ extern "C" { LOG_Z3_mk_fpa_to_real(c, t); RESET_ERROR_CODE(); if (!is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -884,7 +884,7 @@ extern "C" { CHECK_NON_NULL(s, 0); CHECK_VALID_AST(s, 0); if (!is_fp_sort(c, s)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(0); } return mk_c(c)->fpautil().get_ebits(to_sort(s)); @@ -898,7 +898,7 @@ extern "C" { CHECK_NON_NULL(s, 0); CHECK_VALID_AST(s, 0); if (!is_fp_sort(c, s)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(0); } return mk_c(c)->fpautil().get_sbits(to_sort(s)); @@ -912,7 +912,7 @@ extern "C" { CHECK_NON_NULL(t, 0); CHECK_VALID_AST(t, 0); if (sgn == nullptr) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "sign cannot be a nullpointer"); return 0; } ast_manager & m = mk_c(c)->m(); @@ -921,13 +921,13 @@ extern "C" { fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(fid); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return 0; } scoped_mpf val(mpfm); bool r = plugin->is_numeral(to_expr(t), val); if (!r || mpfm.is_nan(val)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return 0; } *sgn = mpfm.sgn(val); @@ -948,13 +948,13 @@ extern "C" { api::context * ctx = mk_c(c); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); RETURN_Z3(nullptr); } scoped_mpf val(mpfm); bool r = plugin->is_numeral(to_expr(t), val); if (!r || mpfm.is_nan(val)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return nullptr; } app * a; @@ -981,13 +981,13 @@ extern "C" { SASSERT(plugin != 0); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); RETURN_Z3(nullptr); } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); RETURN_Z3(nullptr); } unsigned sbits = val.get().get_sbits(); @@ -1014,13 +1014,13 @@ extern "C" { SASSERT(plugin != 0); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return ""; } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return ""; } unsigned sbits = val.get().get_sbits(); @@ -1042,7 +1042,7 @@ extern "C" { CHECK_NON_NULL(t, 0); CHECK_VALID_AST(t, 0); if (n == nullptr) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid nullptr argument"); return 0; } ast_manager & m = mk_c(c)->m(); @@ -1053,7 +1053,7 @@ extern "C" { SASSERT(plugin != 0); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); *n = 0; return 0; } @@ -1063,7 +1063,7 @@ extern "C" { if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val)) || !mpzm.is_uint64(z)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); *n = 0; return 0; } @@ -1085,13 +1085,13 @@ extern "C" { SASSERT(plugin != 0); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return ""; } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); return ""; } unsigned ebits = val.get().get_ebits(); @@ -1120,7 +1120,7 @@ extern "C" { CHECK_NON_NULL(t, 0); CHECK_VALID_AST(t, 0); if (n == nullptr) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid null argument"); return 0; } ast_manager & m = mk_c(c)->m(); @@ -1130,14 +1130,14 @@ extern "C" { SASSERT(plugin != 0); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); *n = 0; return 0; } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); *n = 0; return 0; } @@ -1169,13 +1169,13 @@ extern "C" { fpa_decl_plugin * plugin = (fpa_decl_plugin*)m.get_plugin(fid); expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); RETURN_Z3(nullptr); } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "invalid expression argument, expecting a valid fp, not a NaN"); RETURN_Z3(nullptr); } unsigned ebits = val.get().get_ebits(); @@ -1204,7 +1204,7 @@ extern "C" { CHECK_NON_NULL(t, nullptr); CHECK_VALID_AST(t, nullptr); if (!is_fp(c, t)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "fp sort expected"); RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); @@ -1223,7 +1223,7 @@ extern "C" { !ctx->autil().is_int(to_expr(exp)) || !ctx->autil().is_real(to_expr(sig)) || !fu.is_float(to_sort(s))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(exp), to_expr(sig)); @@ -1239,7 +1239,7 @@ extern "C" { api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return fu.is_nan(to_expr(t)); @@ -1253,7 +1253,7 @@ extern "C" { api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return fu.is_inf(to_expr(t)); @@ -1267,7 +1267,7 @@ extern "C" { api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return fu.is_zero(to_expr(t)); @@ -1281,7 +1281,7 @@ extern "C" { api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return fu.is_normal(to_expr(t)); @@ -1295,7 +1295,7 @@ extern "C" { api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return fu.is_subnormal(to_expr(t)); @@ -1309,7 +1309,7 @@ extern "C" { api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return fu.is_positive(to_expr(t)); @@ -1323,7 +1323,7 @@ extern "C" { api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!is_expr(t) || !fu.is_numeral(to_expr(t))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return fu.is_negative(to_expr(t)); diff --git a/src/api/api_goal.cpp b/src/api/api_goal.cpp index ae48a3f6f..cb3bb7478 100644 --- a/src/api/api_goal.cpp +++ b/src/api/api_goal.cpp @@ -30,7 +30,7 @@ extern "C" { LOG_Z3_mk_goal(c, models, unsat_cores, proofs); RESET_ERROR_CODE(); if (proofs != 0 && !mk_c(c)->m().proofs_enabled()) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "proofs are required, but proofs are not enabled on the context"); RETURN_Z3(nullptr); } Z3_goal_ref * g = alloc(Z3_goal_ref, *mk_c(c)); @@ -119,7 +119,7 @@ extern "C" { LOG_Z3_goal_formula(c, g, idx); RESET_ERROR_CODE(); if (idx >= to_goal_ref(g)->size()) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } expr * result = to_goal_ref(g)->form(idx); @@ -198,6 +198,10 @@ extern "C" { LOG_Z3_goal_to_dimacs_string(c, g); RESET_ERROR_CODE(); std::ostringstream buffer; + if (!to_goal_ref(g)->is_cnf()) { + SET_ERROR_CODE(Z3_INVALID_ARG, "If this is not what you want, then preprocess by optional bit-blasting and applying tseitin-cnf"); + RETURN_Z3(nullptr); + } to_goal_ref(g)->display_dimacs(buffer); // Hack for removing the trailing '\n' std::string result = buffer.str(); diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index 7eb7d2fdd..e9dd3580b 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -94,7 +94,7 @@ extern "C" { CHECK_NON_NULL(m, nullptr); func_interp * _fi = to_model_ref(m)->get_func_interp(to_func_decl(f)); if (!_fi) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } Z3_func_interp_ref * fi = alloc(Z3_func_interp_ref, *mk_c(c), to_model_ref(m)); @@ -123,7 +123,7 @@ extern "C" { RETURN_Z3(of_func_decl(_m->get_constant(i))); } else { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); @@ -142,7 +142,7 @@ extern "C" { CHECK_NON_NULL(m, nullptr); model * _m = to_model_ref(m); if (i >= _m->get_num_functions()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return nullptr; } return of_func_decl(_m->get_function(i)); @@ -187,7 +187,7 @@ extern "C" { LOG_Z3_model_get_sort(c, m, i); RESET_ERROR_CODE(); if (i >= to_model_ref(m)->get_num_uninterpreted_sorts()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } sort * s = to_model_ref(m)->get_uninterpreted_sort(i); @@ -200,7 +200,7 @@ extern "C" { LOG_Z3_model_get_sort_universe(c, m, s); RESET_ERROR_CODE(); if (!to_model_ref(m)->has_uninterpreted_sort(to_sort(s))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } ptr_vector const & universe = to_model_ref(m)->get_universe(to_sort(s)); @@ -242,7 +242,7 @@ extern "C" { RETURN_Z3(of_func_decl(to_func_decl(to_app(a)->get_decl()->get_parameter(0).get_ast()))); } else { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); @@ -269,7 +269,7 @@ extern "C" { RESET_ERROR_CODE(); func_decl* d = to_func_decl(f); if (d->get_arity() != 0) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); } else { model* mdl = to_model_ref(m); @@ -313,7 +313,7 @@ extern "C" { RESET_ERROR_CODE(); CHECK_NON_NULL(f, nullptr); if (i >= to_func_interp_ref(f)->num_entries()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } Z3_func_entry_ref * e = alloc(Z3_func_entry_ref, *mk_c(c), to_func_interp(f)->m_model.get()); @@ -364,7 +364,7 @@ extern "C" { func_interp* _fi = to_func_interp_ref(fi); expr* _value = to_expr(value); if (to_ast_vector_ref(args).size() != _fi->get_arity()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return; } // check sorts of value @@ -416,7 +416,7 @@ extern "C" { LOG_Z3_func_entry_get_arg(c, e, i); RESET_ERROR_CODE(); if (i >= to_func_entry(e)->m_func_interp->get_arity()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } expr * r = to_func_entry(e)->m_func_entry->get_arg(i); @@ -434,7 +434,7 @@ extern "C" { if (g) { return g->num_entries(); } - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return 0; } return 0; @@ -448,7 +448,7 @@ extern "C" { RESET_ERROR_CODE(); CHECK_NON_NULL(m, 0); if (j >= get_model_func_num_entries_core(c, m, i)) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return 0; } Z3_func_decl d = get_model_func_decl_core(c, m, i); diff --git a/src/api/api_numeral.cpp b/src/api/api_numeral.cpp index de9886571..b5458cff2 100644 --- a/src/api/api_numeral.cpp +++ b/src/api/api_numeral.cpp @@ -40,7 +40,7 @@ bool is_numeral_sort(Z3_context c, Z3_sort ty) { bool check_numeral_sort(Z3_context c, Z3_sort ty) { bool is_num = is_numeral_sort(c, ty); if (!is_num) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); } return is_num; } @@ -55,7 +55,7 @@ extern "C" { RETURN_Z3(nullptr); } if (!n) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } sort * _ty = to_sort(ty); @@ -72,7 +72,7 @@ extern "C" { (('p' == *m) || ('P' == *m) || ('+' == *m))))) { - SET_ERROR_CODE(Z3_PARSER_ERROR); + SET_ERROR_CODE(Z3_PARSER_ERROR, nullptr); RETURN_Z3(nullptr); } ++m; @@ -162,7 +162,7 @@ extern "C" { RESET_ERROR_CODE(); expr* e = to_expr(a); if (!e) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return Z3_FALSE; } if (mk_c(c)->autil().is_numeral(e, r)) { @@ -221,7 +221,7 @@ extern "C" { return mk_c(c)->mk_external_string(fu.fm().to_string(tmp)); } else { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return ""; } } @@ -234,7 +234,7 @@ extern "C" { RESET_ERROR_CODE(); expr* e = to_expr(a); if (!e) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return ""; } rational r; @@ -256,7 +256,7 @@ extern "C" { return mk_c(c)->mk_external_string(r.to_string()); } else { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return ""; } Z3_CATCH_RETURN(""); @@ -281,7 +281,7 @@ extern "C" { return Z3_FALSE; } } - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return Z3_FALSE; Z3_CATCH_RETURN(Z3_FALSE); } @@ -293,7 +293,7 @@ extern "C" { LOG_Z3_get_numeral_int(c, v, i); RESET_ERROR_CODE(); if (!i) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return Z3_FALSE; } int64_t l; @@ -311,7 +311,7 @@ extern "C" { LOG_Z3_get_numeral_uint(c, v, u); RESET_ERROR_CODE(); if (!u) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return Z3_FALSE; } uint64_t l; @@ -329,7 +329,7 @@ extern "C" { LOG_Z3_get_numeral_uint64(c, v, u); RESET_ERROR_CODE(); if (!u) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return Z3_FALSE; } rational r; @@ -349,7 +349,7 @@ extern "C" { LOG_Z3_get_numeral_int64(c, v, i); RESET_ERROR_CODE(); if (!i) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return Z3_FALSE; } rational r; @@ -368,7 +368,7 @@ extern "C" { LOG_Z3_get_numeral_rational_int64(c, v, num, den); RESET_ERROR_CODE(); if (!num || !den) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return Z3_FALSE; } rational r; diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index 21c55f428..71f92eeba 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -173,6 +173,7 @@ extern "C" { to_optimize_ptr(o)->get_model(_m); Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); if (_m) { + if (mk_c(c)->params().m_model_compress) _m->compress(); m_ref->m_model = _m; } else { @@ -317,17 +318,15 @@ extern "C" { ctx->set_ignore_check(true); try { if (!parse_smt2_commands(*ctx.get(), s)) { - mk_c(c)->m_parser_error_buffer = errstrm.str(); ctx = nullptr; - SET_ERROR_CODE(Z3_PARSER_ERROR); + SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str().c_str()); return; } } catch (z3_exception& e) { errstrm << e.msg(); - mk_c(c)->m_parser_error_buffer = errstrm.str(); ctx = nullptr; - SET_ERROR_CODE(Z3_PARSER_ERROR); + SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str().c_str()); return; } diff --git a/src/api/api_params.cpp b/src/api/api_params.cpp index d021ed6ad..9d9f5157c 100644 --- a/src/api/api_params.cpp +++ b/src/api/api_params.cpp @@ -171,7 +171,7 @@ extern "C" { LOG_Z3_param_descrs_get_name(c, p, i); RESET_ERROR_CODE(); if (i >= to_param_descrs_ptr(p)->size()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } Z3_symbol result = of_symbol(to_param_descrs_ptr(p)->get_param_name(i)); @@ -185,7 +185,7 @@ extern "C" { RESET_ERROR_CODE(); char const* result = to_param_descrs_ptr(p)->get_descr(to_symbol(s)); if (result == nullptr) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } return mk_c(c)->mk_external_string(result); diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index d791dc2a5..b88f273f9 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -30,15 +30,6 @@ Revision History: extern "C" { - - Z3_string Z3_API Z3_get_parser_error(Z3_context c) { - Z3_TRY; - LOG_Z3_get_parser_error(c); - RESET_ERROR_CODE(); - return mk_c(c)->m_parser_error_buffer.c_str(); - Z3_CATCH_RETURN(""); - } - // --------------- // Support for SMTLIB2 @@ -70,16 +61,14 @@ extern "C" { try { if (!parse_smt2_commands(*ctx.get(), is)) { ctx = nullptr; - mk_c(c)->m_parser_error_buffer = errstrm.str(); - SET_ERROR_CODE(Z3_PARSER_ERROR); + SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str().c_str()); return of_ast_vector(v); } } catch (z3_exception& e) { errstrm << e.msg(); - mk_c(c)->m_parser_error_buffer = errstrm.str(); ctx = nullptr; - SET_ERROR_CODE(Z3_PARSER_ERROR); + SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str().c_str()); return of_ast_vector(v); } ptr_vector::const_iterator it = ctx->begin_assertions(); @@ -118,7 +107,7 @@ 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_FILE_ACCESS_ERROR); + SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR, nullptr); return nullptr; } Z3_ast_vector r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); @@ -141,15 +130,13 @@ extern "C" { ctx->set_diagnostic_stream(ous); try { if (!parse_smt2_commands(*ctx.get(), is)) { - mk_c(c)->m_parser_error_buffer = ous.str(); - SET_ERROR_CODE(Z3_PARSER_ERROR); + SET_ERROR_CODE(Z3_PARSER_ERROR, ous.str().c_str()); RETURN_Z3(mk_c(c)->mk_external_string(ous.str())); } } catch (z3_exception& e) { if (ous.str().empty()) ous << e.msg(); - mk_c(c)->m_parser_error_buffer = ous.str(); - SET_ERROR_CODE(Z3_PARSER_ERROR); + SET_ERROR_CODE(Z3_PARSER_ERROR, ous.str().c_str()); RETURN_Z3(mk_c(c)->mk_external_string(ous.str())); } RETURN_Z3(mk_c(c)->mk_external_string(ous.str())); diff --git a/src/api/api_polynomial.cpp b/src/api/api_polynomial.cpp index 35ece4a59..8c5fc99c9 100644 --- a/src/api/api_polynomial.cpp +++ b/src/api/api_polynomial.cpp @@ -49,7 +49,7 @@ extern "C" { default_expr2polynomial converter(mk_c(c)->m(), pm); if (!converter.to_polynomial(to_expr(p), _p, d) || !converter.to_polynomial(to_expr(q), _q, d)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return nullptr; } Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); diff --git a/src/api/api_qe.cpp b/src/api/api_qe.cpp index 92517b02b..94e83144f 100644 --- a/src/api/api_qe.cpp +++ b/src/api/api_qe.cpp @@ -55,7 +55,7 @@ extern "C" app_ref_vector vars(mk_c(c)->m ()); if (!to_apps(num_bounds, bound, vars)) { - SET_ERROR_CODE (Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } @@ -141,7 +141,7 @@ extern "C" for (unsigned i = 0; i < vVars.size (); ++i) { app *a = to_app (vVars.get (i)); if (a->get_kind () != AST_APP) { - SET_ERROR_CODE (Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } vApps.push_back (a); diff --git a/src/api/api_quant.cpp b/src/api/api_quant.cpp index 6e0162024..6d6d19d56 100644 --- a/src/api/api_quant.cpp +++ b/src/api/api_quant.cpp @@ -62,11 +62,11 @@ extern "C" { Z3_TRY; RESET_ERROR_CODE(); if (!mk_c(c)->m().is_bool(to_expr(body))) { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return nullptr; } if (num_patterns > 0 && num_no_patterns > 0) { - SET_ERROR_CODE(Z3_INVALID_USAGE); + SET_ERROR_CODE(Z3_INVALID_USAGE, nullptr); return nullptr; } expr * const* ps = reinterpret_cast(patterns); @@ -77,7 +77,7 @@ extern "C" { pattern_validator v(mk_c(c)->m()); for (unsigned i = 0; i < num_patterns; i++) { if (!v(num_decls, ps[i], 0, 0)) { - SET_ERROR_CODE(Z3_INVALID_PATTERN); + SET_ERROR_CODE(Z3_INVALID_PATTERN, nullptr); return nullptr; } } @@ -90,7 +90,7 @@ extern "C" { expr_ref result(mk_c(c)->m()); if (num_decls > 0) { result = mk_c(c)->m().mk_quantifier( - (0 != is_forall), + is_forall ? forall_k : exists_k, names.size(), ts, names.c_ptr(), to_expr(body), weight, qid, @@ -144,6 +144,61 @@ extern "C" { return Z3_mk_quantifier(c, 0, weight, num_patterns, patterns, num_decls, types, decl_names, body); } + Z3_ast Z3_API Z3_mk_lambda(Z3_context c, + unsigned num_decls, Z3_sort const types[], + Z3_symbol const decl_names[], + Z3_ast body) { + + Z3_TRY; + LOG_Z3_mk_lambda(c, num_decls, types, decl_names, body); + RESET_ERROR_CODE(); + expr_ref result(mk_c(c)->m()); + if (num_decls == 0) { + SET_ERROR_CODE(Z3_INVALID_USAGE, nullptr); + RETURN_Z3(0); + } + + sort* const* ts = reinterpret_cast(types); + svector names; + for (unsigned i = 0; i < num_decls; ++i) { + names.push_back(to_symbol(decl_names[i])); + } + result = mk_c(c)->m().mk_lambda(names.size(), ts, names.c_ptr(), to_expr(body)); + mk_c(c)->save_ast_trail(result.get()); + return of_ast(result.get()); + Z3_CATCH_RETURN(0); + } + + Z3_ast Z3_API Z3_mk_lambda_const(Z3_context c, + unsigned num_decls, Z3_app const vars[], + Z3_ast body) { + + Z3_TRY; + LOG_Z3_mk_lambda_const(c, num_decls, vars, body); + RESET_ERROR_CODE(); + if (num_decls == 0) { + SET_ERROR_CODE(Z3_INVALID_USAGE, nullptr); + RETURN_Z3(0); + } + + svector _names; + ptr_vector _vars; + ptr_vector _args; + for (unsigned i = 0; i < num_decls; ++i) { + app* a = to_app(vars[i]); + _names.push_back(to_app(a)->get_decl()->get_name()); + _args.push_back(a); + _vars.push_back(mk_c(c)->m().get_sort(a)); + } + expr_ref result(mk_c(c)->m()); + expr_abstract(mk_c(c)->m(), 0, num_decls, _args.c_ptr(), to_expr(body), result); + + result = mk_c(c)->m().mk_lambda(_vars.size(), _vars.c_ptr(), _names.c_ptr(), result); + mk_c(c)->save_ast_trail(result.get()); + return of_ast(result.get()); + Z3_CATCH_RETURN(0); + } + Z3_ast Z3_API Z3_mk_quantifier_const_ex(Z3_context c, Z3_bool is_forall, @@ -165,17 +220,17 @@ extern "C" { svector types; ptr_vector bound_asts; if (num_patterns > 0 && num_no_patterns > 0) { - SET_ERROR_CODE(Z3_INVALID_USAGE); + SET_ERROR_CODE(Z3_INVALID_USAGE, nullptr); RETURN_Z3(nullptr); } if (num_bound == 0) { - SET_ERROR_CODE(Z3_INVALID_USAGE); + SET_ERROR_CODE(Z3_INVALID_USAGE, "number of bound variables is 0"); RETURN_Z3(nullptr); } for (unsigned i = 0; i < num_bound; ++i) { app* a = to_app(bound[i]); if (a->get_kind() != AST_APP) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } symbol s(to_app(a)->get_decl()->get_name()); @@ -183,7 +238,7 @@ extern "C" { types.push_back(of_sort(mk_c(c)->m().get_sort(a))); bound_asts.push_back(a); if (a->get_family_id() != null_family_id || a->get_num_args() != 0) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } @@ -204,7 +259,7 @@ extern "C" { for (unsigned i = 0; i < num_no_patterns; ++i) { expr_ref result(mk_c(c)->m()); if (!is_app(to_expr(no_patterns[i]))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } app* pat = to_app(to_expr(no_patterns[i])); @@ -268,7 +323,7 @@ extern "C" { RESET_ERROR_CODE(); for (unsigned i = 0; i < num_patterns; ++i) { if (!is_app(to_expr(terms[i]))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } } @@ -292,17 +347,27 @@ extern "C" { Z3_TRY; LOG_Z3_is_quantifier_forall(c, a); RESET_ERROR_CODE(); - ast * _a = to_ast(a); - if (_a->get_kind() == AST_QUANTIFIER) { - return to_quantifier(_a)->is_forall(); - } - else { - SET_ERROR_CODE(Z3_SORT_ERROR); - return Z3_FALSE; - } + return ::is_forall(to_ast(a)) ? Z3_TRUE : Z3_FALSE; Z3_CATCH_RETURN(Z3_FALSE); } + Z3_bool Z3_API Z3_is_quantifier_exists(Z3_context c, Z3_ast a) { + Z3_TRY; + LOG_Z3_is_quantifier_exists(c, a); + RESET_ERROR_CODE(); + return ::is_exists(to_ast(a)) ? Z3_TRUE : Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); + } + + Z3_bool Z3_API Z3_is_lambda(Z3_context c, Z3_ast a) { + Z3_TRY; + LOG_Z3_is_lambda(c, a); + RESET_ERROR_CODE(); + return ::is_lambda(to_ast(a)) ? Z3_TRUE : Z3_FALSE; + Z3_CATCH_RETURN(Z3_FALSE); + } + + unsigned Z3_API Z3_get_quantifier_weight(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_quantifier_weight(c, a); @@ -312,7 +377,7 @@ extern "C" { return to_quantifier(_a)->get_weight(); } else { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return 0; } Z3_CATCH_RETURN(0); @@ -327,7 +392,7 @@ extern "C" { return to_quantifier(_a)->get_num_patterns(); } else { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return 0; } Z3_CATCH_RETURN(0); @@ -343,7 +408,7 @@ extern "C" { RETURN_Z3(r); } else { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); @@ -359,7 +424,7 @@ extern "C" { return to_quantifier(_a)->get_num_no_patterns(); } else { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return 0; } Z3_CATCH_RETURN(0); @@ -375,7 +440,7 @@ extern "C" { RETURN_Z3(r); } else { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); @@ -390,7 +455,7 @@ extern "C" { return of_symbol(to_quantifier(_a)->get_decl_names()[i]); } else { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return nullptr; } Z3_CATCH_RETURN(nullptr); @@ -406,7 +471,7 @@ extern "C" { RETURN_Z3(r); } else { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); @@ -422,7 +487,7 @@ extern "C" { RETURN_Z3(r); } else { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); @@ -438,7 +503,7 @@ extern "C" { return to_quantifier(_a)->get_num_decls(); } else { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return 0; } Z3_CATCH_RETURN(0); @@ -453,7 +518,7 @@ extern "C" { return _p->get_num_args(); } else { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); return 0; } Z3_CATCH_RETURN(0); @@ -469,7 +534,7 @@ extern "C" { RETURN_Z3(r); } else { - SET_ERROR_CODE(Z3_SORT_ERROR); + SET_ERROR_CODE(Z3_SORT_ERROR, nullptr); RETURN_Z3(nullptr); } Z3_CATCH_RETURN(nullptr); @@ -485,3 +550,4 @@ extern "C" { } }; + diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp index 3d65cb1cd..d92ff155b 100644 --- a/src/api/api_rcf.cpp +++ b/src/api/api_rcf.cpp @@ -123,7 +123,7 @@ extern "C" { } if (rz == 0) { // it is the zero polynomial - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } av.shrink(rz); diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index 84cfdca32..42979d1ed 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -107,7 +107,7 @@ extern "C" { RESET_ERROR_CODE(); zstring str; if (!mk_c(c)->sutil().str.is_string(to_expr(s), str)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "expression is not a string literal"); return ""; } std::string result = str.encode(); diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 951b9e51e..9ad51aaf4 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -145,10 +145,12 @@ extern "C" { 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); + std::stringstream errstrm; + ctx->set_regular_stream(errstrm); if (!parse_smt2_commands(*ctx.get(), is)) { ctx = nullptr; - SET_ERROR_CODE(Z3_PARSER_ERROR); + SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str().c_str()); return; } @@ -178,7 +180,7 @@ extern "C" { char const* ext = get_extension(file_name); std::ifstream is(file_name); if (!is) { - SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR); + SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR, nullptr); } else if (ext && std::string("dimacs") == ext) { ast_manager& m = to_solver_ref(s)->get_manager(); @@ -291,7 +293,7 @@ extern "C" { RESET_ERROR_CODE(); init_solver(c, s); if (n > to_solver_ref(s)->get_scope_level()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return; } if (n > 0) @@ -372,7 +374,7 @@ extern "C" { 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]))) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, "assumption is not an expression"); return Z3_L_UNDEF; } } @@ -430,9 +432,12 @@ extern "C" { model_ref _m; to_solver_ref(s)->get_model(_m); if (!_m) { - SET_ERROR_CODE(Z3_INVALID_USAGE); + SET_ERROR_CODE(Z3_INVALID_USAGE, "there is no current model"); RETURN_Z3(nullptr); } + if (_m) { + if (mk_c(c)->params().m_model_compress) _m->compress(); + } Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); m_ref->m_model = _m; mk_c(c)->save_object(m_ref); @@ -447,7 +452,7 @@ extern "C" { init_solver(c, s); proof * p = to_solver_ref(s)->get_proof(); if (!p) { - SET_ERROR_CODE(Z3_INVALID_USAGE); + SET_ERROR_CODE(Z3_INVALID_USAGE, "there is no current proof"); RETURN_Z3(nullptr); } mk_c(c)->save_ast_trail(p); @@ -539,7 +544,7 @@ extern "C" { for (ast* e : __assumptions) { if (!is_expr(e)) { _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); - SET_ERROR_CODE(Z3_INVALID_USAGE); + SET_ERROR_CODE(Z3_INVALID_USAGE, "assumption is not an expression"); return Z3_L_UNDEF; } _assumptions.push_back(to_expr(e)); @@ -548,7 +553,7 @@ extern "C" { for (ast* a : __variables) { if (!is_expr(a)) { _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); - SET_ERROR_CODE(Z3_INVALID_USAGE); + SET_ERROR_CODE(Z3_INVALID_USAGE, "variable is not an expression"); return Z3_L_UNDEF; } _variables.push_back(to_expr(a)); @@ -590,7 +595,7 @@ extern "C" { 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); + SET_ERROR_CODE(Z3_INVALID_USAGE, "cube contains a non-expression"); } else { vars.push_back(to_expr(a)); diff --git a/src/api/api_stats.cpp b/src/api/api_stats.cpp index 2e1dac4de..2014d57b8 100644 --- a/src/api/api_stats.cpp +++ b/src/api/api_stats.cpp @@ -67,7 +67,7 @@ extern "C" { LOG_Z3_stats_get_key(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return ""; } return to_stats_ref(s).get_key(idx); @@ -79,7 +79,7 @@ extern "C" { LOG_Z3_stats_is_uint(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return Z3_FALSE; } return to_stats_ref(s).is_uint(idx); @@ -91,7 +91,7 @@ extern "C" { LOG_Z3_stats_is_double(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return Z3_FALSE; } return !to_stats_ref(s).is_uint(idx); @@ -103,11 +103,11 @@ extern "C" { LOG_Z3_stats_get_uint_value(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return 0; } if (!to_stats_ref(s).is_uint(idx)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0; } return to_stats_ref(s).get_uint_value(idx); @@ -119,11 +119,11 @@ extern "C" { LOG_Z3_stats_get_double_value(c, s, idx); RESET_ERROR_CODE(); if (idx >= to_stats_ref(s).size()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return 0.0; } if (to_stats_ref(s).is_uint(idx)) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return 0.0; } return to_stats_ref(s).get_double_value(idx); diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index 345284fd6..7c0143201 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -52,7 +52,7 @@ extern "C" { RESET_ERROR_CODE(); tactic_cmd * t = mk_c(c)->find_tactic_cmd(symbol(name)); if (t == nullptr) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } tactic * new_t = t->mk(mk_c(c)->m()); @@ -82,7 +82,7 @@ extern "C" { RESET_ERROR_CODE(); probe_info * p = mk_c(c)->find_probe(symbol(name)); if (p == nullptr) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); } probe * new_p = p->get(); @@ -324,7 +324,7 @@ extern "C" { LOG_Z3_get_tactic_name(c, idx); RESET_ERROR_CODE(); if (idx >= mk_c(c)->num_tactics()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return ""; } return mk_c(c)->get_tactic(idx)->get_name().bare_str(); @@ -344,7 +344,7 @@ extern "C" { LOG_Z3_get_probe_name(c, idx); RESET_ERROR_CODE(); if (idx >= mk_c(c)->num_probes()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); return ""; } return mk_c(c)->get_probe(idx)->get_name().bare_str(); @@ -381,7 +381,7 @@ extern "C" { RESET_ERROR_CODE(); tactic_cmd * t = mk_c(c)->find_tactic_cmd(symbol(name)); if (t == nullptr) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return ""; } return t->get_descr(); @@ -394,7 +394,7 @@ extern "C" { RESET_ERROR_CODE(); probe_info * p = mk_c(c)->find_probe(symbol(name)); if (p == nullptr) { - SET_ERROR_CODE(Z3_INVALID_ARG); + SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); return ""; } return p->get_descr(); @@ -504,7 +504,7 @@ extern "C" { LOG_Z3_apply_result_get_subgoal(c, r, i); RESET_ERROR_CODE(); if (i > to_apply_result(r)->m_subgoals.size()) { - SET_ERROR_CODE(Z3_IOB); + SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); } Z3_goal_ref * g = alloc(Z3_goal_ref, *mk_c(c)); diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 665ffb438..e1f263e17 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -167,11 +167,6 @@ namespace z3 { } void check_parser_error() const { - Z3_error_code e = Z3_get_error_code(*this); - if (e != Z3_OK && enable_exceptions()) { - Z3_string s = Z3_get_parser_error(*this); - if (s && *s) Z3_THROW(exception(s)); - } check_error(); } @@ -252,7 +247,6 @@ namespace z3 { */ sort array_sort(sort d, sort r); sort array_sort(sort_vector const& d, sort r); - /** \brief Return an enumeration sort: enum_names[0], ..., enum_names[n-1]. \c cs and \c ts are output parameters. The method stores in \c cs the constants corresponding to the enumerated elements, @@ -675,7 +669,21 @@ namespace z3 { \brief Return true if this expression is a quantifier. */ bool is_quantifier() const { return kind() == Z3_QUANTIFIER_AST; } + /** + \brief Return true if this expression is a universal quantifier. + */ + bool is_forall() const { return 0 != Z3_is_quantifier_forall(ctx(), m_ast); } + /** + \brief Return true if this expression is an existential quantifier. + */ + bool is_exists() const { return 0 != Z3_is_quantifier_exists(ctx(), m_ast); } + /** + \brief Return true if this expression is a lambda expression. + */ + bool is_lambda() const { return 0 != Z3_is_lambda(ctx(), m_ast); } + /** + \brief Return true if this expression is a variable. */ bool is_var() const { return kind() == Z3_VAR_AST; } @@ -1638,6 +1646,31 @@ namespace z3 { array vars(xs); Z3_ast r = Z3_mk_exists_const(b.ctx(), 0, vars.size(), vars.ptr(), 0, 0, b); b.check_error(); return expr(b.ctx(), r); } + inline expr lambda(expr const & x, expr const & b) { + check_context(x, b); + Z3_app vars[] = {(Z3_app) x}; + Z3_ast r = Z3_mk_lambda_const(b.ctx(), 1, vars, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr lambda(expr const & x1, expr const & x2, expr const & b) { + check_context(x1, b); check_context(x2, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2}; + Z3_ast r = Z3_mk_lambda_const(b.ctx(), 2, vars, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr lambda(expr const & x1, expr const & x2, expr const & x3, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3 }; + Z3_ast r = Z3_mk_lambda_const(b.ctx(), 3, vars, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr lambda(expr const & x1, expr const & x2, expr const & x3, expr const & x4, expr const & b) { + check_context(x1, b); check_context(x2, b); check_context(x3, b); check_context(x4, b); + Z3_app vars[] = {(Z3_app) x1, (Z3_app) x2, (Z3_app) x3, (Z3_app) x4 }; + Z3_ast r = Z3_mk_lambda_const(b.ctx(), 4, vars, b); b.check_error(); return expr(b.ctx(), r); + } + inline expr lambda(expr_vector const & xs, expr const & b) { + array vars(xs); + Z3_ast r = Z3_mk_lambda_const(b.ctx(), vars.size(), vars.ptr(), b); b.check_error(); return expr(b.ctx(), r); + } + inline expr pble(expr_vector const& es, int const* coeffs, int bound) { assert(es.size() > 0); context& ctx = es[0].ctx(); @@ -2521,7 +2554,6 @@ namespace z3 { array dom(d); Z3_sort s = Z3_mk_array_sort_n(m_ctx, dom.size(), dom.ptr(), r); check_error(); return sort(*this, s); } - inline sort context::enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts) { array _enum_names(n); for (unsigned i = 0; i < n; i++) { _enum_names[i] = Z3_mk_string_symbol(*this, enum_names[i]); } @@ -2785,12 +2817,8 @@ namespace z3 { a.check_error(); return expr(a.ctx(), r); } - inline expr select(expr const & a, int i) { return select(a, a.ctx().num_val(i, a.get_sort().array_domain())); } - inline expr store(expr const & a, expr const & i, expr const & v) { - check_context(a, i); check_context(a, v); - Z3_ast r = Z3_mk_store(a.ctx(), a, i, v); - a.check_error(); - return expr(a.ctx(), r); + inline expr select(expr const & a, int i) { + return select(a, a.ctx().num_val(i, a.get_sort().array_domain())); } inline expr select(expr const & a, expr_vector const & i) { check_context(a, i); @@ -2800,6 +2828,13 @@ namespace z3 { return expr(a.ctx(), r); } + inline expr store(expr const & a, expr const & i, expr const & v) { + check_context(a, i); check_context(a, v); + Z3_ast r = Z3_mk_store(a.ctx(), a, i, v); + a.check_error(); + return expr(a.ctx(), r); + } + inline expr store(expr const & a, int i, expr const & v) { return store(a, a.ctx().num_val(i, a.get_sort().array_domain()), v); } inline expr store(expr const & a, expr i, int v) { return store(a, i, a.ctx().num_val(v, a.get_sort().array_range())); } inline expr store(expr const & a, int i, int v) { diff --git a/src/api/dotnet/AST.cs b/src/api/dotnet/AST.cs index c7ca1851e..2460c50f0 100644 --- a/src/api/dotnet/AST.cs +++ b/src/api/dotnet/AST.cs @@ -43,7 +43,7 @@ namespace Microsoft.Z3 (!Object.ReferenceEquals(a, null) && !Object.ReferenceEquals(b, null) && a.Context.nCtx == b.Context.nCtx && - Native.Z3_is_eq_ast(a.Context.nCtx, a.NativeObject, b.NativeObject) != 0); + 0 != Native.Z3_is_eq_ast(a.Context.nCtx, a.NativeObject, b.NativeObject)); } /// diff --git a/src/api/dotnet/ASTMap.cs b/src/api/dotnet/ASTMap.cs index 596b2943f..f7c1c5914 100644 --- a/src/api/dotnet/ASTMap.cs +++ b/src/api/dotnet/ASTMap.cs @@ -37,7 +37,7 @@ namespace Microsoft.Z3 { Contract.Requires(k != null); - return Native.Z3_ast_map_contains(Context.nCtx, NativeObject, k.NativeObject) != 0; + return 0 != Native.Z3_ast_map_contains(Context.nCtx, NativeObject, k.NativeObject); } /// diff --git a/src/api/dotnet/ArrayExpr.cs b/src/api/dotnet/ArrayExpr.cs index e14bb1083..6c51bfc5b 100644 --- a/src/api/dotnet/ArrayExpr.cs +++ b/src/api/dotnet/ArrayExpr.cs @@ -37,6 +37,6 @@ namespace Microsoft.Z3 { Contract.Requires(ctx != null); } - #endregion + #endregion } } diff --git a/src/api/dotnet/CMakeLists.txt b/src/api/dotnet/CMakeLists.txt index 76516bf39..50f643c8d 100644 --- a/src/api/dotnet/CMakeLists.txt +++ b/src/api/dotnet/CMakeLists.txt @@ -84,6 +84,7 @@ set(Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE IntNum.cs IntSort.cs IntSymbol.cs + Lambda.cs ListSort.cs Log.cs Model.cs diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 38da21370..c8decb59b 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -1963,7 +1963,7 @@ namespace Microsoft.Z3 Contract.Ensures(Contract.Result() != null); CheckContextMatch(t); - return new IntExpr(this, Native.Z3_mk_bv2int(nCtx, t.NativeObject, (signed) ? 1 : 0)); + return new IntExpr(this, Native.Z3_mk_bv2int(nCtx, t.NativeObject, (byte)(signed ? 1 : 0))); } /// @@ -1980,7 +1980,7 @@ namespace Microsoft.Z3 CheckContextMatch(t1); CheckContextMatch(t2); - return new BoolExpr(this, Native.Z3_mk_bvadd_no_overflow(nCtx, t1.NativeObject, t2.NativeObject, (isSigned) ? 1 : 0)); + return new BoolExpr(this, Native.Z3_mk_bvadd_no_overflow(nCtx, t1.NativeObject, t2.NativeObject, (byte)(isSigned ? 1 : 0))); } /// @@ -2031,7 +2031,7 @@ namespace Microsoft.Z3 CheckContextMatch(t1); CheckContextMatch(t2); - return new BoolExpr(this, Native.Z3_mk_bvsub_no_underflow(nCtx, t1.NativeObject, t2.NativeObject, (isSigned) ? 1 : 0)); + return new BoolExpr(this, Native.Z3_mk_bvsub_no_underflow(nCtx, t1.NativeObject, t2.NativeObject, (byte)(isSigned ? 1 : 0))); } /// @@ -2080,7 +2080,7 @@ namespace Microsoft.Z3 CheckContextMatch(t1); CheckContextMatch(t2); - return new BoolExpr(this, Native.Z3_mk_bvmul_no_overflow(nCtx, t1.NativeObject, t2.NativeObject, (isSigned) ? 1 : 0)); + return new BoolExpr(this, Native.Z3_mk_bvmul_no_overflow(nCtx, t1.NativeObject, t2.NativeObject, (byte)(isSigned ? 1 : 0))); } /// @@ -2777,25 +2777,25 @@ namespace Microsoft.Z3 /// /// Create an at-most-k constraint. /// - public BoolExpr MkAtMost(BoolExpr[] args, uint k) + public BoolExpr MkAtMost(IEnumerable args, uint k) { Contract.Requires(args != null); Contract.Requires(Contract.Result() != null); CheckContextMatch(args); - return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint) args.Length, - AST.ArrayToNative(args), k)); + return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint) args.Count(), + AST.EnumToNative(args), k)); } /// /// Create an at-least-k constraint. /// - public BoolExpr MkAtLeast(BoolExpr[] args, uint k) + public BoolExpr MkAtLeast(IEnumerable args, uint k) { Contract.Requires(args != null); Contract.Requires(Contract.Result() != null); CheckContextMatch(args); - return new BoolExpr(this, Native.Z3_mk_atleast(nCtx, (uint) args.Length, - AST.ArrayToNative(args), k)); + return new BoolExpr(this, Native.Z3_mk_atleast(nCtx, (uint) args.Count(), + AST.EnumToNative(args), k)); } /// @@ -3135,8 +3135,8 @@ namespace Microsoft.Z3 public BitVecNum MkBV(bool[] bits) { Contract.Ensures(Contract.Result() != null); - int[] _bits = new int[bits.Length]; - for (int i = 0; i < bits.Length; ++i) _bits[i] = bits[i] ? 1 : 0; + byte[] _bits = new byte[bits.Length]; + for (int i = 0; i < bits.Length; ++i) _bits[i] = (byte)(bits[i] ? 1 : 0); return (BitVecNum)Expr.Create(this, Native.Z3_mk_bv_numeral(nCtx, (uint)bits.Length, _bits)); } @@ -3292,6 +3292,53 @@ namespace Microsoft.Z3 return MkExists(boundConstants, body, weight, patterns, noPatterns, quantifierID, skolemID); } + /// + /// Create a lambda expression. + /// + /// + /// Creates a lambda expression. + /// is an array + /// with the sorts of the bound variables, is an array with the + /// 'names' of the bound variables, and is the body of the + /// lambda. + /// Note that the bound variables are de-Bruijn indices created using . + /// Z3 applies the convention that the last element in and + /// refers to the variable with index 0, the second to last element + /// of and refers to the variable + /// with index 1, etc. + /// + /// the sorts of the bound variables. + /// names of the bound variables + /// the body of the quantifier. + public Lambda MkLambda(Sort[] sorts, Symbol[] names, Expr body) + { + Contract.Requires(sorts != null); + Contract.Requires(names != null); + Contract.Requires(body != null); + Contract.Requires(sorts.Length == names.Length); + Contract.Requires(Contract.ForAll(sorts, s => s != null)); + Contract.Requires(Contract.ForAll(names, n => n != null)); + Contract.Ensures(Contract.Result() != null); + return new Lambda(this, sorts, names, body); + } + + /// + /// Create a lambda expression. + /// + /// + /// Creates a lambda expression using a list of constants that will + /// form the set of bound variables. + /// + /// + public Lambda MkLambda(Expr[] boundConstants, Expr body) + { + Contract.Requires(body != null); + Contract.Requires(boundConstants != null && Contract.ForAll(boundConstants, b => b != null)); + Contract.Ensures(Contract.Result() != null); + return new Lambda(this, boundConstants, body); + } + + #endregion #endregion // Expr @@ -4139,7 +4186,7 @@ namespace Microsoft.Z3 public FPNum MkFPInf(FPSort s, bool negative) { Contract.Ensures(Contract.Result() != null); - return new FPNum(this, Native.Z3_mk_fpa_inf(nCtx, s.NativeObject, negative ? 1 : 0)); + return new FPNum(this, Native.Z3_mk_fpa_inf(nCtx, s.NativeObject, (byte)(negative ? 1 : 0))); } /// @@ -4150,7 +4197,7 @@ namespace Microsoft.Z3 public FPNum MkFPZero(FPSort s, bool negative) { Contract.Ensures(Contract.Result() != null); - return new FPNum(this, Native.Z3_mk_fpa_zero(nCtx, s.NativeObject, negative ? 1 : 0)); + return new FPNum(this, Native.Z3_mk_fpa_zero(nCtx, s.NativeObject, (byte)(negative ? 1 : 0))); } /// @@ -4196,7 +4243,7 @@ namespace Microsoft.Z3 public FPNum MkFPNumeral(bool sgn, uint sig, int exp, FPSort s) { Contract.Ensures(Contract.Result() != null); - return new FPNum(this, Native.Z3_mk_fpa_numeral_int_uint(nCtx, sgn ? 1 : 0, exp, sig, s.NativeObject)); + return new FPNum(this, Native.Z3_mk_fpa_numeral_int_uint(nCtx, (byte)(sgn ? 1 : 0), exp, sig, s.NativeObject)); } /// @@ -4209,7 +4256,7 @@ namespace Microsoft.Z3 public FPNum MkFPNumeral(bool sgn, Int64 exp, UInt64 sig, FPSort s) { Contract.Ensures(Contract.Result() != null); - return new FPNum(this, Native.Z3_mk_fpa_numeral_int64_uint64(nCtx, sgn ? 1 : 0, exp, sig, s.NativeObject)); + return new FPNum(this, Native.Z3_mk_fpa_numeral_int64_uint64(nCtx, (byte)(sgn ? 1 : 0), exp, sig, s.NativeObject)); } /// diff --git a/src/api/dotnet/Expr.cs b/src/api/dotnet/Expr.cs index f09eecbdd..849d1af79 100644 --- a/src/api/dotnet/Expr.cs +++ b/src/api/dotnet/Expr.cs @@ -239,7 +239,7 @@ namespace Microsoft.Z3 /// public bool IsAlgebraicNumber { - get { return Native.Z3_is_algebraic_number(Context.nCtx, NativeObject) != 0; } + get { return 0 != Native.Z3_is_algebraic_number(Context.nCtx, NativeObject); } } #endregion @@ -789,7 +789,7 @@ namespace Microsoft.Z3 /// Check whether expression is a string constant. /// /// a Boolean - public bool IsString { get { return IsApp && 0 != Native.Z3_is_string(Context.nCtx, NativeObject); } } + public bool IsString { get { return IsApp && Native.Z3_is_string(Context.nCtx, NativeObject) != 0; } } /// /// Retrieve string corresponding to string constant. @@ -1458,7 +1458,7 @@ namespace Microsoft.Z3 { get { - return (Native.Z3_is_app(Context.nCtx, NativeObject) != 0 && + return (Native.Z3_is_app(Context.nCtx, NativeObject) != 0 && Native.Z3_get_sort_kind(Context.nCtx, Native.Z3_get_sort(Context.nCtx, NativeObject)) == (uint)Z3_sort_kind.Z3_FINITE_DOMAIN_SORT); } } @@ -1822,11 +1822,12 @@ namespace Microsoft.Z3 IntPtr s = Native.Z3_get_sort(ctx.nCtx, obj); Z3_sort_kind sk = (Z3_sort_kind)Native.Z3_get_sort_kind(ctx.nCtx, s); - if (Native.Z3_is_algebraic_number(ctx.nCtx, obj) != 0) // is this a numeral ast? + if (0 != Native.Z3_is_algebraic_number(ctx.nCtx, obj)) // is this a numeral ast? return new AlgebraicNum(ctx, obj); if (Native.Z3_is_numeral_ast(ctx.nCtx, obj) != 0) { + switch (sk) { case Z3_sort_kind.Z3_INT_SORT: return new IntNum(ctx, obj); diff --git a/src/api/dotnet/FPNum.cs b/src/api/dotnet/FPNum.cs index 808752eaa..b6d349149 100644 --- a/src/api/dotnet/FPNum.cs +++ b/src/api/dotnet/FPNum.cs @@ -111,7 +111,7 @@ namespace Microsoft.Z3 /// public string Exponent(bool biased = true) { - return Native.Z3_fpa_get_numeral_exponent_string(Context.nCtx, NativeObject, biased ? 1 : 0); + return Native.Z3_fpa_get_numeral_exponent_string(Context.nCtx, NativeObject, (byte)(biased ? 1 : 0)); } /// @@ -120,7 +120,7 @@ namespace Microsoft.Z3 public Int64 ExponentInt64(bool biased = true) { Int64 result = 0; - if (Native.Z3_fpa_get_numeral_exponent_int64(Context.nCtx, NativeObject, ref result, biased ? 1 : 0) == 0) + if (Native.Z3_fpa_get_numeral_exponent_int64(Context.nCtx, NativeObject, ref result, (byte)(biased? 1 : 0)) == 0) throw new Z3Exception("Exponent is not a 64 bit integer"); return result; } @@ -133,7 +133,7 @@ namespace Microsoft.Z3 /// public BitVecExpr ExponentBV(bool biased = true) { - return new BitVecExpr(Context, Native.Z3_fpa_get_numeral_exponent_bv(Context.nCtx, NativeObject, biased ? 1 : 0)); + return new BitVecExpr(Context, Native.Z3_fpa_get_numeral_exponent_bv(Context.nCtx, NativeObject, (byte)(biased ? 1 : 0))); } /// diff --git a/src/api/dotnet/Global.cs b/src/api/dotnet/Global.cs index acf3fab32..17963b33d 100644 --- a/src/api/dotnet/Global.cs +++ b/src/api/dotnet/Global.cs @@ -91,7 +91,7 @@ namespace Microsoft.Z3 /// all contexts globally. public static void ToggleWarningMessages(bool enabled) { - Native.Z3_toggle_warning_messages((enabled) ? 1 : 0); + Native.Z3_toggle_warning_messages((byte)(enabled ? 1 : 0)); } /// diff --git a/src/api/dotnet/Goal.cs b/src/api/dotnet/Goal.cs index 03e573538..ef2e9a5da 100644 --- a/src/api/dotnet/Goal.cs +++ b/src/api/dotnet/Goal.cs @@ -251,7 +251,7 @@ namespace Microsoft.Z3 internal Goal(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } internal Goal(Context ctx, bool models, bool unsatCores, bool proofs) - : base(ctx, Native.Z3_mk_goal(ctx.nCtx, (models) ? 1 : 0, (unsatCores) ? 1 : 0, (proofs) ? 1 : 0)) + : base(ctx, Native.Z3_mk_goal(ctx.nCtx, (byte)(models ? 1 : 0), (byte)(unsatCores ? 1 : 0), (byte)(proofs ? 1 : 0))) { Contract.Requires(ctx != null); } diff --git a/src/api/dotnet/Lambda.cs b/src/api/dotnet/Lambda.cs new file mode 100644 index 000000000..b3dc6c01c --- /dev/null +++ b/src/api/dotnet/Lambda.cs @@ -0,0 +1,152 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + Lambda.cs + +Abstract: + + Z3 Managed API: Lambda + +Author: + + Christoph Wintersteiger (cwinter) 2012-03-19 + +Notes: + +--*/ + +using System; +using System.Diagnostics.Contracts; + +namespace Microsoft.Z3 +{ + /// + /// Lambda expressions. + /// + [ContractVerification(true)] + public class Lambda : ArrayExpr + { + /// + /// The number of bound variables. + /// + public uint NumBound + { + get { return Native.Z3_get_quantifier_num_bound(Context.nCtx, NativeObject); } + } + + /// + /// The symbols for the bound variables. + /// + public Symbol[] BoundVariableNames + { + get + { + Contract.Ensures(Contract.Result() != null); + + uint n = NumBound; + Symbol[] res = new Symbol[n]; + for (uint i = 0; i < n; i++) + res[i] = Symbol.Create(Context, Native.Z3_get_quantifier_bound_name(Context.nCtx, NativeObject, i)); + return res; + } + } + + /// + /// The sorts of the bound variables. + /// + public Sort[] BoundVariableSorts + { + get + { + Contract.Ensures(Contract.Result() != null); + + uint n = NumBound; + Sort[] res = new Sort[n]; + for (uint i = 0; i < n; i++) + res[i] = Sort.Create(Context, Native.Z3_get_quantifier_bound_sort(Context.nCtx, NativeObject, i)); + return res; + } + } + + /// + /// The body of the lambda. + /// + public Expr Body + { + get + { + Contract.Ensures(Contract.Result() != null); + + return new BoolExpr(Context, Native.Z3_get_quantifier_body(Context.nCtx, NativeObject)); + } + } + + /// + /// Translates (copies) the lambda to the Context . + /// + /// A context + /// A copy of the lambda which is associated with + new public Lambda Translate(Context ctx) + { + return (Lambda)base.Translate(ctx); + } + + #region Internal + [ContractVerification(false)] // F: Clousot ForAll decompilation gets confused below. Setting verification off until I fixed the bug + internal Lambda(Context ctx, Sort[] sorts, Symbol[] names, Expr body) + : base(ctx, IntPtr.Zero) + { + Contract.Requires(ctx != null); + Contract.Requires(sorts != null); + Contract.Requires(names != null); + Contract.Requires(body != null); + Contract.Requires(sorts.Length == names.Length); + Contract.Requires(Contract.ForAll(sorts, s => s != null)); + Contract.Requires(Contract.ForAll(names, n => n != null)); + Context.CheckContextMatch(sorts); + Context.CheckContextMatch(names); + Context.CheckContextMatch(body); + + if (sorts.Length != names.Length) + throw new Z3Exception("Number of sorts does not match number of names"); + + NativeObject = Native.Z3_mk_lambda(ctx.nCtx, + AST.ArrayLength(sorts), AST.ArrayToNative(sorts), + Symbol.ArrayToNative(names), + body.NativeObject); + + } + + [ContractVerification(false)] // F: Clousot ForAll decompilation gets confused below. Setting verification off until I fixed the bug + internal Lambda(Context ctx, Expr[] bound, Expr body) + : base(ctx, IntPtr.Zero) + { + Contract.Requires(ctx != null); + Contract.Requires(body != null); + + Contract.Requires(bound != null && bound.Length > 0 && Contract.ForAll(bound, n => n != null)); + + Context.CheckContextMatch(bound); + Context.CheckContextMatch(body); + + NativeObject = Native.Z3_mk_lambda_const(ctx.nCtx, + AST.ArrayLength(bound), AST.ArrayToNative(bound), + body.NativeObject); + } + + + internal Lambda(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } + +#if DEBUG + internal override void CheckNativeObject(IntPtr obj) + { + if ((Z3_ast_kind)Native.Z3_get_ast_kind(Context.nCtx, obj) != Z3_ast_kind.Z3_QUANTIFIER_AST) + throw new Z3Exception("Underlying object is not a quantifier"); + base.CheckNativeObject(obj); + } +#endif + #endregion + } +} diff --git a/src/api/dotnet/Model.cs b/src/api/dotnet/Model.cs index d11a57052..96f62c9fb 100644 --- a/src/api/dotnet/Model.cs +++ b/src/api/dotnet/Model.cs @@ -227,7 +227,7 @@ namespace Microsoft.Z3 Contract.Ensures(Contract.Result() != null); IntPtr v = IntPtr.Zero; - if (Native.Z3_model_eval(Context.nCtx, NativeObject, t.NativeObject, (completion) ? 1 : 0, ref v) == 0) + if (Native.Z3_model_eval(Context.nCtx, NativeObject, t.NativeObject, (byte)(completion ? 1 : 0), ref v) == (byte)0) throw new ModelEvaluationFailedException(); else return Expr.Create(Context, v); diff --git a/src/api/dotnet/Params.cs b/src/api/dotnet/Params.cs index 5b143d525..f0f28d8d3 100644 --- a/src/api/dotnet/Params.cs +++ b/src/api/dotnet/Params.cs @@ -35,7 +35,7 @@ namespace Microsoft.Z3 { Contract.Requires(name != null); - Native.Z3_params_set_bool(Context.nCtx, NativeObject, name.NativeObject, (value) ? 1 : 0); + Native.Z3_params_set_bool(Context.nCtx, NativeObject, name.NativeObject, (byte)(value ? 1 : 0)); return this; } @@ -90,7 +90,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); + Native.Z3_params_set_bool(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, (byte)(value ? 1 : 0)); return this; } diff --git a/src/api/dotnet/Quantifier.cs b/src/api/dotnet/Quantifier.cs index eca4e3c7e..d13ca4003 100644 --- a/src/api/dotnet/Quantifier.cs +++ b/src/api/dotnet/Quantifier.cs @@ -33,7 +33,7 @@ namespace Microsoft.Z3 /// public bool IsUniversal { - get { return Native.Z3_is_quantifier_forall(Context.nCtx, NativeObject) != 0; } + get { return 0 != Native.Z3_is_quantifier_forall(Context.nCtx, NativeObject); } } /// @@ -41,7 +41,7 @@ namespace Microsoft.Z3 /// public bool IsExistential { - get { return !IsUniversal; } + get { return 0 != Native.Z3_is_quantifier_exists(Context.nCtx, NativeObject); } } /// @@ -193,7 +193,7 @@ namespace Microsoft.Z3 if (noPatterns == null && quantifierID == null && skolemID == null) { - NativeObject = Native.Z3_mk_quantifier(ctx.nCtx, (isForall) ? 1 : 0, weight, + NativeObject = Native.Z3_mk_quantifier(ctx.nCtx, (byte)(isForall ? 1 : 0) , weight, AST.ArrayLength(patterns), AST.ArrayToNative(patterns), AST.ArrayLength(sorts), AST.ArrayToNative(sorts), Symbol.ArrayToNative(names), @@ -201,7 +201,7 @@ namespace Microsoft.Z3 } else { - NativeObject = Native.Z3_mk_quantifier_ex(ctx.nCtx, (isForall) ? 1 : 0, weight, + NativeObject = Native.Z3_mk_quantifier_ex(ctx.nCtx, (byte)(isForall ? 1 : 0), weight, AST.GetNativeObject(quantifierID), AST.GetNativeObject(skolemID), AST.ArrayLength(patterns), AST.ArrayToNative(patterns), AST.ArrayLength(noPatterns), AST.ArrayToNative(noPatterns), @@ -229,14 +229,14 @@ namespace Microsoft.Z3 if (noPatterns == null && quantifierID == null && skolemID == null) { - NativeObject = Native.Z3_mk_quantifier_const(ctx.nCtx, (isForall) ? 1 : 0, weight, + NativeObject = Native.Z3_mk_quantifier_const(ctx.nCtx, (byte)(isForall ? 1 : 0) , weight, AST.ArrayLength(bound), AST.ArrayToNative(bound), AST.ArrayLength(patterns), AST.ArrayToNative(patterns), body.NativeObject); } else { - NativeObject = Native.Z3_mk_quantifier_const_ex(ctx.nCtx, (isForall) ? 1 : 0, weight, + NativeObject = Native.Z3_mk_quantifier_const_ex(ctx.nCtx, (byte)(isForall ? 1 : 0), weight, AST.GetNativeObject(quantifierID), AST.GetNativeObject(skolemID), AST.ArrayLength(bound), AST.ArrayToNative(bound), AST.ArrayLength(patterns), AST.ArrayToNative(patterns), diff --git a/src/api/dotnet/Sort.cs b/src/api/dotnet/Sort.cs index e6f195434..e32fd1eb3 100644 --- a/src/api/dotnet/Sort.cs +++ b/src/api/dotnet/Sort.cs @@ -41,7 +41,7 @@ namespace Microsoft.Z3 (!Object.ReferenceEquals(a, null) && !Object.ReferenceEquals(b, null) && a.Context == b.Context && - Native.Z3_is_eq_sort(a.Context.nCtx, a.NativeObject, b.NativeObject) != 0); + 0 != Native.Z3_is_eq_sort(a.Context.nCtx, a.NativeObject, b.NativeObject)); } /// diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index 7f0774955..302b3d5bd 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -141,6 +141,7 @@ set(Z3_JAVA_JAR_SOURCE_FILES IntNum.java IntSort.java IntSymbol.java + Lambda.java ListSort.java Log.java ModelDecRefQueue.java diff --git a/src/api/java/Context.java b/src/api/java/Context.java index dee9dbaf6..07be64230 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -518,7 +518,7 @@ public class Context implements AutoCloseable { /** * Creates a fresh constant function declaration with a name prefixed with - * {@code prefix"}. + * {@code prefix}. * @see #mkFuncDecl(String,Sort,Sort) * @see #mkFuncDecl(String,Sort[],Sort) **/ @@ -696,7 +696,7 @@ public class Context implements AutoCloseable { } /** - * Creates the equality {@code x"/> = to every subgoal produced by evaluates to true and >> f = Function('f', IntSort(), IntSort()) + >>> x = Int('x') + >>> q = ForAll(x, f(x) == 0) + >>> q.is_exists() + False + >>> q = Exists(x, f(x) != 0) + >>> q.is_exists() + True + """ + return Z3_is_quantifier_exists(self.ctx_ref(), self.ast) + + def is_lambda(self): + """Return `True` if `self` is a lambda expression. + + >>> f = Function('f', IntSort(), IntSort()) + >>> x = Int('x') + >>> q = Lambda(x, f(x)) + >>> q.is_lambda() + True + >>> q = Exists(x, f(x) != 0) + >>> q.is_lambda() + False + """ + return Z3_is_lambda(self.ctx_ref(), self.ast) + def weight(self): """Return the weight annotation of `self`. @@ -1973,6 +2001,26 @@ def Exists(vs, body, weight=1, qid="", skid="", patterns=[], no_patterns=[]): """ return _mk_quantifier(False, vs, body, weight, qid, skid, patterns, no_patterns) +def Lambda(vs, body): + """Create a Z3 lambda expression. + + >>> f = Function('f', IntSort(), IntSort(), IntSort()) + >>> mem0 = Array('mem0', IntSort(), IntSort()) + >>> lo, hi, e, i = Ints('lo hi e i') + >>> mem1 = Lambda([i], If(And(lo <= i, i <= hi), e, mem0[i])) + >>> mem1 + Lambda(i, If(And(lo <= i, i <= hi), e, mem0[i])) + """ + ctx = body.ctx + if is_app(vs): + vs = [vs] + num_vars = len(vs) + _vs = (Ast * num_vars)() + for i in range(num_vars): + ## TODO: Check if is constant + _vs[i] = vs[i].as_ast() + return QuantifierRef(Z3_mk_lambda_const(ctx.ref(), num_vars, _vs, body.as_ast()), ctx) + ######################################### # # Arithmetic @@ -5582,7 +5630,7 @@ class FuncEntry: >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() - 3 + 1 >>> e = f_i.entry(0) >>> e.num_args() 2 @@ -5600,16 +5648,16 @@ class FuncEntry: >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() - 3 + 1 >>> e = f_i.entry(0) >>> e - [0, 1, 10] + [1, 2, 20] >>> e.num_args() 2 >>> e.arg_value(0) - 0 - >>> e.arg_value(1) 1 + >>> e.arg_value(1) + 2 >>> try: ... e.arg_value(2) ... except IndexError: @@ -5631,14 +5679,14 @@ class FuncEntry: >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() - 3 + 1 >>> e = f_i.entry(0) >>> e - [0, 1, 10] + [1, 2, 20] >>> e.num_args() 2 >>> e.value() - 10 + 20 """ return _to_expr_ref(Z3_func_entry_get_value(self.ctx.ref(), self.entry), self.ctx) @@ -5652,10 +5700,10 @@ class FuncEntry: >>> m = s.model() >>> f_i = m[f] >>> f_i.num_entries() - 3 + 1 >>> e = f_i.entry(0) >>> e.as_list() - [0, 1, 10] + [1, 2, 20] """ args = [ self.arg_value(i) for i in range(self.num_args())] args.append(self.value()) @@ -5693,7 +5741,7 @@ class FuncInterp(Z3PPObject): sat >>> m = s.model() >>> m[f] - [0 -> 1, 1 -> 1, 2 -> 0, else -> 1] + [2 -> 0, else -> 1] >>> m[f].else_value() 1 """ @@ -5713,9 +5761,9 @@ class FuncInterp(Z3PPObject): sat >>> m = s.model() >>> m[f] - [0 -> 1, 1 -> 1, 2 -> 0, else -> 1] + [2 -> 0, else -> 1] >>> m[f].num_entries() - 3 + 1 """ return int(Z3_func_interp_get_num_entries(self.ctx.ref(), self.f)) @@ -5743,14 +5791,10 @@ class FuncInterp(Z3PPObject): sat >>> m = s.model() >>> m[f] - [0 -> 1, 1 -> 1, 2 -> 0, else -> 1] + [2 -> 0, else -> 1] >>> m[f].num_entries() - 3 + 1 >>> m[f].entry(0) - [0, 1] - >>> m[f].entry(1) - [1, 1] - >>> m[f].entry(2) [2, 0] """ if idx >= self.num_entries(): @@ -5777,9 +5821,9 @@ class FuncInterp(Z3PPObject): sat >>> m = s.model() >>> m[f] - [0 -> 1, 1 -> 1, 2 -> 0, else -> 1] + [2 -> 0, else -> 1] >>> m[f].as_list() - [[0, 1], [1, 1], [2, 0], 1] + [[2, 0], 1] """ r = [ self.entry(i).as_list() for i in range(self.num_entries())] r.append(self.else_value()) @@ -5891,7 +5935,7 @@ class ModelRef(Z3PPObject): >>> m[x] 1 >>> m[f] - [1 -> 0, else -> 0] + [else -> 0] """ if __debug__: _z3_assert(isinstance(decl, FuncDeclRef) or is_const(decl), "Z3 declaration expected") @@ -6008,10 +6052,10 @@ class ModelRef(Z3PPObject): >>> m[x] 1 >>> m[f] - [1 -> 0, else -> 0] + [else -> 0] >>> for d in m: print("%s -> %s" % (d, m[d])) x -> 1 - f -> [1 -> 0, else -> 0] + f -> [else -> 0] """ if _is_int(idx): if idx >= len(self): @@ -8351,11 +8395,6 @@ def _dict2darray(decls, ctx): i = i + 1 return sz, _names, _decls -def _handle_parse_error(ex, ctx): - msg = Z3_get_parser_error(ctx.ref()) - if msg != "": - raise Z3Exception(msg) - raise ex def parse_smt2_string(s, sorts={}, decls={}, ctx=None): """Parse a string in SMT 2.0 format using the given sorts and decls. @@ -8375,10 +8414,7 @@ def parse_smt2_string(s, sorts={}, decls={}, ctx=None): ctx = _get_ctx(ctx) ssz, snames, ssorts = _dict2sarray(sorts, ctx) dsz, dnames, ddecls = _dict2darray(decls, ctx) - try: - 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) + return AstVector(Z3_parse_smtlib2_string(ctx.ref(), s, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) def parse_smt2_file(f, sorts={}, decls={}, ctx=None): """Parse a file in SMT 2.0 format using the given sorts and decls. @@ -8388,10 +8424,7 @@ 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 AstVector(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) - except Z3Exception as e: - _handle_parse_error(e, ctx) + return AstVector(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) ######################################### diff --git a/src/api/python/z3/z3printer.py b/src/api/python/z3/z3printer.py index 8fd0c182e..76614910f 100644 --- a/src/api/python/z3/z3printer.py +++ b/src/api/python/z3/z3printer.py @@ -959,8 +959,10 @@ class Formatter: ys_pp = seq3(ys, '[', ']') if a.is_forall(): header = 'ForAll' - else: + elif a.is_exists(): header = 'Exists' + else: + header = 'Lambda' return seq1(header, (ys_pp, body_pp)) def pp_expr(self, a, d, xs): diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 3e47833b4..2657e558d 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -482,6 +482,10 @@ typedef enum T1: (~ p q) [quant-intro T1]: (~ (forall (x) p) (forall (x) q)) + - Z3_OP_PR_BIND: Given a proof p, produces a proof of lambda x . p, where x are free variables in p. + T1: f + [proof-bind T1] forall (x) f + - Z3_OP_PR_DISTRIBUTIVITY: Distributivity proof object. Given that f (= or) distributes over g (= and), produces a proof for @@ -1109,6 +1113,7 @@ typedef enum { Z3_OP_PR_TRANSITIVITY_STAR, Z3_OP_PR_MONOTONICITY, Z3_OP_PR_QUANT_INTRO, + Z3_OP_PR_BIND, Z3_OP_PR_DISTRIBUTIVITY, Z3_OP_PR_AND_ELIM, Z3_OP_PR_NOT_OR_ELIM, @@ -1857,7 +1862,6 @@ extern "C" { */ Z3_sort Z3_API Z3_mk_array_sort_n(Z3_context c, unsigned n, Z3_sort const * domain, Z3_sort range); - /** \brief Create a tuple type. @@ -2946,6 +2950,8 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i); + + /** \brief n-ary Array read. The argument \c a is the array and \c idxs are the indices of the array that gets read. @@ -2955,6 +2961,7 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_select_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs); + /** \brief Array update. @@ -2973,6 +2980,7 @@ extern "C" { */ Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v); + /** \brief n-ary Array update. @@ -3763,6 +3771,54 @@ extern "C" { Z3_ast body ); + /** + \brief Create a lambda expression. It taks an expression \c body that contains bound variables + of the same sorts as the sorts listed in the array \c sorts. The bound variables are de-Bruijn indices created + using #Z3_mk_bound. The array \c decl_names contains the names that the quantified formula uses for the + bound variables. Z3 applies the convention that the last element in the \c decl_names and \c sorts array + refers to the variable with index 0, the second to last element of \c decl_names and \c sorts refers + to the variable with index 1, etc. + The sort of the resulting expression is \c (Array sorts range) where \c range is the sort of \c body. + For example, if the lambda binds two variables of sort \c Int and \c Bool, and the \c body has sort \c Real, + the sort of the expression is \c (Array Int Bool Real). + + \param c logical context + \param num_decls number of variables to be bound. + \param sorts the sorts of the bound variables. + \param decl_names names of the bound variables + \param body the body of the lambda expression. + + \sa Z3_mk_bound + \sa Z3_mk_forall + \sa Z3_mk_lambda_const + + def_API('Z3_mk_lambda', AST, (_in(CONTEXT), _in(UINT), _in_array(1, SORT), _in_array(1, SYMBOL), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_lambda(Z3_context c, + unsigned num_decls, Z3_sort const sorts[], + Z3_symbol const decl_names[], + Z3_ast body); + + /** + \brief Create a lambda expression using a list of constants that form the set + of bound variables + + \param c logical context. + \param num_bound number of constants to be abstracted into bound variables. + \param bound array of constants to be abstracted into bound variables. + \param body the body of the lambda expression. + + \sa Z3_mk_bound + \sa Z3_mk_forall + \sa Z3_mk_lambda + + def_API('Z3_mk_lambda_const', AST, (_in(CONTEXT), _in(UINT), _in_array(1, APP), _in(AST))) + */ + Z3_ast Z3_API Z3_mk_lambda_const(Z3_context c, + unsigned num_bound, Z3_app const bound[], + Z3_ast body); + + /*@}*/ /** @name Accessors */ @@ -4529,14 +4585,29 @@ extern "C" { unsigned Z3_API Z3_get_index_value(Z3_context c, Z3_ast a); /** - \brief Determine if quantifier is universal. - - \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST + \brief Determine if an ast is a universal quantifier. def_API('Z3_is_quantifier_forall', BOOL, (_in(CONTEXT), _in(AST))) */ Z3_bool Z3_API Z3_is_quantifier_forall(Z3_context c, Z3_ast a); + /** + \brief Determine if ast is an existential quantifier. + + + def_API('Z3_is_quantifier_exists', BOOL, (_in(CONTEXT), _in(AST))) + */ + Z3_bool Z3_API Z3_is_quantifier_exists(Z3_context c, Z3_ast a); + + /** + \brief Determine if ast is a lambda expresion. + + \pre Z3_get_ast_kind(a) == Z3_QUANTIFIER_AST + + def_API('Z3_is_lambda', BOOL, (_in(CONTEXT), _in(AST))) + */ + Z3_bool Z3_API Z3_is_lambda(Z3_context c, Z3_ast a); + /** \brief Obtain weight of quantifier. @@ -5209,12 +5280,6 @@ extern "C" { Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str); - /** - \brief Retrieve that last error message information generated from parsing. - - def_API('Z3_get_parser_error', STRING, (_in(CONTEXT), )) - */ - Z3_string Z3_API Z3_get_parser_error(Z3_context c); /*@}*/ /** @name Error Handling */ @@ -5261,11 +5326,6 @@ extern "C" { */ Z3_string Z3_API Z3_get_error_msg(Z3_context c, Z3_error_code err); - /** - \brief Return a string describing the given error code. - Retained function name for backwards compatibility within v4.1 - */ - Z3_string Z3_API Z3_get_error_msg_ex(Z3_context c, Z3_error_code err); /*@}*/ /** @name Miscellaneous */ @@ -5465,6 +5525,11 @@ extern "C" { /** \brief Convert a goal into a DIMACS formatted string. + The goal must be in CNF. You can convert a goal to CNF + by applying the tseitin-cnf tactic. Bit-vectors are not automatically + converted to Booleans either, so the caller intends to + preserve satisfiability, it should apply bit-blasting tactics. + Quantifiers and theory atoms will not be encoded. def_API('Z3_goal_to_dimacs_string', STRING, (_in(CONTEXT), _in(GOAL))) */ diff --git a/src/api/z3_logger.h b/src/api/z3_logger.h index 357d79bcb..211601713 100644 --- a/src/api/z3_logger.h +++ b/src/api/z3_logger.h @@ -49,7 +49,7 @@ void __declspec(noinline) _Z3_append_log(char const * msg) { *g_z3_log << "M \"" static std::ostream & operator<<(std::ostream & out, ll_escaped const & d) { char const * s = d.m_str; while (*s) { - char c = *s; + unsigned char c = *s; if (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '~' || c == '!' || c == '@' || c == '#' || c == '$' || c == '%' || c == '^' || c == '&' || c == '*' || c == '-' || c == '_' || c == '+' || c == '.' || c == '?' || c == '/' || c == ' ' || @@ -57,7 +57,7 @@ static std::ostream & operator<<(std::ostream & out, ll_escaped const & d) { out << c; } else { - char str[4] = {'0', '0', '0', 0}; + unsigned char str[4] = {'0', '0', '0', 0}; str[2] = '0' + (c % 10); c /= 10; str[1] = '0' + (c % 10); diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index fe5bc3af5..be3a02019 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -638,6 +638,11 @@ bool arith_recognizers::is_numeral(expr const * n, rational & val, bool & is_int return true; } +bool arith_recognizers::is_irrational_algebraic_numeral(expr const * n) const { + return is_app(n) && to_app(n)->is_app_of(m_afid, OP_IRRATIONAL_ALGEBRAIC_NUM); +} + + #define IS_INT_EXPR_DEPTH_LIMIT 100 bool arith_recognizers::is_int_expr(expr const *e) const { if (is_int(e)) return true; @@ -678,7 +683,7 @@ void arith_util::init_plugin() { m_plugin = static_cast(m_manager.get_plugin(m_afid)); } -bool arith_util::is_irrational_algebraic_numeral(expr const * n, algebraic_numbers::anum & val) { +bool arith_util::is_irrational_algebraic_numeral2(expr const * n, algebraic_numbers::anum & val) { if (!is_app_of(n, m_afid, OP_IRRATIONAL_ALGEBRAIC_NUM)) return false; am().set(val, to_irrational_algebraic_numeral(n)); diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index d7340297b..f5f82b5fd 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -229,7 +229,7 @@ public: family_id get_family_id() const { return m_afid; } bool is_arith_expr(expr const * n) const { return is_app(n) && to_app(n)->get_family_id() == m_afid; } - bool is_irrational_algebraic_numeral(expr const * n) const { return is_app_of(n, m_afid, OP_IRRATIONAL_ALGEBRAIC_NUM); } + bool is_irrational_algebraic_numeral(expr const * n) const; bool is_numeral(expr const * n, rational & val, bool & is_int) const; bool is_numeral(expr const * n, rational & val) const { bool is_int; return is_numeral(n, val, is_int); } bool is_numeral(expr const * n) const { return is_app_of(n, m_afid, OP_NUM); } @@ -338,8 +338,7 @@ public: return plugin().am(); } - bool is_irrational_algebraic_numeral(expr const * n) const { return is_app_of(n, m_afid, OP_IRRATIONAL_ALGEBRAIC_NUM); } - bool is_irrational_algebraic_numeral(expr const * n, algebraic_numbers::anum & val); + bool is_irrational_algebraic_numeral2(expr const * n, algebraic_numbers::anum & val); algebraic_numbers::anum const & to_irrational_algebraic_numeral(expr const * n); sort * mk_int() { return m_manager.mk_sort(m_afid, INT_SORT); } @@ -535,3 +534,4 @@ inline app_ref operator>(app_ref const& x, app_ref const& y) { } #endif /* ARITH_DECL_PLUGIN_H_ */ + diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 681f64a25..ac7ce156d 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -26,6 +26,9 @@ Revision History: #include "util/string_buffer.h" #include "ast/ast_util.h" #include "ast/ast_smt2_pp.h" +#include "ast/array_decl_plugin.h" +#include "ast/ast_translation.h" + // ----------------------------------- // @@ -360,13 +363,14 @@ app::app(func_decl * decl, unsigned num_args, expr * const * args): // // ----------------------------------- -quantifier::quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, +quantifier::quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, sort* s, int weight, symbol const & qid, symbol const & skid, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns): expr(AST_QUANTIFIER), - m_forall(forall), + m_kind(k), m_num_decls(num_decls), m_expr(body), + m_sort(s), m_depth(::get_depth(body) + 1), m_weight(weight), m_has_unused_vars(true), @@ -385,6 +389,25 @@ quantifier::quantifier(bool forall, unsigned num_decls, sort * const * decl_sort memcpy(const_cast(get_no_patterns()), no_patterns, sizeof(expr *) * num_no_patterns); } +quantifier::quantifier(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, sort* s): + expr(AST_QUANTIFIER), + m_kind(lambda_k), + m_num_decls(num_decls), + m_expr(body), + m_sort(s), + m_depth(::get_depth(body) + 1), + m_weight(0), + m_has_unused_vars(true), + m_has_labels(::has_labels(body)), + m_qid(symbol()), + m_skid(symbol()), + m_num_patterns(0), + m_num_no_patterns(0) { + memcpy(const_cast(get_decl_sorts()), decl_sorts, sizeof(sort *) * num_decls); + memcpy(const_cast(get_decl_names()), decl_names, sizeof(symbol) * num_decls); +} + + // ----------------------------------- // // Auxiliary functions @@ -392,21 +415,16 @@ quantifier::quantifier(bool forall, unsigned num_decls, sort * const * decl_sort // ----------------------------------- sort * get_sort(expr const * n) { - while (true) { - switch(n->get_kind()) { - case AST_APP: - return to_app(n)->get_decl()->get_range(); - case AST_VAR: - return to_var(n)->get_sort(); - case AST_QUANTIFIER: - // The sort of the quantifier is the sort of the nested expression. - // This code assumes the given expression is well-sorted. - n = to_quantifier(n)->get_expr(); - break; - default: - UNREACHABLE(); - return nullptr; - } + switch(n->get_kind()) { + case AST_APP: + return to_app(n)->get_decl()->get_range(); + case AST_VAR: + return to_var(n)->get_sort(); + case AST_QUANTIFIER: + return to_quantifier(n)->get_sort(); + default: + UNREACHABLE(); + return 0; } } @@ -466,7 +484,7 @@ bool compare_nodes(ast const * n1, ast const * n2) { to_var(n1)->get_sort() == to_var(n2)->get_sort(); case AST_QUANTIFIER: return - to_quantifier(n1)->is_forall() == to_quantifier(n2)->is_forall() && + to_quantifier(n1)->get_kind() == to_quantifier(n2)->get_kind() && to_quantifier(n1)->get_num_decls() == to_quantifier(n2)->get_num_decls() && compare_arrays(to_quantifier(n1)->get_decl_sorts(), to_quantifier(n2)->get_decl_sorts(), @@ -566,7 +584,7 @@ unsigned get_node_hash(ast const * n) { case AST_QUANTIFIER: a = ast_array_hash(to_quantifier(n)->get_decl_sorts(), to_quantifier(n)->get_num_decls(), - to_quantifier(n)->is_forall() ? 31 : 19); + to_quantifier(n)->get_kind() == forall_k ? 31 : 19); b = to_quantifier(n)->get_num_patterns(); c = to_quantifier(n)->get_expr()->hash(); mix(a,b,c); @@ -689,7 +707,8 @@ bool basic_decl_plugin::check_proof_sorts(basic_op_kind k, unsigned arity, sort for (unsigned i = 0; i < arity - 1; i++) if (domain[i] != m_proof_sort) return false; - return domain[arity-1] == m_bool_sort || domain[arity-1] == m_proof_sort; +#define is_array(_x_) true + return domain[arity-1] == m_bool_sort || domain[arity-1] == m_proof_sort || is_array(domain[arity-1]); } } @@ -704,7 +723,8 @@ bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, exp return false; return m_manager->get_sort(args[num_args - 1]) == m_bool_sort || - m_manager->get_sort(args[num_args - 1]) == m_proof_sort; + m_manager->get_sort(args[num_args - 1]) == m_proof_sort || + is_lambda(args[num_args-1]); } } @@ -818,6 +838,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_paren case PR_TRANSITIVITY_STAR: return mk_proof_decl("trans*", k, num_parents, m_transitivity_star_decls); case PR_MONOTONICITY: return mk_proof_decl("monotonicity", k, num_parents, m_monotonicity_decls); case PR_QUANT_INTRO: return mk_proof_decl("quant-intro", k, 1, m_quant_intro_decl); + case PR_BIND: UNREACHABLE(); case PR_DISTRIBUTIVITY: return mk_proof_decl("distributivity", k, num_parents, m_distributivity_decls); case PR_AND_ELIM: return mk_proof_decl("and-elim", k, 1, m_and_elim_decl); case PR_NOT_OR_ELIM: return mk_proof_decl("not-or-elim", k, 1, m_not_or_elim_decl); @@ -1054,6 +1075,10 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters // eq and oeq must have at least two arguments, they can have more since they are chainable case OP_EQ: return arity >= 2 ? mk_eq_decl_core("=", OP_EQ, join(arity, domain), m_eq_decls) : nullptr; case OP_OEQ: return arity >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(arity, domain), m_oeq_decls) : nullptr; + case PR_BIND: { + func_decl_info info(m_family_id, PR_BIND); + return m_manager->mk_func_decl(symbol("proof-bind"), arity, domain, m_proof_sort, info); + } case OP_DISTINCT: { func_decl_info info(m_family_id, OP_DISTINCT); info.set_pairwise(); @@ -1097,6 +1122,11 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_OEQ: return num_args >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(num_args, args), m_oeq_decls) : nullptr; case OP_DISTINCT: return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range); + case PR_BIND: { + ptr_buffer sorts; + for (unsigned i = 0; i < num_args; ++i) sorts.push_back(m_manager->get_sort(args[i])); + return mk_func_decl(k, num_parameters, parameters, num_args, sorts.c_ptr(), range); + } default: break; } @@ -1218,7 +1248,6 @@ func_decl * model_value_decl_plugin::mk_func_decl(decl_kind k, unsigned num_para unsigned arity, sort * const * domain, sort * range) { SASSERT(k == OP_MODEL_VALUE); if (arity != 0 || num_parameters != 2 || !parameters[0].is_int() || !parameters[1].is_ast() || !is_sort(parameters[1].get_ast())) { - UNREACHABLE(); m_manager->raise_exception("invalid model value"); return nullptr; } @@ -1290,7 +1319,8 @@ ast_manager::ast_manager(proof_gen_mode m, char const * trace_file, bool is_form m_proof_mode(m), m_trace_stream(nullptr), m_trace_stream_owner(false), - m_rec_fun(":rec-fun") { + m_rec_fun(":rec-fun"), + m_lambda_def(":lambda-def") { if (trace_file) { m_trace_stream = alloc(std::fstream, trace_file, std::ios_base::out); @@ -1312,7 +1342,8 @@ ast_manager::ast_manager(proof_gen_mode m, std::fstream * trace_stream, bool is_ m_proof_mode(m), m_trace_stream(trace_stream), m_trace_stream_owner(false), - m_rec_fun(":rec-fun") { + m_rec_fun(":rec-fun"), + m_lambda_def(":lambda-def") { if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, trace_stream, true); @@ -1340,6 +1371,7 @@ 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; @@ -1508,7 +1540,6 @@ void ast_manager::raise_exception(char const * msg) { throw ast_exception(msg); } -#include "ast/ast_translation.h" void ast_manager::copy_families_plugins(ast_manager const & from) { TRACE("copy_families_plugins", @@ -1761,6 +1792,7 @@ ast * ast_manager::register_node_core(ast * n) { case AST_QUANTIFIER: inc_array_ref(to_quantifier(n)->get_num_decls(), to_quantifier(n)->get_decl_sorts()); inc_ref(to_quantifier(n)->get_expr()); + inc_ref(to_quantifier(n)->get_sort()); inc_array_ref(to_quantifier(n)->get_num_patterns(), to_quantifier(n)->get_patterns()); inc_array_ref(to_quantifier(n)->get_num_no_patterns(), to_quantifier(n)->get_no_patterns()); break; @@ -1824,6 +1856,7 @@ void ast_manager::delete_node(ast * n) { case AST_QUANTIFIER: dec_array_ref(worklist, to_quantifier(n)->get_num_decls(), to_quantifier(n)->get_decl_sorts()); dec_ref(worklist, to_quantifier(n)->get_expr()); + dec_ref(worklist, to_quantifier(n)->get_sort()); dec_array_ref(worklist, to_quantifier(n)->get_num_patterns(), to_quantifier(n)->get_patterns()); dec_array_ref(worklist, to_quantifier(n)->get_num_no_patterns(), to_quantifier(n)->get_no_patterns()); break; @@ -2338,7 +2371,7 @@ bool ast_manager::is_pattern(expr const * n, ptr_vector &args) { } -quantifier * ast_manager::mk_quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, +quantifier * ast_manager::mk_quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight , symbol const & qid, symbol const & skid, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns) { @@ -2353,7 +2386,17 @@ quantifier * ast_manager::mk_quantifier(bool forall, unsigned num_decls, sort * }}); unsigned sz = quantifier::get_obj_size(num_decls, num_patterns, num_no_patterns); void * mem = allocate_node(sz); - quantifier * new_node = new (mem) quantifier(forall, num_decls, decl_sorts, decl_names, body, + + sort* s = nullptr; + if (k == lambda_k) { + array_util autil(*this); + s = autil.mk_array_sort(num_decls, decl_sorts, ::get_sort(body)); + } + else { + s = mk_bool_sort(); + } + + quantifier * new_node = new (mem) quantifier(k, num_decls, decl_sorts, decl_names, body, s, weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); quantifier * r = register_node(new_node); @@ -2370,6 +2413,18 @@ quantifier * ast_manager::mk_quantifier(bool forall, unsigned num_decls, sort * return r; } +quantifier * ast_manager::mk_lambda(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body) { + SASSERT(body); + unsigned sz = quantifier::get_obj_size(num_decls, 0, 0); + void * mem = allocate_node(sz); + array_util autil(*this); + sort* s = autil.mk_array_sort(num_decls, decl_sorts, ::get_sort(body)); + quantifier * new_node = new (mem) quantifier(num_decls, decl_sorts, decl_names, body, s); + quantifier * r = register_node(new_node); + return r; +} + + // Return true if the patterns of q are the given ones. static bool same_patterns(quantifier * q, unsigned num_patterns, expr * const * patterns) { if (num_patterns != q->get_num_patterns()) @@ -2393,7 +2448,7 @@ static bool same_no_patterns(quantifier * q, unsigned num_no_patterns, expr * co quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_patterns, expr * const * patterns, expr * body) { if (q->get_expr() == body && same_patterns(q, num_patterns, patterns)) return q; - return mk_quantifier(q->is_forall(), + return mk_quantifier(q->get_kind(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), @@ -2410,7 +2465,7 @@ quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_pattern quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns, expr * body) { if (q->get_expr() == body && same_patterns(q, num_patterns, patterns) && same_no_patterns(q, num_no_patterns, no_patterns)) return q; - return mk_quantifier(q->is_forall(), + return mk_quantifier(q->get_kind(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), @@ -2427,7 +2482,7 @@ quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_pattern quantifier * ast_manager::update_quantifier(quantifier * q, expr * body) { if (q->get_expr() == body) return q; - return mk_quantifier(q->is_forall(), + return mk_quantifier(q->get_kind(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), @@ -2445,7 +2500,7 @@ quantifier * ast_manager::update_quantifier_weight(quantifier * q, int w) { if (q->get_weight() == w) return q; TRACE("update_quantifier_weight", tout << "#" << q->get_id() << " " << q->get_weight() << " -> " << w << "\n";); - return mk_quantifier(q->is_forall(), + return mk_quantifier(q->get_kind(), q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), @@ -2459,10 +2514,10 @@ quantifier * ast_manager::update_quantifier_weight(quantifier * q, int w) { q->get_no_patterns()); } -quantifier * ast_manager::update_quantifier(quantifier * q, bool is_forall, expr * body) { - if (q->get_expr() == body && q->is_forall() == is_forall) +quantifier * ast_manager::update_quantifier(quantifier * q, quantifier_kind k, expr * body) { + if (q->get_expr() == body && q->get_kind() == k) return q; - return mk_quantifier(is_forall, + return mk_quantifier(k, q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), @@ -2476,10 +2531,10 @@ quantifier * ast_manager::update_quantifier(quantifier * q, bool is_forall, expr q->get_no_patterns()); } -quantifier * ast_manager::update_quantifier(quantifier * q, bool is_forall, unsigned num_patterns, expr * const * patterns, expr * body) { - if (q->get_expr() == body && q->is_forall() == is_forall && same_patterns(q, num_patterns, patterns)) +quantifier * ast_manager::update_quantifier(quantifier * q, quantifier_kind k, unsigned num_patterns, expr * const * patterns, expr * body) { + if (q->get_expr() == body && q->get_kind() == k && same_patterns(q, num_patterns, patterns)) return q; - return mk_quantifier(is_forall, + return mk_quantifier(k, q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), @@ -2786,11 +2841,17 @@ proof * ast_manager::mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, return mk_monotonicity(mk_func_decl(m_basic_family_id, OP_OEQ, 0, nullptr, 2, d), f1, f2, num_proofs, proofs); } + +proof * ast_manager::mk_bind_proof(quantifier * q, proof * p) { + expr* b = mk_lambda(q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), p); + return mk_app(m_basic_family_id, PR_BIND, b); +} + proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) { if (!p) return nullptr; SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); - SASSERT(is_eq(get_fact(p))); + SASSERT(is_eq(get_fact(p)) || is_lambda(get_fact(p))); return mk_app(m_basic_family_id, PR_QUANT_INTRO, p, mk_iff(q1, q2)); } @@ -2798,7 +2859,7 @@ proof * ast_manager::mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof if (!p) return nullptr; SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); - SASSERT(is_oeq(get_fact(p))); + SASSERT(is_oeq(get_fact(p)) || is_lambda(get_fact(p))); return mk_app(m_basic_family_id, PR_QUANT_INTRO, p, mk_oeq(q1, q2)); } diff --git a/src/ast/ast.h b/src/ast/ast.h index b6b71c6a3..aa8669def 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -793,11 +793,18 @@ public: // // ----------------------------------- +enum quantifier_kind { + forall_k, + exists_k, + lambda_k +}; + class quantifier : public expr { friend class ast_manager; - bool m_forall; + quantifier_kind m_kind; unsigned m_num_decls; expr * m_expr; + sort * m_sort; unsigned m_depth; // extra fields int m_weight; @@ -813,12 +820,18 @@ class quantifier : public expr { return sizeof(quantifier) + num_decls * (sizeof(sort *) + sizeof(symbol)) + (num_patterns + num_no_patterns) * sizeof(expr*); } - quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, + quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, sort* s, int weight, symbol const & qid, symbol const & skid, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns); + + quantifier(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, sort* sort); + public: - bool is_forall() const { return m_forall; } - bool is_exists() const { return !m_forall; } + quantifier_kind get_kind() const { return m_kind; } +// bool is_forall() const { return m_kind == forall_k; } +// bool is_exists() const { return m_kind == exists_k; } +// bool is_lambda() const { return m_kind == lambda_k; } + unsigned get_num_decls() const { return m_num_decls; } sort * const * get_decl_sorts() const { return reinterpret_cast(m_patterns_decls); } symbol const * get_decl_names() const { return reinterpret_cast(get_decl_sorts() + m_num_decls); } @@ -826,6 +839,8 @@ public: symbol const & get_decl_name(unsigned idx) const { return get_decl_names()[idx]; } expr * get_expr() const { return m_expr; } + sort * get_sort() const { return m_sort; } + unsigned get_depth() const { return m_depth; } int get_weight() const { return m_weight; } @@ -844,6 +859,7 @@ public: void set_no_unused_vars() { m_has_unused_vars = false; } bool has_labels() const { return m_has_labels; } + unsigned get_num_children() const { return 1 + get_num_patterns() + get_num_no_patterns(); } expr * get_child(unsigned idx) const { @@ -871,8 +887,9 @@ inline bool is_app(ast const * n) { return n->get_kind() == AST_APP; } inline bool is_var(ast const * n) { return n->get_kind() == AST_VAR; } inline bool is_var(ast const * n, unsigned& idx) { return is_var(n) && (idx = static_cast(n)->get_idx(), true); } inline bool is_quantifier(ast const * n) { return n->get_kind() == AST_QUANTIFIER; } -inline bool is_forall(ast const * n) { return is_quantifier(n) && static_cast(n)->is_forall(); } -inline bool is_exists(ast const * n) { return is_quantifier(n) && static_cast(n)->is_exists(); } +inline bool is_forall(ast const * n) { return is_quantifier(n) && static_cast(n)->get_kind() == forall_k; } +inline bool is_exists(ast const * n) { return is_quantifier(n) && static_cast(n)->get_kind() == exists_k; } +inline bool is_lambda(ast const * n) { return is_quantifier(n) && static_cast(n)->get_kind() == lambda_k; } // ----------------------------------- // @@ -950,7 +967,7 @@ protected: family_id m_family_id; virtual void set_manager(ast_manager * m, family_id id) { - SASSERT(m_manager == 0); + SASSERT(m_manager == nullptr); m_manager = m; m_family_id = id; } @@ -1043,7 +1060,7 @@ enum basic_sort_kind { enum basic_op_kind { OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, LAST_BASIC_OP, - PR_UNDEF, PR_TRUE, PR_ASSERTED, PR_GOAL, PR_MODUS_PONENS, PR_REFLEXIVITY, PR_SYMMETRY, PR_TRANSITIVITY, PR_TRANSITIVITY_STAR, PR_MONOTONICITY, PR_QUANT_INTRO, + PR_UNDEF, PR_TRUE, PR_ASSERTED, PR_GOAL, PR_MODUS_PONENS, PR_REFLEXIVITY, PR_SYMMETRY, PR_TRANSITIVITY, PR_TRANSITIVITY_STAR, PR_MONOTONICITY, PR_QUANT_INTRO, PR_BIND, PR_DISTRIBUTIVITY, PR_AND_ELIM, PR_NOT_OR_ELIM, PR_REWRITE, PR_REWRITE_STAR, PR_PULL_QUANT, PR_PUSH_QUANT, PR_ELIM_UNUSED_VARS, PR_DER, PR_QUANT_INST, @@ -1487,6 +1504,7 @@ protected: #endif ast_manager * m_format_manager; // hack for isolating format objects in a different manager. symbol m_rec_fun; + symbol m_lambda_def; void init(); @@ -1594,9 +1612,12 @@ public: bool contains(ast * a) const { return m_ast_table.contains(a); } bool is_rec_fun_def(quantifier* q) const { return q->get_qid() == m_rec_fun; } + bool is_lambda_def(quantifier* q) const { return q->get_qid() == m_lambda_def; } symbol const& rec_fun_qid() const { return m_rec_fun; } + symbol const& lambda_def_qid() const { return m_lambda_def; } + unsigned get_num_asts() const { return m_ast_table.size(); } void debug_ref_count() { m_debug_ref_count = true; } @@ -1892,7 +1913,7 @@ public: public: - quantifier * mk_quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, + quantifier * mk_quantifier(quantifier_kind k, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, unsigned num_patterns = 0, expr * const * patterns = nullptr, unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr); @@ -1901,7 +1922,7 @@ public: int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, unsigned num_patterns = 0, expr * const * patterns = nullptr, unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr) { - return mk_quantifier(true, num_decls, decl_sorts, decl_names, body, weight, qid, skid, num_patterns, patterns, + return mk_quantifier(forall_k, num_decls, decl_sorts, decl_names, body, weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); } @@ -1909,10 +1930,12 @@ public: int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, unsigned num_patterns = 0, expr * const * patterns = nullptr, unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr) { - return mk_quantifier(false, num_decls, decl_sorts, decl_names, body, weight, qid, skid, num_patterns, patterns, + return mk_quantifier(exists_k, num_decls, decl_sorts, decl_names, body, weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); } + quantifier * mk_lambda(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body); + quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body); quantifier * update_quantifier(quantifier * q, unsigned new_num_patterns, expr * const * new_patterns, unsigned new_num_no_patterns, expr * const * new_no_patterns, expr * new_body); @@ -1921,9 +1944,9 @@ public: quantifier * update_quantifier_weight(quantifier * q, int new_weight); - quantifier * update_quantifier(quantifier * q, bool new_is_forall, expr * new_body); + quantifier * update_quantifier(quantifier * q, quantifier_kind new_kind, expr * new_body); - quantifier * update_quantifier(quantifier * q, bool new_is_forall, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body); + quantifier * update_quantifier(quantifier * q, quantifier_kind new_kind, unsigned new_num_patterns, expr * const * new_patterns, expr * new_body); // ----------------------------------- // @@ -2213,6 +2236,7 @@ public: proof * mk_rewrite(expr * s, expr * t); proof * mk_oeq_rewrite(expr * s, expr * t); proof * mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); + proof * mk_bind_proof(quantifier * q, proof * p); proof * mk_pull_quant(expr * e, quantifier * q); proof * mk_push_quant(quantifier * q, expr * e); proof * mk_elim_unused_vars(quantifier * q, expr * r); diff --git a/src/ast/ast_ll_pp.cpp b/src/ast/ast_ll_pp.cpp index cbdd94efb..7e2478347 100644 --- a/src/ast/ast_ll_pp.cpp +++ b/src/ast/ast_ll_pp.cpp @@ -242,7 +242,7 @@ public: void operator()(quantifier * n) { display_def_header(n); - m_out << "(" << (n->is_forall() ? "forall" : "exists") << " "; + m_out << "(" << (n->get_kind() == forall_k ? "forall" : (n->get_kind() == exists_k ? "exists" : "lambda")) << " "; unsigned num_decls = n->get_num_decls(); m_out << "(vars "; for (unsigned i = 0; i < num_decls; i++) { diff --git a/src/ast/ast_lt.cpp b/src/ast/ast_lt.cpp index b6b52966e..a8d703259 100644 --- a/src/ast/ast_lt.cpp +++ b/src/ast/ast_lt.cpp @@ -102,7 +102,7 @@ bool lt(ast * n1, ast * n2) { UNREACHABLE(); return false; case AST_QUANTIFIER: - check_bool(to_quantifier(n1)->is_forall(), to_quantifier(n2)->is_forall()); + check_value(to_quantifier(n1)->get_kind(), to_quantifier(n2)->get_kind()); check_value(to_quantifier(n1)->get_num_decls(), to_quantifier(n2)->get_num_decls()); check_value(to_quantifier(n1)->get_num_patterns(), to_quantifier(n2)->get_num_patterns()); check_value(to_quantifier(n1)->get_num_no_patterns(), to_quantifier(n2)->get_num_no_patterns()); diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 9d27ffb24..3a55bbb0b 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -994,7 +994,7 @@ class smt2_printer { } format * f_decls = pp_var_decls(q); format * fs[2] = { f_decls, f_body }; - char const * header = q->is_forall() ? "forall" : "exists"; + char const * header = q->get_kind() == forall_k ? "forall" : (q->get_kind() == exists_k ? "exists" : "lambda"); format * f = mk_seq3(m(), fs, fs+2, f2f(), header, 1, SMALL_INDENT); info f_info = m_info_stack.back(); @@ -1041,6 +1041,10 @@ class smt2_printer { } void process(expr * n, format_ref & r) { + if (!n) { + r = mk_string(m(), "null"); + return; + } reset_stacks(); SASSERT(&(r.get_manager()) == &(fm())); m_soccs(n); @@ -1126,6 +1130,10 @@ public: } void operator()(func_decl * f, format_ref & r, char const* cmd) { + if (!f) { + r = mk_string(m(), "null"); + return; + } unsigned arity = f->get_arity(); unsigned len; format * fname = m_env.pp_fdecl_name(f, len); @@ -1189,15 +1197,16 @@ void mk_smt2_format(unsigned sz, expr * const* es, smt2_pp_environment & env, pa format_ref_vector fmts(fm(m)); for (unsigned i = 0; i < sz; ++i) { - format_ref fr(fm(m)); + format_ref fr(fm(m)); pr(es[i], num_vars, var_prefix, fr, var_names); - fmts.push_back(fr); + fmts.push_back(std::move(fr)); } r = mk_seq(m, fmts.c_ptr(), fmts.c_ptr() + fmts.size(), f2f()); } std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & env, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix) { + if (!n) return out << "null"; ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; @@ -1209,6 +1218,7 @@ std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & e } std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p, unsigned indent) { + if (s == nullptr) return out << "null"; ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; @@ -1220,6 +1230,7 @@ std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & e } std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { + if (!f) return out << "null"; ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; @@ -1231,6 +1242,7 @@ std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environmen } 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) { + if (!f) return out << "null"; ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 22adf42d9..2cce53e3a 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -495,11 +495,10 @@ class smt_printer { m_qlists.push_back(q); m_out << "("; - if (q->is_forall()) { - m_out << "forall "; - } - else { - m_out << "exists "; + switch (q->get_kind()) { + case forall_k: m_out << "forall "; break; + case exists_k: m_out << "exists "; break; + case lambda_k: m_out << "lambda "; break; } m_out << "("; for (unsigned i = 0; i < q->get_num_decls(); ++i) { diff --git a/src/ast/ast_translation.cpp b/src/ast/ast_translation.cpp index 5ab6ab351..278c2dff9 100644 --- a/src/ast/ast_translation.cpp +++ b/src/ast/ast_translation.cpp @@ -279,7 +279,7 @@ ast * ast_translation::process(ast const * _n) { expr ** pats = reinterpret_cast(m_result_stack.c_ptr() + fr.m_rpos + num_decls + 1); unsigned num_no_pats = to_quantifier(n)->get_num_no_patterns(); expr ** no_pats = pats + num_pats; - quantifier * new_q = m_to_manager.mk_quantifier(to_quantifier(n)->is_forall(), + quantifier * new_q = m_to_manager.mk_quantifier(to_quantifier(n)->get_kind(), num_decls, dsorts, dnames, diff --git a/src/ast/ast_util.cpp b/src/ast/ast_util.cpp index 42c0d698b..601eee58e 100644 --- a/src/ast/ast_util.cpp +++ b/src/ast/ast_util.cpp @@ -128,11 +128,9 @@ bool is_clause(ast_manager & m, expr * n) { if (is_literal(m, n)) return true; if (m.is_or(n)) { - unsigned num_args = to_app(n)->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - if (!is_literal(m, to_app(n)->get_arg(i))) - return false; - } + for (expr* arg : *to_app(n)) + if (!is_literal(m, arg)) + return false; return true; } return false; @@ -211,8 +209,8 @@ expr_ref push_not(const expr_ref& e) { return expr_ref(m.mk_false(), m); } expr_ref_vector args(m); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - args.push_back(push_not(expr_ref(a->get_arg(i), m))); + for (expr* arg : *a) { + args.push_back(push_not(expr_ref(arg, m))); } return mk_or(args); } @@ -221,8 +219,8 @@ expr_ref push_not(const expr_ref& e) { return expr_ref(m.mk_true(), m); } expr_ref_vector args(m); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - args.push_back(push_not(expr_ref(a->get_arg(i), m))); + for (expr* arg : *a) { + args.push_back(push_not(expr_ref(arg, m))); } return mk_and(args); } @@ -260,44 +258,38 @@ void flatten_and(expr_ref_vector& result) { ast_manager& m = result.get_manager(); expr* e1, *e2, *e3; for (unsigned i = 0; i < result.size(); ++i) { - if (m.is_and(result[i].get())) { - app* a = to_app(result[i].get()); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(a->get_arg(j)); - } + if (m.is_and(result.get(i))) { + app* a = to_app(result.get(i)); + for (expr* arg : *a) result.push_back(arg); result[i] = result.back(); result.pop_back(); --i; } - else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { + else if (m.is_not(result.get(i), e1) && m.is_not(e1, e2)) { result[i] = e2; --i; } - else if (m.is_not(result[i].get(), e1) && m.is_or(e1)) { + else if (m.is_not(result.get(i), e1) && m.is_or(e1)) { app* a = to_app(e1); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(m.mk_not(a->get_arg(j))); - } + for (expr* arg : *a) result.push_back(m.mk_not(arg)); result[i] = result.back(); result.pop_back(); --i; } - else if (m.is_not(result[i].get(), e1) && m.is_implies(e1,e2,e3)) { + else if (m.is_not(result.get(i), e1) && m.is_implies(e1,e2,e3)) { result.push_back(e2); result[i] = m.mk_not(e3); --i; } - else if (m.is_true(result[i].get()) || - (m.is_not(result[i].get(), e1) && + else if (m.is_true(result.get(i)) || + (m.is_not(result.get(i), e1) && m.is_false(e1))) { result[i] = result.back(); result.pop_back(); --i; } - else if (m.is_false(result[i].get()) || - (m.is_not(result[i].get(), e1) && + else if (m.is_false(result.get(i)) || + (m.is_not(result.get(i), e1) && m.is_true(e1))) { result.reset(); result.push_back(m.mk_false()); @@ -323,44 +315,38 @@ void flatten_or(expr_ref_vector& result) { ast_manager& m = result.get_manager(); expr* e1, *e2, *e3; for (unsigned i = 0; i < result.size(); ++i) { - if (m.is_or(result[i].get())) { - app* a = to_app(result[i].get()); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(a->get_arg(j)); - } + if (m.is_or(result.get(i))) { + app* a = to_app(result.get(i)); + for (expr* arg : *a) result.push_back(arg); result[i] = result.back(); result.pop_back(); --i; } - else if (m.is_not(result[i].get(), e1) && m.is_not(e1, e2)) { + else if (m.is_not(result.get(i), e1) && m.is_not(e1, e2)) { result[i] = e2; --i; } - else if (m.is_not(result[i].get(), e1) && m.is_and(e1)) { + else if (m.is_not(result.get(i), e1) && m.is_and(e1)) { app* a = to_app(e1); - unsigned num_args = a->get_num_args(); - for (unsigned j = 0; j < num_args; ++j) { - result.push_back(m.mk_not(a->get_arg(j))); - } + for (expr* arg : *a) result.push_back(m.mk_not(arg)); result[i] = result.back(); result.pop_back(); --i; } - else if (m.is_implies(result[i].get(),e2,e3)) { + else if (m.is_implies(result.get(i),e2,e3)) { result.push_back(e3); result[i] = m.mk_not(e2); --i; } - else if (m.is_false(result[i].get()) || - (m.is_not(result[i].get(), e1) && + else if (m.is_false(result.get(i)) || + (m.is_not(result.get(i), e1) && m.is_true(e1))) { result[i] = result.back(); result.pop_back(); --i; } - else if (m.is_true(result[i].get()) || - (m.is_not(result[i].get(), e1) && + else if (m.is_true(result.get(i)) || + (m.is_not(result.get(i), e1) && m.is_false(e1))) { result.reset(); result.push_back(m.mk_true()); diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index e128f2c03..0d4d4d8be 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -354,6 +354,8 @@ public: MATCH_BINARY(is_bv_mul); MATCH_BINARY(is_bv_sle); MATCH_BINARY(is_bv_ule); + MATCH_BINARY(is_bv_ashr); + MATCH_BINARY(is_bv_lshr); MATCH_BINARY(is_bv_shl); MATCH_BINARY(is_bv_urem); MATCH_BINARY(is_bv_srem); diff --git a/src/ast/expr_abstract.cpp b/src/ast/expr_abstract.cpp index c45e445fd..022045a1d 100644 --- a/src/ast/expr_abstract.cpp +++ b/src/ast/expr_abstract.cpp @@ -115,7 +115,7 @@ void expr_abstract(ast_manager& m, unsigned base, unsigned num_bound, expr* cons tout << result << "\n";); } -expr_ref mk_quantifier(bool is_forall, ast_manager& m, unsigned num_bound, app* const* bound, expr* n) { +expr_ref mk_quantifier(quantifier_kind k, ast_manager& m, unsigned num_bound, app* const* bound, expr* n) { expr_ref result(m); expr_abstract(m, 0, num_bound, (expr* const*)bound, n, result); if (num_bound > 0) { @@ -125,7 +125,7 @@ expr_ref mk_quantifier(bool is_forall, ast_manager& m, unsigned num_bound, app* sorts.push_back(m.get_sort(bound[i])); names.push_back(bound[i]->get_decl()->get_name()); } - result = m.mk_quantifier(is_forall, num_bound, sorts.c_ptr(), names.c_ptr(), result); + result = m.mk_quantifier(k, num_bound, sorts.c_ptr(), names.c_ptr(), result); } TRACE("expr_abstract", tout << expr_ref(n, m) << "\n"; @@ -137,9 +137,9 @@ expr_ref mk_quantifier(bool is_forall, ast_manager& m, unsigned num_bound, app* } expr_ref mk_forall(ast_manager& m, unsigned num_bound, app* const* bound, expr* n) { - return mk_quantifier(true, m, num_bound, bound, n); + return mk_quantifier(forall_k, m, num_bound, bound, n); } expr_ref mk_exists(ast_manager& m, unsigned num_bound, app* const* bound, expr* n) { - return mk_quantifier(false, m, num_bound, bound, n); + return mk_quantifier(exists_k, m, num_bound, bound, n); } diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index b079fc2ca..dd3f35a2a 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -3108,12 +3108,12 @@ void fpa2bv_converter::mk_to_ieee_bv_unspecified(func_decl * f, unsigned num, ex expr_ref exp_bv(m), exp_all_ones(m); exp_bv = m_bv_util.mk_extract(ebits+sbits-2, sbits-1, result); exp_all_ones = m.mk_eq(exp_bv, m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, ebits))); - m_extra_assertions.push_back(exp_all_ones); + m_extra_assertions.push_back(std::move(exp_all_ones)); expr_ref sig_bv(m), sig_is_non_zero(m); sig_bv = m_bv_util.mk_extract(sbits-2, 0, result); sig_is_non_zero = m.mk_not(m.mk_eq(sig_bv, m_bv_util.mk_numeral(0, sbits-1))); - m_extra_assertions.push_back(sig_is_non_zero); + m_extra_assertions.push_back(std::move(sig_is_non_zero)); } TRACE("fpa2bv_to_ieee_bv_unspecified", tout << "result=" << mk_ismt2_pp(result, m) << std::endl;); diff --git a/src/ast/fpa/fpa2bv_rewriter.cpp b/src/ast/fpa/fpa2bv_rewriter.cpp index 2ddc69aaf..cc25905f0 100644 --- a/src/ast/fpa/fpa2bv_rewriter.cpp +++ b/src/ast/fpa/fpa2bv_rewriter.cpp @@ -194,6 +194,9 @@ bool fpa2bv_rewriter_cfg::reduce_quantifier(quantifier * old_q, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { + if (is_lambda(old_q)) { + return false; + } unsigned curr_sz = m_bindings.size(); SASSERT(old_q->get_num_decls() <= curr_sz); unsigned num_decls = old_q->get_num_decls(); @@ -217,7 +220,7 @@ bool fpa2bv_rewriter_cfg::reduce_quantifier(quantifier * old_q, new_decl_names.push_back(n); } } - result = m().mk_quantifier(old_q->is_forall(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), + result = m().mk_quantifier(old_q->get_kind(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); result_pr = nullptr; diff --git a/src/ast/macros/macro_finder.cpp b/src/ast/macros/macro_finder.cpp index 5a46da65a..b7771110a 100644 --- a/src/ast/macros/macro_finder.cpp +++ b/src/ast/macros/macro_finder.cpp @@ -23,7 +23,7 @@ Revision History: #include "ast/ast_ll_pp.h" bool macro_finder::is_macro(expr * n, app_ref & head, expr_ref & def) { - if (!is_quantifier(n) || !to_quantifier(n)->is_forall()) + if (!is_forall(n)) return false; TRACE("macro_finder", tout << "processing: " << mk_pp(n, m) << "\n";); expr * body = to_quantifier(n)->get_expr(); @@ -46,7 +46,7 @@ bool macro_finder::is_macro(expr * n, app_ref & head, expr_ref & def) { For case 2 & 3, the new quantifiers are stored in new_exprs and new_prs. */ bool macro_finder::is_arith_macro(expr * n, proof * pr, expr_dependency * dep, expr_ref_vector & new_exprs, proof_ref_vector & new_prs, expr_dependency_ref_vector & new_deps) { - if (!is_quantifier(n) || !to_quantifier(n)->is_forall()) + if (!is_forall(n)) return false; expr * body = to_quantifier(n)->get_expr(); unsigned num_decls = to_quantifier(n)->get_num_decls(); @@ -117,7 +117,7 @@ bool macro_finder::is_arith_macro(expr * n, proof * pr, expr_dependency * dep, e } bool macro_finder::is_arith_macro(expr * n, proof * pr, vector& new_fmls) { - if (!is_quantifier(n) || !to_quantifier(n)->is_forall()) + if (!is_forall(n)) return false; expr * body = to_quantifier(n)->get_expr(); unsigned num_decls = to_quantifier(n)->get_num_decls(); diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp index 2f429ccf7..dd774772a 100644 --- a/src/ast/macros/macro_manager.cpp +++ b/src/ast/macros/macro_manager.cpp @@ -283,13 +283,11 @@ struct macro_manager::macro_expander_cfg : public default_rewriter_cfg { subst_args[nidx] = n->get_arg(i); } var_subst s(m); - expr_ref rr(m); - s(def, num, subst_args.c_ptr(), rr); + expr_ref rr = s(def, num, subst_args.c_ptr()); m_trail.push_back(rr); r = rr; if (m.proofs_enabled()) { - expr_ref instance(m); - s(q->get_expr(), num, subst_args.c_ptr(), instance); + expr_ref instance = s(q->get_expr(), num, subst_args.c_ptr()); proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.c_ptr()); proof * q_pr = nullptr; mm.m_decl2macro_pr.find(d, q_pr); diff --git a/src/ast/macros/macro_util.cpp b/src/ast/macros/macro_util.cpp index b2f31e374..2858614d9 100644 --- a/src/ast/macros/macro_util.cpp +++ b/src/ast/macros/macro_util.cpp @@ -334,7 +334,7 @@ bool macro_util::is_pseudo_head(expr * n, unsigned num_decls, app_ref & head, ap where t is a ground term, (f X) is the head. */ bool macro_util::is_pseudo_predicate_macro(expr * n, app_ref & head, app_ref & t, expr_ref & def) { - if (!is_quantifier(n) || !to_quantifier(n)->is_forall()) + if (!is_forall(n)) return false; TRACE("macro_util", tout << "processing: " << mk_pp(n, m_manager) << "\n";); expr * body = to_quantifier(n)->get_expr(); @@ -485,7 +485,7 @@ void macro_util::normalize_expr(app * head, unsigned num_decls, expr * t, expr_r if (var_mapping[i] != 0) tout << "#" << i << " -> " << mk_ll_pp(var_mapping[i], m_manager); }); - subst(t, var_mapping.size(), var_mapping.c_ptr(), norm_t); + norm_t = subst(t, var_mapping.size(), var_mapping.c_ptr()); } else { norm_t = t; diff --git a/src/ast/macros/quasi_macros.cpp b/src/ast/macros/quasi_macros.cpp index 3a0735e25..ee1b77545 100644 --- a/src/ast/macros/quasi_macros.cpp +++ b/src/ast/macros/quasi_macros.cpp @@ -155,7 +155,7 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const { // f[X] contains all universally quantified variables, and f does not occur in T[X]. TRACE("quasi_macros", tout << "Checking for quasi macro: " << mk_pp(e, m_manager) << std::endl;); - if (is_quantifier(e) && to_quantifier(e)->is_forall()) { + if (is_forall(e)) { quantifier * q = to_quantifier(e); expr * qe = q->get_expr(); if ((m_manager.is_eq(qe))) { @@ -251,7 +251,7 @@ void quasi_macros::quasi_macro_to_macro(quantifier * q, app * a, expr * t, quant eq = m_manager.mk_eq(appl, ite); - macro = m_manager.mk_quantifier(true, new_var_names_rev.size(), + macro = m_manager.mk_quantifier(forall_k, new_var_names_rev.size(), new_qsorts_rev.c_ptr(), new_var_names_rev.c_ptr(), eq); } diff --git a/src/ast/normal_forms/defined_names.cpp b/src/ast/normal_forms/defined_names.cpp index eb6da9400..b63c947a9 100644 --- a/src/ast/normal_forms/defined_names.cpp +++ b/src/ast/normal_forms/defined_names.cpp @@ -16,17 +16,19 @@ Author: Revision History: --*/ -#include "ast/normal_forms/defined_names.h" #include "util/obj_hashtable.h" +#include "ast/normal_forms/defined_names.h" #include "ast/used_vars.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/array_decl_plugin.h" struct defined_names::impl { typedef obj_map expr2name; typedef obj_map expr2proof; - ast_manager & m_manager; + ast_manager & m; symbol m_z3name; /** @@ -60,9 +62,9 @@ struct defined_names::impl { app * gen_name(expr * e, sort_ref_buffer & var_sorts, buffer & var_names); void cache_new_name(expr * e, app * name); void cache_new_name_intro_proof(expr * e, proof * pr); - void bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref & result); - void bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref_buffer & result); - virtual void mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer const & var_names, expr_ref & new_def); + void bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref & result, symbol const& qid = symbol::null); + void bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref_buffer & result, symbol const& qid = symbol::null); + virtual void mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer & var_names, expr_ref & new_def); bool mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr); void push_scope(); void pop_scope(unsigned num_scopes); @@ -74,12 +76,13 @@ struct defined_names::impl { struct defined_names::pos_impl : public defined_names::impl { pos_impl(ast_manager & m, char const * fresh_prefix):impl(m, fresh_prefix) {} - void mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer const & var_names, expr_ref & new_def) override; + void mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer & var_names, expr_ref & new_def) override; + }; defined_names::impl::impl(ast_manager & m, char const * prefix): - m_manager(m), + m(m), m_exprs(m), m_names(m), m_apply_proofs(m) { @@ -107,20 +110,20 @@ app * defined_names::impl::gen_name(expr * e, sort_ref_buffer & var_sorts, buffe sort * s = uv.get(i); if (s) { domain.push_back(s); - new_args.push_back(m_manager.mk_var(i, s)); + new_args.push_back(m.mk_var(i, s)); var_sorts.push_back(s); } else { - var_sorts.push_back(m_manager.mk_bool_sort()); // could be any sort. + var_sorts.push_back(m.mk_bool_sort()); // could be any sort. } var_names.push_back(symbol(i)); } - sort * range = m_manager.get_sort(e); - func_decl * new_skolem_decl = m_manager.mk_fresh_func_decl(m_z3name, symbol::null, domain.size(), domain.c_ptr(), range); - app * n = m_manager.mk_app(new_skolem_decl, new_args.size(), new_args.c_ptr()); - TRACE("mk_definition_bug", tout << "gen_name: " << mk_ismt2_pp(n, m_manager) << "\n"; - for (unsigned i = 0; i < var_sorts.size(); i++) tout << mk_pp(var_sorts[i], m_manager) << " "; + sort * range = m.get_sort(e); + func_decl * new_skolem_decl = m.mk_fresh_func_decl(m_z3name, symbol::null, domain.size(), domain.c_ptr(), range); + app * n = m.mk_app(new_skolem_decl, new_args.size(), new_args.c_ptr()); + TRACE("mk_definition_bug", tout << "gen_name: " << mk_ismt2_pp(n, m) << "\n"; + for (unsigned i = 0; i < var_sorts.size(); i++) tout << mk_pp(var_sorts[i], m) << " "; tout << "\n";); return n; } @@ -148,22 +151,22 @@ void defined_names::impl::cache_new_name_intro_proof(expr * e, proof * pr) { A quantifier is added around \c def_conjunct, if sorts and names are not empty. In this case, The application \c name is used as a pattern for the new quantifier. */ -void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref & result) { +void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref & result, symbol const& qid) { SASSERT(sorts.size() == names.size()); if (sorts.empty()) result = def_conjunct; else { - expr * patterns[1] = { m_manager.mk_pattern(name) }; - quantifier_ref q(m_manager); - q = m_manager.mk_forall(sorts.size(), + expr * patterns[1] = { m.mk_pattern(name) }; + quantifier_ref q(m); + q = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), def_conjunct, - 1, symbol::null, symbol::null, + 1, qid, symbol::null, 1, patterns); - TRACE("mk_definition_bug", tout << "before elim_unused_vars:\n" << mk_ismt2_pp(q, m_manager) << "\n";); - elim_unused_vars(m_manager, q, params_ref(), result); - TRACE("mk_definition_bug", tout << "after elim_unused_vars:\n" << mk_ismt2_pp(result, m_manager) << "\n";); + TRACE("mk_definition_bug", tout << "before elim_unused_vars:\n" << mk_ismt2_pp(q, m) << "\n";); + result = elim_unused_vars(m, q, params_ref()); + TRACE("mk_definition_bug", tout << "after elim_unused_vars:\n" << result << "\n";); } } @@ -172,44 +175,81 @@ void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref_buffer & result) { - expr_ref tmp(m_manager); - bound_vars(sorts, names, def_conjunct, name, tmp); +void defined_names::impl::bound_vars(sort_ref_buffer const & sorts, buffer const & names, expr * def_conjunct, app * name, expr_ref_buffer & result, symbol const& qid) { + expr_ref tmp(m); + bound_vars(sorts, names, def_conjunct, name, tmp, qid); result.push_back(tmp); } -#define MK_OR m_manager.mk_or -#define MK_NOT m_manager.mk_not -#define MK_EQ m_manager.mk_eq +#define MK_OR m.mk_or +#define MK_NOT m.mk_not +#define MK_EQ m.mk_eq -void defined_names::impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer const & var_names, expr_ref & new_def) { - expr_ref_buffer defs(m_manager); - if (m_manager.is_bool(e)) { +void defined_names::impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer & var_names, expr_ref & new_def) { + expr_ref_buffer defs(m); + if (m.is_bool(e)) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(n), e), n, defs); bound_vars(var_sorts, var_names, MK_OR(n, MK_NOT(e)), n, defs); } - else if (m_manager.is_term_ite(e)) { + else if (m.is_term_ite(e)) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(to_app(e)->get_arg(0)), MK_EQ(n, to_app(e)->get_arg(1))), n, defs); bound_vars(var_sorts, var_names, MK_OR(to_app(e)->get_arg(0), MK_EQ(n, to_app(e)->get_arg(2))), n, defs); } + else if (is_lambda(e)) { + // n(y) = \x . M[x,y] + // => + // n(y)[x] = M, forall x y + // + // NB. The pattern is incomplete. + // consider store(a, i, v) == \lambda j . if i = j then v else a[j] + // the instantiation rules for store(a, i, v) are: + // sotre(a, i, v)[j] = if i = j then v else a[j] with patterns {a[j], store(a, i, v)} { store(a, i, v)[j] } + // The first pattern is not included. + // TBD use a model-based scheme for exracting instantiations instead of + // using multi-patterns. + // + + quantifier* q = to_quantifier(e); + expr_ref_vector args(m); + expr_ref n2(m), n3(m); + var_shifter vs(m); + vs(n, q->get_num_decls(), n2); + args.push_back(n2); + var_sorts.append(q->get_num_decls(), q->get_decl_sorts()); + var_names.append(q->get_num_decls(), q->get_decl_names()); + for (unsigned i = 0; i < q->get_num_decls(); ++i) { + args.push_back(m.mk_var(q->get_num_decls() - i - 1, q->get_decl_sort(i))); + } + array_util autil(m); + func_decl * f = 0; + if (autil.is_as_array(n2, f)) { + n3 = m.mk_app(f, args.size()-1, args.c_ptr() + 1); + } + else { + n3 = autil.mk_select(args.size(), args.c_ptr()); + } + bound_vars(var_sorts, var_names, MK_EQ(q->get_expr(), n3), to_app(n3), defs, m.lambda_def_qid()); + + } else { bound_vars(var_sorts, var_names, MK_EQ(e, n), n, defs); } - new_def = defs.size() == 1 ? defs[0] : m_manager.mk_and(defs.size(), defs.c_ptr()); + new_def = mk_and(m, defs.size(), defs.c_ptr()); } -void defined_names::pos_impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer const & var_names, expr_ref & new_def) { + +void defined_names::pos_impl::mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer & var_names, expr_ref & new_def) { bound_vars(var_sorts, var_names, MK_OR(MK_NOT(n), e), n, new_def); } bool defined_names::impl::mk_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr) { - TRACE("mk_definition_bug", tout << "making name for:\n" << mk_ismt2_pp(e, m_manager) << "\n";); + TRACE("mk_definition_bug", tout << "making name for:\n" << mk_ismt2_pp(e, m) << "\n";); app * n_ptr; if (m_expr2name.find(e, n_ptr)) { TRACE("mk_definition_bug", tout << "name for expression is already cached..., returning false...\n";); n = n_ptr; - if (m_manager.proofs_enabled()) { + if (m.proofs_enabled()) { proof * pr_ptr = nullptr; m_expr2proof.find(e, pr_ptr); SASSERT(pr_ptr); @@ -218,24 +258,24 @@ bool defined_names::impl::mk_name(expr * e, expr_ref & new_def, proof_ref & new_ return false; } else { - sort_ref_buffer var_sorts(m_manager); + sort_ref_buffer var_sorts(m); buffer var_names; n = gen_name(e, var_sorts, var_names); cache_new_name(e, n); - TRACE("mk_definition_bug", tout << "name: " << mk_ismt2_pp(n, m_manager) << "\n";); + TRACE("mk_definition_bug", tout << "name: " << mk_ismt2_pp(n, m) << "\n";); // variables are in reverse order in quantifiers std::reverse(var_sorts.c_ptr(), var_sorts.c_ptr() + var_sorts.size()); std::reverse(var_names.c_ptr(), var_names.c_ptr() + var_names.size()); mk_definition(e, n, var_sorts, var_names, new_def); - TRACE("mk_definition_bug", tout << "new_def:\n" << mk_ismt2_pp(new_def, m_manager) << "\n";); + TRACE("mk_definition_bug", tout << "new_def:\n" << mk_ismt2_pp(new_def, m) << "\n";); - if (m_manager.proofs_enabled()) { - new_def_pr = m_manager.mk_def_intro(new_def); - pr = m_manager.mk_apply_def(e, n, new_def_pr); + if (m.proofs_enabled()) { + new_def_pr = m.mk_def_intro(new_def); + pr = m.mk_apply_def(e, n, new_def_pr); cache_new_name_intro_proof(e, pr); } return true; @@ -257,7 +297,7 @@ void defined_names::impl::pop_scope(unsigned num_scopes) { SASSERT(sz == m_names.size()); while (old_sz != sz) { --sz; - if (m_manager.proofs_enabled()) { + if (m.proofs_enabled()) { m_expr2proof.erase(m_exprs.back()); m_apply_proofs.pop_back(); } @@ -296,6 +336,15 @@ bool defined_names::mk_pos_name(expr * e, expr_ref & new_def, proof_ref & new_de return m_pos_impl->mk_name(e, new_def, new_def_pr, n, pr); } +expr_ref defined_names::mk_definition(expr * e, app * n) { + ast_manager& m = m_impl->m; + sort_ref_buffer var_sorts(m); + expr_ref new_def(m); + buffer var_names; + m_impl->mk_definition(e, n, var_sorts, var_names, new_def); + return new_def; +} + void defined_names::push() { m_impl->push_scope(); m_pos_impl->push_scope(); diff --git a/src/ast/normal_forms/defined_names.h b/src/ast/normal_forms/defined_names.h index 764822f66..a3a603d21 100644 --- a/src/ast/normal_forms/defined_names.h +++ b/src/ast/normal_forms/defined_names.h @@ -77,6 +77,11 @@ public: */ bool mk_pos_name(expr * e, expr_ref & new_def, proof_ref & new_def_pr, app_ref & n, proof_ref & pr); + /** + \brief Create a definition for 'n' using 'e'. + */ + expr_ref mk_definition(expr * e, app * n); + void push(); void pop(unsigned num_scopes); void reset(); diff --git a/src/ast/normal_forms/name_exprs.cpp b/src/ast/normal_forms/name_exprs.cpp index 3e5503ea7..5e9af1c2d 100644 --- a/src/ast/normal_forms/name_exprs.cpp +++ b/src/ast/normal_forms/name_exprs.cpp @@ -22,7 +22,7 @@ Notes: class name_exprs_core : public name_exprs { struct cfg : public default_rewriter_cfg { - ast_manager & m_manager; + ast_manager & m; defined_names & m_defined_names; expr_predicate & m_pred; @@ -33,7 +33,7 @@ class name_exprs_core : public name_exprs { proof_ref_vector * m_def_proofs; cfg(ast_manager & m, defined_names & n, expr_predicate & pred): - m_manager(m), + m(m), m_defined_names(n), m_pred(pred), m_r(m), @@ -43,12 +43,12 @@ class name_exprs_core : public name_exprs { } void gen_name_for_expr(expr * n, expr * & t, proof * & t_pr) { - expr_ref new_def(m_manager); - proof_ref new_def_pr(m_manager); + expr_ref new_def(m); + proof_ref new_def_pr(m); if (m_defined_names.mk_name(n, new_def, new_def_pr, m_r, m_pr)) { m_def_exprs->push_back(new_def); - if (m_manager.proofs_enabled()) + if (m.proofs_enabled()) m_def_proofs->push_back(new_def_pr); } @@ -57,7 +57,7 @@ class name_exprs_core : public name_exprs { } bool get_subst(expr * s, expr * & t, proof * & t_pr) { - TRACE("name_exprs", tout << "get_subst:\n" << mk_ismt2_pp(s, m_manager) << "\n";); + TRACE("name_exprs", tout << "get_subst:\n" << mk_ismt2_pp(s, m) << "\n";); if (m_pred(s)) { gen_name_for_expr(s, t, t_pr); return true; @@ -84,7 +84,7 @@ public: m_cfg.m_def_exprs = &new_defs; m_cfg.m_def_proofs = &new_def_proofs; m_rw(n, r, p); - TRACE("name_exprs", tout << mk_ismt2_pp(n, m_rw.m()) << "\n---->\n" << mk_ismt2_pp(r, m_rw.m()) << "\n";); + TRACE("name_exprs", tout << mk_ismt2_pp(n, m_rw.m()) << "\n---->\n" << r << "\n";); } @@ -99,11 +99,11 @@ name_exprs * mk_expr_namer(ast_manager & m, defined_names & n, expr_predicate & class name_quantifier_labels : public name_exprs_core { class pred : public expr_predicate { - ast_manager & m_manager; + ast_manager & m; public: - pred(ast_manager & m):m_manager(m) {} + pred(ast_manager & m):m(m) {} bool operator()(expr * t) override { - return is_quantifier(t) || m_manager.is_label(t); + return is_quantifier(t) || m.is_label(t); } }; @@ -124,16 +124,16 @@ name_exprs * mk_quantifier_label_namer(ast_manager & m, defined_names & n) { class name_nested_formulas : public name_exprs_core { struct pred : public expr_predicate { - ast_manager & m_manager; + ast_manager & m; expr * m_root; - pred(ast_manager & m):m_manager(m), m_root(nullptr) {} + pred(ast_manager & m):m(m), m_root(0) {} bool operator()(expr * t) override { - TRACE("name_exprs", tout << "name_nested_formulas::pred:\n" << mk_ismt2_pp(t, m_manager) << "\n";); + TRACE("name_exprs", tout << "name_nested_formulas::pred:\n" << mk_ismt2_pp(t, m) << "\n";); if (is_app(t)) - return to_app(t)->get_family_id() == m_manager.get_basic_family_id() && to_app(t)->get_num_args() > 0 && t != m_root; - return m_manager.is_label(t) || is_quantifier(t); + return to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0 && t != m_root; + return m.is_label(t) || is_quantifier(t); } }; diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index c7f20ced6..7786def0b 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -17,16 +17,16 @@ Notes: Major revision on 2011-10-06 --*/ + +#include "util/warning.h" +#include "util/cooperate.h" #include "ast/normal_forms/nnf.h" #include "ast/normal_forms/nnf_params.hpp" -#include "util/warning.h" #include "ast/used_vars.h" #include "ast/well_sorted.h" -#include "ast/rewriter/var_subst.h" - -#include "ast/normal_forms/name_exprs.h" #include "ast/act_cache.h" -#include "util/cooperate.h" +#include "ast/rewriter/var_subst.h" +#include "ast/normal_forms/name_exprs.h" #include "ast/ast_smt2_pp.h" @@ -67,36 +67,42 @@ enum nnf_mode { class skolemizer { typedef act_cache cache; - ast_manager & m_manager; + ast_manager & m; symbol m_sk_hack; bool m_sk_hack_enabled; cache m_cache; cache m_cache_pr; void process(quantifier * q, expr_ref & r, proof_ref & p) { + if (q->get_kind() == lambda_k) { + TRACE("nnf", tout << expr_ref(q, m) << "\n";); + r = q; + p = nullptr; + return; + } used_vars uv; uv(q); - SASSERT(is_well_sorted(m(), q)); + SASSERT(is_well_sorted(m, q)); unsigned sz = uv.get_max_found_var_idx_plus_1(); ptr_buffer sorts; - expr_ref_vector args(m()); + expr_ref_vector args(m); for (unsigned i = 0; i < sz; i++) { sort * s = uv.get(i); if (s != nullptr) { sorts.push_back(s); - args.push_back(m().mk_var(i, s)); + args.push_back(m.mk_var(i, s)); } } TRACE("skolemizer", tout << "skid: " << q->get_skid() << "\n";); - - expr_ref_vector substitution(m()); + + expr_ref_vector substitution(m); unsigned num_decls = q->get_num_decls(); for (unsigned i = num_decls; i > 0; ) { --i; sort * r = q->get_decl_sort(i); - func_decl * sk_decl = m().mk_fresh_func_decl(q->get_decl_name(i), q->get_skid(), sorts.size(), sorts.c_ptr(), r); - app * sk = m().mk_app(sk_decl, args.size(), args.c_ptr()); + func_decl * sk_decl = m.mk_fresh_func_decl(q->get_decl_name(i), q->get_skid(), sorts.size(), sorts.c_ptr(), r); + app * sk = m.mk_app(sk_decl, args.size(), args.c_ptr()); substitution.push_back(sk); } // @@ -106,7 +112,7 @@ class skolemizer { for (unsigned i = 0; i < sz; i++) { sort * s = uv.get(i); if (s != nullptr) - substitution.push_back(m().mk_var(i, s)); + substitution.push_back(m.mk_var(i, s)); else substitution.push_back(nullptr); } @@ -118,9 +124,9 @@ class skolemizer { // // (VAR 0) should be in the last position of substitution. // - var_subst s(m()); - SASSERT(is_well_sorted(m(), q->get_expr())); - expr_ref tmp(m()); + var_subst s(m); + SASSERT(is_well_sorted(m, q->get_expr())); + expr_ref tmp(m); expr * body = q->get_expr(); if (m_sk_hack_enabled) { unsigned num_patterns = q->get_num_patterns(); @@ -128,27 +134,27 @@ class skolemizer { expr * p = q->get_pattern(i); if (is_sk_hack(p)) { expr * sk_hack = to_app(p)->get_arg(0); - if (q->is_forall()) // check whether is in negative/positive context. - tmp = m().mk_or(body, m().mk_not(sk_hack)); // negative context + if (q->get_kind() == forall_k) // check whether is in negative/positive context. + tmp = m.mk_or(body, m.mk_not(sk_hack)); // negative context else - tmp = m().mk_and(body, sk_hack); // positive context + tmp = m.mk_and(body, sk_hack); // positive context body = tmp; } } } - s(body, substitution.size(), substitution.c_ptr(), r); + r = s(body, substitution.size(), substitution.c_ptr()); p = nullptr; - if (m().proofs_enabled()) { - if (q->is_forall()) - p = m().mk_skolemization(m().mk_not(q), m().mk_not(r)); + if (m.proofs_enabled()) { + if (q->get_kind() == forall_k) + p = m.mk_skolemization(m.mk_not(q), m.mk_not(r)); else - p = m().mk_skolemization(q, r); + p = m.mk_skolemization(q, r); } } public: skolemizer(ast_manager & m): - m_manager(m), + m(m), m_sk_hack("sk_hack"), m_sk_hack_enabled(false), m_cache(m), @@ -159,25 +165,23 @@ public: m_sk_hack_enabled = f; } - ast_manager & m() const { return m_manager; } - void operator()(quantifier * q, expr_ref & r, proof_ref & p) { r = m_cache.find(q); if (r.get() != nullptr) { p = nullptr; - if (m().proofs_enabled()) + if (m.proofs_enabled()) p = static_cast(m_cache_pr.find(q)); } else { process(q, r, p); m_cache.insert(q, r); - if (m().proofs_enabled()) + if (m.proofs_enabled()) m_cache_pr.insert(q, p); } } bool is_sk_hack(expr * p) const { - SASSERT(m().is_pattern(p)); + SASSERT(m.is_pattern(p)); if (to_app(p)->get_num_args() != 1) return false; expr * body = to_app(p)->get_arg(0); @@ -186,7 +190,7 @@ public: func_decl * f = to_app(body)->get_decl(); if (!(f->get_name() == m_sk_hack && f->get_arity() == 1)) return false; - if (!m().is_bool(body)) { + if (!m.is_bool(body)) { warning_msg("sk_hack constant must return a Boolean"); return false; } @@ -233,8 +237,8 @@ struct nnf::imp { #define POS_NQ_CIDX 1 // positive polarity and not nested in a quantifier #define NEG_Q_CIDX 2 // negative polarity and nested in a quantifier #define POS_Q_CIDX 3 // positive polarity and nested in a quantifier - - ast_manager & m_manager; + + ast_manager & m; vector m_frame_stack; expr_ref_vector m_result_stack; @@ -263,7 +267,7 @@ struct nnf::imp { unsigned long long m_max_memory; // in bytes imp(ast_manager & m, defined_names & n, params_ref const & p): - m_manager(m), + m(m), m_result_stack(m), m_todo_defs(m), m_todo_proofs(m), @@ -279,9 +283,9 @@ struct nnf::imp { m_name_quant = mk_quantifier_label_namer(m, n); } - ast_manager & m() const { return m_manager; } + // ast_manager & m() const { return m; } - bool proofs_enabled() const { return m().proofs_enabled(); } + bool proofs_enabled() const { return m.proofs_enabled(); } ~imp() { for (unsigned i = 0; i < 4; i++) { @@ -334,7 +338,7 @@ struct nnf::imp { } void push_frame(expr * t, bool pol, bool in_q, bool cache_res) { - m_frame_stack.push_back(frame(expr_ref(t, m()), pol, in_q, cache_res, m_result_stack.size())); + m_frame_stack.push_back(frame(expr_ref(t, m), pol, in_q, cache_res, m_result_stack.size())); } static unsigned get_cache_idx(bool pol, bool in_q) { @@ -382,8 +386,8 @@ struct nnf::imp { cooperate("nnf"); if (memory::get_allocation_size() > m_max_memory) throw nnf_exception(Z3_MAX_MEMORY_MSG); - if (m().canceled()) - throw nnf_exception(m().limit().get_cancel_msg()); + if (m.canceled()) + throw nnf_exception(m.limit().get_cancel_msg()); } void set_new_child_flag() { @@ -397,16 +401,16 @@ struct nnf::imp { } void skip(expr * t, bool pol) { - expr * r = pol ? t : m().mk_not(t); + expr * r = pol ? t : m.mk_not(t); m_result_stack.push_back(r); if (proofs_enabled()) { - m_result_pr_stack.push_back(m().mk_oeq_reflexivity(r)); + m_result_pr_stack.push_back(m.mk_oeq_reflexivity(r)); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); } } bool visit(expr * t, bool pol, bool in_q) { - SASSERT(m().is_bool(t)); + SASSERT(m.is_bool(t)); if (m_mode == NNF_SKOLEM || (m_mode == NNF_QUANT && !in_q)) { if (!has_quantifiers(t) && !has_labels(t)) { @@ -456,12 +460,12 @@ struct nnf::imp { proof * mk_proof(bool pol, unsigned num_parents, proof * const * parents, app * old_e, app * new_e) { if (pol) { if (old_e->get_decl() == new_e->get_decl()) - return m().mk_oeq_congruence(old_e, new_e, num_parents, parents); - else - return m().mk_nnf_pos(old_e, new_e, num_parents, parents); + return m.mk_oeq_congruence(old_e, new_e, num_parents, parents); + else + return m.mk_nnf_pos(old_e, new_e, num_parents, parents); } - else - return m().mk_nnf_neg(old_e, new_e, num_parents, parents); + else + return m.mk_nnf_neg(old_e, new_e, num_parents, parents); } bool process_and_or(app * t, frame & fr) { @@ -473,11 +477,11 @@ struct nnf::imp { return false; } app * r; - if (m().is_and(t) == fr.m_pol) - r = m().mk_and(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); + if (m.is_and(t) == fr.m_pol) + r = m.mk_and(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); else - r = m().mk_or(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); - + r = m.mk_or(t->get_num_args(), m_result_stack.c_ptr() + fr.m_spos); + m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { @@ -500,7 +504,7 @@ struct nnf::imp { if (proofs_enabled()) { pr = m_result_pr_stack.back(); if (!fr.m_pol) { - pr = m().mk_nnf_neg(t, r, 1, &pr); + pr = m.mk_nnf_neg(t, r, 1, &pr); m_result_pr_stack.pop_back(); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); @@ -526,10 +530,10 @@ struct nnf::imp { app * r; if (fr.m_pol) - r = m().mk_or(2, m_result_stack.c_ptr() + fr.m_spos); + r = m.mk_or(2, m_result_stack.c_ptr() + fr.m_spos); else - r = m().mk_and(2, m_result_stack.c_ptr() + fr.m_spos); - + r = m.mk_and(2, m_result_stack.c_ptr() + fr.m_spos); + m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { @@ -570,7 +574,7 @@ struct nnf::imp { expr * _then = rs[2]; expr * _else = rs[3]; - app * r = m().mk_and(m().mk_or(_not_cond, _then), m().mk_or(_cond, _else)); + app * r = m.mk_and(m.mk_or(_not_cond, _then), m.mk_or(_cond, _else)); m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { @@ -582,7 +586,7 @@ struct nnf::imp { return true; } - bool is_eq(app * t) const { return m().is_eq(t); } + bool is_eq(app * t) const { return m.is_eq(t); } bool process_iff_xor(app * t, frame & fr) { SASSERT(t->get_num_args() == 2); @@ -614,10 +618,10 @@ struct nnf::imp { expr * not_rhs = rs[3]; app * r; - if (is_eq(t) == fr.m_pol) - r = m().mk_and(m().mk_or(not_lhs, rhs), m().mk_or(lhs, not_rhs)); + if (is_eq(t) == fr.m_pol) + r = m.mk_and(m.mk_or(not_lhs, rhs), m.mk_or(lhs, not_rhs)); else - r = m().mk_and(m().mk_or(lhs, rhs), m().mk_or(not_lhs, not_rhs)); + r = m.mk_and(m.mk_or(lhs, rhs), m.mk_or(not_lhs, not_rhs)); m_result_stack.shrink(fr.m_spos); m_result_stack.push_back(r); if (proofs_enabled()) { @@ -630,7 +634,7 @@ struct nnf::imp { } bool process_eq(app * t, frame & fr) { - if (m().is_iff(t)) + if (m.is_bool(t->get_arg(0))) return process_iff_xor(t, fr); else return process_default(t, fr); @@ -639,21 +643,20 @@ struct nnf::imp { bool process_default(app * t, frame & fr) { SASSERT(fr.m_i == 0); if (m_mode == NNF_FULL || t->has_quantifiers() || t->has_labels()) { - expr_ref n2(m()); - proof_ref pr2(m()); + expr_ref n2(m); + proof_ref pr2(m); if (m_mode == NNF_FULL || (m_mode != NNF_SKOLEM && fr.m_in_q)) m_name_nested_formulas->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2); else m_name_quant->operator()(t, m_todo_defs, m_todo_proofs, n2, pr2); if (!fr.m_pol) - n2 = m().mk_not(n2); - + n2 = m.mk_not(n2); m_result_stack.push_back(n2); if (proofs_enabled()) { if (!fr.m_pol) { proof * prs[1] = { pr2 }; - pr2 = m().mk_oeq_congruence(m().mk_not(t), static_cast(n2.get()), 1, prs); + pr2 = m.mk_oeq_congruence(m.mk_not(t), static_cast(n2.get()), 1, prs); } m_result_pr_stack.push_back(pr2); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); @@ -681,24 +684,24 @@ struct nnf::imp { buffer names; bool pos; - m().is_label(t, pos, names); - expr_ref r(m()); - proof_ref pr(m()); + m.is_label(t, pos, names); + expr_ref r(m); + proof_ref pr(m); if (fr.m_pol == pos) { - expr * lbl_lit = m().mk_label_lit(names.size(), names.c_ptr()); - r = m().mk_and(arg, lbl_lit); + expr * lbl_lit = m.mk_label_lit(names.size(), names.c_ptr()); + r = m.mk_and(arg, lbl_lit); if (proofs_enabled()) { - expr_ref aux(m_manager); - aux = m().mk_label(true, names.size(), names.c_ptr(), arg); - pr = m().mk_transitivity(mk_proof(fr.m_pol, 1, &arg_pr, t, to_app(aux)), - m().mk_iff_oeq(m().mk_rewrite(aux, r))); + expr_ref aux(m); + aux = m.mk_label(true, names.size(), names.c_ptr(), arg); + pr = m.mk_transitivity(mk_proof(fr.m_pol, 1, &arg_pr, t, to_app(aux)), + m.mk_iff_oeq(m.mk_rewrite(aux, r))); } } else { r = arg; if (proofs_enabled()) { - proof * p1 = m().mk_iff_oeq(m().mk_rewrite(t, t->get_arg(0))); - pr = m().mk_transitivity(p1, arg_pr); + proof * p1 = m.mk_iff_oeq(m.mk_rewrite(t, t->get_arg(0))); + pr = m.mk_transitivity(p1, arg_pr); } } @@ -713,9 +716,9 @@ struct nnf::imp { } bool process_app(app * t, frame & fr) { - TRACE("nnf", tout << mk_ismt2_pp(t, m()) << "\n";); - SASSERT(m().is_bool(t)); - if (t->get_family_id() == m().get_basic_family_id()) { + TRACE("nnf", tout << mk_ismt2_pp(t, m) << "\n";); + SASSERT(m.is_bool(t)); + if (t->get_family_id() == m.get_basic_family_id()) { switch (static_cast(t->get_decl_kind())) { case OP_AND: case OP_OR: return process_and_or(t, fr); @@ -734,7 +737,7 @@ struct nnf::imp { } } - if (m().is_label(t)) { + if (m.is_label(t)) { return process_label(t, fr); } @@ -747,28 +750,51 @@ struct nnf::imp { } bool process_quantifier(quantifier * q, frame & fr) { - expr_ref r(m()); - proof_ref pr(m()); + TRACE("nnf", tout << expr_ref(q, m) << "\n";); + expr_ref r(m); + proof_ref pr(m); if (fr.m_i == 0) { fr.m_i = 1; - if (q->is_forall() == fr.m_pol || !m_skolemize) { + if (is_lambda(q)) { + if (!visit(q->get_expr(), fr.m_pol, true)) + return false; + } + else if (is_forall(q) == fr.m_pol || !m_skolemize) { if (!visit(q->get_expr(), fr.m_pol, true)) return false; } else { m_skolemizer(q, r, pr); - if (!visit(r, !q->is_forall(), fr.m_in_q)) + if (!visit(r, !is_forall(q), fr.m_in_q)) return false; } } - if (q->is_forall() == fr.m_pol || !m_skolemize) { + if (is_lambda(q)) { + expr * new_expr = m_result_stack.back(); + quantifier * new_q = m.update_quantifier(q, new_expr); + proof * new_q_pr = nullptr; + if (proofs_enabled()) { + // proof * new_expr_pr = m_result_pr_stack.back(); + new_q_pr = m.mk_rewrite(q, new_q); // TBD use new_expr_pr + } + + m_result_stack.pop_back(); + m_result_stack.push_back(new_q); + if (proofs_enabled()) { + m_result_pr_stack.pop_back(); + m_result_pr_stack.push_back(new_q_pr); + SASSERT(m_result_stack.size() == m_result_pr_stack.size()); + } + return true; + } + else if (is_forall(q) == fr.m_pol || !m_skolemize) { expr * new_expr = m_result_stack.back(); proof * new_expr_pr = proofs_enabled() ? m_result_pr_stack.back() : nullptr; ptr_buffer new_patterns; - if (q->is_forall() == fr.m_pol) { + if (is_forall(q) == fr.m_pol) { // collect non sk_hack patterns unsigned num_patterns = q->get_num_patterns(); for (unsigned i = 0; i < num_patterns; i++) { @@ -785,14 +811,19 @@ struct nnf::imp { quantifier * new_q = nullptr; proof * new_q_pr = nullptr; if (fr.m_pol) { - new_q = m().update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), new_expr); - if (proofs_enabled()) - new_q_pr = m().mk_nnf_pos(q, new_q, 1, &new_expr_pr); + new_q = m.update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), new_expr); + if (proofs_enabled()) { + new_expr_pr = m.mk_bind_proof(q, new_expr_pr); + new_q_pr = m.mk_nnf_pos(q, new_q, 1, &new_expr_pr); + } } else { - new_q = m().update_quantifier(q, !q->is_forall(), new_patterns.size(), new_patterns.c_ptr(), new_expr); - if (proofs_enabled()) - new_q_pr = m().mk_nnf_neg(q, new_q, 1, &new_expr_pr); + quantifier_kind k = is_forall(q)? exists_k : forall_k; + new_q = m.update_quantifier(q, k, new_patterns.size(), new_patterns.c_ptr(), new_expr); + if (proofs_enabled()) { + new_expr_pr = m.mk_bind_proof(q, new_expr_pr); + new_q_pr = m.mk_nnf_neg(q, new_q, 1, &new_expr_pr); + } } m_result_stack.pop_back(); @@ -809,7 +840,7 @@ struct nnf::imp { // However, the proof must be updated if (proofs_enabled()) { m_skolemizer(q, r, pr); // retrieve the proof - pr = m().mk_transitivity(pr, m_result_pr_stack.back()); + pr = m.mk_transitivity(pr, m_result_pr_stack.back()); m_result_pr_stack.pop_back(); m_result_pr_stack.push_back(pr); SASSERT(m_result_stack.size() == m_result_pr_stack.size()); @@ -827,14 +858,14 @@ struct nnf::imp { result_pr = m_result_pr_stack.back(); m_result_pr_stack.pop_back(); if (result_pr.get() == nullptr) - result_pr = m().mk_reflexivity(t); + result_pr = m.mk_reflexivity(t); SASSERT(m_result_pr_stack.empty()); } } void process(expr * t, expr_ref & result, proof_ref & result_pr) { - TRACE("nnf", tout << "processing:\n" << mk_ismt2_pp(t, m()) << "\n";); - SASSERT(m().is_bool(t)); + TRACE("nnf", tout << "processing:\n" << mk_ismt2_pp(t, m) << "\n";); + SASSERT(m.is_bool(t)); if (visit(t, true /* positive polarity */, false /* not nested in quantifier */)) { recover_result(t, result, result_pr); @@ -883,13 +914,13 @@ struct nnf::imp { unsigned old_sz2 = new_def_proofs.size(); for (unsigned i = 0; i < m_todo_defs.size(); i++) { - expr_ref dr(m()); - proof_ref dpr(m()); + expr_ref dr(m); + proof_ref dpr(m); process(m_todo_defs.get(i), dr, dpr); new_defs.push_back(dr); if (proofs_enabled()) { - proof * new_pr = m().mk_modus_ponens(m_todo_proofs.get(i), dpr); - new_def_proofs.push_back(new_pr); + proof * new_pr = m.mk_modus_ponens(m_todo_proofs.get(i), dpr); + new_def_proofs.push_back(new_pr); } } std::reverse(new_defs.c_ptr() + old_sz1, new_defs.c_ptr() + new_defs.size()); @@ -909,7 +940,7 @@ nnf::~nnf() { void nnf::operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) { m_imp->operator()(n, new_defs, new_def_proofs, r, p); - TRACE("nnf_result", tout << mk_ismt2_pp(n, m_imp->m()) << "\nNNF result:\n" << mk_ismt2_pp(r, m_imp->m()) << "\n";); + TRACE("nnf_result", tout << expr_ref(n, r.get_manager()) << "\nNNF result:\n" << new_defs << "\n" << r << "\n";); } void nnf::updt_params(params_ref const & p) { diff --git a/src/ast/normal_forms/pull_quant.cpp b/src/ast/normal_forms/pull_quant.cpp index 06881f97b..9ff75ca25 100644 --- a/src/ast/normal_forms/pull_quant.cpp +++ b/src/ast/normal_forms/pull_quant.cpp @@ -49,7 +49,8 @@ struct pull_quant::imp { if (is_quantifier(child)) { quantifier * q = to_quantifier(child); expr * body = q->get_expr(); - result = m_manager.update_quantifier(q, !q->is_forall(), m_manager.mk_not(body)); + quantifier_kind k = q->get_kind() == forall_k ? exists_k : forall_k; + result = m_manager.update_quantifier(q, k, m_manager.mk_not(body)); return true; } else { @@ -149,7 +150,7 @@ struct pull_quant::imp { // 3) MBQI std::reverse(var_sorts.begin(), var_sorts.end()); std::reverse(var_names.begin(), var_names.end()); - result = m_manager.mk_quantifier(forall_children, + result = m_manager.mk_quantifier(forall_children ? forall_k : exists_k, var_sorts.size(), var_sorts.c_ptr(), var_names.c_ptr(), @@ -220,9 +221,7 @@ struct pull_quant::imp { expr_ref_buffer new_args(m_manager); expr_ref new_arg(m_manager); ptr_buffer proofs; - unsigned num = to_app(n)->get_num_args(); - for (unsigned i = 0; i < num; i++) { - expr * arg = to_app(n)->get_arg(i); + for (expr * arg : *to_app(n)) { pull_quant1(arg , new_arg); new_args.push_back(new_arg); if (new_arg != arg) @@ -277,10 +276,13 @@ struct pull_quant::imp { expr_ref & result, proof_ref & result_pr) { - if (old_q->is_exists()) { + if (is_exists(old_q)) { UNREACHABLE(); return false; } + if (is_lambda(old_q)) { + return false; + } if (!is_forall(new_body)) return false; diff --git a/src/ast/pattern/expr_pattern_match.cpp b/src/ast/pattern/expr_pattern_match.cpp index c441fb4ce..47b3e9203 100644 --- a/src/ast/pattern/expr_pattern_match.cpp +++ b/src/ast/pattern/expr_pattern_match.cpp @@ -53,7 +53,7 @@ expr_pattern_match::match_quantifier(quantifier* qf, app_ref_vector& patterns, u m_regs[0] = qf->get_expr(); for (unsigned i = 0; i < m_precompiled.size(); ++i) { quantifier* qf2 = m_precompiled[i].get(); - if (qf2->is_forall() != qf->is_forall()) { + if (qf2->get_kind() != qf->get_kind() || is_lambda(qf)) { continue; } if (qf2->get_num_decls() != qf->get_num_decls()) { diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index 3208a50fe..f6c8788d0 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -582,7 +582,7 @@ bool pattern_inference_cfg::reduce_quantifier( proof_ref & result_pr) { TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";); - if (!q->is_forall()) { + if (!is_forall(q)) { return false; } @@ -673,6 +673,7 @@ bool pattern_inference_cfg::reduce_quantifier( new_q = m.update_quantifier_weight(new_q, weight); if (m.proofs_enabled()) { proof* new_body_pr = m.mk_reflexivity(new_body); + new_body_pr = m.mk_bind_proof(new_q, new_body_pr); result_pr = m.mk_quant_intro(q, new_q, new_body_pr); } @@ -690,7 +691,7 @@ bool pattern_inference_cfg::reduce_quantifier( } new_q = m.update_quantifier(result2, new_patterns.size(), (expr**) new_patterns.c_ptr(), result2->get_expr()); if (m.proofs_enabled()) { - result_pr = m.mk_transitivity(new_pr, m.mk_quant_intro(result2, new_q, m.mk_reflexivity(new_q->get_expr()))); + result_pr = m.mk_transitivity(new_pr, m.mk_quant_intro(result2, new_q, m.mk_bind_proof(new_q, m.mk_reflexivity(new_q->get_expr())))); } TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m) << "\n";); } diff --git a/src/ast/pb_decl_plugin.cpp b/src/ast/pb_decl_plugin.cpp index c8e707cee..dfdcc6206 100644 --- a/src/ast/pb_decl_plugin.cpp +++ b/src/ast/pb_decl_plugin.cpp @@ -92,7 +92,7 @@ func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p } void pb_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { - if (logic == symbol::null || logic == "QF_FD" || logic == "ALL") { + if (logic == symbol::null || logic == "QF_FD" || logic == "ALL" || logic == "HORN") { op_names.push_back(builtin_name(m_at_most_sym.bare_str(), OP_AT_MOST_K)); op_names.push_back(builtin_name(m_at_least_sym.bare_str(), OP_AT_LEAST_K)); op_names.push_back(builtin_name(m_pble_sym.bare_str(), OP_PB_LE)); diff --git a/src/ast/proofs/proof_checker.cpp b/src/ast/proofs/proof_checker.cpp index e148299be..fd6ea43e1 100644 --- a/src/ast/proofs/proof_checker.cpp +++ b/src/ast/proofs/proof_checker.cpp @@ -140,8 +140,8 @@ bool proof_checker::check1_spc(proof* p, expr_ref_vector& side_conditions) { proof_ref_vector proofs(m); if (match_proof(p, proofs)) { - for (unsigned i = 0; i < proofs.size(); ++i) { - add_premise(proofs[i].get()); + for (proof* pr : proofs) { + add_premise(pr); } } switch(k) { @@ -188,22 +188,22 @@ bool proof_checker::check1_spc(proof* p, expr_ref_vector& side_conditions) { bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { decl_kind k = p->get_decl_kind(); - expr_ref fml0(m), fml1(m), fml2(m), fml(m); - expr_ref t1(m), t2(m); - expr_ref s1(m), s2(m); - expr_ref u1(m), u2(m); - expr_ref fact(m), body1(m), body2(m); - expr_ref l1(m), l2(m), r1(m), r2(m); - func_decl_ref d1(m), d2(m), d3(m); - proof_ref p0(m), p1(m), p2(m); + expr* fml0 = nullptr, *fml1 = nullptr, *fml2 = nullptr, *fml = nullptr; + expr* t1 = nullptr, *t2 = nullptr; + expr* s1 = nullptr, *s2 = nullptr; + expr* u1 = nullptr, *u2 = nullptr; + expr* fact = nullptr, *body1 = nullptr, *body2 = nullptr; + expr* l1 = nullptr, *l2 = nullptr, *r1 = nullptr, *r2 = nullptr; + func_decl* d1 = nullptr, *d2 = nullptr, *d3 = nullptr; + proof* p0 = nullptr, *p1 = nullptr, *p2 = nullptr; proof_ref_vector proofs(m); - func_decl_ref f1(m), f2(m); - expr_ref_vector terms1(m), terms2(m), terms(m); + func_decl* f1 = nullptr, *f2 = nullptr; + ptr_vector terms1, terms2, terms; sort_ref_vector decls1(m), decls2(m); if (match_proof(p, proofs)) { - for (unsigned i = 0; i < proofs.size(); ++i) { - add_premise(proofs.get(i)); + for (proof* pr : proofs) { + add_premise(pr); } } @@ -219,11 +219,11 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { case PR_MODUS_PONENS: { if (match_fact(p, fact) && match_proof(p, p0, p1) && - match_fact(p0.get(), fml0) && - match_fact(p1.get(), fml1) && - (match_implies(fml1.get(), t1, t2) || match_iff(fml1.get(), t1, t2)) && - (fml0.get() == t1.get()) && - (fact.get() == t2.get())) { + match_fact(p0, fml0) && + match_fact(p1, fml1) && + (match_implies(fml1, t1, t2) || match_iff(fml1, t1, t2)) && + (fml0 == t1) && + (fact == t2)) { return true; } UNREACHABLE(); @@ -233,7 +233,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { if (match_fact(p, fact) && match_proof(p) && (match_equiv(fact, t1, t2) || match_oeq(fact, t1, t2)) && - (t1.get() == t2.get())) { + (t1 == t2)) { return true; } UNREACHABLE(); @@ -242,12 +242,12 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { case PR_SYMMETRY: { if (match_fact(p, fact) && match_proof(p, p1) && - match_fact(p1.get(), fml) && - match_binary(fact.get(), d1, l1, r1) && - match_binary(fml.get(), d2, l2, r2) && - SAME_OP(d1.get(), d2.get()) && - l1.get() == r2.get() && - r1.get() == l2.get()) { + match_fact(p1, fml) && + match_binary(fact, d1, l1, r1) && + match_binary(fml, d2, l2, r2) && + SAME_OP(d1, d2) && + l1 == r2 && + r1 == l2) { // TBD d1, d2 is a symmetric predicate return true; } @@ -257,16 +257,16 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { case PR_TRANSITIVITY: { if (match_fact(p, fact) && match_proof(p, p1, p2) && - match_fact(p1.get(), fml1) && - match_fact(p2.get(), fml2) && - match_binary(fact.get(), d1, t1, t2) && - match_binary(fml1.get(), d2, s1, s2) && - match_binary(fml2.get(), d3, u1, u2) && - d1.get() == d2.get() && - d2.get() == d3.get() && - t1.get() == s1.get() && - s2.get() == u1.get() && - u2.get() == t2.get()) { + match_fact(p1, fml1) && + match_fact(p2, fml2) && + match_binary(fact, d1, t1, t2) && + match_binary(fml1, d2, s1, s2) && + match_binary(fml2, d3, u1, u2) && + d1 == d2 && + d2 == d3 && + t1 == s1 && + s2 == u1 && + u2 == t2) { // TBD d1 is some transitive predicate. return true; } @@ -275,13 +275,13 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } case PR_TRANSITIVITY_STAR: { if (match_fact(p, fact) && - match_binary(fact.get(), d1, t1, t2)) { + match_binary(fact, d1, t1, t2)) { u_map vertices; // TBD check that d1 is transitive, symmetric. - for (unsigned i = 0; i < proofs.size(); ++i) { - if (match_fact(proofs[i].get(), fml) && - match_binary(fml.get(), d2, s1, s2) && - d1.get() == d2.get()) { + for (proof* pr : proofs) { + if (match_fact(pr, fml) && + match_binary(fml, d2, s1, s2) && + d1 == d2) { unsigned id1 = s1->get_id(); unsigned id2 = s2->get_id(); #define INSERT(_id) if (vertices.contains(_id)) vertices.remove(_id); else vertices.insert(_id, true); @@ -304,24 +304,24 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { case PR_MONOTONICITY: { TRACE("proof_checker", tout << mk_bounded_pp(p, m, 3) << "\n";); if (match_fact(p, fact) && - match_binary(fact.get(), d1, t1, t2) && - match_app(t1.get(), f1, terms1) && - match_app(t2.get(), f2, terms2) && - f1.get() == f2.get() && + match_binary(fact, d1, t1, t2) && + match_app(t1, f1, terms1) && + match_app(t2, f2, terms2) && + f1 == f2 && terms1.size() == terms2.size()) { // TBD: d1 is monotone. for (unsigned i = 0; i < terms1.size(); ++i) { - expr* term1 = terms1[i].get(); - expr* term2 = terms2[i].get(); + expr* term1 = terms1[i]; + expr* term2 = terms2[i]; if (term1 != term2) { bool found = false; - for(unsigned j = 0; j < proofs.size() && !found; ++j) { - found = - match_fact(proofs[j].get(), fml) && - match_binary(fml.get(), d2, s1, s2) && - SAME_OP(d1.get(), d2.get()) && - s1.get() == term1 && - s2.get() == term2; + for (proof* pr : proofs) { + found |= + match_fact(pr, fml) && + match_binary(fml, d2, s1, s2) && + SAME_OP(d1, d2) && + s1 == term1 && + s2 == term2; } if (!found) { UNREACHABLE(); @@ -337,18 +337,24 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { case PR_QUANT_INTRO: { if (match_proof(p, p1) && match_fact(p, fact) && - match_fact(p1.get(), fml) && - (match_iff(fact.get(), t1, t2) || match_oeq(fact.get(), t1, t2)) && - (match_iff(fml.get(), s1, s2) || match_oeq(fml.get(), s1, s2)) && - m.is_oeq(fact.get()) == m.is_oeq(fml.get()) && - is_quantifier(t1.get()) && - is_quantifier(t2.get()) && - to_quantifier(t1.get())->get_expr() == s1.get() && - to_quantifier(t2.get())->get_expr() == s2.get() && - to_quantifier(t1.get())->get_num_decls() == to_quantifier(t2.get())->get_num_decls() && - to_quantifier(t1.get())->is_forall() == to_quantifier(t2.get())->is_forall()) { - quantifier* q1 = to_quantifier(t1.get()); - quantifier* q2 = to_quantifier(t2.get()); + match_fact(p1, fml) && + (is_lambda(fact) || is_lambda(fml))) + return true; + + if (match_proof(p, p1) && + match_fact(p, fact) && + match_fact(p1, fml) && + (match_iff(fact, t1, t2) || match_oeq(fact, t1, t2)) && + (match_iff(fml, s1, s2) || match_oeq(fml, s1, s2)) && + m.is_oeq(fact) == m.is_oeq(fml) && + is_quantifier(t1) && + is_quantifier(t2) && + to_quantifier(t1)->get_expr() == s1 && + to_quantifier(t2)->get_expr() == s2 && + to_quantifier(t1)->get_num_decls() == to_quantifier(t2)->get_num_decls() && + to_quantifier(t1)->get_kind() == to_quantifier(t2)->get_kind()) { + quantifier* q1 = to_quantifier(t1); + quantifier* q2 = to_quantifier(t2); for (unsigned i = 0; i < q1->get_num_decls(); ++i) { if (q1->get_decl_sort(i) != q2->get_decl_sort(i)) { // term is not well-typed. @@ -361,40 +367,43 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { UNREACHABLE(); return false; } + case PR_BIND: + // it is a lambda expression returning a proof object. + if (!is_lambda(to_app(p)->get_arg(0))) + return false; + // check that body is a proof object. + return true; + case PR_DISTRIBUTIVITY: { if (match_fact(p, fact) && match_proof(p) && - match_equiv(fact.get(), t1, t2)) { - side_conditions.push_back(fact.get()); + match_equiv(fact, t1, t2)) { + side_conditions.push_back(fact); return true; } UNREACHABLE(); return false; } case PR_AND_ELIM: { - expr_ref_vector terms(m); if (match_proof(p, p1) && match_fact(p, fact) && - match_fact(p1.get(), fml) && - match_and(fml.get(), terms)) { - for (unsigned i = 0; i < terms.size(); ++i) { - if (terms[i].get() == fact.get()) { - return true; - } - } + match_fact(p1, fml) && + match_and(fml, terms)) { + for (expr* t : terms) + if (t == fact) return true; } UNREACHABLE(); return false; } case PR_NOT_OR_ELIM: { - expr_ref_vector terms(m); + if (match_proof(p, p1) && match_fact(p, fact) && - match_fact(p1.get(), fml) && - match_not(fml.get(), fml1) && - match_or(fml1.get(), terms)) { - for (unsigned i = 0; i < terms.size(); ++i) { - if (match_negated(terms[i].get(), fact.get())) { + match_fact(p1, fml) && + match_not(fml, fml1) && + match_or(fml1, terms)) { + for (expr* t : terms) { + if (match_negated(t, fact)) { return true; } } @@ -405,8 +414,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { case PR_REWRITE: { if (match_fact(p, fact) && match_proof(p) && - match_equiv(fact.get(), t1, t2)) { - side_conditions.push_back(fact.get()); + match_equiv(fact, t1, t2)) { + side_conditions.push_back(fact); return true; } IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); @@ -414,12 +423,12 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } case PR_REWRITE_STAR: { if (match_fact(p, fact) && - match_equiv(fact.get(), t1, t2)) { + match_equiv(fact, t1, t2)) { expr_ref_vector rewrite_eq(m); - rewrite_eq.push_back(fact.get()); - for (unsigned i = 0; i < proofs.size(); ++i) { - if (match_fact(proofs[i].get(), fml)) { - rewrite_eq.push_back(m.mk_not(fml.get())); + rewrite_eq.push_back(fact); + for (proof* pr : proofs) { + if (match_fact(pr, fml)) { + rewrite_eq.push_back(m.mk_not(fml)); } } expr_ref rewrite_cond(m); @@ -433,8 +442,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { case PR_PULL_QUANT: { if (match_proof(p) && match_fact(p, fact) && - match_iff(fact.get(), t1, t2) && - is_quantifier(t2.get())) { + match_iff(fact, t1, t2) && + is_quantifier(t2)) { // TBD: check the enchilada. return true; } @@ -444,16 +453,16 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { case PR_PUSH_QUANT: { if (match_proof(p) && match_fact(p, fact) && - match_iff(fact.get(), t1, t2) && - is_quantifier(t1.get()) && - match_and(to_quantifier(t1.get())->get_expr(), terms1) && - match_and(t2.get(), terms2) && + match_iff(fact, t1, t2) && + is_quantifier(t1) && + match_and(to_quantifier(t1)->get_expr(), terms1) && + match_and(t2, terms2) && terms1.size() == terms2.size()) { - quantifier * q1 = to_quantifier(t1.get()); + quantifier * q1 = to_quantifier(t1); for (unsigned i = 0; i < terms1.size(); ++i) { - if (is_quantifier(terms2[i].get()) && - to_quantifier(terms2[i].get())->get_expr() == terms1[i].get() && - to_quantifier(terms2[i].get())->get_num_decls() == q1->get_num_decls()) { + if (is_quantifier(terms2[i]) && + to_quantifier(terms2[i])->get_expr() == terms1[i] && + to_quantifier(terms2[i])->get_num_decls() == q1->get_num_decls()) { // ok. } else { @@ -467,9 +476,9 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { case PR_ELIM_UNUSED_VARS: { if (match_proof(p) && match_fact(p, fact) && - match_iff(fact.get(), t1, t2)) { + match_iff(fact, t1, t2)) { // TBD: - // match_quantifier(t1.get(), is_forall1, decls1, body1) + // match_quantifier(t1, is_forall1, decls1, body1) // filter out decls1 that occur in body1. // if list is empty, then t2 could be just body1. // otherwise t2 is also a quantifier. @@ -482,7 +491,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { bool is_forall = false; if (match_proof(p) && match_fact(p, fact) && - match_iff(fact.get(), t1, t2) && + match_iff(fact, t1, t2) && match_quantifier(t1, is_forall, decls1, body1) && is_forall) { // TBD: check that terms are set of equalities. @@ -503,18 +512,18 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { case PR_LEMMA: { if (match_proof(p, p1) && match_fact(p, fact) && - match_fact(p1.get(), fml) && - m.is_false(fml.get())) { + match_fact(p1, fml) && + m.is_false(fml)) { expr_ref_vector hypotheses(m); expr_ref_vector ors(m); - get_hypotheses(p1.get(), hypotheses); + get_hypotheses(p1, hypotheses); if (hypotheses.size() == 1 && match_negated(hypotheses.get(0), fact)) { // Suppose fact is (or a b c) and hypothesis is (not (or a b c)) // That is, (or a b c) should be viewed as a 'quoted expression' and a unary clause, // instead of a clause with three literals. return true; } - get_ors(fact.get(), ors); + get_ors(fact, ors); for (unsigned i = 0; i < hypotheses.size(); ++i) { bool found = false; unsigned j; @@ -524,14 +533,9 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { if (!found) { TRACE("pr_lemma_bug", tout << "i: " << i << "\n"; - tout << "ORs:\n"; - for (unsigned i = 0; i < ors.size(); i++) { - tout << mk_pp(ors.get(i), m) << "\n"; - } - tout << "HYPOTHESIS:\n"; - for (unsigned i = 0; i < hypotheses.size(); i++) { - tout << mk_pp(hypotheses.get(i), m) << "\n"; - }); + tout << "ORs:\n" << ors << "\n"; + tout << "HYPOTHESIS:\n" << hypotheses << "\n"; + ); UNREACHABLE(); return false; } @@ -549,14 +553,14 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { proofs.size() == 2 && match_fact(proofs[0].get(), fml1) && match_fact(proofs[1].get(), fml2) && - m.is_complement(fml1.get(), fml2.get()) && - m.is_false(fact.get())) { + m.is_complement(fml1, fml2) && + m.is_false(fact)) { return true; } if (match_fact(p, fact) && proofs.size() > 1 && match_fact(proofs.get(0), fml) && - match_or(fml.get(), terms1)) { + match_or(fml, terms1)) { for (unsigned i = 1; i < proofs.size(); ++i) { if (!match_fact(proofs.get(i), fml2)) { return false; @@ -575,7 +579,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { TRACE("pr_unit_bug", tout << "Parents:\n"; for (unsigned i = 0; i < proofs.size(); i++) { - expr_ref p(m); + expr* p = nullptr; match_fact(proofs.get(i), p); tout << mk_pp(p, m) << "\n"; } @@ -592,16 +596,15 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } switch(terms1.size()) { case 0: - return m.is_false(fact.get()); + return m.is_false(fact); case 1: - return fact.get() == terms1[0].get(); + return fact == terms1[0]; default: { - if (match_or(fact.get(), terms2)) { - for (unsigned i = 0; i < terms1.size(); ++i) { + if (match_or(fact, terms2)) { + for (expr* term1 : terms1) { bool found = false; - expr* term1 = terms1[i].get(); - for (unsigned j = 0; !found && j < terms2.size(); ++j) { - found = term1 == terms2[j].get(); + for (expr* term2 : terms2) { + found = term1 == term2; } if (!found) { IF_VERBOSE(0, verbose_stream() << "Premise not found:" << mk_pp(term1, m) << "\n";); @@ -611,8 +614,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { return true; } IF_VERBOSE(0, verbose_stream() << "Conclusion is not a disjunction:\n"; - verbose_stream() << mk_pp(fml.get(), m) << "\n"; - verbose_stream() << mk_pp(fact.get(), m) << "\n";); + verbose_stream() << mk_pp(fml, m) << "\n"; + verbose_stream() << mk_pp(fact, m) << "\n";); return false; } @@ -626,10 +629,10 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // iff_true(?rule(?p1, ?fml), (iff ?fml true)) if (match_proof(p, p1) && match_fact(p, fact) && - match_fact(p1.get(), fml1) && - match_iff(fact.get(), l1, r1) && - fml1.get() == l1.get() && - r1.get() == m.mk_true()) { + match_fact(p1, fml1) && + match_iff(fact, l1, r1) && + fml1 == l1 && + r1 == m.mk_true()) { return true; } UNREACHABLE(); @@ -639,11 +642,11 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // iff_false(?rule(?p1, (not ?fml)), (iff ?fml false)) if (match_proof(p, p1) && match_fact(p, fact) && - match_fact(p1.get(), fml1) && - match_iff(fact.get(), l1, r1) && - match_not(fml1.get(), t1) && - t1.get() == l1.get() && - r1.get() == m.mk_false()) { + match_fact(p1, fml1) && + match_iff(fact, l1, r1) && + match_not(fml1, t1) && + t1 == l1 && + r1 == m.mk_false()) { return true; } UNREACHABLE(); @@ -653,12 +656,12 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // commutativity(= (?c ?t1 ?t2) (?c ?t2 ?t1)) if (match_fact(p, fact) && match_proof(p) && - match_equiv(fact.get(), t1, t2) && - match_binary(t1.get(), d1, s1, s2) && - match_binary(t2.get(), d2, u1, u2) && - s1.get() == u2.get() && - s2.get() == u1.get() && - d1.get() == d2.get() && + match_equiv(fact, t1, t2) && + match_binary(t1, d1, s1, s2) && + match_binary(t2, d2, u1, u2) && + s1 == u2 && + s2 == u1 && + d1 == d2 && d1->is_commutative()) { return true; } @@ -669,7 +672,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // axiom(?fml) if (match_fact(p, fact) && match_proof(p) && - m.is_bool(fact.get())) { + m.is_bool(fact)) { return true; } UNREACHABLE(); @@ -684,7 +687,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // if (match_fact(p, fact) && match_proof(p) && - m.is_bool(fact.get())) { + m.is_bool(fact)) { return true; } UNREACHABLE(); @@ -692,7 +695,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } case PR_APPLY_DEF: { if (match_fact(p, fact) && - match_oeq(fact.get(), t1, t2)) { + match_oeq(fact, t1, t2)) { // TBD: must definitions be in proofs? return true; } @@ -703,11 +706,11 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // axiom(?rule(?p1,(iff ?t1 ?t2)), (~ ?t1 ?t2)) if (match_fact(p, fact) && match_proof(p, p1) && - match_oeq(fact.get(), t1, t2) && - match_fact(p1.get(), fml) && - match_iff(fml.get(), s1, s2) && - s1.get() == t1.get() && - s2.get() == t2.get()) { + match_oeq(fact, t1, t2) && + match_fact(p1, fml) && + match_iff(fml, s1, s2) && + s1 == t1 && + s2 == t2) { return true; } UNREACHABLE(); @@ -725,18 +728,19 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // (exists ?x (p ?x y)) -> (p (sk y) y) // (not (forall ?x (p ?x y))) -> (not (p (sk y) y)) if (match_fact(p, fact) && - match_oeq(fact.get(), t1, t2)) { + match_oeq(fact, t1, t2)) { quantifier* q = nullptr; - expr* e = t1.get(); + expr* e = t1; bool is_forall = false; - if (match_not(t1.get(), s1)) { - e = s1.get(); + if (match_not(t1, s1)) { + e = s1; is_forall = true; } if (is_quantifier(e)) { - q = to_quantifier(e); + SASSERT(!is_lambda(e)); + q = to_quantifier(e); // TBD check that quantifier is properly instantiated - return is_forall == q->is_forall(); + return is_forall == ::is_forall(q); } } UNREACHABLE(); @@ -745,11 +749,11 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { case PR_MODUS_PONENS_OEQ: { if (match_fact(p, fact) && match_proof(p, p0, p1) && - match_fact(p0.get(), fml0) && - match_fact(p1.get(), fml1) && - match_oeq(fml1.get(), t1, t2) && - fml0.get() == t1.get() && - fact.get() == t2.get()) { + match_fact(p0, fml0) && + match_fact(p1, fml1) && + match_oeq(fml1, t1, t2) && + fml0 == t1 && + fact == t2) { return true; } UNREACHABLE(); @@ -784,7 +788,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // SASSERT(to_quantifier(premise)->get_num_decls() == sub.size()); premise = to_quantifier(premise)->get_expr(); } - vs(premise, sub.size(), sub.c_ptr(), premise); + premise = vs(premise, sub.size(), sub.c_ptr()); } fmls.push_back(premise.get()); TRACE("proof_checker", @@ -896,7 +900,7 @@ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) { } } -bool proof_checker::match_fact(proof const* p, expr_ref& fact) const { +bool proof_checker::match_fact(proof const* p, expr*& fact) const { if (m.is_proof(p) && m.has_fact(p)) { fact = m.get_fact(p); @@ -918,7 +922,7 @@ bool proof_checker::match_proof(proof const* p) const { m.get_num_parents(p) == 0; } -bool proof_checker::match_proof(proof const* p, proof_ref& p0) const { +bool proof_checker::match_proof(proof const* p, proof*& p0) const { if (m.is_proof(p) && m.get_num_parents(p) == 1) { p0 = m.get_parent(p, 0); @@ -927,7 +931,7 @@ bool proof_checker::match_proof(proof const* p, proof_ref& p0) const { return false; } -bool proof_checker::match_proof(proof const* p, proof_ref& p0, proof_ref& p1) const { +bool proof_checker::match_proof(proof const* p, proof*& p0, proof*& p1) const { if (m.is_proof(p) && m.get_num_parents(p) == 2) { p0 = m.get_parent(p, 0); @@ -948,7 +952,7 @@ bool proof_checker::match_proof(proof const* p, proof_ref_vector& parents) const } -bool proof_checker::match_binary(expr const* e, func_decl_ref& d, expr_ref& t1, expr_ref& t2) const { +bool proof_checker::match_binary(expr const* e, func_decl*& d, expr*& t1, expr*& t2) const { if (e->get_kind() == AST_APP && to_app(e)->get_num_args() == 2) { d = to_app(e)->get_decl(); @@ -960,21 +964,21 @@ bool proof_checker::match_binary(expr const* e, func_decl_ref& d, expr_ref& t1, } -bool proof_checker::match_app(expr const* e, func_decl_ref& d, expr_ref_vector& terms) const { +bool proof_checker::match_app(expr const* e, func_decl*& d, ptr_vector& terms) const { if (e->get_kind() == AST_APP) { d = to_app(e)->get_decl(); - for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { - terms.push_back(to_app(e)->get_arg(i)); + for (expr* arg : *to_app(e)) { + terms.push_back(arg); } return true; } return false; } -bool proof_checker::match_quantifier(expr const* e, bool& is_univ, sort_ref_vector& sorts, expr_ref& body) const { +bool proof_checker::match_quantifier(expr const* e, bool& is_univ, sort_ref_vector& sorts, expr*& body) const { if (is_quantifier(e)) { quantifier const* q = to_quantifier(e); - is_univ = q->is_forall(); + is_univ = is_forall(q); body = q->get_expr(); for (unsigned i = 0; i < q->get_num_decls(); ++i) { sorts.push_back(q->get_decl_sort(i)); @@ -984,7 +988,7 @@ bool proof_checker::match_quantifier(expr const* e, bool& is_univ, sort_ref_vect return false; } -bool proof_checker::match_op(expr const* e, decl_kind k, expr_ref& t1, expr_ref& t2) const { +bool proof_checker::match_op(expr const* e, decl_kind k, expr*& t1, expr*& t2) const { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k && @@ -996,20 +1000,19 @@ bool proof_checker::match_op(expr const* e, decl_kind k, expr_ref& t1, expr_ref& return false; } -bool proof_checker::match_op(expr const* e, decl_kind k, expr_ref_vector& terms) const { +bool proof_checker::match_op(expr const* e, decl_kind k, ptr_vector& terms) const { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k) { - for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { - terms.push_back(to_app(e)->get_arg(i)); - } + for (expr* arg : *to_app(e)) + terms.push_back(arg); return true; } return false; } -bool proof_checker::match_op(expr const* e, decl_kind k, expr_ref& t) const { +bool proof_checker::match_op(expr const* e, decl_kind k, expr*& t) const { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k && @@ -1020,43 +1023,43 @@ bool proof_checker::match_op(expr const* e, decl_kind k, expr_ref& t) const { return false; } -bool proof_checker::match_not(expr const* e, expr_ref& t) const { +bool proof_checker::match_not(expr const* e, expr*& t) const { return match_op(e, OP_NOT, t); } -bool proof_checker::match_or(expr const* e, expr_ref_vector& terms) const { +bool proof_checker::match_or(expr const* e, ptr_vector& terms) const { return match_op(e, OP_OR, terms); } -bool proof_checker::match_and(expr const* e, expr_ref_vector& terms) const { +bool proof_checker::match_and(expr const* e, ptr_vector& terms) const { return match_op(e, OP_AND, terms); } -bool proof_checker::match_iff(expr const* e, expr_ref& t1, expr_ref& t2) const { +bool proof_checker::match_iff(expr const* e, expr*& t1, expr*& t2) const { return match_op(e, OP_EQ, t1, t2) && m.is_bool(t1); } -bool proof_checker::match_equiv(expr const* e, expr_ref& t1, expr_ref& t2) const { +bool proof_checker::match_equiv(expr const* e, expr*& t1, expr*& t2) const { return match_oeq(e, t1, t2) || match_eq(e, t1, t2); } -bool proof_checker::match_implies(expr const* e, expr_ref& t1, expr_ref& t2) const { +bool proof_checker::match_implies(expr const* e, expr*& t1, expr*& t2) const { return match_op(e, OP_IMPLIES, t1, t2); } -bool proof_checker::match_eq(expr const* e, expr_ref& t1, expr_ref& t2) const { +bool proof_checker::match_eq(expr const* e, expr*& t1, expr*& t2) const { return match_op(e, OP_EQ, t1, t2); } -bool proof_checker::match_oeq(expr const* e, expr_ref& t1, expr_ref& t2) const { +bool proof_checker::match_oeq(expr const* e, expr*& t1, expr*& t2) const { return match_op(e, OP_OEQ, t1, t2); } bool proof_checker::match_negated(expr const* a, expr* b) const { - expr_ref t(m); + expr* t = nullptr; return - (match_not(a, t) && t.get() == b) || - (match_not(b, t) && t.get() == a); + (match_not(a, t) && t == b) || + (match_not(b, t) && t == a); } void proof_checker::get_ors(expr* e, expr_ref_vector& ors) { @@ -1073,8 +1076,7 @@ void proof_checker::get_ors(expr* e, expr_ref_vector& ors) { void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { ptr_vector stack; - expr* h = nullptr; - expr_ref hyp(m); + expr* h = nullptr, *hyp = nullptr; stack.push_back(p); while (!stack.empty()) { @@ -1085,9 +1087,9 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { continue; } if (is_hypothesis(p) && match_fact(p, hyp)) { - hyp = mk_atom(hyp.get()); - m_pinned.push_back(hyp.get()); - m_hypotheses.insert(p, hyp.get()); + hyp = mk_atom(hyp); + m_pinned.push_back(hyp); + m_hypotheses.insert(p, hyp); stack.pop_back(); continue; } @@ -1128,7 +1130,7 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { ptr_buffer todo; expr_mark mark; todo.push_back(h); - expr_ref a(m), b(m); + expr* a = nullptr, *b = nullptr; while (!todo.empty()) { h = todo.back(); @@ -1139,11 +1141,11 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { } mark.mark(h, true); if (match_cons(h, a, b)) { - todo.push_back(a.get()); - todo.push_back(b.get()); + todo.push_back(a); + todo.push_back(b); } else if (match_atom(h, a)) { - ante.push_back(a.get()); + ante.push_back(a); } else { SASSERT(match_nil(h)); @@ -1167,7 +1169,7 @@ bool proof_checker::match_nil(expr const* e) const { to_app(e)->get_decl_kind() == OP_NIL; } -bool proof_checker::match_cons(expr const* e, expr_ref& a, expr_ref& b) const { +bool proof_checker::match_cons(expr const* e, expr*& a, expr*& b) const { if (is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_CONS) { @@ -1179,7 +1181,7 @@ bool proof_checker::match_cons(expr const* e, expr_ref& a, expr_ref& b) const { } -bool proof_checker::match_atom(expr const* e, expr_ref& a) const { +bool proof_checker::match_atom(expr const* e, expr*& a) const { if (is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_ATOM) { @@ -1358,7 +1360,7 @@ bool proof_checker::check_arith_proof(proof* p) { dump_proof(p); return true; } - expr_ref fact(m); + expr* fact = nullptr; proof_ref_vector proofs(m); if (!match_fact(p, fact)) { diff --git a/src/ast/proofs/proof_checker.h b/src/ast/proofs/proof_checker.h index ac0e31dbd..39c776392 100644 --- a/src/ast/proofs/proof_checker.h +++ b/src/ast/proofs/proof_checker.h @@ -77,33 +77,33 @@ private: bool check1_spc(proof* p, expr_ref_vector& side_conditions); bool check_arith_proof(proof* p); bool check_arith_literal(bool is_pos, app* lit, rational const& coeff, expr_ref& sum, bool& is_strict); - bool match_fact(proof const* p, expr_ref& fact) const; + bool match_fact(proof const* p, expr*& fact) const; void add_premise(proof* p); bool match_proof(proof const* p) const; - bool match_proof(proof const* p, proof_ref& p0) const; - bool match_proof(proof const* p, proof_ref& p0, proof_ref& p1) const; + bool match_proof(proof const* p, proof*& p0) const; + bool match_proof(proof const* p, proof*& p0, proof*& p1) const; bool match_proof(proof const* p, proof_ref_vector& parents) const; - bool match_binary(expr const* e, func_decl_ref& d, expr_ref& t1, expr_ref& t2) const; - bool match_op(expr const* e, decl_kind k, expr_ref& t1, expr_ref& t2) const; - bool match_op(expr const* e, decl_kind k, expr_ref& t) const; - bool match_op(expr const* e, decl_kind k, expr_ref_vector& terms) const; - bool match_iff(expr const* e, expr_ref& t1, expr_ref& t2) const; - bool match_implies(expr const* e, expr_ref& t1, expr_ref& t2) const; - bool match_eq(expr const* e, expr_ref& t1, expr_ref& t2) const; - bool match_oeq(expr const* e, expr_ref& t1, expr_ref& t2) const; - bool match_not(expr const* e, expr_ref& t) const; - bool match_or(expr const* e, expr_ref_vector& terms) const; - bool match_and(expr const* e, expr_ref_vector& terms) const; - bool match_app(expr const* e, func_decl_ref& d, expr_ref_vector& terms) const; - bool match_quantifier(expr const*, bool& is_univ, sort_ref_vector&, expr_ref& body) const; + bool match_binary(expr const* e, func_decl*& d, expr*& t1, expr*& t2) const; + bool match_op(expr const* e, decl_kind k, expr*& t1, expr*& t2) const; + bool match_op(expr const* e, decl_kind k, expr*& t) const; + bool match_op(expr const* e, decl_kind k, ptr_vector& terms) const; + bool match_iff(expr const* e, expr*& t1, expr*& t2) const; + bool match_implies(expr const* e, expr*& t1, expr*& t2) const; + bool match_eq(expr const* e, expr*& t1, expr*& t2) const; + bool match_oeq(expr const* e, expr*& t1, expr*& t2) const; + bool match_not(expr const* e, expr*& t) const; + bool match_or(expr const* e, ptr_vector& terms) const; + bool match_and(expr const* e, ptr_vector& terms) const; + bool match_app(expr const* e, func_decl*& d, ptr_vector& terms) const; + bool match_quantifier(expr const*, bool& is_univ, sort_ref_vector&, expr*& body) const; bool match_negated(expr const* a, expr* b) const; - bool match_equiv(expr const* a, expr_ref& t1, expr_ref& t2) const; + bool match_equiv(expr const* a, expr*& t1, expr*& t2) const; void get_ors(expr* e, expr_ref_vector& ors); void get_hypotheses(proof* p, expr_ref_vector& ante); bool match_nil(expr const* e) const; - bool match_cons(expr const* e, expr_ref& a, expr_ref& b) const; - bool match_atom(expr const* e, expr_ref& a) const; + bool match_cons(expr const* e, expr*& a, expr*& b) const; + bool match_atom(expr const* e, expr*& a) const; expr* mk_nil(); expr* mk_cons(expr* a, expr* b); expr* mk_atom(expr* e); diff --git a/src/ast/proofs/proof_utils.cpp b/src/ast/proofs/proof_utils.cpp index 97213c235..5483a9ea0 100644 --- a/src/ast/proofs/proof_utils.cpp +++ b/src/ast/proofs/proof_utils.cpp @@ -975,9 +975,7 @@ private: void compose(expr_ref_vector& sub, expr_ref_vector const& s0) { for (unsigned i = 0; i < sub.size(); ++i) { - expr_ref e(m); - var_subst(m, false)(sub[i].get(), s0.size(), s0.c_ptr(), e); - sub[i] = e; + sub[i] = var_subst(m, false)(sub[i].get(), s0.size(), s0.c_ptr()); } } @@ -995,7 +993,7 @@ private: tout << sub.size() << "\n";); return; } - var_subst(m, false)(q->get_expr(), sub.size(), sub.c_ptr(), fml); + fml = var_subst(m, false)(q->get_expr(), sub.size(), sub.c_ptr()); } }; diff --git a/src/ast/proofs/proof_utils.h b/src/ast/proofs/proof_utils.h index 729a30eb0..e3ad8d06e 100644 --- a/src/ast/proofs/proof_utils.h +++ b/src/ast/proofs/proof_utils.h @@ -164,10 +164,12 @@ public: // skip (asserted m_aux) else if (m.is_asserted(arg, a) && a == m_aux.get()) { dirty = true; + args.push_back(m.mk_true_proof()); } // skip (hypothesis m_aux) else if (m.is_hypothesis(arg, a) && a == m_aux.get()) { dirty = true; + args.push_back(m.mk_true_proof()); } else if (is_app(arg) && cache.find(to_app(arg), r)) { dirty |= (arg != r); args.push_back(r); @@ -188,14 +190,18 @@ public: app_ref newp(m); if (!dirty) { newp = p; } else if (m.is_unit_resolution(p)) { - if (args.size() == 2) - // unit resolution with m_aux that got collapsed to nothing - { newp = to_app(args.get(0)); } + ptr_buffer parents; + for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i) { + app *arg = to_app(args.get(i)); + if (!m.is_true(m.get_fact(arg))) + parents.push_back(arg); + } + // unit resolution that collapsed to nothing + if (parents.size() == 1) { + newp = parents.get(0); + } else { - ptr_vector parents; - for (unsigned i = 0, sz = args.size() - 1; i < sz; ++i) - { parents.push_back(to_app(args.get(i))); } - SASSERT(parents.size() == args.size() - 1); + // rebuild unit resolution newp = m.mk_unit_resolution(parents.size(), parents.c_ptr()); // XXX the old and new facts should be // equivalent. The test here is much @@ -203,9 +209,11 @@ public: SASSERT(m.get_fact(newp) == args.back()); pinned.push_back(newp); } - } else if (matches_fact(args, a)) { + } + else if (matches_fact(args, a)) { newp = to_app(a); - } else { + } + else { expr_ref papp(m); mk_app(p->get_decl(), args, papp); newp = to_app(papp.get()); diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index 379020331..7a03d84d7 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -801,106 +801,104 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu return BR_DONE; } if (m_util.is_numeral(arg2, v2, is_int) && v2.is_one()) { - result = arg1; - return BR_DONE; - } - if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { - return BR_FAILED; - } - if (arg1 == arg2) { - expr_ref zero(m_util.mk_int(0), m()); - result = m().mk_ite(m().mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); - return BR_REWRITE3; - } - if (divides(arg1, arg2, result)) { - return BR_REWRITE_FULL; - } + result = arg1; + return BR_DONE; + } + if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { + return BR_FAILED; + } + if (arg1 == arg2) { + expr_ref zero(m_util.mk_int(0), m()); + result = m().mk_ite(m().mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); + return BR_REWRITE3; + } + if (divides(arg1, arg2, result)) { + return BR_REWRITE_FULL; + } return BR_FAILED; } - - -// -// implement div ab ac = floor( ab / ac) = floor (b / c) = div b c + +// +// implement div ab ac = floor( ab / ac) = floor (b / c) = div b c // -bool arith_rewriter::divides(expr* num, expr* den, expr_ref& result) { - expr_fast_mark1 mark; - rational num_r(1), den_r(1); - expr* num_e = nullptr, *den_e = nullptr; - ptr_buffer args1, args2; - flat_mul(num, args1); - flat_mul(den, args2); - for (expr * arg : args1) { - mark.mark(arg); - if (m_util.is_numeral(arg, num_r)) num_e = arg; - } - for (expr* arg : args2) { - if (mark.is_marked(arg)) { - result = remove_divisor(arg, num, den); - return true; - } - if (m_util.is_numeral(arg, den_r)) den_e = arg; - } - rational g = gcd(num_r, den_r); - if (!g.is_one()) { - SASSERT(g.is_pos()); - // replace num_e, den_e by their gcd reduction. - for (unsigned i = 0; i < args1.size(); ++i) { - if (args1[i] == num_e) { - args1[i] = m_util.mk_numeral(num_r / g, true); - break; - } - } - for (unsigned i = 0; i < args2.size(); ++i) { - if (args2[i] == den_e) { - args2[i] = m_util.mk_numeral(den_r / g, true); - break; - } - } - - num = m_util.mk_mul(args1.size(), args1.c_ptr()); - den = m_util.mk_mul(args2.size(), args2.c_ptr()); - result = m_util.mk_idiv(num, den); - return true; - } - return false; -} - -expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) { - ptr_buffer args1, args2; - flat_mul(num, args1); - flat_mul(den, args2); - remove_divisor(arg, args1); - remove_divisor(arg, args2); - expr_ref zero(m_util.mk_int(0), m()); - num = args1.empty() ? m_util.mk_int(1) : m_util.mk_mul(args1.size(), args1.c_ptr()); - den = args2.empty() ? m_util.mk_int(1) : m_util.mk_mul(args2.size(), args2.c_ptr()); - return expr_ref(m().mk_ite(m().mk_eq(zero, arg), m_util.mk_idiv(zero, zero), m_util.mk_idiv(num, den)), m()); -} - -void arith_rewriter::flat_mul(expr* e, ptr_buffer& args) { - args.push_back(e); - for (unsigned i = 0; i < args.size(); ++i) { - e = args[i]; - if (m_util.is_mul(e)) { - args.append(to_app(e)->get_num_args(), to_app(e)->get_args()); - args[i] = args.back(); - args.shrink(args.size()-1); - --i; - } - } -} - -void arith_rewriter::remove_divisor(expr* d, ptr_buffer& args) { - for (unsigned i = 0; i < args.size(); ++i) { - if (args[i] == d) { - args[i] = args.back(); - args.shrink(args.size()-1); - return; - } - } - UNREACHABLE(); -} +bool arith_rewriter::divides(expr* num, expr* den, expr_ref& result) { + expr_fast_mark1 mark; + rational num_r(1), den_r(1); + expr* num_e = nullptr, *den_e = nullptr; + ptr_buffer args1, args2; + flat_mul(num, args1); + flat_mul(den, args2); + for (expr * arg : args1) { + mark.mark(arg); + if (m_util.is_numeral(arg, num_r)) num_e = arg; + } + for (expr* arg : args2) { + if (mark.is_marked(arg)) { + result = remove_divisor(arg, num, den); + return true; + } + if (m_util.is_numeral(arg, den_r)) den_e = arg; + } + rational g = gcd(num_r, den_r); + if (!g.is_one()) { + SASSERT(g.is_pos()); + // replace num_e, den_e by their gcd reduction. + for (unsigned i = 0; i < args1.size(); ++i) { + if (args1[i] == num_e) { + args1[i] = m_util.mk_numeral(num_r / g, true); + break; + } + } + for (unsigned i = 0; i < args2.size(); ++i) { + if (args2[i] == den_e) { + args2[i] = m_util.mk_numeral(den_r / g, true); + break; + } + } + num = m_util.mk_mul(args1.size(), args1.c_ptr()); + den = m_util.mk_mul(args2.size(), args2.c_ptr()); + result = m_util.mk_idiv(num, den); + return true; + } + return false; +} +expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) { + ptr_buffer args1, args2; + flat_mul(num, args1); + flat_mul(den, args2); + remove_divisor(arg, args1); + remove_divisor(arg, args2); + expr_ref zero(m_util.mk_int(0), m()); + num = args1.empty() ? m_util.mk_int(1) : m_util.mk_mul(args1.size(), args1.c_ptr()); + den = args2.empty() ? m_util.mk_int(1) : m_util.mk_mul(args2.size(), args2.c_ptr()); + return expr_ref(m().mk_ite(m().mk_eq(zero, arg), m_util.mk_idiv(zero, zero), m_util.mk_idiv(num, den)), m()); +} + +void arith_rewriter::flat_mul(expr* e, ptr_buffer& args) { + args.push_back(e); + for (unsigned i = 0; i < args.size(); ++i) { + e = args[i]; + if (m_util.is_mul(e)) { + args.append(to_app(e)->get_num_args(), to_app(e)->get_args()); + args[i] = args.back(); + args.shrink(args.size()-1); + --i; + } + } +} + +void arith_rewriter::remove_divisor(expr* d, ptr_buffer& args) { + for (unsigned i = 0; i < args.size(); ++i) { + if (args[i] == d) { + args[i] = args.back(); + args.shrink(args.size()-1); + return; + } + } + UNREACHABLE(); +} + br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(m().get_sort(arg1)); numeral v1, v2; @@ -1159,49 +1157,44 @@ br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) { result = m_util.mk_numeral(floor(a), true); return BR_DONE; } - else if (m_util.is_to_real(arg, x)) { + + if (m_util.is_to_real(arg, x)) { result = x; return BR_DONE; } - else { - if (m_util.is_add(arg) || m_util.is_mul(arg) || m_util.is_power(arg)) { - // Try to apply simplifications such as: - // (to_int (+ 1.0 (to_real x))) --> (+ 1 x) - // if all arguments of arg are - // - integer numerals, OR - // - to_real applications - // then, to_int can be eliminated - unsigned num_args = to_app(arg)->get_num_args(); - unsigned i = 0; - for (; i < num_args; i++) { - expr * c = to_app(arg)->get_arg(i); - if (m_util.is_numeral(c, a) && a.is_int()) - continue; - if (m_util.is_to_real(c)) - continue; - break; // failed + if (m_util.is_add(arg) || m_util.is_mul(arg) || m_util.is_power(arg)) { + // Try to apply simplifications such as: + // (to_int (+ 1.0 (to_real x)) y) --> (+ 1 x (to_int y)) + + expr_ref_buffer int_args(m()), real_args(m()); + for (expr* c : *to_app(arg)) { + if (m_util.is_numeral(c, a) && a.is_int()) { + int_args.push_back(m_util.mk_numeral(a, true)); } - if (i == num_args) { - // simplification can be applied - expr_ref_buffer new_args(m()); - for (i = 0; i < num_args; i++) { - expr * c = to_app(arg)->get_arg(i); - if (m_util.is_numeral(c, a) && a.is_int()) { - new_args.push_back(m_util.mk_numeral(a, true)); - } - else { - VERIFY (m_util.is_to_real(c, x)); - new_args.push_back(x); - } - } - SASSERT(num_args == new_args.size()); - result = m().mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), new_args.size(), new_args.c_ptr()); - return BR_REWRITE1; + else if (m_util.is_to_real(c, x)) { + int_args.push_back(x); + } + else { + real_args.push_back(c); } } - return BR_FAILED; + if (real_args.empty()) { + result = m().mk_app(get_fid(), to_app(arg)->get_decl()->get_decl_kind(), int_args.size(), int_args.c_ptr()); + return BR_REWRITE1; + } + if (!int_args.empty() && (m_util.is_add(arg) || m_util.is_mul(arg))) { + decl_kind k = to_app(arg)->get_decl()->get_decl_kind(); + expr_ref t1(m().mk_app(get_fid(), k, int_args.size(), int_args.c_ptr()), m()); + expr_ref t2(m().mk_app(get_fid(), k, real_args.size(), real_args.c_ptr()), m()); + int_args.reset(); + int_args.push_back(t1); + int_args.push_back(m_util.mk_to_int(t2)); + result = m().mk_app(get_fid(), k, int_args.size(), int_args.c_ptr()); + return BR_REWRITE3; + } } + return BR_FAILED; } br_status arith_rewriter::mk_to_real_core(expr * arg, expr_ref & result) { diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h index fa8677941..93b6e5ad5 100644 --- a/src/ast/rewriter/arith_rewriter.h +++ b/src/ast/rewriter/arith_rewriter.h @@ -96,10 +96,9 @@ class arith_rewriter : public poly_rewriter { expr * mk_sin_value(rational const & k); app * mk_sqrt(rational const & k); bool divides(expr* d, expr* n, expr_ref& result); - expr_ref remove_divisor(expr* arg, expr* num, expr* den); - void flat_mul(expr* e, ptr_buffer& args); - void remove_divisor(expr* d, ptr_buffer& args); - + expr_ref remove_divisor(expr* arg, expr* num, expr* den); + void flat_mul(expr* e, ptr_buffer& args); + void remove_divisor(expr* d, ptr_buffer& args); public: arith_rewriter(ast_manager & m, params_ref const & p = params_ref()): poly_rewriter(m, p) { diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp index fb7783fe9..83749c82d 100644 --- a/src/ast/rewriter/array_rewriter.cpp +++ b/src/ast/rewriter/array_rewriter.cpp @@ -20,6 +20,7 @@ Notes: #include "ast/rewriter/array_rewriter_params.hpp" #include "ast/ast_lt.h" #include "ast/ast_pp.h" +#include "ast/rewriter/var_subst.h" void array_rewriter::updt_params(params_ref const & _p) { array_rewriter_params p(_p); @@ -196,6 +197,17 @@ br_status array_rewriter::mk_select_core(unsigned num_args, expr * const * args, return BR_DONE; } + if (is_lambda(args[0])) { + // anywhere lambda reduction as opposed to whnf + // select(lambda(X) M, N) -> M[N/X] + quantifier* q = to_quantifier(args[0]); + SASSERT(q->get_num_decls() == num_args - 1); + var_subst subst(m()); + result = subst(q->get_expr(), num_args - 1, args + 1); + return BR_REWRITE_FULL; + + } + if (m_util.is_as_array(args[0])) { // select(as-array[f], I) --> f(I) func_decl * f = m_util.get_as_array_func_decl(to_app(args[0])); diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp index ba2c6547d..fe750acdd 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp @@ -89,6 +89,7 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { expr_ref_vector m_out; obj_map m_const2bits; expr_ref_vector m_bindings; + unsigned_vector m_shifts; func_decl_ref_vector m_keys; expr_ref_vector m_values; unsigned_vector m_keyval_lim; @@ -579,19 +580,36 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); } SASSERT(new_bindings.size() == q->get_num_decls()); i = q->get_num_decls(); + unsigned shift = j; + if (!m_shifts.empty()) shift += m_shifts.back(); while (i > 0) { i--; m_bindings.push_back(new_bindings[i]); + m_shifts.push_back(shift); } } return true; } bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { - if (m_blast_quant) { - if (t->get_idx() >= m_bindings.size()) + if (m_blast_quant) { + if (m_bindings.empty()) return false; - result = m_bindings.get(m_bindings.size() - t->get_idx() - 1); + unsigned shift = m_shifts.back(); + if (t->get_idx() >= m_bindings.size()) { + if (shift == 0) + return false; + result = m_manager.mk_var(t->get_idx() + shift, t->get_sort()); + } + else { + unsigned offset = m_bindings.size() - t->get_idx() - 1; + result = m_bindings.get(offset); + shift = shift - m_shifts[offset]; + if (shift > 0) { + var_shifter vs(m_manager); + vs(result, shift, result); + } + } result_pr = nullptr; return true; } @@ -636,11 +654,12 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); new_decl_names.push_back(n); } } - result = m().mk_quantifier(old_q->is_forall(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), + result = m().mk_quantifier(old_q->get_kind(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); result_pr = nullptr; m_bindings.shrink(old_sz); + m_shifts.shrink(old_sz); return true; } }; diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 46613a12e..d61c906be 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -588,58 +588,39 @@ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) VERIFY(m().is_ite(ite, cond, t, e)); SASSERT(m().is_value(val)); - if (m().is_value(t) && m().is_value(e)) { - if (t != val && e != val) { - TRACE("try_ite_value", tout << mk_ismt2_pp(t, m()) << " " << mk_ismt2_pp(e, m()) << " " << mk_ismt2_pp(val, m()) << "\n"; - tout << t << " " << e << " " << val << "\n";); - result = m().mk_false(); - } - else if (t == val && e == val) { + if (m().are_distinct(val, e)) { + result = m().mk_and(m().mk_eq(t, val), cond); + return BR_REWRITE2; + } + if (m().are_distinct(val, t)) { + result = m().mk_and(m().mk_eq(e, val), m().mk_not(cond)); + return BR_REWRITE2; + } + if (m().are_equal(val, t)) { + if (m().are_equal(val, e)) { result = m().mk_true(); - } - else if (t == val) { - result = cond; + return BR_DONE; } else { - SASSERT(e == val); - mk_not(cond, result); + result = m().mk_or(m().mk_eq(e, val), cond); } - return BR_DONE; + return BR_REWRITE2; } - if (m_ite_extra_rules) { - if (m().is_value(t)) { - if (val == t) { - result = m().mk_or(cond, m().mk_eq(val, e)); - } - else { - mk_not(cond, result); - result = m().mk_and(result, m().mk_eq(val, e)); - } - return BR_REWRITE2; - } - if (m().is_value(e)) { - if (val == e) { - mk_not(cond, result); - result = m().mk_or(result, m().mk_eq(val, t)); - } - else { - result = m().mk_and(cond, m().mk_eq(val, t)); - } - return BR_REWRITE2; - } + if (m().are_equal(val, e)) { + result = m().mk_or(m().mk_eq(t, val), m().mk_not(cond)); + return BR_REWRITE2; } - { - expr* cond2, *t2, *e2; - if (m().is_ite(t, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) { - try_ite_value(to_app(t), val, result); - result = m().mk_ite(cond, result, m().mk_eq(e, val)); - return BR_REWRITE2; - } - if (m().is_ite(e, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) { - try_ite_value(to_app(e), val, result); - result = m().mk_ite(cond, m().mk_eq(t, val), result); - return BR_REWRITE2; - } + + expr* cond2 = nullptr, *t2 = nullptr, *e2 = nullptr; + if (m().is_ite(t, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) { + try_ite_value(to_app(t), val, result); + result = m().mk_ite(cond, result, m().mk_eq(e, val)); + return BR_REWRITE2; + } + if (m().is_ite(e, cond2, t2, e2) && m().is_value(t2) && m().is_value(e2)) { + try_ite_value(to_app(e), val, result); + result = m().mk_ite(cond, m().mk_eq(t, val), result); + return BR_REWRITE2; } return BR_FAILED; diff --git a/src/ast/rewriter/bv_elim.cpp b/src/ast/rewriter/bv_elim.cpp index 0c6484ed6..f5690b680 100644 --- a/src/ast/rewriter/bv_elim.cpp +++ b/src/ast/rewriter/bv_elim.cpp @@ -87,20 +87,16 @@ bool bv_elim_cfg::reduce_quantifier(quantifier * q, expr* const* sub = subst_map.c_ptr(); unsigned sub_size = subst_map.size(); - subst(old_body, sub_size, sub, new_body); + new_body = subst(old_body, sub_size, sub); for (unsigned j = 0; j < q->get_num_patterns(); j++) { - expr_ref pat(m); - subst(new_patterns[j], sub_size, sub, pat); - pats.push_back(pat); + pats.push_back(subst(new_patterns[j], sub_size, sub)); } for (unsigned j = 0; j < q->get_num_no_patterns(); j++) { - expr_ref nopat(m); - subst(new_no_patterns[j], sub_size, sub, nopat); - no_pats.push_back(nopat); + no_pats.push_back(subst(new_no_patterns[j], sub_size, sub)); } - result = m.mk_quantifier(true, + result = m.mk_quantifier(forall_k, names.size(), sorts.c_ptr(), names.c_ptr(), diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index a0adb6398..c81c7385a 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -1383,7 +1383,7 @@ br_status bv_rewriter::mk_bv2int(expr * arg, expr_ref & result) { --i; tmp = args[i].get(); tmp = m_autil.mk_mul(m_autil.mk_numeral(power(numeral(2), sz), true), tmp); - args[i] = tmp; + args[i] = std::move(tmp); sz += get_bv_size(to_app(arg)->get_arg(i)); } result = m_autil.mk_add(args.size(), args.c_ptr()); @@ -2400,8 +2400,8 @@ bool bv_rewriter::isolate_term(expr* lhs, expr* rhs, expr_ref& result) { return false; } unsigned sz = to_app(rhs)->get_num_args(); - expr_ref t1(m()), t2(m()); - t1 = to_app(rhs)->get_arg(0); + expr * t1 = to_app(rhs)->get_arg(0); + expr_ref t2(m()); if (sz > 2) { t2 = m().mk_app(get_fid(), OP_BADD, sz-1, to_app(rhs)->get_args()+1); } @@ -2597,14 +2597,10 @@ br_status bv_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { result = m().mk_bool_val(new_lhs == new_rhs); return BR_DONE; } - } - else { - new_lhs = lhs; - new_rhs = rhs; + lhs = new_lhs; + rhs = new_rhs; } - lhs = new_lhs; - rhs = new_rhs; // Try to rewrite t1 + t2 = c --> t1 = c - t2 // Reason: it is much cheaper to bit-blast. if (isolate_term(lhs, rhs, result)) { diff --git a/src/ast/rewriter/bv_trailing.cpp b/src/ast/rewriter/bv_trailing.cpp index ad9350eca..433ef6f66 100644 --- a/src/ast/rewriter/bv_trailing.cpp +++ b/src/ast/rewriter/bv_trailing.cpp @@ -125,7 +125,7 @@ struct bv_trailing::imp { for (unsigned i = 0; i < num; ++i) { expr * const curr = a->get_arg(i); VERIFY(to_rm == remove_trailing(curr, to_rm, tmp, depth - 1)); - new_args.push_back(tmp); + new_args.push_back(std::move(tmp)); } result = m.mk_app(m_util.get_fid(), OP_BADD, new_args.size(), new_args.c_ptr()); return to_rm; @@ -150,7 +150,7 @@ struct bv_trailing::imp { numeral c_val; unsigned c_sz; if (!m_util.is_numeral(tmp, c_val, c_sz) || !c_val.is_one()) - new_args.push_back(tmp); + new_args.push_back(std::move(tmp)); const unsigned sz = m_util.get_bv_size(coefficient); const unsigned new_sz = sz - retv; @@ -204,7 +204,7 @@ struct bv_trailing::imp { expr_ref_vector new_args(m); for (unsigned j = 0; j < i; ++j) new_args.push_back(a->get_arg(j)); - if (new_last) new_args.push_back(new_last); + if (new_last) new_args.push_back(std::move(new_last)); result = new_args.size() == 1 ? new_args.get(0) : m_util.mk_concat(new_args.size(), new_args.c_ptr()); return retv; diff --git a/src/ast/rewriter/der.cpp b/src/ast/rewriter/der.cpp index 54409e9c2..027846c31 100644 --- a/src/ast/rewriter/der.cpp +++ b/src/ast/rewriter/der.cpp @@ -133,7 +133,7 @@ void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) { // Eliminate variables that have become unused if (reduced && is_forall(r)) { quantifier * q = to_quantifier(r); - elim_unused_vars(m_manager, q, params_ref(), r); + r = elim_unused_vars(m_manager, q, params_ref()); if (m_manager.proofs_enabled()) { proof * p1 = m_manager.mk_elim_unused_vars(q, r); pr = m_manager.mk_transitivity(pr, p1); @@ -343,8 +343,7 @@ void der::create_substitution(unsigned sz) { expr_ref cur(m_map[m_order[i]], m_manager); // do all the previous substitutions before inserting - expr_ref r(m_manager); - m_subst(cur, m_subst_map.size(), m_subst_map.c_ptr(), r); + expr_ref r = m_subst(cur, m_subst_map.size(), m_subst_map.c_ptr()); unsigned inx = sz - m_order[i]- 1; SASSERT(m_subst_map[inx]==0); @@ -369,21 +368,18 @@ void der::apply_substitution(quantifier * q, expr_ref & r) { unsigned sz = m_new_args.size(); expr_ref t(m_manager); t = (sz == 1) ? m_new_args[0] : m_manager.mk_or(sz, m_new_args.c_ptr()); - expr_ref new_e(m_manager); - m_subst(t, m_subst_map.size(), m_subst_map.c_ptr(), new_e); + expr_ref new_e = m_subst(t, m_subst_map.size(), m_subst_map.c_ptr()); // don't forget to update the quantifier patterns expr_ref_buffer new_patterns(m_manager); expr_ref_buffer new_no_patterns(m_manager); for (unsigned j = 0; j < q->get_num_patterns(); j++) { - expr_ref new_pat(m_manager); - m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_pat); + expr_ref new_pat = m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr()); new_patterns.push_back(new_pat); } for (unsigned j = 0; j < q->get_num_no_patterns(); j++) { - expr_ref new_nopat(m_manager); - m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_nopat); + expr_ref new_nopat = m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr()); new_no_patterns.push_back(new_nopat); } diff --git a/src/ast/rewriter/distribute_forall.cpp b/src/ast/rewriter/distribute_forall.cpp index 77db3bc28..54d7e2eca 100644 --- a/src/ast/rewriter/distribute_forall.cpp +++ b/src/ast/rewriter/distribute_forall.cpp @@ -100,7 +100,7 @@ void distribute_forall::reduce1_app(app * a) { void distribute_forall::reduce1_quantifier(quantifier * q) { // This transformation is applied after skolemization/quantifier elimination. So, all quantifiers are universal. - SASSERT(q->is_forall()); + SASSERT(q->get_kind() == forall_k); // This transformation is applied after basic pre-processing steps. // So, we can assume that @@ -126,8 +126,7 @@ void distribute_forall::reduce1_quantifier(quantifier * q) { br.mk_not(arg, not_arg); quantifier_ref tmp_q(m_manager); tmp_q = m_manager.update_quantifier(q, not_arg); - expr_ref new_q(m_manager); - elim_unused_vars(m_manager, tmp_q, params_ref(), new_q); + expr_ref new_q = elim_unused_vars(m_manager, tmp_q, params_ref()); new_args.push_back(new_q); } expr_ref result(m_manager); diff --git a/src/ast/rewriter/elim_bounds.cpp b/src/ast/rewriter/elim_bounds.cpp index a640081ba..02d3217cf 100644 --- a/src/ast/rewriter/elim_bounds.cpp +++ b/src/ast/rewriter/elim_bounds.cpp @@ -114,7 +114,7 @@ bool elim_bounds_cfg::reduce_quantifier(quantifier * q, expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { - if (!q->is_forall()) { + if (!is_forall(q)) { return false; } unsigned num_vars = q->get_num_decls(); @@ -194,7 +194,7 @@ bool elim_bounds_cfg::reduce_quantifier(quantifier * q, } quantifier_ref new_q(m); new_q = m.update_quantifier(q, new_body); - elim_unused_vars(m, new_q, params_ref(), result); + result = elim_unused_vars(m, new_q, params_ref()); result_pr = m.mk_rewrite(q, result); TRACE("elim_bounds", tout << mk_pp(q, m) << "\n" << result << "\n";); return true; diff --git a/src/ast/rewriter/enum2bv_rewriter.cpp b/src/ast/rewriter/enum2bv_rewriter.cpp index 814a25ee9..65250282c 100644 --- a/src/ast/rewriter/enum2bv_rewriter.cpp +++ b/src/ast/rewriter/enum2bv_rewriter.cpp @@ -159,6 +159,8 @@ struct enum2bv_rewriter::imp { expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { + + if (q->get_kind() == lambda_k) return false; m_sorts.reset(); expr_ref_vector bounds(m); bool found = false; @@ -182,15 +184,20 @@ struct enum2bv_rewriter::imp { } expr_ref new_body_ref(old_body, m), tmp(m); if (!bounds.empty()) { - if (q->is_forall()) { + switch (q->get_kind()) { + case forall_k: new_body_ref = m.mk_implies(mk_and(bounds), new_body_ref); - } - else { + break; + case exists_k: bounds.push_back(new_body_ref); new_body_ref = mk_and(bounds); + break; + case lambda_k: + UNREACHABLE(); + break; } } - result = m.mk_quantifier(q->is_forall(), q->get_num_decls(), m_sorts.c_ptr(), q->get_decl_names(), new_body_ref, + result = m.mk_quantifier(q->get_kind(), q->get_num_decls(), m_sorts.c_ptr(), q->get_decl_names(), new_body_ref, q->get_weight(), q->get_qid(), q->get_skid(), q->get_num_patterns(), new_patterns, q->get_num_no_patterns(), new_no_patterns); diff --git a/src/ast/rewriter/inj_axiom.cpp b/src/ast/rewriter/inj_axiom.cpp index 184c84144..5fa60ad93 100644 --- a/src/ast/rewriter/inj_axiom.cpp +++ b/src/ast/rewriter/inj_axiom.cpp @@ -32,7 +32,7 @@ bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result) { expr* arg1 = nullptr, * arg2 = nullptr, *narg = nullptr; expr* app1 = nullptr, * app2 = nullptr; expr* var1 = nullptr, * var2 = nullptr; - if (q->is_forall() && m.is_or(n, arg1, arg2)) { + if (is_forall(q) && m.is_or(n, arg1, arg2)) { if (m.is_not(arg2)) std::swap(arg1, arg2); if (m.is_not(arg1, narg) && diff --git a/src/ast/rewriter/quant_hoist.cpp b/src/ast/rewriter/quant_hoist.cpp index 2f1116299..b5cf57927 100644 --- a/src/ast/rewriter/quant_hoist.cpp +++ b/src/ast/rewriter/quant_hoist.cpp @@ -66,7 +66,7 @@ public: TRACE("qe_verbose", tout << mk_pp(fml, m) << "\n"; tout << mk_pp(result, m) << "\n";); - fml = result; + fml = std::move(result); } void extract_quantifier(quantifier* q, app_ref_vector& vars, expr_ref& result, bool use_fresh) { @@ -79,12 +79,12 @@ public: vars.push_back(a); } expr * const * exprs = (expr* const*) (vars.c_ptr() + vars.size()- nd); - instantiate(m, q, exprs, result); + result = instantiate(m, q, exprs); } - unsigned pull_quantifier(bool is_forall, expr_ref& fml, ptr_vector* sorts, svector* names, bool use_fresh, bool rewrite_ok) { + unsigned pull_quantifier(bool _is_forall, expr_ref& fml, ptr_vector* sorts, svector* names, bool use_fresh, bool rewrite_ok) { unsigned index = var_counter().get_next_var(fml); - while (is_quantifier(fml) && (is_forall == to_quantifier(fml)->is_forall())) { + while (is_quantifier(fml) && _is_forall == is_forall(fml)) { quantifier* q = to_quantifier(fml); index += q->get_num_decls(); if (names) { @@ -99,7 +99,7 @@ public: return index; } app_ref_vector vars(m); - pull_quantifier(is_forall, fml, vars, use_fresh, rewrite_ok); + pull_quantifier(_is_forall, fml, vars, use_fresh, rewrite_ok); if (vars.empty()) { return index; } @@ -277,12 +277,16 @@ private: } case AST_QUANTIFIER: { quantifier* q = to_quantifier(fml); - expr_ref tmp(m); - if (!is_compatible(qt, q->is_forall())) { + if (is_lambda(q)) { result = fml; break; } - set_quantifier_type(qt, q->is_forall()); + expr_ref tmp(m); + if (!is_compatible(qt, is_forall(q))) { + result = fml; + break; + } + set_quantifier_type(qt, is_forall(q)); extract_quantifier(q, vars, tmp, use_fresh); pull_quantifier(tmp, qt, vars, result, use_fresh, rewrite_ok); break; diff --git a/src/ast/rewriter/rewriter.cpp b/src/ast/rewriter/rewriter.cpp index 4356f8f45..80b690428 100644 --- a/src/ast/rewriter/rewriter.cpp +++ b/src/ast/rewriter/rewriter.cpp @@ -208,12 +208,10 @@ void rewriter_core::cleanup() { #ifdef _TRACE void rewriter_core::display_stack(std::ostream & out, unsigned pp_depth) { - svector::iterator it = m_frame_stack.begin(); - svector::iterator end = m_frame_stack.end(); - for (; it != end; ++it) { - out << mk_bounded_pp(it->m_curr, m(), pp_depth) << "\n"; - out << "state: " << it->m_state << "\n"; - out << "cache: " << it->m_cache_result << ", new_child: " << it->m_new_child << ", max-depth: " << it->m_max_depth << ", i: " << it->m_i << "\n"; + for (frame& f : m_frame_stack) { + out << mk_bounded_pp(f.m_curr, m(), pp_depth) << "\n"; + out << "state: " << f.m_state << "\n"; + out << "cache: " << f.m_cache_result << ", new_child: " << f.m_new_child << ", max-depth: " << f.m_max_depth << ", i: " << f.m_i << "\n"; out << "------------------\n"; } } diff --git a/src/ast/rewriter/rewriter.h b/src/ast/rewriter/rewriter.h index c761b5ca3..84fef488f 100644 --- a/src/ast/rewriter/rewriter.h +++ b/src/ast/rewriter/rewriter.h @@ -350,11 +350,13 @@ public: void update_inv_binding_at(unsigned i, expr* binding); void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void operator()(expr * t, expr_ref & result) { operator()(t, result, m_pr); } - void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result) { + expr_ref operator()(expr * n, unsigned num_bindings, expr * const * bindings) { + expr_ref result(m()); SASSERT(!m_proof_gen); reset(); set_inv_bindings(num_bindings, bindings); operator()(n, result); + return result; } void resume(expr_ref & result, proof_ref & result_pr); diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index e8a14b953..ebe52f52a 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -448,7 +448,7 @@ void rewriter_tpl::process_app(app * t, frame & fr) { m_r = result_stack().back(); if (!is_ground(m_r)) { m_inv_shifter(m_r, num_args, tmp); - m_r = tmp; + m_r = std::move(tmp); } result_stack().shrink(fr.m_spos); result_stack().push_back(m_r); @@ -542,7 +542,7 @@ void rewriter_tpl::process_quantifier(quantifier * q, frame & fr) { } result_stack().shrink(fr.m_spos); result_stack().push_back(m_r.get()); - SASSERT(m().is_bool(m_r)); + SASSERT(m().get_sort(q) == m().get_sort(m_r)); if (!ProofGen) { SASSERT(num_decls <= m_bindings.size()); m_bindings.shrink(m_bindings.size() - num_decls); diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 8f7b56381..af5ef3b0b 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -36,7 +36,7 @@ expr_ref sym_expr::accept(expr* e) { switch (m_ty) { case t_pred: { var_subst subst(m); - subst(m_t, 1, &e, result); + result = subst(m_t, 1, &e); break; } case t_char: @@ -470,7 +470,7 @@ br_status seq_rewriter::mk_seq_unit(expr* e, expr_ref& result) { if (bvu.is_bv(e) && bvu.is_numeral(e, n_val, n_size) && n_size == 8) { // convert to string constant zstring str(n_val.get_unsigned()); - TRACE("seq", tout << "rewrite seq.unit of 8-bit value " << n_val.to_string() << " to string constant \"" << str<< "\"" << std::endl;); + TRACE("seq_verbose", tout << "rewrite seq.unit of 8-bit value " << n_val.to_string() << " to string constant \"" << str<< "\"" << std::endl;); result = m_util.str.mk_string(str); return BR_DONE; } diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index 912df0fc9..eb819c988 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -16,6 +16,7 @@ Author: Notes: --*/ +#include "util/cooperate.h" #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/rewriter_params.hpp" #include "ast/rewriter/bool_rewriter.h" @@ -28,13 +29,13 @@ Notes: #include "ast/rewriter/pb_rewriter.h" #include "ast/rewriter/seq_rewriter.h" #include "ast/rewriter/rewriter_def.h" +#include "ast/rewriter/var_subst.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" -#include "util/cooperate.h" -#include "ast/rewriter/var_subst.h" #include "ast/ast_util.h" #include "ast/well_sorted.h" +namespace { struct th_rewriter_cfg : public default_rewriter_cfg { bool_rewriter m_b_rw; arith_rewriter m_a_rw; @@ -337,16 +338,16 @@ struct th_rewriter_cfg : public default_rewriter_cfg { family_id fid = t->get_family_id(); if (fid == m_a_rw.get_fid()) { switch (t->get_decl_kind()) { - case OP_ADD: n = m_a_util.mk_numeral(rational(0), m().get_sort(t)); return true; - case OP_MUL: n = m_a_util.mk_numeral(rational(1), m().get_sort(t)); return true; + case OP_ADD: n = m_a_util.mk_numeral(rational::zero(), m().get_sort(t)); return true; + case OP_MUL: n = m_a_util.mk_numeral(rational::one(), m().get_sort(t)); return true; default: return false; } } if (fid == m_bv_rw.get_fid()) { switch (t->get_decl_kind()) { - case OP_BADD: n = m_bv_util.mk_numeral(rational(0), m().get_sort(t)); return true; - case OP_BMUL: n = m_bv_util.mk_numeral(rational(1), m().get_sort(t)); return true; + case OP_BADD: n = m_bv_util.mk_numeral(rational::zero(), m().get_sort(t)); return true; + case OP_BMUL: n = m_bv_util.mk_numeral(rational::one(), m().get_sort(t)); return true; default: return false; } @@ -468,9 +469,9 @@ struct th_rewriter_cfg : public default_rewriter_cfg { // terms matched... bool is_int = m_a_util.is_int(t1); if (!new_t1) - new_t1 = m_a_util.mk_numeral(rational(0), is_int); + new_t1 = m_a_util.mk_numeral(rational::zero(), is_int); if (!new_t2) - new_t2 = m_a_util.mk_numeral(rational(0), is_int); + new_t2 = m_a_util.mk_numeral(rational::zero(), is_int); // mk common part ptr_buffer args; for (unsigned i = 0; i < num1; i++) { @@ -606,7 +607,8 @@ struct th_rewriter_cfg : public default_rewriter_cfg { quantifier_ref q1(m()); proof * p1 = nullptr; if (is_quantifier(new_body) && - to_quantifier(new_body)->is_forall() == old_q->is_forall() && + to_quantifier(new_body)->get_kind() == old_q->get_kind() && + to_quantifier(new_body)->get_kind() != lambda_k && !old_q->has_patterns() && !to_quantifier(new_body)->has_patterns()) { @@ -619,7 +621,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts()); names.append(nested_q->get_num_decls(), nested_q->get_decl_names()); - q1 = m().mk_quantifier(old_q->is_forall(), + q1 = m().mk_quantifier(old_q->get_kind(), sorts.size(), sorts.c_ptr(), names.c_ptr(), @@ -653,17 +655,19 @@ struct th_rewriter_cfg : public default_rewriter_cfg { SASSERT(is_well_sorted(m(), q1)); } - elim_unused_vars(m(), q1, params_ref(), result); + SASSERT(m().get_sort(old_q) == m().get_sort(q1)); + result = elim_unused_vars(m(), q1, params_ref()); - TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << mk_ismt2_pp(result, m()) << "\n";); + TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << result << "\n";); result_pr = nullptr; if (m().proofs_enabled()) { proof * p2 = nullptr; - if (q1.get() != result.get()) + if (q1.get() != result.get() && q1->get_kind() != lambda_k) p2 = m().mk_elim_unused_vars(q1, result); result_pr = m().mk_transitivity(p1, p2); } + SASSERT(m().get_sort(old_q) == m().get_sort(result)); return true; } @@ -706,6 +710,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { }; +} template class rewriter_tpl; @@ -761,8 +766,8 @@ unsigned th_rewriter::get_num_steps() const { void th_rewriter::cleanup() { ast_manager & m = m_imp->m(); - dealloc(m_imp); - m_imp = alloc(imp, m, m_params); + m_imp->~imp(); + new (m_imp) imp(m, m_params); } void th_rewriter::reset() { @@ -773,7 +778,7 @@ void th_rewriter::reset() { void th_rewriter::operator()(expr_ref & term) { expr_ref result(term.get_manager()); m_imp->operator()(term, result); - term = result; + term = std::move(result); } void th_rewriter::operator()(expr * t, expr_ref & result) { @@ -784,8 +789,8 @@ void th_rewriter::operator()(expr * t, expr_ref & result, proof_ref & result_pr) m_imp->operator()(t, result, result_pr); } -void th_rewriter::operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result) { - m_imp->operator()(n, num_bindings, bindings, result); +expr_ref th_rewriter::operator()(expr * n, unsigned num_bindings, expr * const * bindings) { + return m_imp->operator()(n, num_bindings, bindings); } void th_rewriter::set_substitution(expr_substitution * s) { diff --git a/src/ast/rewriter/th_rewriter.h b/src/ast/rewriter/th_rewriter.h index db495e8da..19a99f4d3 100644 --- a/src/ast/rewriter/th_rewriter.h +++ b/src/ast/rewriter/th_rewriter.h @@ -45,7 +45,7 @@ public: void operator()(expr_ref& term); void operator()(expr * t, expr_ref & result); void operator()(expr * t, expr_ref & result, proof_ref & result_pr); - void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result); + expr_ref operator()(expr * n, unsigned num_bindings, expr * const * bindings); expr_ref mk_app(func_decl* f, unsigned num_args, expr* const* args); diff --git a/src/ast/rewriter/var_subst.cpp b/src/ast/rewriter/var_subst.cpp index 7877cf1d2..cbd79e08c 100644 --- a/src/ast/rewriter/var_subst.cpp +++ b/src/ast/rewriter/var_subst.cpp @@ -23,10 +23,11 @@ Notes: #include "ast/well_sorted.h" #include "ast/for_each_expr.h" -void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, expr_ref & result) { +expr_ref var_subst::operator()(expr * n, unsigned num_args, expr * const * args) { + expr_ref result(m_reducer.m()); if (is_ground(n)) { result = n; - return; + return result; } SASSERT(is_well_sorted(result.m(), n)); m_reducer.reset(); @@ -41,6 +42,7 @@ void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, exp for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m_reducer.m()) << "\n"; tout << "\n------>\n"; tout << mk_ismt2_pp(result, m_reducer.m()) << "\n";); + return result; } unused_vars_eliminator::unused_vars_eliminator(ast_manager & m, params_ref const & params) : @@ -49,16 +51,22 @@ unused_vars_eliminator::unused_vars_eliminator(ast_manager & m, params_ref const m_ignore_patterns_on_ground_qbody = m_params.get_bool("ignore_patterns_on_ground_qbody", true); } -void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { +expr_ref unused_vars_eliminator::operator()(quantifier* q) { + expr_ref result(m); SASSERT(is_well_sorted(m, q)); + TRACE("elim_unused_vars", tout << expr_ref(q, m) << "\n";); + if (is_lambda(q)) { + result = q; + return result; + } if (m_ignore_patterns_on_ground_qbody && is_ground(q->get_expr())) { // Ignore patterns if the body is a ground formula. result = q->get_expr(); - return; + return result; } if (!q->may_have_unused_vars()) { result = q; - return; + return result; } m_used.reset(); m_used.process(q->get_expr()); @@ -73,7 +81,7 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { if (m_used.uses_all_vars(num_decls)) { q->set_no_unused_vars(); result = q; - return; + return result; } ptr_buffer used_decl_sorts; @@ -120,11 +128,11 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { expr_ref new_expr(m); - m_subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr(), new_expr); + new_expr = m_subst(q->get_expr(), var_mapping.size(), var_mapping.c_ptr()); if (num_removed == num_decls) { result = new_expr; - return; + return result; } expr_ref tmp(m); @@ -132,15 +140,15 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { expr_ref_buffer new_no_patterns(m); for (unsigned i = 0; i < num_patterns; i++) { - m_subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); + tmp = m_subst(q->get_pattern(i), var_mapping.size(), var_mapping.c_ptr()); new_patterns.push_back(tmp); } for (unsigned i = 0; i < num_no_patterns; i++) { - m_subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr(), tmp); + tmp = m_subst(q->get_no_pattern(i), var_mapping.size(), var_mapping.c_ptr()); new_no_patterns.push_back(tmp); } - result = m.mk_quantifier(q->is_forall(), + result = m.mk_quantifier(q->get_kind(), used_decl_sorts.size(), used_decl_sorts.c_ptr(), used_decl_names.c_ptr(), @@ -154,24 +162,28 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { new_no_patterns.c_ptr()); to_quantifier(result)->set_no_unused_vars(); SASSERT(is_well_sorted(m, result)); + return result; } -void elim_unused_vars(ast_manager & m, quantifier * q, params_ref const & params, expr_ref & result) { +expr_ref elim_unused_vars(ast_manager & m, quantifier * q, params_ref const & params) { unused_vars_eliminator el(m, params); - el(q, result); + expr_ref result = el(q); + TRACE("elim_unused_vars", tout << expr_ref(q, m) << " -> " << result << "\n";); + return result; } -void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref & result) { +expr_ref instantiate(ast_manager & m, quantifier * q, expr * const * exprs) { var_subst subst(m); - expr_ref new_expr(m); - subst(q->get_expr(), q->get_num_decls(), exprs, new_expr); - TRACE("var_subst", tout << mk_pp(q, m) << "\n" << mk_pp(new_expr, m) << "\n";); + expr_ref new_expr(m), result(m); + new_expr = subst(q->get_expr(), q->get_num_decls(), exprs); + TRACE("var_subst", tout << mk_pp(q, m) << "\n" << new_expr << "\n";); inv_var_shifter shift(m); shift(new_expr, q->get_num_decls(), result); SASSERT(is_well_sorted(m, result)); TRACE("instantiate_bug", tout << mk_ismt2_pp(q, m) << "\nusing\n"; for (unsigned i = 0; i < q->get_num_decls(); i++) tout << mk_ismt2_pp(exprs[i], m) << "\n"; tout << "\n----->\n" << mk_ismt2_pp(result, m) << "\n";); + return result; } static void get_free_vars_offset(expr_sparse_mark& mark, ptr_vector& todo, unsigned offset, expr* e, ptr_vector& sorts) { diff --git a/src/ast/rewriter/var_subst.h b/src/ast/rewriter/var_subst.h index b6a25cfdb..ff0217585 100644 --- a/src/ast/rewriter/var_subst.h +++ b/src/ast/rewriter/var_subst.h @@ -48,7 +48,7 @@ public: Otherwise, (VAR 0) is stored in the first position, (VAR 1) in the second, and so on. */ - void operator()(expr * n, unsigned num_args, expr * const * args, expr_ref & result); + expr_ref operator()(expr * n, unsigned num_args, expr * const * args); void reset() { m_reducer.reset(); } }; @@ -63,10 +63,10 @@ class unused_vars_eliminator { bool m_ignore_patterns_on_ground_qbody; public: unused_vars_eliminator(ast_manager & m, params_ref const & params); - void operator()(quantifier* q, expr_ref& r); + expr_ref operator()(quantifier* q); }; -void elim_unused_vars(ast_manager & m, quantifier * q, params_ref const & params, expr_ref & r); +expr_ref elim_unused_vars(ast_manager & m, quantifier * q, params_ref const & params); /** \brief Instantiate quantifier q using the given exprs. @@ -77,7 +77,7 @@ void elim_unused_vars(ast_manager & m, quantifier * q, params_ref const & params ... (VAR (q->get_num_decls() - 1)) is stored in the first position of the array. */ -void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref & result); +expr_ref instantiate(ast_manager & m, quantifier * q, expr * const * exprs); /** \brief Enumerate set of free variables in expression. diff --git a/src/ast/substitution/substitution.cpp b/src/ast/substitution/substitution.cpp index 39e9e07a2..7e981aab8 100644 --- a/src/ast/substitution/substitution.cpp +++ b/src/ast/substitution/substitution.cpp @@ -196,16 +196,16 @@ void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, e expr_ref_vector pats(m_manager), no_pats(m_manager); for (unsigned i = 0; i < q->get_num_patterns(); ++i) { subst.apply(num_actual_offsets, deltas, expr_offset(q->get_pattern(i), off), s1, t1, er); - pats.push_back(er); + pats.push_back(std::move(er)); } for (unsigned i = 0; i < q->get_num_no_patterns(); ++i) { subst.apply(num_actual_offsets, deltas, expr_offset(q->get_no_pattern(i), off), s1, t1, er); - no_pats.push_back(er); + no_pats.push_back(std::move(er)); } subst.apply(num_actual_offsets, deltas, body, s1, t1, er); er = m_manager.update_quantifier(q, pats.size(), pats.c_ptr(), no_pats.size(), no_pats.c_ptr(), er); m_todo.pop_back(); - m_new_exprs.push_back(er); + m_new_exprs.push_back(std::move(er)); m_apply_cache.insert(n, er); break; } diff --git a/src/ast/well_sorted.cpp b/src/ast/well_sorted.cpp index f78cd9121..b300b9ac3 100644 --- a/src/ast/well_sorted.cpp +++ b/src/ast/well_sorted.cpp @@ -35,9 +35,10 @@ struct well_sorted_proc { void operator()(quantifier * n) { expr const * e = n->get_expr(); - if (!m_manager.is_bool(e)) { + if (!is_lambda(n) && !m_manager.is_bool(e)) { warning_msg("quantifier's body must be a boolean."); m_error = true; + UNREACHABLE(); } } diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 846fb1ded..e65eb1b32 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -59,7 +59,7 @@ public: if (c == nullptr) { std::string err_msg("unknown command '"); err_msg = err_msg + s.bare_str() + "'"; - throw cmd_exception(err_msg); + throw cmd_exception(std::move(err_msg)); } m_cmds.push_back(s); } @@ -384,7 +384,7 @@ class set_option_cmd : public set_get_option_cmd { std::string msg = "error setting '"; msg += opt_name.str(); msg += "', option value cannot be modified after initialization"; - throw cmd_exception(msg); + throw cmd_exception(std::move(msg)); } } diff --git a/src/cmd_context/check_logic.cpp b/src/cmd_context/check_logic.cpp index 55be27c6d..57e15c6a3 100644 --- a/src/cmd_context/check_logic.cpp +++ b/src/cmd_context/check_logic.cpp @@ -17,6 +17,7 @@ Revision History: --*/ #include "cmd_context/check_logic.h" +#include "solver/smt_logics.h" #include "ast/arith_decl_plugin.h" #include "ast/array_decl_plugin.h" #include "ast/bv_decl_plugin.h" @@ -453,7 +454,7 @@ struct check_logic::imp { else if (fid == m_dt_util.get_family_id() && m_dt) { // nothing to check } - else if (fid == m_pb_util.get_family_id() && m_logic == "QF_FD") { + else if (fid == m_pb_util.get_family_id() && smt_logics::logic_has_pb(m_logic)) { // nothing to check } else { diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 6dd7fbcaa..585dd0fa6 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -369,7 +369,7 @@ void stream_ref::set(char const * name) { std::string msg = "failed to set output stream '"; msg += name; msg += "'"; - throw cmd_exception(msg); + throw cmd_exception(std::move(msg)); } SASSERT(m_stream); } @@ -493,6 +493,7 @@ cmd_context::~cmd_context() { if (m_main_ctx) { set_verbose_stream(std::cerr); } + pop(m_scopes.size()); finalize_cmds(); finalize_tactic_cmds(); finalize_probes(); @@ -1070,7 +1071,7 @@ void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * arg tout << "body:\n" << mk_ismt2_pp(_t, m()) << "\n"; tout << "args:\n"; for (unsigned i = 0; i < num_args; i++) tout << mk_ismt2_pp(args[i], m()) << "\n" << mk_pp(m().get_sort(args[i]), m()) << "\n";); var_subst subst(m()); - subst(_t, num_args, args, result); + result = subst(_t, num_args, args); if (well_sorted_check_enabled() && !is_well_sorted(m(), result)) throw cmd_exception("invalid macro application, sort mismatch ", s); return; @@ -1625,6 +1626,7 @@ void cmd_context::display_dimacs() { void cmd_context::display_model(model_ref& mdl) { if (mdl) { if (m_mc0) (*m_mc0)(mdl); + if (m_params.m_model_compress) mdl->compress(); model_params p; if (p.v1() || p.v2()) { std::ostringstream buffer; diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index ff39907da..dc3880674 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -34,6 +34,7 @@ context_params::context_params() { m_debug_ref_count = false; m_smtlib2_compliant = false; m_well_sorted_check = false; + m_model_compress = true; m_timeout = UINT_MAX; m_rlimit = 0; updt_params(); @@ -102,6 +103,9 @@ void context_params::set(char const * param, char const * value) { else if (p == "model_validate") { set_bool(m_model_validate, param, value); } + else if (p == "model_compress") { + set_bool(m_model_compress, param, value); + } else if (p == "dump_models") { set_bool(m_dump_models, param, value); } @@ -146,6 +150,7 @@ void context_params::updt_params(params_ref const & p) { m_proof = p.get_bool("proof", m_proof); m_model = p.get_bool("model", m_model); m_model_validate = p.get_bool("model_validate", m_model_validate); + m_model_compress = p.get_bool("model_compress", m_model_compress); m_dump_models = p.get_bool("dump_models", m_dump_models); m_trace = p.get_bool("trace", m_trace); m_trace_file_name = p.get_str("trace_file_name", "z3.log"); @@ -162,6 +167,7 @@ void context_params::collect_param_descrs(param_descrs & d) { d.insert("type_check", CPK_BOOL, "type checker (alias for well_sorted_check)", "true"); d.insert("auto_config", CPK_BOOL, "use heuristics to automatically select solver and configure it", "true"); d.insert("model_validate", CPK_BOOL, "validate models produced by solvers", "false"); + d.insert("model_compress", CPK_BOOL, "compress models for easier consumption", "true"); d.insert("dump_models", CPK_BOOL, "dump models whenever check-sat returns sat", "false"); d.insert("trace", CPK_BOOL, "trace generation for VCC", "false"); d.insert("trace_file_name", CPK_STRING, "trace out file name (see option 'trace')", "z3.log"); diff --git a/src/cmd_context/context_params.h b/src/cmd_context/context_params.h index 3d2947b7a..8ca36dfc9 100644 --- a/src/cmd_context/context_params.h +++ b/src/cmd_context/context_params.h @@ -40,6 +40,7 @@ public: bool m_well_sorted_check; bool m_model; bool m_model_validate; + bool m_model_compress; bool m_dump_models; bool m_unsat_core; bool m_smtlib2_compliant; // it must be here because it enable/disable the use of coercions in the ast_manager. diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index 64eeac42e..235576735 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -135,9 +135,8 @@ public: m_idx++; } void execute(cmd_context & ctx) override { - expr_ref r(ctx.m()); beta_reducer p(ctx.m()); - p(m_source, m_subst.size(), m_subst.c_ptr(), r); + expr_ref r = p(m_source, m_subst.size(), m_subst.c_ptr()); store_expr_ref(ctx, m_target, r.get()); } }; @@ -274,8 +273,7 @@ UNARY_CMD(elim_unused_vars_cmd, "dbg-elim-unused-vars", "", "eliminate unu ctx.display(ctx.regular_stream(), arg); return; } - expr_ref r(ctx.m()); - elim_unused_vars(ctx.m(), to_quantifier(arg), gparams::get_ref(), r); + expr_ref r = elim_unused_vars(ctx.m(), to_quantifier(arg), gparams::get_ref()); SASSERT(!is_quantifier(r) || !to_quantifier(r)->may_have_unused_vars()); ctx.display(ctx.regular_stream(), r); ctx.regular_stream() << std::endl; @@ -307,8 +305,7 @@ public: if (num != m_q->get_num_decls()) throw cmd_exception("invalid command, mismatch between the number of quantified variables and the number of arguments."); unsigned i = num; - while (i > 0) { - --i; + while (i-- > 0) { sort * s = ctx.m().get_sort(ts[i]); if (s != m_q->get_decl_sort(i)) { std::ostringstream buffer; @@ -320,8 +317,7 @@ public: } void execute(cmd_context & ctx) override { - expr_ref r(ctx.m()); - instantiate(ctx.m(), m_q, m_args.c_ptr(), r); + expr_ref r = instantiate(ctx.m(), m_q, m_args.c_ptr()); ctx.display(ctx.regular_stream(), r); ctx.regular_stream() << std::endl; } @@ -484,11 +480,9 @@ public: solver_ref sNotA = sf(m, p, false /* no proofs */, true, true, symbol::null); solver_ref sNotB = sf(m, p, false /* no proofs */, true, true, symbol::null); sA->assert_expr(a); - sNotA->assert_expr(m.mk_not(a)); sB->assert_expr(b); - sNotB->assert_expr(m.mk_not(b)); qe::euf_arith_mbi_plugin pA(sA.get(), sNotA.get()); - qe::euf_arith_mbi_plugin pB(sB.get(), sNotB.get()); + qe::prop_mbi_plugin pB(sB.get()); pA.set_shared(vars); pB.set_shared(vars); lbool res = mbi.pogo(pA, pB, itp); diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp index 5a0f6468b..8c88b1680 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 "ast/ast_pp.h" #include using namespace format_ns; @@ -768,9 +769,8 @@ struct pdecl_manager::app_sort_info : public pdecl_manager::sort_info { } else { out << "(" << m_decl->get_name(); - for (unsigned i = 0; i < m_args.size(); i++) { - out << " "; - m.display(out, m_args[i]); + for (auto arg : m_args) { + m.display(out << " ", arg); } out << ")"; } @@ -782,8 +782,8 @@ struct pdecl_manager::app_sort_info : public pdecl_manager::sort_info { } else { ptr_buffer b; - for (unsigned i = 0; i < m_args.size(); i++) - b.push_back(m.pp(m_args[i])); + for (auto arg : m_args) + b.push_back(m.pp(arg)); return mk_seq1(m.m(), b.begin(), b.end(), f2f(), m_decl->get_name().str().c_str()); } } @@ -807,8 +807,8 @@ struct pdecl_manager::indexed_sort_info : public pdecl_manager::sort_info { } else { out << "(_ " << m_decl->get_name(); - for (unsigned i = 0; i < m_indices.size(); i++) { - out << " " << m_indices[i]; + for (auto idx : m_indices) { + out << " " << idx; } out << ")"; } @@ -821,8 +821,8 @@ struct pdecl_manager::indexed_sort_info : public pdecl_manager::sort_info { else { ptr_buffer b; b.push_back(mk_string(m.m(), m_decl->get_name().str().c_str())); - for (unsigned i = 0; i < m_indices.size(); i++) - b.push_back(mk_unsigned(m.m(), m_indices[i])); + for (auto idx : m_indices) + b.push_back(mk_unsigned(m.m(), idx)); return mk_seq1(m.m(), b.begin(), b.end(), f2f(), "_"); } } @@ -853,6 +853,15 @@ pdecl_manager::pdecl_manager(ast_manager & m): pdecl_manager::~pdecl_manager() { dec_ref(m_list); reset_sort_info(); + for (auto const& kv : m_sort2psort) { + del_decl_core(kv.m_value); + TRACE("pdecl_manager", tout << "orphan: " << mk_pp(kv.m_key, m()) << "\n";); + } + for (auto* p : m_table) { + del_decl_core(p); + } + m_sort2psort.reset(); + m_table.reset(); SASSERT(m_sort2psort.empty()); SASSERT(m_table.empty()); } @@ -946,13 +955,15 @@ void pdecl_manager::del_decl_core(pdecl * p) { } void pdecl_manager::del_decl(pdecl * p) { - TRACE("register_psort", tout << "del psort "; p->display(tout); tout << "\n";); + TRACE("pdecl_manager", tout << "del psort "; p->display(tout); tout << "\n";); if (p->is_psort()) { psort * _p = static_cast(p); - if (_p->is_sort_wrapper()) + if (_p->is_sort_wrapper()) { m_sort2psort.erase(static_cast(_p)->get_sort()); - else + } + else { m_table.erase(_p); + } } del_decl_core(p); } diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index cbe44da2d..cf0fd5111 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -32,14 +32,6 @@ Notes: #include "cmd_context/cmd_context_to_goal.h" #include "cmd_context/echo_tactic.h" -tactic_cmd::~tactic_cmd() { - dealloc(m_factory); -} - -tactic * tactic_cmd::mk(ast_manager & m) { - return (*m_factory)(m, params_ref()); -} - probe_info::probe_info(symbol const & n, char const * d, probe * p): m_name(n), m_descr(d), diff --git a/src/cmd_context/tactic_cmds.h b/src/cmd_context/tactic_cmds.h index fc15c795b..691771509 100644 --- a/src/cmd_context/tactic_cmds.h +++ b/src/cmd_context/tactic_cmds.h @@ -19,30 +19,28 @@ Notes: #define TACTIC_CMDS_H_ #include "ast/ast.h" +#include "util/params.h" #include "util/cmd_context_types.h" #include "util/ref.h" class tactic; class probe; -class tactic_factory; + +typedef tactic* (*tactic_factory)(ast_manager&, const params_ref&); class tactic_cmd { - symbol m_name; - char const * m_descr; - tactic_factory * m_factory; + symbol m_name; + char const * m_descr; + tactic_factory m_factory; public: - tactic_cmd(symbol const & n, char const * d, tactic_factory * f): - m_name(n), m_descr(d), m_factory(f) { - SASSERT(m_factory); - } - - ~tactic_cmd(); + tactic_cmd(symbol const & n, char const * d, tactic_factory f): + m_name(n), m_descr(d), m_factory(f) {} symbol get_name() const { return m_name; } char const * get_descr() const { return m_descr; } - tactic * mk(ast_manager & m); + tactic * mk(ast_manager & m) { return m_factory(m, params_ref()); } }; void install_core_tactic_cmds(cmd_context & ctx); diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 09f6d5c2d..d1bb90f0c 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -52,7 +52,20 @@ namespace opt { } } m_coeff = r.m_coeff; - if (r.m_type == opt::t_lt) m_coeff += m_div; + switch (r.m_type) { + case opt::t_lt: + m_coeff += m_div; + break; + case opt::t_le: + // for: ax >= t, then x := (t + a - 1) div a + if (m_div.is_pos()) { + m_coeff += m_div; + m_coeff -= rational::one(); + } + break; + default: + break; + } normalize(); SASSERT(m_div.is_pos()); } @@ -253,6 +266,7 @@ namespace opt { void model_based_opt::update_value(unsigned x, rational const& val) { rational old_val = m_var2value[x]; m_var2value[x] = val; + SASSERT(val.is_int() || !is_int(x)); unsigned_vector const& row_ids = m_var2row_ids[x]; for (unsigned row_id : row_ids) { rational coeff = get_coefficient(row_id, x); @@ -517,6 +531,7 @@ namespace opt { SASSERT(t_le == dst.m_type && t_le == src.m_type); SASSERT(src_c.is_int()); SASSERT(dst_c.is_int()); + SASSERT(m_var2value[x].is_int()); rational abs_src_c = abs(src_c); rational abs_dst_c = abs(dst_c); @@ -792,6 +807,7 @@ namespace opt { unsigned v = m_var2value.size(); m_var2value.push_back(value); m_var2is_int.push_back(is_int); + SASSERT(value.is_int() || !is_int); m_var2row_ids.push_back(unsigned_vector()); return v; } @@ -976,7 +992,7 @@ namespace opt { // There are only upper or only lower bounds. if (row_index == UINT_MAX) { if (compute_def) { - if (lub_index != UINT_MAX) { + if (lub_index != UINT_MAX) { result = solve_for(lub_index, x, true); } else if (glb_index != UINT_MAX) { @@ -998,19 +1014,12 @@ namespace opt { SASSERT(lub_index != UINT_MAX); SASSERT(glb_index != UINT_MAX); if (compute_def) { -#if 0 - def d1(m_rows[lub_index], x); - def d2(m_rows[glb_index], x); - result = (d1 + d2)/2; -#else if (lub_size <= glb_size) { result = def(m_rows[lub_index], x); } else { result = def(m_rows[glb_index], x); } - m_var2value[x] = eval(result); -#endif } // The number of matching lower and upper bounds is small. @@ -1107,8 +1116,7 @@ namespace opt { } def result = project(y, compute_def); if (compute_def) { - result = (result * D) + u; - m_var2value[x] = eval(result); + result = (result * D) + u; } SASSERT(!compute_def || eval(result) == eval(x)); return result; diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 2eb687dae..38eb4b00b 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -15,11 +15,12 @@ Author: Revision History: --*/ -#include "model/func_interp.h" -#include "ast/rewriter/var_subst.h" #include "util/obj_hashtable.h" +#include "ast/rewriter/var_subst.h" #include "ast/ast_pp.h" #include "ast/ast_smt2_pp.h" +#include "ast/ast_util.h" +#include "model/func_interp.h" func_entry::func_entry(ast_manager & m, unsigned arity, expr * const * args, expr * result): m_args_are_values(true), @@ -77,10 +78,7 @@ func_interp::func_interp(ast_manager & m, unsigned arity): } func_interp::~func_interp() { - ptr_vector::iterator it = m_entries.begin(); - ptr_vector::iterator end = m_entries.end(); - for (; it != end; ++it) { - func_entry * curr = *it; + for (func_entry* curr : m_entries) { curr->deallocate(m_manager, m_arity); } m_manager.dec_ref(m_else); @@ -90,10 +88,7 @@ func_interp::~func_interp() { func_interp * func_interp::copy() const { func_interp * new_fi = alloc(func_interp, m_manager, m_arity); - ptr_vector::const_iterator it = m_entries.begin(); - ptr_vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - func_entry * curr = *it; + for (func_entry * curr : m_entries) { new_fi->insert_new_entry(curr->get_args(), curr->get_result()); } new_fi->set_else(m_else); @@ -141,9 +136,11 @@ void func_interp::set_else(expr * e) { return; reset_interp_cache(); + + TRACE("func_interp", tout << "set_else: " << expr_ref(e, m()) << "\n";); + ptr_vector args; while (e && is_fi_entry_expr(e, args)) { - TRACE("func_interp", tout << "fi entry expr: " << mk_ismt2_pp(e, m()) << std::endl;); insert_entry(args.c_ptr(), to_app(e)->get_arg(1)); e = to_app(e)->get_arg(2); } @@ -161,10 +158,7 @@ bool func_interp::is_constant() const { return false; if (!is_ground(m_else)) return false; - ptr_vector::const_iterator it = m_entries.begin(); - ptr_vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - func_entry * curr = *it; + for (func_entry* curr : m_entries) { if (curr->get_result() != m_else) return false; } @@ -177,10 +171,7 @@ bool func_interp::is_constant() const { args_are_values to true if for all entries e e.args_are_values() is true. */ func_entry * func_interp::get_entry(expr * const * args) const { - ptr_vector::const_iterator it = m_entries.begin(); - ptr_vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - func_entry * curr = *it; + for (func_entry* curr : m_entries) { if (curr->eq_args(m(), m_arity, args)) return curr; } @@ -224,7 +215,7 @@ bool func_interp::eval_else(expr * const * args, expr_ref & result) const { return false; var_subst s(m_manager, false); SASSERT(!s.std_order()); // (VAR 0) <- args[0], (VAR 1) <- args[1], ... - s(m_else, m_arity, args, result); + result = s(m_else, m_arity, args); return true; } @@ -237,10 +228,7 @@ expr * func_interp::get_max_occ_result() const { obj_map num_occs; expr * r_max = nullptr; unsigned max = 0; - ptr_vector::const_iterator it = m_entries.begin(); - ptr_vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - func_entry * curr = *it; + for (func_entry * curr : m_entries) { expr * r = curr->get_result(); unsigned occs = 0; num_occs.find(r, occs); @@ -262,15 +250,11 @@ void func_interp::compress() { return; // nothing to be done if (!is_ground(m_else)) return; // forall entries e in m_entries e.get_result() is ground - unsigned i = 0; unsigned j = 0; - unsigned sz = m_entries.size(); m_args_are_values = true; - for (; i < sz; i++) { - func_entry * curr = m_entries[i]; + for (func_entry * curr : m_entries) { if (curr->get_result() != m_else) { - m_entries[j] = curr; - j++; + m_entries[j++] = curr; if (!curr->args_are_values()) m_args_are_values = false; } @@ -278,10 +262,58 @@ void func_interp::compress() { curr->deallocate(m_manager, m_arity); } } - if (j < sz) { + if (j < m_entries.size()) { reset_interp_cache(); m_entries.shrink(j); } + // other compression, if else is a default branch. + // or function encode identity. + if (m_manager.is_false(m_else)) { + expr_ref new_else(get_interp(), m_manager); + for (func_entry * curr : m_entries) { + curr->deallocate(m_manager, m_arity); + } + m_entries.reset(); + reset_interp_cache(); + m_manager.inc_ref(new_else); + m_manager.dec_ref(m_else); + m_else = new_else; + } + else if (!m_entries.empty() && is_identity()) { + for (func_entry * curr : m_entries) { + curr->deallocate(m_manager, m_arity); + } + m_entries.reset(); + reset_interp_cache(); + expr_ref new_else(m_manager.mk_var(0, m_manager.get_sort(m_else)), m_manager); + m_manager.inc_ref(new_else); + m_manager.dec_ref(m_else); + m_else = new_else; + } +} + +/** + * \brief check if function is identity + */ +bool func_interp::is_identity() const { + if (m_arity != 1) return false; + if (m_else == nullptr) return false; + + // all entries map a value to itself + for (func_entry * curr : m_entries) { + if (curr->get_arg(0) != curr->get_result()) return false; + if (curr->get_result() == m_else) return false; + } + if (is_var(m_else)) return true; + if (!m_manager.is_value(m_else)) return false; + sort_size const& sz = m_manager.get_sort(m_else)->get_num_elements(); + if (!sz.is_finite()) return false; + + // + // the else entry is a value not covered by other entries + // it, together with the entries covers the entire domain. + // + return (sz.size() == m_entries.size() + 1); } expr * func_interp::get_interp_core() const { @@ -289,10 +321,10 @@ expr * func_interp::get_interp_core() const { return nullptr; expr * r = m_else; ptr_buffer vars; - ptr_vector::const_iterator it = m_entries.begin(); - ptr_vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - func_entry * curr = *it; + for (func_entry * curr : m_entries) { + if (m_else == curr->get_result()) { + continue; + } if (vars.empty()) { for (unsigned i = 0; i < m_arity; i++) { vars.push_back(m_manager.mk_var(i, m_manager.get_sort(curr->get_arg(i)))); @@ -303,12 +335,17 @@ expr * func_interp::get_interp_core() const { eqs.push_back(m_manager.mk_eq(vars[i], curr->get_arg(i))); } SASSERT(eqs.size() == m_arity); - expr * cond; - if (m_arity == 1) - cond = eqs.get(0); - else - cond = m_manager.mk_and(eqs.size(), eqs.c_ptr()); - r = m_manager.mk_ite(cond, curr->get_result(), r); + expr * cond = mk_and(m_manager, eqs.size(), eqs.c_ptr()); + expr * th = curr->get_result(); + if (m_manager.is_true(th)) { + r = m_manager.mk_or(cond, r); + } + else if (m_manager.is_false(th)) { + r = m_manager.mk_and(m_manager.mk_not(cond), r); + } + else { + r = m_manager.mk_ite(cond, th, r); + } } return r; } @@ -327,12 +364,9 @@ expr * func_interp::get_interp() const { func_interp * func_interp::translate(ast_translation & translator) const { func_interp * new_fi = alloc(func_interp, translator.to(), m_arity); - ptr_vector::const_iterator it = m_entries.begin(); - ptr_vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - func_entry * curr = *it; + for (func_entry * curr : m_entries) { ptr_buffer new_args; - for (unsigned i=0; iget_arg(i))); new_fi->insert_new_entry(new_args.c_ptr(), translator(curr->get_result())); } diff --git a/src/model/func_interp.h b/src/model/func_interp.h index e2b98d6ea..543124793 100644 --- a/src/model/func_interp.h +++ b/src/model/func_interp.h @@ -57,6 +57,7 @@ public: expr * get_result() const { return m_result; } expr * get_arg(unsigned idx) const { return m_args[idx]; } expr * const * get_args() const { return m_args; } + /** \brief Return true if m.are_equal(m_args[i], args[i]) for all i in [0, arity) */ @@ -101,6 +102,8 @@ public: func_entry * get_entry(expr * const * args) const; bool eval_else(expr * const * args, expr_ref & result) const; unsigned num_entries() const { return m_entries.size(); } + ptr_vector::const_iterator begin() const { return m_entries.begin(); } + ptr_vector::const_iterator end() const { return m_entries.end(); } func_entry const * const * get_entries() const { return m_entries.c_ptr(); } func_entry const * get_entry(unsigned idx) const { return m_entries[idx]; } @@ -113,6 +116,7 @@ public: private: bool is_fi_entry_expr(expr * e, ptr_vector & args); + bool is_identity() const; }; #endif diff --git a/src/model/model.cpp b/src/model/model.cpp index 6ddf9765c..83adfc87e 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -16,34 +16,36 @@ Author: Revision History: --*/ -#include "model/model.h" +#include "util/top_sort.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "ast/rewriter/var_subst.h" +#include "ast/rewriter/th_rewriter.h" #include "ast/array_decl_plugin.h" #include "ast/well_sorted.h" #include "ast/used_symbols.h" +#include "ast/for_each_expr.h" +#include "ast/for_each_ast.h" +#include "model/model.h" #include "model/model_evaluator.h" model::model(ast_manager & m): model_core(m), - m_mev(*this) { + m_mev(*this), + m_cleaned(false) { } model::~model() { for (auto & kv : m_usort2universe) { - m_manager.dec_ref(kv.m_key); - m_manager.dec_array_ref(kv.m_value->size(), kv.m_value->c_ptr()); + m.dec_ref(kv.m_key); + m.dec_array_ref(kv.m_value->size(), kv.m_value->c_ptr()); dealloc(kv.m_value); } } - - void model::copy_const_interps(model const & source) { - for (auto const& kv : source.m_interp) { + for (auto const& kv : source.m_interp) register_decl(kv.m_key, kv.m_value); - } } void model::copy_func_interps(model const & source) { @@ -57,13 +59,11 @@ void model::copy_usort_interps(model const & source) { } model * model::copy() const { - model * m = alloc(model, m_manager); - - m->copy_const_interps(*this); - m->copy_func_interps(*this); - m->copy_usort_interps(*this); - - return m; + model * mdl = alloc(model, m); + mdl->copy_const_interps(*this); + mdl->copy_func_interps(*this); + mdl->copy_usort_interps(*this); + return mdl; } bool model::eval_expr(expr * e, expr_ref & result, bool model_completion) { @@ -94,13 +94,13 @@ struct model::value_proc : public some_value_proc { expr * model::get_some_value(sort * s) { value_proc p(*this); - return m_manager.get_some_value(s, &p); + return m.get_some_value(s, &p); } ptr_vector const & model::get_universe(sort * s) const { ptr_vector * u = nullptr; m_usort2universe.find(s, u); - SASSERT(u != 0); + SASSERT(u != nullptr); return *u; } @@ -120,11 +120,11 @@ sort * model::get_uninterpreted_sort(unsigned idx) const { void model::register_usort(sort * s, unsigned usize, expr * const * universe) { sort2universe::obj_map_entry * entry = m_usort2universe.insert_if_not_there2(s, 0); - m_manager.inc_array_ref(usize, universe); + m.inc_array_ref(usize, universe); if (entry->get_data().m_value == 0) { // new entry m_usorts.push_back(s); - m_manager.inc_ref(s); + m.inc_ref(s); ptr_vector * new_u = alloc(ptr_vector); new_u->append(usize, universe); entry->get_data().m_value = new_u; @@ -133,7 +133,7 @@ void model::register_usort(sort * s, unsigned usize, expr * const * universe) { // updating ptr_vector * u = entry->get_data().m_value; SASSERT(u); - m_manager.dec_array_ref(u->size(), u->c_ptr()); + m.dec_array_ref(u->size(), u->c_ptr()); u->append(usize, universe); } } @@ -154,8 +154,8 @@ model * model::translate(ast_translation & translator) const { // Translate usort interps for (auto const& kv : m_usort2universe) { ptr_vector new_universe; - for (unsigned i=0; i < kv.m_value->size(); i++) - new_universe.push_back(translator(kv.m_value->get(i))); + for (expr* e : *kv.m_value) + new_universe.push_back(translator(e)); res->register_usort(translator(kv.m_key), new_universe.size(), new_universe.c_ptr()); @@ -164,22 +164,322 @@ model * model::translate(ast_translation & translator) const { return res; } +struct model::top_sort : public ::top_sort { + th_rewriter m_rewrite; + obj_map m_occur_count; + + top_sort(ast_manager& m): + m_rewrite(m) + {} + + void add_occurs(func_decl* f) { + m_occur_count.insert(f, occur_count(f) + 1); + } + + unsigned occur_count(func_decl* f) const { + unsigned count = 0; + m_occur_count.find(f, count); + return count; + } + + ~top_sort() override {} +}; + +void model::compress() { + if (m_cleaned) return; + + // stratify m_finterp and m_decls in a topological sort + // such that functions f1 < f2 then f1 does not use f2. + // then for each function in order clean-up the interpretations + // by substituting in auxiliary definitions that can be eliminated. + + func_decl_ref_vector pinned(m); + while (true) { + top_sort ts(m); + collect_deps(ts); + ts.topological_sort(); + for (func_decl * f : ts.top_sorted()) { + cleanup_interp(ts, f); + } + + func_decl_set removed; + ts.m_occur_count.reset(); + for (func_decl * f : ts.top_sorted()) { + collect_occs(ts, f); + } + + // remove auxiliary declarations that are not used. + for (func_decl * f : ts.top_sorted()) { + if (f->is_skolem() && ts.occur_count(f) == 0) { + pinned.push_back(f); + unregister_decl(f); + removed.insert(f); + } + } + if (removed.empty()) break; + remove_decls(m_decls, removed); + remove_decls(m_func_decls, removed); + remove_decls(m_const_decls, removed); + } + m_cleaned = true; + reset_eval_cache(); +} + + +void model::collect_deps(top_sort& ts) { + for (auto const& kv : m_finterp) { + ts.insert(kv.m_key, collect_deps(ts, kv.m_value)); + } + for (auto const& kv : m_interp) { + ts.insert(kv.m_key, collect_deps(ts, kv.m_value)); + } +} + +struct model::deps_collector { + model& m; + top_sort& ts; + func_decl_set& s; + array_util autil; + deps_collector(model& m, top_sort& ts, func_decl_set& s): m(m), ts(ts), s(s), autil(m.get_manager()) {} + void operator()(app* a) { + func_decl* f = a->get_decl(); + if (autil.is_as_array(f)) { + f = autil.get_as_array_func_decl(a); + } + if (m.has_interpretation(f)) { + s.insert(f); + ts.add_occurs(f); + } + } + void operator()(expr* ) {} +}; + +struct model::occs_collector { + top_sort& ts; + occs_collector(top_sort& ts): ts(ts) {} + void operator()(func_decl* f) { + ts.add_occurs(f); + } + void operator()(ast*) {} +}; + + +model::func_decl_set* model::collect_deps(top_sort& ts, expr * e) { + func_decl_set* s = alloc(func_decl_set); + deps_collector collector(*this, ts, *s); + if (e) for_each_expr(collector, e); + return s; +} + +model::func_decl_set* model::collect_deps(top_sort& ts, func_interp * fi) { + func_decl_set* s = alloc(func_decl_set); + deps_collector collector(*this, ts, *s); + fi->compress(); + expr* e = fi->get_else(); + if (e) for_each_expr(collector, e); + return s; +} + + +/** + \brief Inline interpretations of skolem functions +*/ + +void model::cleanup_interp(top_sort& ts, func_decl* f) { + unsigned pid = ts.partition_id(f); + expr * e1 = get_const_interp(f); + if (e1) { + expr_ref e2 = cleanup_expr(ts, e1, pid); + if (e2 != e1) + register_decl(f, e2); + return; + } + func_interp* fi = get_func_interp(f); + if (fi) { + e1 = fi->get_else(); + expr_ref e2 = cleanup_expr(ts, e1, pid); + if (e1 != e2) + fi->set_else(e2); + } +} + +void model::collect_occs(top_sort& ts, func_decl* f) { + expr * e = get_const_interp(f); + if (e) { + collect_occs(ts, e); + } + else { + func_interp* fi = get_func_interp(f); + if (fi) { + e = fi->get_else(); + collect_occs(ts, e); + } + } +} + +void model::collect_occs(top_sort& ts, expr* e) { + occs_collector collector(ts); + for_each_ast(collector, e, true); +} + +bool model::can_inline_def(top_sort& ts, func_decl* f) { + if (ts.occur_count(f) <= 1) return true; + func_interp* fi = get_func_interp(f); + if (!fi) return false; + if (fi->get_else() == nullptr) return false; + expr* e = fi->get_else(); + obj_hashtable subs; + ptr_buffer todo; + todo.push_back(e); + while (!todo.empty()) { + if (fi->num_entries() + subs.size() > 8) return false; + expr* e = todo.back(); + todo.pop_back(); + if (subs.contains(e)) continue; + subs.insert(e); + if (is_app(e)) { + for (expr* arg : *to_app(e)) { + todo.push_back(arg); + } + } + else if (is_quantifier(e)) { + todo.push_back(to_quantifier(e)->get_expr()); + } + } + return true; +} + + +expr_ref model::cleanup_expr(top_sort& ts, expr* e, unsigned current_partition) { + if (!e) return expr_ref(0, m); + + TRACE("model", tout << "cleaning up:\n" << mk_pp(e, m) << "\n";); + + obj_map cache; + expr_ref_vector trail(m); + ptr_buffer todo; + ptr_buffer args; + todo.push_back(e); + array_util autil(m); + func_interp* fi = nullptr; + unsigned pid = 0; + expr_ref new_t(m); + + while (!todo.empty()) { + expr* a = todo.back(); + switch(a->get_kind()) { + case AST_APP: { + app * t = to_app(a); + func_decl* f = t->get_decl(); + bool visited = true; + + args.reset(); + for (expr* t_arg : *t) { + expr * arg = nullptr; + if (!cache.find(t_arg, arg)) { + visited = false; + todo.push_back(t_arg); + } + else { + args.push_back(arg); + } + } + if (!visited) { + continue; + } + fi = nullptr; + if (autil.is_as_array(a)) { + func_decl* f = autil.get_as_array_func_decl(a); + // only expand auxiliary definitions that occur once. + if (can_inline_def(ts, f)) { + fi = get_func_interp(f); + } + } + + if (fi && fi->get_interp()) { + f = autil.get_as_array_func_decl(a); + expr_ref_vector sargs(m); + sort_ref_vector vars(m); + svector var_names; + for (unsigned i = 0; i < f->get_arity(); ++i) { + var_names.push_back(symbol(i)); + vars.push_back(f->get_domain(f->get_arity() - i - 1)); + } + new_t = m.mk_lambda(vars.size(), vars.c_ptr(), var_names.c_ptr(), fi->get_interp()); + } + else if (f->is_skolem() && can_inline_def(ts, f) && (fi = get_func_interp(f)) && + fi->get_interp() && (!ts.partition_ids().find(f, pid) || pid != current_partition)) { + var_subst vs(m); + // ? TBD args.reverse(); + new_t = vs(fi->get_interp(), args.size(), args.c_ptr()); + } +#if 0 + else if (is_uninterp_const(a) && !get_const_interp(f)) { + new_t = get_some_value(f->get_range()); + register_decl(f, new_t); + } +#endif + else { + new_t = ts.m_rewrite.mk_app(f, args.size(), args.c_ptr()); + } + + if (t != new_t.get()) trail.push_back(new_t); + todo.pop_back(); + cache.insert(t, new_t); + break; + } + default: + SASSERT(a != nullptr); + cache.insert(a, a); + todo.pop_back(); + break; + } + } + + ts.m_rewrite(cache[e], new_t); + return new_t; +} + +void model::remove_decls(ptr_vector & decls, func_decl_set const & s) { + unsigned j = 0; + for (func_decl* f : decls) { + if (!s.contains(f)) { + decls[j++] = f; + } + } + decls.shrink(j); +} + + +expr_ref model::get_inlined_const_interp(func_decl* f) { + expr* v = get_const_interp(f); + if (!v) return expr_ref(0, m); + top_sort st(m); + expr_ref result1(v, m); + expr_ref result2 = cleanup_expr(st, v, UINT_MAX); + while (result1 != result2) { + result1 = result2; + result2 = cleanup_expr(st, result1, UINT_MAX); + } + return result2; +} + expr_ref model::operator()(expr* t) { return m_mev(t); } expr_ref_vector model::operator()(expr_ref_vector const& ts) { - expr_ref_vector rs(m()); + expr_ref_vector rs(m); for (expr* t : ts) rs.push_back((*this)(t)); return rs; } bool model::is_true(expr* t) { - return m().is_true((*this)(t)); + return m.is_true((*this)(t)); } bool model::is_false(expr* t) { - return m().is_false((*this)(t)); + return m.is_false((*this)(t)); } bool model::is_true(expr_ref_vector const& ts) { diff --git a/src/model/model.h b/src/model/model.h index 9399be285..0b74de771 100644 --- a/src/model/model.h +++ b/src/model/model.h @@ -30,12 +30,28 @@ typedef ref model_ref; class model : public model_core { protected: typedef obj_map*> sort2universe; + typedef obj_hashtable func_decl_set; ptr_vector m_usorts; sort2universe m_usort2universe; model_evaluator m_mev; + bool m_cleaned; struct value_proc; + struct deps_collector; + struct occs_collector; + struct top_sort; + + func_decl_set* collect_deps(top_sort& ts, expr * e); + func_decl_set* collect_deps(top_sort& ts, func_interp* fi); + void collect_deps(top_sort& ts); + void collect_occs(top_sort& ts, func_decl* f); + void collect_occs(top_sort& ts, expr* e); + void cleanup_interp(top_sort& ts, func_decl * f); + expr_ref cleanup_expr(top_sort& ts, expr* e, unsigned current_partition); + void remove_decls(ptr_vector & decls, func_decl_set const & s); + bool can_inline_def(top_sort& ts, func_decl* f); + public: model(ast_manager & m); ~model() override; @@ -54,6 +70,8 @@ public: sort * get_uninterpreted_sort(unsigned idx) const override; bool has_uninterpreted_sort(sort * s) const; + expr_ref get_inlined_const_interp(func_decl* f); + // // Primitives for building models // @@ -63,6 +81,8 @@ public: // model * translate(ast_translation & translator) const; + void compress(); + void set_model_completion(bool f) { m_mev.set_model_completion(f); } void updt_params(params_ref const & p) { m_mev.updt_params(p); } diff --git a/src/model/model2expr.cpp b/src/model/model2expr.cpp index 990f9df0f..4ee518282 100644 --- a/src/model/model2expr.cpp +++ b/src/model/model2expr.cpp @@ -147,7 +147,7 @@ void model2expr(model& md, expr_ref& result) { } if (f->get_arity() > 0) { var_subst vs(m, false); - vs(tmp, rev_vars.size(), rev_vars.c_ptr(), tmp); + tmp = vs(tmp, rev_vars.size(), rev_vars.c_ptr()); tmp = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp); } conjs.push_back(tmp); diff --git a/src/model/model_core.cpp b/src/model/model_core.cpp index d41854c22..d92e81421 100644 --- a/src/model/model_core.cpp +++ b/src/model/model_core.cpp @@ -20,12 +20,12 @@ Revision History: model_core::~model_core() { for (auto & kv : m_interp) { - m_manager.dec_ref(kv.m_key); - m_manager.dec_ref(kv.m_value); + m.dec_ref(kv.m_key); + m.dec_ref(kv.m_value); } for (auto & kv : m_finterp) { - m_manager.dec_ref(kv.m_key); + m.dec_ref(kv.m_key); dealloc(kv.m_value); } } @@ -47,32 +47,33 @@ bool model_core::eval(func_decl* f, expr_ref & r) const { void model_core::register_decl(func_decl * d, expr * v) { SASSERT(d->get_arity() == 0); + TRACE("model", tout << "register " << d->get_name() << "\n";); decl2expr::obj_map_entry * entry = m_interp.insert_if_not_there2(d, nullptr); if (entry->get_data().m_value == nullptr) { // new entry m_decls.push_back(d); m_const_decls.push_back(d); - m_manager.inc_ref(d); - m_manager.inc_ref(v); + m.inc_ref(d); + m.inc_ref(v); entry->get_data().m_value = v; } else { // replacing entry - m_manager.inc_ref(v); - m_manager.dec_ref(entry->get_data().m_value); + m.inc_ref(v); + m.dec_ref(entry->get_data().m_value); entry->get_data().m_value = v; } } void model_core::register_decl(func_decl * d, func_interp * fi) { SASSERT(d->get_arity() > 0); - SASSERT(&fi->m() == &m_manager); + SASSERT(&fi->m() == &m); decl2finterp::obj_map_entry * entry = m_finterp.insert_if_not_there2(d, nullptr); if (entry->get_data().m_value == nullptr) { // new entry m_decls.push_back(d); m_func_decls.push_back(d); - m_manager.inc_ref(d); + m.inc_ref(d); entry->get_data().m_value = fi; } else { @@ -85,23 +86,23 @@ void model_core::register_decl(func_decl * d, func_interp * fi) { void model_core::unregister_decl(func_decl * d) { decl2expr::obj_map_entry * ec = m_interp.find_core(d); - if (ec && ec->get_data().m_value != 0) { + if (ec) { auto k = ec->get_data().m_key; auto v = ec->get_data().m_value; m_interp.remove(d); m_const_decls.erase(d); - m_manager.dec_ref(k); - m_manager.dec_ref(v); + m.dec_ref(k); + m.dec_ref(v); return; } decl2finterp::obj_map_entry * ef = m_finterp.find_core(d); - if (ef && ef->get_data().m_value != 0) { + if (ef) { auto k = ef->get_data().m_key; auto v = ef->get_data().m_value; m_finterp.remove(d); m_func_decls.erase(d); - m_manager.dec_ref(k); + m.dec_ref(k); dealloc(v); } } diff --git a/src/model/model_core.h b/src/model/model_core.h index 3f1e92bad..7f5051386 100644 --- a/src/model/model_core.h +++ b/src/model/model_core.h @@ -27,7 +27,7 @@ class model_core { protected: typedef obj_map decl2expr; typedef obj_map decl2finterp; - ast_manager & m_manager; + ast_manager & m; unsigned m_ref_count; decl2expr m_interp; //!< interpretation for uninterpreted constants decl2finterp m_finterp; //!< interpretation for uninterpreted functions @@ -36,11 +36,10 @@ protected: ptr_vector m_func_decls; public: - model_core(ast_manager & m):m_manager(m), m_ref_count(0) { } + model_core(ast_manager & m):m(m), m_ref_count(0) { } virtual ~model_core(); - ast_manager & get_manager() const { return m_manager; } - ast_manager& m() const { return m_manager; } + ast_manager & get_manager() const { return m; } unsigned get_num_decls() const { return m_decls.size(); } func_decl * get_decl(unsigned i) const { return m_decls[i]; } @@ -49,6 +48,14 @@ public: func_interp * get_func_interp(func_decl * d) const { func_interp * fi; return m_finterp.find(d, fi) ? fi : nullptr; } bool eval(func_decl * f, expr_ref & r) const; + bool is_true_decl(func_decl *f) const { + expr_ref r(m); + return eval(f, r) && m.is_true(r); + } + bool is_false_decl(func_decl *f) const { + expr_ref r(m); + return eval(f, r) && m.is_false(r); + } unsigned get_num_constants() const { return m_const_decls.size(); } unsigned get_num_functions() const { return m_func_decls.size(); } @@ -65,6 +72,11 @@ public: virtual expr * get_some_value(sort * s) = 0; + expr * get_some_const_interp(func_decl * d) { + expr * r = get_const_interp(d); + if (r) return r; + return get_some_value(d->get_range()); + } // // Reference counting // @@ -78,4 +90,7 @@ public: }; +std::ostream& operator<<(std::ostream& out, model_core const& m); + + #endif diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index f7c7fa74d..0fe1fe7c6 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -16,10 +16,12 @@ Author: Revision History: --*/ +#include "util/cooperate.h" #include "model/model.h" #include "model/model_evaluator_params.hpp" -#include "ast/rewriter/rewriter_types.h" #include "model/model_evaluator.h" +#include "model/model_v2_pp.h" +#include "ast/rewriter/rewriter_types.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/arith_rewriter.h" #include "ast/rewriter/bv_rewriter.h" @@ -29,7 +31,6 @@ Revision History: #include "ast/rewriter/array_rewriter.h" #include "ast/rewriter/fpa_rewriter.h" #include "ast/rewriter/rewriter_def.h" -#include "util/cooperate.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "model/model_smt2_pp.h" @@ -274,7 +275,7 @@ struct evaluator_cfg : public default_rewriter_cfg { fi->set_else(m.get_some_value(f->get_range())); var_subst vs(m, false); - vs(fi->get_interp(), num, args, result); + result = vs(fi->get_interp(), num, args); return BR_REWRITE_FULL; } diff --git a/src/model/model_smt2_pp.cpp b/src/model/model_smt2_pp.cpp index bc1900ba7..807e2b4e7 100644 --- a/src/model/model_smt2_pp.cpp +++ b/src/model/model_smt2_pp.cpp @@ -304,6 +304,6 @@ void model_smt2_pp(std::ostream & out, ast_manager & m, model_core const & md, u } std::ostream& operator<<(std::ostream& out, model_core const& m) { - model_smt2_pp(out, m.m(), m, 0); + model_smt2_pp(out, m.get_manager(), m, 0); return out; } diff --git a/src/model/model_smt2_pp.h b/src/model/model_smt2_pp.h index b38bd631d..c43e26ca6 100644 --- a/src/model/model_smt2_pp.h +++ b/src/model/model_smt2_pp.h @@ -25,6 +25,5 @@ Revision History: void model_smt2_pp(std::ostream & out, ast_printer_context & ctx, model_core const & m, unsigned indent); void model_smt2_pp(std::ostream & out, ast_manager & m, model_core const & md, unsigned indent); -std::ostream& operator<<(std::ostream& out, model_core const& m); #endif diff --git a/src/muz/base/bind_variables.cpp b/src/muz/base/bind_variables.cpp index 2ba7d1473..cbafc57b0 100644 --- a/src/muz/base/bind_variables.cpp +++ b/src/muz/base/bind_variables.cpp @@ -38,7 +38,7 @@ expr_ref bind_variables::operator()(expr* fml, bool is_forall) { if (!m_names.empty()) { m_bound.reverse(); m_names.reverse(); - result = m.mk_quantifier(is_forall, m_bound.size(), m_bound.c_ptr(), m_names.c_ptr(), result); + result = m.mk_quantifier(is_forall ? forall_k : exists_k, m_bound.size(), m_bound.c_ptr(), m_names.c_ptr(), result); } m_pinned.reset(); m_cache.reset(); diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 220b2516d..1c9abf78c 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -307,6 +307,10 @@ namespace datalog { bool context::instantiate_quantifiers() const { return m_params->xform_instantiate_quantifiers(); } bool context::array_blast() const { return m_params->xform_array_blast(); } bool context::array_blast_full() const { return m_params->xform_array_blast_full(); } + bool context::elim_term_ite() const {return m_params->xform_elim_term_ite();} + unsigned context::blast_term_ite_inflation() const { + return m_params->xform_elim_term_ite_inflation(); + } void context::register_finite_sort(sort * s, sort_kind k) { @@ -1044,7 +1048,7 @@ namespace datalog { quantifier* q = to_quantifier(body); expr* e = q->get_expr(); if (m.is_implies(e, body, e2)) { - fml = m.mk_quantifier(false, q->get_num_decls(), + fml = m.mk_quantifier(exists_k, q->get_num_decls(), q->get_decl_sorts(), q->get_decl_names(), body); } @@ -1251,13 +1255,10 @@ namespace datalog { obj_map max_vars; for (unsigned i = 0; i < rules.size(); ++i) { expr* r = rules[i].get(); - if (!is_quantifier(r)) { + if (!is_forall(r)) { continue; } quantifier* q = to_quantifier(r); - if (!q->is_forall()) { - continue; - } if (has_quantifiers(q->get_expr())) { continue; } @@ -1292,7 +1293,7 @@ namespace datalog { subst.push_back(fresh_vars[vars[max_var]].get()); } - vsubst(q->get_expr(), subst.size(), subst.c_ptr(), res); + res = vsubst(q->get_expr(), subst.size(), subst.c_ptr()); rules[i] = res.get(); } } diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index 6e6bc80dc..23926c3d1 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -280,6 +280,8 @@ namespace datalog { bool xform_coi() const; bool array_blast() const; bool array_blast_full() const; + bool elim_term_ite() const; + unsigned blast_term_ite_inflation() const; void register_finite_sort(sort * s, sort_kind k); diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index dd6728486..d709209e1 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -292,7 +292,7 @@ namespace datalog { args.push_back(m.mk_var(0, m.mk_bool_sort())); } } - sub(q, args.size(), args.c_ptr(), q); + q = sub(q, args.size(), args.c_ptr()); vars.reset(); m_free_vars(q); vars.append(m_free_vars.size(), m_free_vars.c_ptr()); @@ -438,11 +438,7 @@ namespace datalog { e = e2; } } - if (is_quantifier(e)) { - q = to_quantifier(e); - return q->is_forall(); - } - return false; + return ::is_forall(e); } @@ -745,7 +741,7 @@ namespace datalog { expr_ref unbound_tail_pre_quant(m), fixed_tail(m), quant_tail(m); var_subst vs(m, false); - vs(unbound_tail, subst.size(), subst.c_ptr(), unbound_tail_pre_quant); + unbound_tail_pre_quant = vs(unbound_tail, subst.size(), subst.c_ptr()); quant_tail = m.mk_exists(q_var_cnt, qsorts.c_ptr(), qnames.c_ptr(), unbound_tail_pre_quant); @@ -816,10 +812,10 @@ namespace datalog { app_ref_vector new_tail(m); svector tail_neg; var_subst vs(m, false); - vs(r->get_head(), sz, es, tmp); + tmp = vs(r->get_head(), sz, es); new_head = to_app(tmp); for (unsigned i = 0; i < r->get_tail_size(); ++i) { - vs(r->get_tail(i), sz, es, tmp); + tmp = vs(r->get_tail(i), sz, es); new_tail.push_back(to_app(tmp)); tail_neg.push_back(r->is_neg_tail(i)); } @@ -984,8 +980,7 @@ namespace datalog { var_subst vs(m, false); - expr_ref new_head_e(m); - vs(m_head, subst_vals.size(), subst_vals.c_ptr(), new_head_e); + expr_ref new_head_e = vs(m_head, subst_vals.size(), subst_vals.c_ptr()); m.inc_ref(new_head_e); m.dec_ref(m_head); @@ -993,8 +988,7 @@ namespace datalog { for (unsigned i = 0; i < m_tail_size; i++) { app * old_tail = get_tail(i); - expr_ref new_tail_e(m); - vs(old_tail, subst_vals.size(), subst_vals.c_ptr(), new_tail_e); + expr_ref new_tail_e = vs(old_tail, subst_vals.size(), subst_vals.c_ptr()); bool sign = is_neg_tail(i); m.inc_ref(new_tail_e); m.dec_ref(old_tail); diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h index 2537adbb0..102427a4a 100644 --- a/src/muz/base/dl_rule.h +++ b/src/muz/base/dl_rule.h @@ -82,12 +82,10 @@ namespace datalog { quantifier_finder_proc() : m_exist(false), m_univ(false) {} void operator()(var * n) { } void operator()(quantifier * n) { - if (n->is_forall()) { - m_univ = true; - } - else { - SASSERT(n->is_exists()); - m_exist = true; + switch (n->get_kind()) { + case forall_k: m_univ = true; break; + case exists_k: m_exist = true; break; + case lambda_k: UNREACHABLE(); } } void operator()(app * n) { } diff --git a/src/muz/base/dl_rule_set.cpp b/src/muz/base/dl_rule_set.cpp index acd200d44..28abbba41 100644 --- a/src/muz/base/dl_rule_set.cpp +++ b/src/muz/base/dl_rule_set.cpp @@ -31,27 +31,20 @@ namespace datalog { rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed): m_context(o.m_context) { if (reversed) { - iterator oit = o.begin(); - iterator oend = o.end(); - for (; oit!=oend; ++oit) { - func_decl * pred = oit->m_key; - item_set & orig_items = *oit->get_value(); + for (auto & kv : o) { + func_decl * pred = kv.m_key; + item_set & orig_items = *kv.get_value(); ensure_key(pred); - item_set::iterator dit = orig_items.begin(); - item_set::iterator dend = orig_items.end(); - for (; dit!=dend; ++dit) { - func_decl * master_pred = *dit; + for (func_decl * master_pred : orig_items) { insert(master_pred, pred); } } } else { - iterator oit = o.begin(); - iterator oend = o.end(); - for (; oit!=oend; ++oit) { - func_decl * pred = oit->m_key; - item_set & orig_items = *oit->get_value(); + for (auto & kv : o) { + func_decl * pred = kv.m_key; + item_set & orig_items = *kv.get_value(); m_data.insert(pred, alloc(item_set, orig_items)); } } @@ -86,14 +79,10 @@ namespace datalog { void rule_dependencies::populate(const rule_set & rules) { SASSERT(m_data.empty()); - rule_set::decl2rules::iterator it = rules.m_head2rules.begin(); - rule_set::decl2rules::iterator end = rules.m_head2rules.end(); - for (; it != end; ++it) { - ptr_vector * rules = it->m_value; - ptr_vector::iterator it2 = rules->begin(); - ptr_vector::iterator end2 = rules->end(); - for (; it2 != end2; ++it2) { - populate(*it2); + for (auto & kv : rules.m_head2rules) { + ptr_vector * rules = kv.m_value; + for (rule* r : *rules) { + populate(r); } } } @@ -150,54 +139,41 @@ namespace datalog { void rule_dependencies::restrict(const item_set & allowed) { ptr_vector to_remove; - iterator pit = begin(); - iterator pend = end(); - for (; pit!=pend; ++pit) { - func_decl * pred = pit->m_key; + for (auto const& kv : *this) { + func_decl * pred = kv.m_key; if (!allowed.contains(pred)) { to_remove.insert(pred); continue; } - item_set& itms = *pit->get_value(); + item_set& itms = *kv.get_value(); set_intersection(itms, allowed); } - ptr_vector::iterator rit = to_remove.begin(); - ptr_vector::iterator rend = to_remove.end(); - for (; rit != rend; ++rit) { - remove_m_data_entry(*rit); - } + for (func_decl* f : to_remove) + remove_m_data_entry(f); } void rule_dependencies::remove(func_decl * itm) { remove_m_data_entry(itm); - iterator pit = begin(); - iterator pend = end(); - for (; pit != pend; ++pit) { - item_set & itms = *pit->get_value(); + for (auto const& kv : *this) { + item_set & itms = *kv.get_value(); itms.remove(itm); } } void rule_dependencies::remove(const item_set & to_remove) { - item_set::iterator rit = to_remove.begin(); - item_set::iterator rend = to_remove.end(); - for (; rit!=rend; ++rit) { - remove_m_data_entry(*rit); + for (auto * item : to_remove) { + remove_m_data_entry(item); } - iterator pit = begin(); - iterator pend = end(); - for (; pit!=pend; ++pit) { - item_set * itms = pit->get_value(); + for (auto & kv : *this) { + item_set * itms = kv.get_value(); set_difference(*itms, to_remove); } } unsigned rule_dependencies::out_degree(func_decl * f) const { unsigned res = 0; - iterator pit = begin(); - iterator pend = end(); - for (; pit!=pend; ++pit) { - item_set & itms = *pit->get_value(); + for (auto & kv : *this) { + item_set & itms = *kv.get_value(); if (itms.contains(f)) { res++; } @@ -333,19 +309,11 @@ namespace datalog { void rule_set::inherit_predicates(rule_set const& other) { m_refs.append(other.m_refs); set_union(m_output_preds, other.m_output_preds); - { - obj_map::iterator it = other.m_orig2pred.begin(); - obj_map::iterator end = other.m_orig2pred.end(); - for (; it != end; ++it) { - m_orig2pred.insert(it->m_key, it->m_value); - } + for (auto & kv : other.m_orig2pred) { + m_orig2pred.insert(kv.m_key, kv.m_value); } - { - obj_map::iterator it = other.m_pred2orig.begin(); - obj_map::iterator end = other.m_pred2orig.end(); - for (; it != end; ++it) { - m_pred2orig.insert(it->m_key, it->m_value); - } + for (auto & kv : other.m_pred2orig) { + m_pred2orig.insert(kv.m_key, kv.m_value); } } @@ -537,26 +505,19 @@ namespace datalog { void rule_set::display_deps( std::ostream & out ) const { const pred_set_vector & strats = get_strats(); - pred_set_vector::const_iterator sit = strats.begin(); - pred_set_vector::const_iterator send = strats.end(); - for (; sit!=send; ++sit) { - func_decl_set & strat = **sit; - func_decl_set::iterator fit=strat.begin(); - func_decl_set::iterator fend=strat.end(); - bool non_empty = false; - for (; fit!=fend; ++fit) { - func_decl * first = *fit; - const func_decl_set & deps = m_deps.get_deps(first); - func_decl_set::iterator dit=deps.begin(); - func_decl_set::iterator dend=deps.end(); - for (; dit!=dend; ++dit) { - non_empty = true; - func_decl * dep = *dit; - out<get_name()<<" -> "<get_name()<<"\n"; - } - } - if (non_empty && sit!=send) { + bool non_empty = false; + for (func_decl_set* strat : strats) { + if (non_empty) { out << "\n"; + non_empty = false; + } + + for (func_decl * first : *strat) { + const func_decl_set & deps = m_deps.get_deps(first); + for (func_decl * dep : deps) { + non_empty = true; + out<get_name()<<" -> " <get_name()<<"\n"; + } } } } @@ -568,11 +529,8 @@ namespace datalog { // ----------------------------------- rule_stratifier::~rule_stratifier() { - comp_vector::iterator it = m_strats.begin(); - comp_vector::iterator end = m_strats.end(); - for (; it!=end; ++it) { - SASSERT(*it); - dealloc(*it); + for (auto * t : m_strats) { + dealloc(t); } } @@ -617,10 +575,8 @@ namespace datalog { m_stack_P.push_back(el); const item_set & children = m_deps.get_deps(el); - item_set::iterator cit=children.begin(); - item_set::iterator cend=children.end(); - for (; cit!=cend; ++cit) { - traverse(*cit); + for (T* ch : children) { + traverse(ch); } if (el == m_stack_P.back()) { @@ -646,10 +602,8 @@ namespace datalog { } //detect strong components - rule_dependencies::iterator it = m_deps.begin(); - rule_dependencies::iterator end = m_deps.end(); - for (; it!=end; ++it) { - T * el = it->m_key; + for (auto const& kv : m_deps) { + T * el = kv.m_key; //we take a note of the preorder number with which this sweep started m_first_preorder = m_next_preorder; traverse(el); @@ -662,19 +616,13 @@ namespace datalog { in_degrees.resize(m_components.size()); //init in_degrees - it = m_deps.begin(); - end = m_deps.end(); - for (; it != end; ++it) { - T * el = it->m_key; - item_set * out_edges = it->m_value; + for (auto const& kv : m_deps) { + T * el = kv.m_key; + item_set * out_edges = kv.m_value; - unsigned el_comp = 0; - VERIFY( m_component_nums.find(el, el_comp) ); + unsigned el_comp = m_component_nums[el]; - item_set::iterator eit = out_edges->begin(); - item_set::iterator eend = out_edges->end(); - for (; eit!=eend; ++eit) { - T * tgt = *eit; + for (T * tgt : *out_edges) { unsigned tgt_comp = m_component_nums.find(tgt); @@ -701,15 +649,9 @@ namespace datalog { unsigned strats_index = 0; while (strats_index < m_strats.size()) { //m_strats.size() changes inside the loop! item_set * comp = m_strats[strats_index]; - item_set::iterator cit=comp->begin(); - item_set::iterator cend=comp->end(); - for (; cit!=cend; ++cit) { - T * el = *cit; + for (T * el : *comp) { const item_set & deps = m_deps.get_deps(el); - item_set::iterator eit=deps.begin(); - item_set::iterator eend=deps.end(); - for (; eit!=eend; ++eit) { - T * tgt = *eit; + for (T * tgt : deps) { unsigned tgt_comp = 0; VERIFY( m_component_nums.find(tgt, tgt_comp) ); @@ -724,7 +666,7 @@ namespace datalog { m_components[tgt_comp] = 0; } } - traverse(*cit); + traverse(el); } } strats_index++; @@ -738,12 +680,9 @@ namespace datalog { SASSERT(m_pred_strat_nums.empty()); unsigned strat_cnt = m_strats.size(); - for (unsigned strat_index=0; strat_indexbegin(); - item_set::iterator cend=comp->end(); - for (; cit != cend; ++cit) { - T * el = *cit; + for (T * el : *comp) { m_pred_strat_nums.insert(el, strat_index); } } @@ -761,10 +700,8 @@ namespace datalog { m_deps.display(out << "dependencies\n"); out << "strata\n"; for (unsigned i = 0; i < m_strats.size(); ++i) { - item_set::iterator it = m_strats[i]->begin(); - item_set::iterator end = m_strats[i]->end(); - for (; it != end; ++it) { - out << (*it)->get_name() << " "; + for (auto * item : *m_strats[i]) { + out << item->get_name() << " "; } out << "\n"; } diff --git a/src/muz/base/dl_util.cpp b/src/muz/base/dl_util.cpp index 6c52d0537..fdafea924 100644 --- a/src/muz/base/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -115,8 +115,7 @@ namespace datalog { expr_ref tmp(m); for (unsigned i = 0; i < tgt.size(); ++i) { if (tgt[i].get()) { - vs(tgt[i].get(), sub.size(), sub.c_ptr(), tmp); - tgt[i] = tmp; + tgt[i] = vs(tgt[i].get(), sub.size(), sub.c_ptr()); } else { tgt[i] = sub[i]; diff --git a/src/muz/base/fp_params.pyg b/src/muz/base/fp_params.pyg index c96a12ca2..ee842d28e 100644 --- a/src/muz/base/fp_params.pyg +++ b/src/muz/base/fp_params.pyg @@ -132,10 +132,12 @@ def_module_params('fp', ('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'), ('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"), ('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"), + ('xform.elim_term_ite', BOOL, False, 'Eliminate term-ite expressions'), + ('xform.elim_term_ite.inflation', UINT, 3, 'Maximum inflation for non-Boolean ite-terms blasting: 0 (none), k (multiplicative)'), ('spacer.propagate', BOOL, True, 'Enable propagate/pushing phase'), ('spacer.max_level', UINT, UINT_MAX, "Maximum level to explore"), ('spacer.elim_aux', BOOL, True, "Eliminate auxiliary variables in reachability facts"), - ('spacer.blast_term_ite', BOOL, True, "Expand non-Boolean ite-terms"), + ('spacer.blast_term_ite_inflation', UINT, 3, 'Maximum inflation for non-Boolean ite-terms expansion: 0 (none), k (multiplicative)'), ('spacer.reach_dnf', BOOL, True, "Restrict reachability facts to DNF"), ('bmc.linear_unrolling_depth', UINT, UINT_MAX, "Maximal level to explore"), ('spacer.iuc.split_farkas_literals', BOOL, False, "Split Farkas literals"), @@ -150,6 +152,8 @@ def_module_params('fp', ('spacer.keep_proxy', BOOL, True, 'keep proxy variables (internal parameter)'), ('spacer.q3', BOOL, True, 'Allow quantified lemmas in frames'), ('spacer.q3.instantiate', BOOL, True, 'Instantiate quantified lemmas'), + ('spacer.q3.use_qgen', BOOL, False, 'use quantified lemma generalizer'), + ('spacer.q3.qgen.normalize', BOOL, True, 'normalize cube before quantified generalization'), ('spacer.iuc', UINT, 1, '0 = use old implementation of unsat-core-generation, ' + '1 = use new implementation of IUC generation, ' + @@ -160,13 +164,10 @@ def_module_params('fp', '2 = use Gaussian elimination optimization (broken), 3 = use additive IUC plugin'), ('spacer.iuc.old_hyp_reducer', BOOL, False, 'use old hyp reducer instead of new implementation, for debugging only'), ('spacer.validate_lemmas', BOOL, False, 'Validate each lemma after generalization'), - ('spacer.reuse_pobs', BOOL, True, 'Reuse pobs'), ('spacer.ground_pobs', BOOL, True, 'Ground pobs by using values from a model'), ('spacer.iuc.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut (for debugging)'), ('spacer.iuc.debug_proof', BOOL, False, 'prints proof used by unsat-core-learner for debugging purposes (debugging)'), ('spacer.simplify_pob', BOOL, False, 'simplify pobs by removing redundant constraints'), - ('spacer.q3.use_qgen', BOOL, False, 'use quantified lemma generalizer'), - ('spacer.q3.qgen.normalize', BOOL, True, 'normalize cube before quantified generalization'), ('spacer.p3.share_lemmas', BOOL, False, 'Share frame lemmas'), ('spacer.p3.share_invariants', BOOL, False, "Share invariants lemmas"), ('spacer.min_level', UINT, 0, 'Minimal level to explore'), diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index 97432f544..5f69e4c37 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -108,20 +108,20 @@ namespace datalog { // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { - vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), trm); + trm = vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr()); conjs.push_back(m.mk_eq(trm, mk_q_arg(p, k, true))); } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { - vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), trm); + trm = vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr()); conjs.push_back(m.mk_eq(trm, mk_q_arg(q, k, false))); } func_decl_ref qr = mk_q_func_decl(q); conjs.push_back(m.mk_app(qr, m_bv.mk_bv_sub(var, mk_q_one()))); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { - vs(r.get_tail(j), sub.size(), sub.c_ptr(), trm); + trm = vs(r.get_tail(j), sub.size(), sub.c_ptr()); conjs.push_back(trm); } if (r.get_uninterpreted_tail_size() > 0) { @@ -238,7 +238,7 @@ namespace datalog { var_subst vs(m, false); num = mk_q_num(i); expr* nums[1] = { num }; - vs(t, 1, nums, tmp); + tmp = vs(t, 1, nums); return (*model)(tmp); } @@ -535,7 +535,7 @@ namespace datalog { for (unsigned j = 0; j < sz; ++j) { func_decl* head_j = r->get_decl(j); app* body_j = r->get_tail(j); - vs(body_j, sub.size(), sub.c_ptr(), prop_body); + prop_body = vs(body_j, sub.size(), sub.c_ptr()); prs.push_back(get_proof(md, head_j, to_app(prop_body), level-1)); positions.push_back(std::make_pair(j+1,0)); substs.push_back(expr_ref_vector(m)); @@ -609,11 +609,9 @@ namespace datalog { } expr_ref skolemize_vars(rule& r, expr_ref_vector const& args, ptr_vector const& vars, expr* e) { - expr_ref result(m); expr_ref_vector binding = mk_skolem_binding(r, vars, args); var_subst vs(m, false); - vs(e, binding.size(), binding.c_ptr(), result); - return result; + return vs(e, binding.size(), binding.c_ptr()); } func_decl_ref mk_body_func(rule& r, ptr_vector const& args, unsigned index, sort* s) { @@ -646,8 +644,8 @@ namespace datalog { return expr_ref(e, m); } var_subst vs(m, false); - vs(e, binding.size(), binding.c_ptr(), tmp); - vs(pat, binding.size(), binding.c_ptr(), head); + tmp = vs(e, binding.size(), binding.c_ptr()); + head = vs(pat, binding.size(), binding.c_ptr()); patterns.push_back(m.mk_pattern(to_app(head))); symbol qid, skid; return expr_ref(m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), tmp, 1, qid, skid, 1, patterns.c_ptr()), m); @@ -892,7 +890,7 @@ namespace datalog { // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { - vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp); + tmp = vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr()); expr_ref arg = mk_arg(p, k, path_var, trace_arg); conjs.push_back(m.mk_eq(tmp, arg)); } @@ -906,7 +904,7 @@ namespace datalog { } func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { - vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp); + tmp = vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr()); expr_ref arg = mk_arg(q, k, path_arg, vars[j].get()); conjs.push_back(m.mk_eq(tmp, arg)); } @@ -914,7 +912,7 @@ namespace datalog { conjs.push_back(m.mk_app(q_pred, vars[j].get(), path_arg)); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { - vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp); + tmp = vs(r.get_tail(j), sub.size(), sub.c_ptr()); conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); @@ -1048,7 +1046,7 @@ namespace datalog { expr_ref fml(m), head(m), tmp(m); app_ref path1(m); - var_subst vs(m, false); + // var_subst vs(m, false); mk_subst(*rules[i], path, trace, sub); rm.to_formula(*rules[i], fml); prs.push_back(rules[i]->get_proof()); @@ -1399,20 +1397,20 @@ namespace datalog { // apply substitution to body. var_subst vs(m, false); for (unsigned k = 0; k < p->get_arity(); ++k) { - vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr(), tmp); + tmp = vs(r.get_head()->get_arg(k), sub.size(), sub.c_ptr()); conjs.push_back(m.mk_eq(tmp, mk_level_arg(p, k, level))); } for (unsigned j = 0; j < r.get_uninterpreted_tail_size(); ++j) { SASSERT(level > 0); func_decl* q = r.get_decl(j); for (unsigned k = 0; k < q->get_arity(); ++k) { - vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr(), tmp); + tmp = vs(r.get_tail(j)->get_arg(k), sub.size(), sub.c_ptr()); conjs.push_back(m.mk_eq(tmp, mk_level_arg(q, k, level-1))); } conjs.push_back(mk_level_predicate(q, level-1)); } for (unsigned j = r.get_uninterpreted_tail_size(); j < r.get_tail_size(); ++j) { - vs(r.get_tail(j), sub.size(), sub.c_ptr(), tmp); + tmp = vs(r.get_tail(j), sub.size(), sub.c_ptr()); conjs.push_back(tmp); } bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), rule_body); diff --git a/src/muz/clp/clp_context.cpp b/src/muz/clp/clp_context.cpp index 8ed017214..f6ebfbfb4 100644 --- a/src/muz/clp/clp_context.cpp +++ b/src/muz/clp/clp_context.cpp @@ -121,7 +121,7 @@ namespace datalog { m_ground[i] = m.mk_fresh_const("c", fv[i]); } } - m_var_subst(e, m_ground.size(), m_ground.c_ptr(), e); + e = m_var_subst(e, m_ground.size(), m_ground.c_ptr()); } static bool rule_sort_fn(const rule *r1, const rule *r2) { diff --git a/src/muz/fp/horn_tactic.cpp b/src/muz/fp/horn_tactic.cpp index eef95915d..65be3e9ae 100644 --- a/src/muz/fp/horn_tactic.cpp +++ b/src/muz/fp/horn_tactic.cpp @@ -211,7 +211,7 @@ class horn_tactic : public tactic { default: msg << "formula is not in Horn fragment: " << mk_pp(g->form(i), m) << "\n"; TRACE("horn", tout << msg.str();); - throw tactic_exception(msg.str().c_str()); + throw tactic_exception(msg.str()); } } diff --git a/src/muz/rel/check_relation.cpp b/src/muz/rel/check_relation.cpp index bd8fb4778..3602db6d9 100644 --- a/src/muz/rel/check_relation.cpp +++ b/src/muz/rel/check_relation.cpp @@ -59,9 +59,7 @@ namespace datalog { for (unsigned i = 0; i < sig.size(); ++i) { vars.push_back(m.mk_const(symbol(i), sig[i])); } - expr_ref result(m); - sub(fml, vars.size(), vars.c_ptr(), result); - return result; + return sub(fml, vars.size(), vars.c_ptr()); } void check_relation::add_fact(const relation_fact & f) { @@ -292,7 +290,7 @@ namespace datalog { } } var_subst sub(m, false); - sub(fml, vars.size(), vars.c_ptr(), fml1); + fml1 = sub(fml, vars.size(), vars.c_ptr()); bound.reverse(); fml1 = m.mk_exists(bound.size(), bound.c_ptr(), names.c_ptr(), fml1); return fml1; @@ -333,7 +331,7 @@ namespace datalog { for (unsigned i = 0; i < sig2.size(); ++i) { vars.push_back(m.mk_var(i + sig1.size(), sig2[i])); } - sub(fml2, vars.size(), vars.c_ptr(), fml2); + fml2 = sub(fml2, vars.size(), vars.c_ptr()); fml1 = m.mk_and(fml1, fml2); for (unsigned i = 0; i < cols1.size(); ++i) { unsigned v1 = cols1[i]; @@ -372,14 +370,14 @@ namespace datalog { expr_ref fml1(m), fml2(m); src.to_formula(fml1); dst.to_formula(fml2); - subst(fml1, sub.size(), sub.c_ptr(), fml1); + fml1 = subst(fml1, sub.size(), sub.c_ptr()); expr_ref_vector vars(m); for (unsigned i = 0; i < sig2.size(); ++i) { vars.push_back(m.mk_const(symbol(i), sig2[i])); } - subst(fml1, vars.size(), vars.c_ptr(), fml1); - subst(fml2, vars.size(), vars.c_ptr(), fml2); + fml1 = subst(fml1, vars.size(), vars.c_ptr()); + fml2 = subst(fml2, vars.size(), vars.c_ptr()); check_equiv("permutation", fml1, fml2); } @@ -405,8 +403,8 @@ namespace datalog { strm << "x" << i; vars.push_back(m.mk_const(symbol(strm.str().c_str()), sig[i])); } - sub(fml1, vars.size(), vars.c_ptr(), fml1); - sub(fml2, vars.size(), vars.c_ptr(), fml2); + fml1 = sub(fml1, vars.size(), vars.c_ptr()); + fml2 = sub(fml2, vars.size(), vars.c_ptr()); check_equiv("filter", fml1, fml2); } @@ -453,8 +451,8 @@ namespace datalog { strm << "x" << i; vars.push_back(m.mk_const(symbol(strm.str().c_str()), sig[i])); } - sub(fml1, vars.size(), vars.c_ptr(), fml1); - sub(fml2, vars.size(), vars.c_ptr(), fml2); + fml1 = sub(fml1, vars.size(), vars.c_ptr()); + fml2 = sub(fml2, vars.size(), vars.c_ptr()); check_equiv("union", fml1, fml2); @@ -466,13 +464,13 @@ namespace datalog { // dst \ dst0 == delta & dst & \ dst0 expr_ref fml4(m), fml5(m); fml4 = m.mk_and(fml2, m.mk_not(dst0)); - sub(fml4, vars.size(), vars.c_ptr(), fml4); - sub(d, vars.size(), vars.c_ptr(), d); + fml4 = sub(fml4, vars.size(), vars.c_ptr()); + d = sub(d, vars.size(), vars.c_ptr()); check_contains("union_delta low", d, fml4); // // delta >= delta0 // - sub(delta0, vars.size(), vars.c_ptr(), d0); + d0 = sub(delta0, vars.size(), vars.c_ptr()); check_contains("union delta0", d, d0); // @@ -480,8 +478,8 @@ namespace datalog { // fml4 = m.mk_or(fml2, delta0); fml5 = m.mk_or(d, dst0); - sub(fml4, vars.size(), vars.c_ptr(), fml4); - sub(fml5, vars.size(), vars.c_ptr(), fml5); + fml4 = sub(fml4, vars.size(), vars.c_ptr()); + fml5 = sub(fml5, vars.size(), vars.c_ptr()); check_equiv("union no overflow", fml4, fml5); } } diff --git a/src/muz/rel/dl_compiler.cpp b/src/muz/rel/dl_compiler.cpp index 2de0679a5..e4b20d250 100644 --- a/src/muz/rel/dl_compiler.cpp +++ b/src/muz/rel/dl_compiler.cpp @@ -725,8 +725,7 @@ namespace datalog { } } - expr_ref renamed(m); - m_context.get_var_subst()(filter_cond, binding.size(), binding.c_ptr(), renamed); + expr_ref renamed = m_context.get_var_subst()(filter_cond, binding.size(), binding.c_ptr()); app_ref app_renamed(to_app(renamed), m); if (remove_columns.empty()) { if (!dealloc) diff --git a/src/muz/rel/dl_finite_product_relation.cpp b/src/muz/rel/dl_finite_product_relation.cpp index 030fc1327..fb80a2105 100644 --- a/src/muz/rel/dl_finite_product_relation.cpp +++ b/src/muz/rel/dl_finite_product_relation.cpp @@ -1311,8 +1311,7 @@ namespace datalog { if(m_rel_cond_columns.empty()) { expr_ref_vector renaming(m_manager); get_renaming_args(r.m_sig2table, r.get_signature(), renaming); - expr_ref table_cond(m_manager); - m_subst(condition, renaming.size(), renaming.c_ptr(), table_cond); + expr_ref table_cond = m_subst(condition, renaming.size(), renaming.c_ptr()); m_table_filter = rmgr.mk_filter_interpreted_fn(r.get_table(), to_app(table_cond)); } else { @@ -1361,9 +1360,7 @@ namespace datalog { continue; } if(!m_rel_filter) { - expr_ref inner_cond(m_manager); - m_subst(m_cond, m_renaming_for_inner_rel.size(), m_renaming_for_inner_rel.c_ptr(), - inner_cond); + expr_ref inner_cond = m_subst(m_cond, m_renaming_for_inner_rel.size(), m_renaming_for_inner_rel.c_ptr()); m_rel_filter = rmgr.mk_filter_interpreted_fn(*inner, to_app(inner_cond)); } (*m_rel_filter)(*inner); @@ -1411,11 +1408,10 @@ namespace datalog { //create the condition with table values substituted in and relation values properly renamed expr_ref inner_cond(m_manager); - m_subst(m_cond, m_renaming_for_inner_rel.size(), m_renaming_for_inner_rel.c_ptr(), - inner_cond); + inner_cond = m_subst(m_cond, m_renaming_for_inner_rel.size(), m_renaming_for_inner_rel.c_ptr()); relation_base * new_rel = old_rel.clone(); - + scoped_ptr filter = rmgr.mk_filter_interpreted_fn(*new_rel, to_app(inner_cond)); (*filter)(*new_rel); diff --git a/src/muz/rel/dl_mk_explanations.cpp b/src/muz/rel/dl_mk_explanations.cpp index 1a1a47f74..f69c1adc1 100644 --- a/src/muz/rel/dl_mk_explanations.cpp +++ b/src/muz/rel/dl_mk_explanations.cpp @@ -471,8 +471,7 @@ namespace datalog { SASSERT(!r.is_undefined(i) || !contains_var(m_new_rule, i)); subst_arg[ofs-i] = r.m_data.get(i); } - expr_ref res(m_manager); - m_subst(m_new_rule, subst_arg.size(), subst_arg.c_ptr(), res); + expr_ref res = m_subst(m_new_rule, subst_arg.size(), subst_arg.c_ptr()); r.m_data[m_col_idx] = to_app(res); } }; diff --git a/src/muz/rel/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp index ac3d30e9e..f89b2150c 100644 --- a/src/muz/rel/dl_mk_simple_joins.cpp +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -64,7 +64,7 @@ namespace datalog { SASSERT(m_consumers > 0); cost amortized = m_total_cost/m_consumers; if (m_stratified) { - return amortized * ( (amortized>0) ? (1/16.0f) : 16.0f); + return amortized * ( (amortized > 0) ? (1/16.0f) : 16.0f); } else { return amortized; @@ -85,29 +85,29 @@ namespace datalog { m_src_stratum = std::max(pl.get_stratum(t1->get_decl()), pl.get_stratum(t2->get_decl())); } m_rules.push_back(r); - if (pl.m_rules_content.find(r).size()>2) { + if (pl.m_rules_content.find(r).size() > 2) { m_consumers++; } if (m_stratified) { unsigned head_stratum = pl.get_stratum(r->get_decl()); - SASSERT(head_stratum>=m_src_stratum); - if (head_stratum==m_src_stratum) { - m_stratified = false; - } + SASSERT(head_stratum >= m_src_stratum); + m_stratified = (head_stratum > m_src_stratum); } idx_set_union(m_all_nonlocal_vars, non_local_vars_normalized); + TRACE("dl", tout << "all-nonlocal: " << m_all_nonlocal_vars << "\n";); } + /** \brief Remove rule from the pair record. Return true if no rules remain in the pair, and so it should be removed. */ bool remove_rule(rule * r, unsigned original_length) { VERIFY( remove_from_vector(m_rules, r) ); - if (original_length>2) { - SASSERT(m_consumers>0); + if (original_length > 2) { + SASSERT(m_consumers > 0); m_consumers--; } - SASSERT(!m_rules.empty() || m_consumers==0); + SASSERT(!m_rules.empty() || m_consumers == 0); return m_rules.empty(); } private: @@ -146,70 +146,60 @@ namespace datalog { { } - ~join_planner() - { - cost_map::iterator it = m_costs.begin(); - cost_map::iterator end = m_costs.end(); - for (; it != end; ++it) { - dealloc(it->m_value); + ~join_planner() { + for (auto & kv : m_costs) { + dealloc(kv.m_value); } m_costs.reset(); } + private: void get_normalizer(app * t, unsigned & next_var, expr_ref_vector & result) const { - SASSERT(result.size()>0); + SASSERT(!result.empty()); unsigned res_ofs = result.size()-1; - unsigned n=t->get_num_args(); - for(unsigned i=0; iget_arg(i))); - var * v = to_var(t->get_arg(i)); - unsigned var_idx = v->get_idx(); - if (result[res_ofs-var_idx]==nullptr) { - result[res_ofs-var_idx]=m.mk_var(next_var, v->get_sort()); - next_var++; + for (expr* arg : *t) { + unsigned var_idx = to_var(arg)->get_idx(); + if (!result.get(res_ofs - var_idx)) { + result[res_ofs - var_idx] = m.mk_var(next_var++, m.get_sort(arg)); } } } - void get_normalizer(app * t1, app * t2, expr_ref_vector & result) const { - SASSERT(result.empty()); - if (t1->get_num_args()==0 && t2->get_num_args()==0) { - return; //nothing to normalize + expr_ref_vector get_normalizer(app * t1, app * t2) const { + expr_ref_vector result(m); + if (t1->get_num_args() == 0 && t2->get_num_args() == 0) { + return result; //nothing to normalize } SASSERT(!t1->is_ground() || !t2->is_ground()); unsigned max_var_idx = 0; - { - var_idx_set& orig_var_set = rm.collect_vars(t1, t2); - var_idx_set::iterator ovit = orig_var_set.begin(); - var_idx_set::iterator ovend = orig_var_set.end(); - for(; ovit!=ovend; ++ovit) { - unsigned var_idx = *ovit; - if (var_idx>max_var_idx) { - max_var_idx = var_idx; - } + + var_idx_set& orig_var_set = rm.collect_vars(t1, t2); + for (unsigned var_idx : orig_var_set) { + if (var_idx>max_var_idx) { + max_var_idx = var_idx; } } - if (t1->get_decl()!=t2->get_decl()) { - if (t1->get_decl()->get_id()get_decl()->get_id()) { + if (t1->get_decl() != t2->get_decl()) { + if (t1->get_decl()->get_id() < t2->get_decl()->get_id()) { std::swap(t1, t2); } } else { - int_vector norm1(max_var_idx+1, -1); - int_vector norm2(max_var_idx+1, -1); - unsigned n=t1->get_num_args(); - SASSERT(n==t2->get_num_args()); - for(unsigned i=0; iget_num_args(); + SASSERT(n == t2->get_num_args()); + for (unsigned i = 0; i < n; ++i) { //We assume that the mk_simple_joins transformer is applied after mk_filter_rules, //so the only literals which appear in pairs are the ones that contain only variables. var * v1 = to_var(t1->get_arg(i)); var * v2 = to_var(t2->get_arg(i)); - if (v1->get_sort()!=v2->get_sort()) { + if (v1->get_sort() != v2->get_sort()) { //different sorts mean we can distinguish the two terms - if (v1->get_sort()->get_id()get_sort()->get_id()) { + if (v1->get_sort()->get_id() < v2->get_sort()->get_id()) { std::swap(t1, t2); } break; @@ -218,36 +208,37 @@ namespace datalog { unsigned v2_idx = v2->get_idx(); //since the rules already went through the mk_filter_rules transformer, //variables must be linear - SASSERT(norm1[v1_idx]==-1); - SASSERT(norm2[v2_idx]==-1); + SASSERT(norm1[v1_idx] == -1); + SASSERT(norm2[v2_idx] == -1); - if (norm2[v1_idx]!=norm1[v2_idx]) { + if (norm2[v1_idx] != norm1[v2_idx]) { //now we can distinguish the two terms - if (norm2[v1_idx](nullptr)); + result.resize(max_var_idx + 1, static_cast(nullptr)); unsigned next_var = 0; get_normalizer(t1, next_var, result); get_normalizer(t2, next_var, result); + return result; } + app_pair get_key(app * t1, app * t2) { - expr_ref_vector norm_subst(m); - get_normalizer(t1, t2, norm_subst); + expr_ref_vector norm_subst = get_normalizer(t1, t2); expr_ref t1n_ref(m); expr_ref t2n_ref(m); - m_var_subst(t1, norm_subst.size(), norm_subst.c_ptr(), t1n_ref); - m_var_subst(t2, norm_subst.size(), norm_subst.c_ptr(), t2n_ref); + t1n_ref = m_var_subst(t1, norm_subst.size(), norm_subst.c_ptr()); + t2n_ref = m_var_subst(t2, norm_subst.size(), norm_subst.c_ptr()); app * t1n = to_app(t1n_ref); app * t2n = to_app(t2n_ref); if (t1n->get_id() > t2n->get_id()) { @@ -256,6 +247,7 @@ namespace datalog { m_pinned.push_back(t1n); m_pinned.push_back(t2n); + TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << " |-> " << t1n_ref << " " << t2n_ref << "\n";); return app_pair(t1n, t2n); } @@ -267,30 +259,25 @@ namespace datalog { by the time of a call to this function */ void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) { - SASSERT(t1!=t2); + SASSERT (t1!=t2); cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), nullptr); pair_info * & ptr_inf = e->get_data().m_value; - if (ptr_inf==nullptr) { + if (ptr_inf == nullptr) { ptr_inf = alloc(pair_info); } pair_info & inf = *ptr_inf; - expr_ref_vector normalizer(m); - get_normalizer(t1, t2, normalizer); + expr_ref_vector normalizer = get_normalizer(t1, t2); unsigned norm_ofs = normalizer.size()-1; var_idx_set normalized_vars; - var_idx_set::iterator vit = non_local_vars.begin(); - var_idx_set::iterator vend = non_local_vars.end(); - for(; vit!=vend; ++vit) { - unsigned norm_var = to_var(normalizer.get(norm_ofs-*vit))->get_idx(); + for (auto idx : non_local_vars) { + unsigned norm_var = to_var(normalizer.get(norm_ofs - idx))->get_idx(); normalized_vars.insert(norm_var); } inf.add_rule(*this, t1, t2, r, normalized_vars, non_local_vars); TRACE("dl", tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << " "; - vit = non_local_vars.begin(); - for (; vit != vend; ++vit) tout << *vit << " "; - tout << "\n"; + tout << non_local_vars << "\n"; r->display(m_context, tout); if (inf.can_be_joined()) tout << "cost: " << inf.get_cost() << "\n";); @@ -298,8 +285,7 @@ namespace datalog { void remove_rule_from_pair(app_pair key, rule * r, unsigned original_len) { pair_info * ptr = nullptr; - if (m_costs.find(key, ptr) && ptr && - ptr->remove_rule(r, original_len)) { + if (m_costs.find(key, ptr) && ptr && ptr->remove_rule(r, original_len)) { SASSERT(ptr->m_rules.empty()); m_costs.remove(key); dealloc(ptr); @@ -309,28 +295,32 @@ namespace datalog { void register_rule(rule * r) { rule_counter counter; counter.count_rule_vars(r, 1); + TRACE("dl", tout << "counter: "; for (auto const& kv: counter) tout << kv.m_key << ": " << kv.m_value << " "; tout << "\n";); ptr_vector & rule_content = m_rules_content.insert_if_not_there2(r, ptr_vector())->get_data().m_value; SASSERT(rule_content.empty()); - unsigned pos_tail_size=r->get_positive_tail_size(); - for(unsigned i=0; idisplay(m_context, tout << "register ");); + + unsigned pos_tail_size = r->get_positive_tail_size(); + for (unsigned i = 0; i < pos_tail_size; i++) { rule_content.push_back(r->get_tail(i)); } - for(unsigned i=0; i+1 < pos_tail_size; i++) { + for (unsigned i = 0; i+1 < pos_tail_size; i++) { app * t1 = r->get_tail(i); var_idx_set t1_vars = rm.collect_vars(t1); counter.count_vars(t1, -1); //temporarily remove t1 variables from counter - for(unsigned j=i+1; jget_tail(j); counter.count_vars(t2, -1); //temporarily remove t2 variables from counter - var_idx_set scope_vars = rm.collect_vars(t2); - scope_vars |= t1_vars; + var_idx_set t2_vars = rm.collect_vars(t2); + t2_vars |= t1_vars; var_idx_set non_local_vars; counter.collect_positive(non_local_vars); counter.count_vars(t2, 1); //restore t2 variables in counter - set_intersection(non_local_vars, scope_vars); + set_intersection(non_local_vars, t2_vars); + TRACE("dl", tout << "non-local vars: " << non_local_vars << "\n";); register_pair(t1, t2, r, non_local_vars); } counter.count_vars(t1, 1); //restore t1 variables in counter @@ -338,11 +328,10 @@ namespace datalog { } bool extract_argument_info(unsigned var_idx, app * t, expr_ref_vector & args, - ptr_vector & domain) { - unsigned n=t->get_num_args(); - for(unsigned i=0; iget_arg(i)); - if (v->get_idx()==var_idx) { + ptr_vector & domain) { + for (expr* arg : *t) { + var * v = to_var(arg); + if (v->get_idx() == var_idx) { args.push_back(v); domain.push_back(m.get_sort(v)); return true; @@ -354,33 +343,27 @@ namespace datalog { void join_pair(app_pair pair_key) { app * t1 = pair_key.first; app * t2 = pair_key.second; - pair_info* infp = nullptr; - if (!m_costs.find(pair_key, infp) || !infp) { - UNREACHABLE(); - return; - } - pair_info & inf = *infp; + pair_info & inf = *m_costs[pair_key]; SASSERT(!inf.m_rules.empty()); - var_idx_set & output_vars = inf.m_all_nonlocal_vars; + var_idx_set const & output_vars = inf.m_all_nonlocal_vars; expr_ref_vector args(m); ptr_vector domain; unsigned arity = output_vars.num_elems(); - idx_set::iterator ovit=output_vars.begin(); - idx_set::iterator ovend=output_vars.end(); - //TODO: improve quadratic complexity - for(;ovit!=ovend;++ovit) { - unsigned var_idx=*ovit; - - bool found=extract_argument_info(var_idx, t1, args, domain); + for (unsigned var_idx : output_vars) { + bool found = extract_argument_info(var_idx, t1, args, domain); if (!found) { - found=extract_argument_info(var_idx, t2, args, domain); + found = extract_argument_info(var_idx, t2, args, domain); } SASSERT(found); } + TRACE("dl", + tout << mk_pp(t1, m) << " " << mk_pp(t2, m) << " arity: " << arity << "\n"; + tout << "output: " << output_vars << "\n"; + tout << "args: " << args << "\n";); - SASSERT(args.size()==arity); - SASSERT(domain.size()==arity); + SASSERT(args.size() == arity); + SASSERT(domain.size() == arity); rule * one_parent = inf.m_rules.back(); @@ -415,8 +398,7 @@ namespace datalog { rule_hashtable processed_rules; rule_vector rules(inf.m_rules); - for (unsigned i = 0; i < rules.size(); ++i) { - rule* r = rules[i]; + for (rule * r : rules) { if (!processed_rules.contains(r)) { apply_binary_rule(r, pair_key, head); processed_rules.insert(r); @@ -425,33 +407,34 @@ namespace datalog { // SASSERT(!m_costs.contains(pair_key)); } - void replace_edges(rule * r, const ptr_vector & removed_tails, - const ptr_vector & added_tails0, const ptr_vector & rule_content) { + void replace_edges(rule * r, const app_ref_vector & removed_tails, + const app_ref_vector & added_tails0, const ptr_vector & rule_content) { SASSERT(removed_tails.size()>=added_tails0.size()); unsigned len = rule_content.size(); unsigned original_len = len+removed_tails.size()-added_tails0.size(); - ptr_vector added_tails(added_tails0); //we need a copy since we'll be modifying it + app_ref_vector added_tails(added_tails0); //we need a copy since we'll be modifying it + TRACE("dl", tout << added_tails << "\n";); unsigned rt_sz = removed_tails.size(); //remove edges between removed tails - for(unsigned i=0; iget_tail_size(); - unsigned pos_tail_size=r->get_positive_tail_size(); + unsigned tail_size = r->get_tail_size(); + unsigned pos_tail_size = r->get_positive_tail_size(); - for(unsigned i=pos_tail_size; iget_tail(i), 1); } - for(unsigned i=0; i & rule_content = m_rules_content.find(r); unsigned len = rule_content.size(); - if (len==1) { + if (len == 1) { return; } + TRACE("dl", + r->display(m_context, tout << "rule "); + tout << "pair: " << mk_pp(t1, m) << " " << mk_pp(t2, m) << "\n"; + tout << mk_pp(t_new, m) << "\n"; + tout << "all-non-local: " << m_costs[pair_key]->m_all_nonlocal_vars << "\n"; + for (app* a : rule_content) tout << mk_pp(a, m) << " "; tout << "\n";); + + rule_counter counter; + counter.count_rule_vars(r, 1); func_decl * t1_pred = t1->get_decl(); func_decl * t2_pred = t2->get_decl(); - ptr_vector removed_tails; - ptr_vector added_tails; - for(unsigned i1=0; i1get_decl()!=t1_pred) { + if (rt1->get_decl() != t1_pred) { continue; } + var_idx_set rt1_vars = rm.collect_vars(rt1); + counter.count_vars(rt1, -1); + + + var_idx_set t1_vars = rm.collect_vars(t1); unsigned i2start = (t1_pred==t2_pred) ? (i1+1) : 0; - for(unsigned i2=i2start; i2get_decl()!=t2_pred) { + if (i1 == i2 || rt2->get_decl() != t2_pred) { continue; } - if (get_key(rt1, rt2)!=pair_key) { + if (get_key(rt1, rt2) != pair_key) { continue; - } - expr_ref_vector normalizer(m); - get_normalizer(rt1, rt2, normalizer); + } + expr_ref_vector denormalizer(m); + expr_ref_vector normalizer = get_normalizer(rt1, rt2); reverse_renaming(m, normalizer, denormalizer); expr_ref new_transf(m); - m_var_subst(t_new, denormalizer.size(), denormalizer.c_ptr(), new_transf); + new_transf = m_var_subst(t_new, denormalizer.size(), denormalizer.c_ptr()); + var_idx_set transf_vars = rm.collect_vars(new_transf); + TRACE("dl", tout << mk_pp(rt1, m) << " " << mk_pp(rt2, m) << " -> " << new_transf << "\n";); + counter.count_vars(rt2, -1); + var_idx_set rt2_vars = rm.collect_vars(rt2); + var_idx_set tr_vars = rm.collect_vars(new_transf); + rt2_vars |= rt1_vars; + var_idx_set non_local_vars; + counter.collect_positive(non_local_vars); + set_intersection(non_local_vars, rt2_vars); + counter.count_vars(rt2, +1); + // require that tr_vars contains non_local_vars + TRACE("dl", tout << "non-local : " << non_local_vars << " tr_vars " << tr_vars << " rt12_vars " << rt2_vars << "\n";); + if (!non_local_vars.subset_of(tr_vars)) { + expr_ref_vector normalizer2 = get_normalizer(rt2, rt1); + TRACE("dl", tout << normalizer << "\nnorm\n" << normalizer2 << "\n";); + denormalizer.reset(); + reverse_renaming(m, normalizer2, denormalizer); + new_transf = m_var_subst(t_new, denormalizer.size(), denormalizer.c_ptr()); + SASSERT(non_local_vars.subset_of(rm.collect_vars(new_transf))); + TRACE("dl", tout << mk_pp(rt2, m) << " " << mk_pp(rt1, m) << " -> " << new_transf << "\n";); + } app * new_lit = to_app(new_transf); - m_pinned.push_back(new_lit); - rule_content[i1]=new_lit; - rule_content[i2]=rule_content.back(); + rule_content[i1] = new_lit; + rule_content[i2] = rule_content.back(); rule_content.pop_back(); len--; //here the bound of both loops changes!!! removed_tails.push_back(rt1); removed_tails.push_back(rt2); added_tails.push_back(new_lit); - //this exits the inner loop, the outer one continues in case there will - //be other matches + // this exits the inner loop, the outer one continues in case there will + // be other matches break; } + counter.count_vars(rt1, 1); } SASSERT(!removed_tails.empty()); SASSERT(!added_tails.empty()); m_modified_rules = true; + TRACE("dl", tout << "replace rule content\n";); replace_edges(r, removed_tails, added_tails, rule_content); } cost get_domain_size(func_decl * pred, unsigned arg_index) const { relation_sort sort = pred->get_domain(arg_index); return static_cast(m_context.get_sort_size_estimate(sort)); - //unsigned sz; - //if (!m_context.get_sort_size(sort, sz)) { - // sz=UINT_MAX; - //} - //return static_cast(sz); } unsigned get_stratum(func_decl * pred) const { @@ -569,7 +585,7 @@ namespace datalog { cost estimate_size(app * t) const { func_decl * pred = t->get_decl(); - unsigned n=pred->get_arity(); + unsigned n = pred->get_arity(); rel_context_base* rel = m_context.get_rel_context(); if (!rel) { return cost(1); @@ -579,10 +595,10 @@ namespace datalog { || rm.is_saturated(pred)) { SASSERT(rm.try_get_relation(pred)); //if it is saturated, it should exist unsigned rel_size_int = rel->get_relation(pred).get_size_estimate_rows(); - if (rel_size_int!=0) { + if (rel_size_int != 0) { cost rel_size = static_cast(rel_size_int); cost curr_size = rel_size; - for(unsigned i=0; iget_arg(i))) { curr_size /= get_domain_size(pred, i); } @@ -591,7 +607,7 @@ namespace datalog { } } cost res = 1; - for(unsigned i=0; iget_arg(i))) { res *= get_domain_size(pred, i); } @@ -607,7 +623,7 @@ namespace datalog { vi.populate(t1, t2); unsigned n = vi.size(); // remove contributions from joined columns. - for(unsigned i=0; iget_arg(arg_index1))); @@ -634,24 +650,6 @@ namespace datalog { cost res = estimate_size(t1)*estimate_size(t2)/ inters_size; // (inters_size*inters_size); //cost res = -inters_size; - /*unsigned t1_strat = get_stratum(t1_pred); - SASSERT(t1_strat<=m_head_stratum); - if (t1_strat0) { - res /= 2; - } - else { - res *= 2; - } - } - }*/ - TRACE("report_costs", display_predicate(m_context, t1, tout); display_predicate(m_context, t2, tout); @@ -665,16 +663,14 @@ namespace datalog { bool found = false; cost best_cost; - cost_map::iterator it = m_costs.begin(); - cost_map::iterator end = m_costs.end(); - for(; it!=end; ++it) { - app_pair key = it->m_key; - pair_info & inf = *it->m_value; + for (auto const& kv : m_costs) { + app_pair key = kv.m_key; + pair_info & inf = *kv.m_value; if (!inf.can_be_joined()) { continue; } cost c = inf.get_cost(); - if (!found || cm_key; - ptr_vector content = rcit->m_value; - SASSERT(content.size()<=2); - if (content.size()==orig_r->get_positive_tail_size()) { + rule_set * result = alloc(rule_set, m_context); + for (auto& kv : m_rules_content) { + rule * orig_r = kv.m_key; + ptr_vector content = kv.m_value; + SASSERT(content.size() <= 2); + if (content.size() == orig_r->get_positive_tail_size()) { //rule did not change result->add_rule(orig_r); continue; @@ -720,7 +713,7 @@ namespace datalog { ptr_vector tail(content); svector negs(tail.size(), false); unsigned or_len = orig_r->get_tail_size(); - for(unsigned i=orig_r->get_positive_tail_size(); iget_positive_tail_size(); i < or_len; i++) { tail.push_back(orig_r->get_tail(i)); negs.push_back(orig_r->is_neg_tail(i)); } diff --git a/src/muz/rel/dl_relation_manager.cpp b/src/muz/rel/dl_relation_manager.cpp index ff12e66c9..89f7fdda3 100644 --- a/src/muz/rel/dl_relation_manager.cpp +++ b/src/muz/rel/dl_relation_manager.cpp @@ -1395,8 +1395,7 @@ namespace datalog { args.push_back(m_decl_util.mk_numeral(el, m_free_vars[i])); } - expr_ref ground(m_ast_manager); - m_vs(m_condition.get(), args.size(), args.c_ptr(), ground); + expr_ref ground = m_vs(m_condition.get(), args.size(), args.c_ptr()); m_simp(ground); return m_ast_manager.is_false(ground); diff --git a/src/muz/rel/dl_sieve_relation.cpp b/src/muz/rel/dl_sieve_relation.cpp index a5ad20059..c4c8959ef 100644 --- a/src/muz/rel/dl_sieve_relation.cpp +++ b/src/muz/rel/dl_sieve_relation.cpp @@ -85,7 +85,7 @@ namespace datalog { s.push_back(m.mk_var(idx, sig[i])); } get_inner().to_formula(tmp); - get_plugin().get_context().get_var_subst()(tmp, sz, s.c_ptr(), fml); + fml = get_plugin().get_context().get_var_subst()(tmp, sz, s.c_ptr()); } @@ -584,8 +584,7 @@ namespace datalog { } subst_vect[subst_ofs-i] = m.mk_var(r.m_sig2inner[i], sig[i]); } - expr_ref inner_cond(m); - get_context().get_var_subst()(condition, subst_vect.size(), subst_vect.c_ptr(), inner_cond); + expr_ref inner_cond = get_context().get_var_subst()(condition, subst_vect.size(), subst_vect.c_ptr()); relation_mutator_fn * inner_fun = get_manager().mk_filter_interpreted_fn(r.get_inner(), to_app(inner_cond)); if(!inner_fun) { diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index f3ceee9c8..693812a31 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -55,6 +55,8 @@ Notes: #include "muz/spacer/spacer_sat_answer.h" +#define WEAKNESS_MAX 65535 + namespace spacer { /// pob -- proof obligation @@ -66,8 +68,8 @@ pob::pob (pob* parent, pred_transformer& pt, m_binding(m_pt.get_ast_manager()), m_new_post (m_pt.get_ast_manager ()), m_level (level), m_depth (depth), - m_open (true), m_use_farkas (true), m_weakness(0), - m_blocked_lvl(0) { + m_open (true), m_use_farkas (true), m_in_queue(false), + m_weakness(0), m_blocked_lvl(0) { if (add_to_parent && m_parent) { m_parent->add_child(*this); } @@ -79,6 +81,7 @@ void pob::set_post(expr* post) { } void pob::set_post(expr* post, app_ref_vector const &binding) { + SASSERT(!is_in_queue()); normalize(post, m_post, m_pt.get_context().simplify_pob(), m_pt.get_context().use_euf_gen()); @@ -88,6 +91,7 @@ void pob::set_post(expr* post, app_ref_vector const &binding) { } void pob::inherit(pob const &p) { + SASSERT(!is_in_queue()); SASSERT(m_parent == p.m_parent); SASSERT(&m_pt == &p.m_pt); SASSERT(m_post == p.m_post); @@ -105,17 +109,10 @@ void pob::inherit(pob const &p) { m_derivation = nullptr; } -void pob::clean () { - if (m_new_post) { - m_post = m_new_post; - m_new_post.reset(); - } -} - void pob::close () { if (!m_open) { return; } - reset (); + m_derivation = nullptr; m_open = false; for (unsigned i = 0, sz = m_kids.size (); i < sz; ++i) { m_kids [i]->close(); } @@ -129,7 +126,15 @@ void pob::get_skolems(app_ref_vector &v) { } } - + std::ostream &pob::display(std::ostream &out, bool full) const { + out << pt().head()->get_name () + << " level: " << level() + << " depth: " << depth() + << " post_id: " << post()->get_id() + << (is_in_queue() ? " in_queue" : ""); + if (full) out << "\n" << m_post; + return out; + } // ---------------- // pob_queue @@ -137,17 +142,24 @@ void pob::get_skolems(app_ref_vector &v) { pob* pob_queue::top () { /// nothing in the queue - if (m_obligations.empty()) { return nullptr; } + if (m_data.empty()) { return nullptr; } /// top queue element is above max level - if (m_obligations.top()->level() > m_max_level) { return nullptr; } + if (m_data.top()->level() > m_max_level) { return nullptr; } /// top queue element is at the max level, but at a higher than base depth - if (m_obligations.top ()->level () == m_max_level && - m_obligations.top()->depth() > m_min_depth) { return nullptr; } + if (m_data.top ()->level () == m_max_level && + m_data.top()->depth() > m_min_depth) { return nullptr; } /// there is something good in the queue - return m_obligations.top ().get (); + return m_data.top (); } +void pob_queue::pop() { + pob *p = m_data.top(); + p->set_in_queue(false); + m_data.pop(); +} + + void pob_queue::set_root(pob& root) { m_root = &root; @@ -156,19 +168,28 @@ void pob_queue::set_root(pob& root) reset(); } -pob_queue::~pob_queue() {} - void pob_queue::reset() { - while (!m_obligations.empty()) { m_obligations.pop(); } - if (m_root) { m_obligations.push(m_root); } + while (!m_data.empty()) { + pob *p = m_data.top(); + m_data.pop(); + p->set_in_queue(false); + } + if (m_root) { + SASSERT(!m_root->is_in_queue()); + m_root->set_in_queue(true); + m_data.push(m_root.get()); + } } void pob_queue::push(pob &n) { - TRACE("pob_queue", - tout << "pob_queue::push(" << n.post()->get_id() << ")\n";); - m_obligations.push (&n); - n.get_context().new_pob_eh(&n); + if (!n.is_in_queue()) { + TRACE("pob_queue", + tout << "pob_queue::push(" << n.post()->get_id() << ")\n";); + n.set_in_queue(true); + m_data.push (&n); + n.get_context().new_pob_eh(&n); + } } // ---------------- @@ -266,6 +287,8 @@ pob *derivation::create_next_child(model &mdl) m_evars.reset(); pt().mbp(vars, m_trans, mdl, true, pt().get_context().use_ground_pob()); + CTRACE("spacer", !vars.empty(), + tout << "Failed to eliminate: " << vars << "\n";); m_evars.append (vars); vars.reset(); } @@ -295,6 +318,8 @@ pob *derivation::create_next_child(model &mdl) vars.append(m_evars); pt().mbp(vars, post, mdl, true, pt().get_context().use_ground_pob()); + CTRACE("spacer", !vars.empty(), + tout << "Failed to eliminate: " << vars << "\n";); //qe::reduce_array_selects (*mev.get_model (), post); } else { @@ -398,6 +423,8 @@ pob *derivation::create_next_child () this->pt().mbp(vars, m_trans, *mdl, true, this->pt().get_context().use_ground_pob()); // keep track of implicitly quantified variables + CTRACE("spacer", !vars.empty(), + tout << "Failed to eliminate: " << vars << "\n";); m_evars.append (vars); vars.reset(); } @@ -462,8 +489,11 @@ void derivation::premise::set_summary (expr * summary, bool must, lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : m_ref_count(0), m(manager), m_body(body, m), m_cube(m), - m_zks(m), m_bindings(m), m_lvl(lvl), m_init_lvl(m_lvl), - m_pob(nullptr), m_ctp(nullptr), m_external(false), m_bumped(0) { + m_zks(m), m_bindings(m), + m_pob(nullptr), m_ctp(nullptr), + m_lvl(lvl), m_init_lvl(m_lvl), + m_bumped(0), m_weakness(WEAKNESS_MAX), + m_external(false), m_blocked(false) { SASSERT(m_body); normalize(m_body, m_body); } @@ -471,8 +501,11 @@ lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : lemma::lemma(pob_ref const &p) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), - m_zks(m), m_bindings(m), m_lvl(p->level()), m_init_lvl(m_lvl), - m_pob(p), m_ctp(nullptr), m_external(false), m_bumped(0) { + m_zks(m), m_bindings(m), + m_pob(p), m_ctp(nullptr), + m_lvl(p->level()), m_init_lvl(m_lvl), + m_bumped(0), m_weakness(p->weakness()), + m_external(false), m_blocked(false) { SASSERT(m_pob); m_pob->get_skolems(m_zks); add_binding(m_pob->get_binding()); @@ -482,8 +515,11 @@ lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : m_ref_count(0), m(p->get_ast_manager()), m_body(m), m_cube(m), - m_zks(m), m_bindings(m), m_lvl(p->level()), m_init_lvl(m_lvl), - m_pob(p), m_ctp(nullptr), m_external(false), m_bumped(0) + m_zks(m), m_bindings(m), + m_pob(p), m_ctp(nullptr), + m_lvl(p->level()), m_init_lvl(m_lvl), + m_bumped(0), m_weakness(p->weakness()), + m_external(false), m_blocked(false) { if (m_pob) { m_pob->get_skolems(m_zks); @@ -510,7 +546,8 @@ void lemma::mk_expr_core() { SASSERT(!m_cube.empty()); m_body = ::mk_and(m_cube); // normalize works better with a cube - normalize(m_body, m_body); + normalize(m_body, m_body, false /* no simplify bounds */, false /* term_graph */); + m_body = ::push_not(m_body); if (!m_zks.empty() && has_zk_const(m_body)) { @@ -526,7 +563,7 @@ void lemma::mk_expr_core() { sorts.push_back(get_sort(zks.get(i))); names.push_back(zks.get(i)->get_decl()->get_name()); } - m_body = m.mk_quantifier(true, zks.size(), + m_body = m.mk_quantifier(forall_k, zks.size(), sorts.c_ptr(), names.c_ptr(), m_body, 15, symbol(m_body->get_id())); @@ -633,7 +670,7 @@ void lemma::instantiate(expr * const * exprs, expr_ref &result, expr *e) { expr *body = to_quantifier(lem)->get_expr(); unsigned num_decls = to_quantifier(lem)->get_num_decls(); var_subst vs(m, false); - vs(body, num_decls, exprs, result); + result = vs(body, num_decls, exprs); } void lemma::set_level (unsigned lvl) { @@ -820,7 +857,7 @@ const datalog::rule *pred_transformer::find_rule(model &model) { for (auto &kv : m_pt_rules) { app *tag = kv.m_value->tag(); - if (model.eval(tag->get_decl(), val) && m.is_true(val)) { + if (model.is_true_decl(tag->get_decl())) { return &kv.m_value->rule(); } } @@ -911,7 +948,7 @@ void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only) << " " << head ()->get_name () << " " << mk_pp (l, m) << "\n";); - STRACE ("spacer.expand-add", + STRACE ("spacer_progress", tout << "** add-lemma: " << pp_level (lvl) << " " << head ()->get_name () << " " << mk_epp (l, m) << "\n"; @@ -1162,7 +1199,16 @@ expr_ref pred_transformer::get_origin_summary (model &mdl, } // bail out of if the model is insufficient - if (!mdl.is_true(summary)) return expr_ref(m); + // (skip quantified lemmas cause we can't validate them in the model) + // TBD: for quantified lemmas use current instances + flatten_and(summary); + for (auto *s : summary) { + if (!is_quantifier(s) && !mdl.is_true(s)) { + TRACE("spacer", tout << "Summary not true in the model: " + << mk_pp(s, m) << "\n";); + return expr_ref(m); + } + } // -- pick an implicant expr_ref_vector lits(m); @@ -1254,7 +1300,7 @@ bool pred_transformer::is_qblocked (pob &n) { // solver->get_itp_core(core); // expr_ref c(m); // c = mk_and(core); - // STRACE("spacer.expand-add", tout << "core: " << mk_epp(c,m) << "\n";); + // STRACE("spacer_progress", tout << "core: " << mk_epp(c,m) << "\n";); // } return res == l_false; } @@ -1347,7 +1393,7 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, if (is_sat == l_true || is_sat == l_undef) { if (core) { core->reset(); } - if (model) { + if (model && model->get()) { r = find_rule(**model, is_concrete, reach_pred_used, num_reuse_reach); TRACE ("spacer", tout << "reachable " << "is_concrete " << is_concrete << " rused: "; @@ -1387,7 +1433,12 @@ bool pred_transformer::is_ctp_blocked(lemma *lem) { // -- find rule of the ctp const datalog::rule *r; r = find_rule(*ctp); - if (r == nullptr) {return false;} + if (r == nullptr) { + // no rules means lemma is blocked forever because + // it does not satisfy some derived facts + lem->set_blocked(true); + return true; + } // -- find predicates along the rule find_predecessors(*r, m_predicates); @@ -1410,6 +1461,8 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, unsigned& solver_level, expr_ref_vector* core) { + if (lem->is_blocked()) return false; + m_stats.m_num_is_invariant++; if (is_ctp_blocked(lem)) { m_stats.m_num_ctp_blocked++; @@ -1451,7 +1504,7 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, SASSERT (level <= solver_level); } else if (r == l_true) { - // optionally remove unused symbols from the model + // TBD: optionally remove unused symbols from the model if (mdl_ref_ptr) {lem->set_ctp(*mdl_ref_ptr);} } else {lem->reset_ctp();} @@ -1621,9 +1674,7 @@ void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule) ground_free_vars(trans, var_reprs, aux_vars, ut_size == 0); SASSERT(is_all_non_null(var_reprs)); - expr_ref tmp(m); - var_subst(m, false)(trans, var_reprs.size (), - (expr*const*)var_reprs.c_ptr(), tmp); + expr_ref tmp = var_subst(m, false)(trans, var_reprs.size (), (expr*const*)var_reprs.c_ptr()); flatten_and (tmp, side); trans = mk_and(side); side.reset (); @@ -1632,7 +1683,10 @@ void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule) // rewrite and simplify th_rewriter rw(m); rw(trans); - if (ctx.blast_term_ite()) {blast_term_ite(trans, 3); rw(trans);} + if (ctx.blast_term_ite_inflation() > 0) { + blast_term_ite(trans, ctx.blast_term_ite_inflation()); + rw(trans); + } TRACE("spacer_init_rule", tout << mk_pp(trans, m) << "\n";); // allow quantifiers in init rule @@ -2122,25 +2176,17 @@ void pred_transformer::frames::simplify_formulas () /// pred_transformer::pobs -pob* pred_transformer::pobs::mk_pob(pob *parent, - unsigned level, unsigned depth, - expr *post, app_ref_vector const &b) { - - if (!m_pt.ctx.reuse_pobs()) { - pob* n = alloc(pob, parent, m_pt, level, depth); - n->set_post(post, b); - return n; - } - +pob* pred_transformer::pob_manager::mk_pob(pob *parent, + unsigned level, unsigned depth, + expr *post, + app_ref_vector const &b) { // create a new pob and set its post to normalize it pob p(parent, m_pt, level, depth, false); p.set_post(post, b); if (m_pobs.contains(p.post())) { - auto &buf = m_pobs[p.post()]; - for (unsigned i = 0, sz = buf.size(); i < sz; ++i) { - pob *f = buf.get(i); - if (f->parent() == parent) { + for (auto *f : m_pobs[p.post()]) { + if (f->parent() == parent && !f->is_in_queue()) { f->inherit(p); return f; } @@ -2162,7 +2208,21 @@ pob* pred_transformer::pobs::mk_pob(pob *parent, return n; } - +pob* pred_transformer::pob_manager::find_pob(pob* parent, expr *post) { + pob p(parent, m_pt, 0, 0, false); + p.set_post(post); + pob *res = nullptr; + if (m_pobs.contains(p.post())) { + for (auto *f : m_pobs[p.post()]) { + if (f->parent() == parent) { + // try to find pob not already in queue + if (!f->is_in_queue()) return f; + res = f; + } + } + } + return res; +} // ---------------- @@ -2208,8 +2268,7 @@ void context::updt_params() { m_use_euf_gen = m_params.spacer_use_euf_gen(); m_use_ctp = m_params.spacer_ctp(); m_use_inc_clause = m_params.spacer_use_inc_clause(); - m_blast_term_ite = m_params.spacer_blast_term_ite(); - m_reuse_pobs = m_params.spacer_reuse_pobs(); + m_blast_term_ite_inflation = m_params.spacer_blast_term_ite_inflation(); m_use_ind_gen = m_params.spacer_use_inductive_generalizer(); m_use_array_eq_gen = m_params.spacer_use_array_eq_generalizer(); m_validate_lemmas = m_params.spacer_validate_lemmas(); @@ -3003,7 +3062,7 @@ lbool context::solve_core (unsigned from_lvl) m_stats.m_max_depth = std::max(m_stats.m_max_depth, lvl); IF_VERBOSE(1,verbose_stream() << "Entering level "<< lvl << "\n";); - STRACE("spacer.expand-add", tout << "\n* LEVEL " << lvl << "\n";); + STRACE("spacer_progress", tout << "\n* LEVEL " << lvl << "\n";); IF_VERBOSE(1, if (m_params.print_statistics ()) { @@ -3057,27 +3116,19 @@ bool context::check_reachability () } SASSERT (m_pob_queue.top ()); - // -- remove all closed nodes and updated all dirty nodes + // -- remove all closed nodes // -- this is necessary because there is no easy way to // -- remove nodes from the priority queue. - while (m_pob_queue.top ()->is_closed () || - m_pob_queue.top()->is_dirty()) { - pob_ref n = m_pob_queue.top (); - m_pob_queue.pop (); - if (n->is_closed()) { - IF_VERBOSE (1, - verbose_stream () << "Deleting closed node: " - << n->pt ().head ()->get_name () - << "(" << n->level () << ", " << n->depth () << ")" - << " " << n->post ()->get_id () << "\n";); - if (m_pob_queue.is_root(*n)) { return true; } - SASSERT (m_pob_queue.top ()); - } else if (n->is_dirty()) { - n->clean (); - // -- the node n might not be at the top after it is cleaned - m_pob_queue.push (*n); - } else - { UNREACHABLE(); } + while (m_pob_queue.top ()->is_closed ()) { + pob_ref n = m_pob_queue.top(); + m_pob_queue.pop(); + IF_VERBOSE (1, + verbose_stream () << "Deleting closed node: " + << n->pt ().head ()->get_name () + << "(" << n->level () << ", " << n->depth () << ")" + << " " << n->post ()->get_id () << "\n";); + if (m_pob_queue.is_root(*n)) {return true;} + SASSERT (m_pob_queue.top ()); } SASSERT (m_pob_queue.top ()); @@ -3171,6 +3222,7 @@ bool context::is_reachable(pob &n) unsigned num_reuse_reach = 0; unsigned saved = n.level (); + // TBD: don't expose private field n.m_level = infty_level (); lbool res = n.pt().is_reachable(n, nullptr, &mdl, uses_level, is_concrete, r, @@ -3261,7 +3313,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) << " fvsz: " << n.get_free_vars_size() << "\n" << mk_pp(n.post(), m) << "\n";); - STRACE ("spacer.expand-add", + STRACE ("spacer_progress", tout << "** expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" @@ -3307,7 +3359,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) } if (/* XXX noop */ n.pt().is_qblocked(n)) { - STRACE("spacer.expand-add", + STRACE("spacer_progress", tout << "This pob can be blocked by instantiation\n";); } @@ -3415,10 +3467,21 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) // Optionally update the node to be the negation of the lemma if (v && m_use_lemma_as_pob) { - n.new_post (mk_and(lemma->get_cube())); - n.set_farkas_generalizer (false); - // XXX hack while refactoring is in progress - n.clean(); + expr_ref c(m); + c = mk_and(lemma->get_cube()); + // check that the post condition is different + if (c != n.post()) { + pob *f = n.pt().find_pob(n.parent(), c); + // skip if a similar pob is already in the queue + if (f != &n && (!f || !f->is_in_queue())) { + f = n.pt().mk_pob(n.parent(), n.level(), + n.depth(), c, n.get_binding()); + SASSERT(!f->is_in_queue()); + f->inc_level(); + //f->set_farkas_generalizer(false); + out.push_back(f); + } + } } // schedule the node to be placed back in the queue @@ -3436,7 +3499,7 @@ lbool context::expand_pob(pob& n, pob_ref_buffer &out) } case l_undef: // something went wrong - if (n.weakness() < 100 /* MAX_WEAKENSS */) { + if (n.weakness() < 10 /* MAX_WEAKENSS */) { bool has_new_child = false; SASSERT(m_weak_abs); m_stats.m_expand_pob_undef++; @@ -3491,7 +3554,7 @@ bool context::propagate(unsigned min_prop_lvl, if (m_simplify_formulas_pre) { simplify_formulas(); } - STRACE ("spacer.expand-add", tout << "Propagating\n";); + STRACE ("spacer_progress", tout << "Propagating\n";); IF_VERBOSE (1, verbose_stream () << "Propagating: " << std::flush;); @@ -3934,7 +3997,7 @@ bool context::is_inductive() { } /// pob_lt operator -inline bool pob_lt::operator() (const pob *pn1, const pob *pn2) const +inline bool pob_lt_proc::operator() (const pob *pn1, const pob *pn2) const { SASSERT (pn1); SASSERT (pn2); diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 37b035b98..48c27f96d 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -122,12 +122,14 @@ class lemma { expr_ref_vector m_cube; app_ref_vector m_zks; app_ref_vector m_bindings; + pob_ref m_pob; + model_ref m_ctp; // counter-example to pushing unsigned m_lvl; // current level of the lemma unsigned m_init_lvl; // level at which lemma was created - pob_ref m_pob; - model_ref m_ctp; // counter-example to pushing - bool m_external; - unsigned m_bumped; + unsigned m_bumped:16; + unsigned m_weakness:16; + unsigned m_external:1; + unsigned m_blocked:1; void mk_expr_core(); void mk_cube_core(); @@ -154,12 +156,15 @@ public: bool has_pob() {return m_pob;} pob_ref &get_pob() {return m_pob;} - inline unsigned weakness(); + unsigned weakness() {return m_weakness;} void add_skolem(app *zk, app* b); - inline void set_external(bool ext){m_external = ext;} - inline bool external() { return m_external;} + void set_external(bool ext){m_external = ext;} + bool external() { return m_external;} + + bool is_blocked() {return m_blocked;} + void set_blocked(bool v) {m_blocked=v;} bool is_inductive() const {return is_infty_level(m_lvl);} unsigned level () const {return m_lvl;} @@ -268,18 +273,31 @@ class pred_transformer { }; /** - manager of proof-obligations (pobs) + manager of proof-obligations (pob_manager) + + Pobs are determined uniquely by their post-condition and a parent pob. + They are managed by pob_manager and remain live through the + life of the manager */ - class pobs { + class pob_manager { + // a buffer that contains space for one pob and allocates more + // space if needed typedef ptr_buffer pob_buffer; + // Type for the map from post-conditions to pobs. The common + // case is that each post-condition corresponds to a single + // pob. Other cases are handled by expanding the buffer typedef obj_map expr2pob_buffer; + // parent predicate transformer pred_transformer &m_pt; + // map from post-conditions to pobs expr2pob_buffer m_pobs; + + // a store pob_ref_vector m_pinned; public: - pobs(pred_transformer &pt) : m_pt(pt) {} + pob_manager(pred_transformer &pt) : m_pt(pt) {} pob* mk_pob(pob *parent, unsigned level, unsigned depth, expr *post, app_ref_vector const &b); @@ -290,6 +308,7 @@ class pred_transformer { } unsigned size() const {return m_pinned.size();} + pob* find_pob(pob* parent, expr *post); }; class pt_rule { @@ -361,7 +380,7 @@ class pred_transformer { ptr_vector m_rules; // rules used to derive transformer scoped_ptr m_solver; // solver context ref m_reach_solver; // context for reachability facts - pobs m_pobs; // proof obligations created so far + pob_manager m_pobs; // proof obligations created so far frames m_frames; // frames with lemmas reach_fact_ref_vector m_reach_facts; // reach facts unsigned m_rf_init_sz; // number of reach fact from INIT @@ -481,6 +500,9 @@ public: expr *post, app_ref_vector const &b){ return m_pobs.mk_pob(parent, level, depth, post, b); } + pob* find_pob(pob *parent, expr *post) { + return m_pobs.find_pob(parent, post); + } pob* mk_pob(pob *parent, unsigned level, unsigned depth, expr *post) { @@ -548,6 +570,7 @@ public: * A proof obligation. */ class pob { + // TBD: remove this friend class context; unsigned m_ref_count; /// parent node @@ -562,14 +585,15 @@ class pob { /// new post to be swapped in for m_post expr_ref m_new_post; /// level at which to decide the post - unsigned m_level; - - unsigned m_depth; + unsigned m_level:16; + unsigned m_depth:16; /// whether a concrete answer to the post is found - bool m_open; + unsigned m_open:1; /// whether to use farkas generalizer to construct a lemma blocking this node - bool m_use_farkas; + unsigned m_use_farkas:1; + /// true if this pob is in pob_queue + unsigned m_in_queue:1; unsigned m_weakness; /// derivation representing the position of this node in the parent's rule @@ -584,17 +608,25 @@ class pob { // depth -> watch std::map m_expand_watches; unsigned m_blocked_lvl; + public: pob (pob* parent, pred_transformer& pt, unsigned level, unsigned depth=0, bool add_to_parent=true); ~pob() {if (m_parent) { m_parent->erase_child(*this); }} + // TBD: move into constructor and make private + void set_post(expr *post, app_ref_vector const &binding); + void set_post(expr *post); + unsigned weakness() {return m_weakness;} void bump_weakness() {m_weakness++;} void reset_weakness() {m_weakness=0;} - void inc_level () {m_level++; m_depth++;reset_weakness();} + void inc_level () { + SASSERT(!is_in_queue()); + m_level++; m_depth++;reset_weakness(); + } void inherit(pob const &p); void set_derivation (derivation *d) {m_derivation = d;} @@ -614,32 +646,25 @@ public: unsigned level () const { return m_level; } unsigned depth () const {return m_depth;} unsigned width () const {return m_kids.size();} - unsigned blocked_at(unsigned lvl=0){return (m_blocked_lvl = std::max(lvl, m_blocked_lvl)); } + unsigned blocked_at(unsigned lvl=0){ + return (m_blocked_lvl = std::max(lvl, m_blocked_lvl)); + } + bool is_in_queue() const {return m_in_queue;} + void set_in_queue(bool v) {m_in_queue = v;} bool use_farkas_generalizer () const {return m_use_farkas;} void set_farkas_generalizer (bool v) {m_use_farkas = v;} expr* post() const { return m_post.get (); } - void set_post(expr *post); - void set_post(expr *post, app_ref_vector const &binding); - - /// indicate that a new post should be set for the node - void new_post(expr *post) {if (post != m_post) {m_new_post = post;}} - /// true if the node needs to be updated outside of the priority queue - bool is_dirty () {return m_new_post;} - /// clean a dirty node - void clean(); - - void reset () {clean (); m_derivation = nullptr; m_open = true;} bool is_closed () const { return !m_open; } void close(); - const ptr_vector &children() {return m_kids;} + const ptr_vector &children() const {return m_kids;} void add_child (pob &v) {m_kids.push_back (&v);} void erase_child (pob &v) {m_kids.erase (&v);} - const ptr_vector &lemmas() {return m_lemmas;} + const ptr_vector &lemmas() const {return m_lemmas;} void add_lemma(lemma* new_lemma) {m_lemmas.push_back(new_lemma);} bool is_ground () const { return m_binding.empty (); } @@ -652,8 +677,14 @@ public: */ void get_skolems(app_ref_vector& v); - void on_expand() { m_expand_watches[m_depth].start(); if (m_parent.get()){m_parent.get()->on_expand();} } - void off_expand() { m_expand_watches[m_depth].stop(); if (m_parent.get()){m_parent.get()->off_expand();} }; + void on_expand() { + m_expand_watches[m_depth].start(); + if (m_parent.get()){m_parent.get()->on_expand();} + } + void off_expand() { + m_expand_watches[m_depth].stop(); + if (m_parent.get()){m_parent.get()->off_expand();} + } double get_expand_time(unsigned depth) { return m_expand_watches[depth].get_seconds();} void inc_ref () {++m_ref_count;} @@ -662,6 +693,7 @@ public: if (m_ref_count == 0) {dealloc(this);} } + std::ostream &display(std::ostream &out, bool full = false) const; class on_expand_event { pob &m_p; @@ -671,26 +703,20 @@ public: }; }; +inline std::ostream &operator<<(std::ostream &out, pob const &p) { + return p.display(out); +} -struct pob_lt : - public std::binary_function -{bool operator() (const pob *pn1, const pob *pn2) const;}; - -struct pob_gt : - public std::binary_function { - pob_lt lt; - bool operator() (const pob *n1, const pob *n2) const - {return lt(n2, n1);} +struct pob_lt_proc : public std::binary_function { + bool operator() (const pob *pn1, const pob *pn2) const; }; -struct pob_ref_gt : - public std::binary_function { - pob_gt gt; - bool operator() (const pob_ref &n1, const pob_ref &n2) const - {return gt (n1.get (), n2.get ());} +struct pob_gt_proc : public std::binary_function { + bool operator() (const pob *n1, const pob *n2) const { + return pob_lt_proc()(n2, n1); + } }; -inline unsigned lemma::weakness() {return m_pob ? m_pob->weakness() : UINT_MAX;} /** */ class derivation { @@ -767,36 +793,41 @@ public: class pob_queue { + + typedef std::priority_queue, pob_gt_proc> pob_queue_ty; pob_ref m_root; unsigned m_max_level; unsigned m_min_depth; - std::priority_queue, - pob_ref_gt> m_obligations; + pob_queue_ty m_data; public: pob_queue(): m_root(nullptr), m_max_level(0), m_min_depth(0) {} - ~pob_queue(); + ~pob_queue() {} void reset(); - pob * top (); - void pop () {m_obligations.pop ();} + pob* top(); + void pop(); void push (pob &n); void inc_level () { - SASSERT (!m_obligations.empty () || m_root); + SASSERT (!m_data.empty () || m_root); m_max_level++; m_min_depth++; - if (m_root && m_obligations.empty()) { m_obligations.push(m_root); } + if (m_root && m_data.empty()) { + SASSERT(!m_root->is_in_queue()); + m_root->set_in_queue(true); + m_data.push(m_root.get()); + } } pob& get_root() const {return *m_root.get ();} void set_root(pob& n); - bool is_root (pob& n) const {return m_root.get () == &n;} + bool is_root(pob& n) const {return m_root.get () == &n;} unsigned max_level() const {return m_max_level;} unsigned min_depth() const {return m_min_depth;} - size_t size() const {return m_obligations.size();} + size_t size() const {return m_data.size();} }; @@ -909,8 +940,6 @@ class context { bool m_use_euf_gen; bool m_use_ctp; bool m_use_inc_clause; - bool m_blast_term_ite; - bool m_reuse_pobs; bool m_use_ind_gen; bool m_use_array_eq_gen; bool m_validate_lemmas; @@ -932,6 +961,7 @@ class context { unsigned m_push_pob_max_depth; unsigned m_max_level; unsigned m_restart_initial_threshold; + unsigned m_blast_term_ite_inflation; scoped_ptr_vector m_callbacks; json_marshaller m_json_marshaller; @@ -1006,8 +1036,7 @@ public: bool simplify_pob() {return m_simplify_pob;} bool use_ctp() {return m_use_ctp;} bool use_inc_clause() {return m_use_inc_clause;} - bool blast_term_ite() {return m_blast_term_ite;} - bool reuse_pobs() {return m_reuse_pobs;} + unsigned blast_term_ite_inflation() {return m_blast_term_ite_inflation;} bool elim_aux() {return m_elim_aux;} bool reach_dnf() {return m_reach_dnf;} diff --git a/src/muz/spacer/spacer_iuc_proof.h b/src/muz/spacer/spacer_iuc_proof.h index a3044ca53..538ab4ff2 100644 --- a/src/muz/spacer/spacer_iuc_proof.h +++ b/src/muz/spacer/spacer_iuc_proof.h @@ -33,7 +33,7 @@ public: bool is_h_marked(proof* p) {return m_h_mark.is_marked(p);} bool is_b_pure (proof *p) { - return !is_h_marked (p) && is_core_pure(m.get_fact (p)); + return !is_h_marked (p) && !this->is_a_marked(p) && is_core_pure(m.get_fact (p)); } void display_dot(std::ostream &out); diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp index a68db4c0a..27b8ee357 100644 --- a/src/muz/spacer/spacer_iuc_solver.cpp +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -322,6 +322,24 @@ void iuc_solver::get_iuc(expr_ref_vector &core) // -- new hypothesis reducer else { +#if 0 + static unsigned bcnt = 0; + { + bcnt++; + TRACE("spacer", tout << "Dumping pf bcnt: " << bcnt << "\n";); + if (bcnt == 123) { + std::ofstream ofs; + ofs.open("/tmp/bpf_" + std::to_string(bcnt) + ".dot"); + iuc_proof iuc_pf_before(m, res.get(), core_lits); + iuc_pf_before.display_dot(ofs); + ofs.close(); + + proof_checker pc(m); + expr_ref_vector side(m); + ENSURE(pc.check(res, side)); + } + } +#endif scoped_watch _t_ (m_hyp_reduce2_sw); // pre-process proof for better iuc extraction @@ -356,6 +374,22 @@ void iuc_solver::get_iuc(expr_ref_vector &core) iuc_proof iuc_pf(m, res, core_lits); +#if 0 + static unsigned cnt = 0; + { + cnt++; + TRACE("spacer", tout << "Dumping pf cnt: " << cnt << "\n";); + if (cnt == 123) { + std::ofstream ofs; + ofs.open("/tmp/pf_" + std::to_string(cnt) + ".dot"); + iuc_pf.display_dot(ofs); + ofs.close(); + proof_checker pc(m); + expr_ref_vector side(m); + ENSURE(pc.check(res, side)); + } + } +#endif unsat_core_learner learner(m, iuc_pf); unsat_core_plugin* plugin; diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp index 4e6b37a0a..04b001151 100644 --- a/src/muz/spacer/spacer_pdr.cpp +++ b/src/muz/spacer/spacer_pdr.cpp @@ -236,7 +236,7 @@ lbool context::gpdr_solve_core() { for (lvl = 0; lvl < max_level; ++lvl) { checkpoint(); IF_VERBOSE(1,verbose_stream() << "GPDR Entering level "<< lvl << "\n";); - STRACE("spacer.expand-add", tout << "\n* LEVEL " << lvl << "\n";); + STRACE("spacer_progress", tout << "\n* LEVEL " << lvl << "\n";); m_expanded_lvl = infty_level(); m_stats.m_max_query_lvl = lvl; if (gpdr_check_reachability(lvl, ms)) {return l_true;} diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index 082cc4b5d..ed02513f1 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -29,36 +29,172 @@ Revision History: namespace spacer { -// arithmetic lemma recognizer -bool is_arith_lemma(ast_manager& m, proof* pr) -{ - // arith lemmas: second parameter specifies exact type of lemma, - // could be "farkas", "triangle-eq", "eq-propagate", - // "assign-bounds", maybe also something else - if (pr->get_decl_kind() == PR_TH_LEMMA) { - func_decl* d = pr->get_decl(); - symbol sym; - return d->get_num_parameters() >= 1 && - d->get_parameter(0).is_symbol(sym) && - sym == "arith"; - } - return false; -} - -// farkas lemma recognizer -bool is_farkas_lemma(ast_manager& m, proof* pr) -{ - if (pr->get_decl_kind() == PR_TH_LEMMA) + // arithmetic lemma recognizer + bool is_arith_lemma(ast_manager& m, proof* pr) { - func_decl* d = pr->get_decl(); - symbol sym; - return d->get_num_parameters() >= 2 && - d->get_parameter(0).is_symbol(sym) && sym == "arith" && - d->get_parameter(1).is_symbol(sym) && sym == "farkas"; + // arith lemmas: second parameter specifies exact type of lemma, + // could be "farkas", "triangle-eq", "eq-propagate", + // "assign-bounds", maybe also something else + if (pr->get_decl_kind() == PR_TH_LEMMA) { + func_decl* d = pr->get_decl(); + symbol sym; + return d->get_num_parameters() >= 1 && + d->get_parameter(0).is_symbol(sym) && + sym == "arith"; + } + return false; } - return false; -} + // farkas lemma recognizer + bool is_farkas_lemma(ast_manager& m, proof* pr) + { + if (pr->get_decl_kind() == PR_TH_LEMMA) + { + func_decl* d = pr->get_decl(); + symbol sym; + return d->get_num_parameters() >= 2 && + d->get_parameter(0).is_symbol(sym) && sym == "arith" && + d->get_parameter(1).is_symbol(sym) && sym == "farkas"; + } + return false; + } + + static bool is_assign_bounds_lemma(ast_manager &m, proof *pr) { + if (pr->get_decl_kind() == PR_TH_LEMMA) + { + func_decl* d = pr->get_decl(); + symbol sym; + return d->get_num_parameters() >= 2 && + d->get_parameter(0).is_symbol(sym) && sym == "arith" && + d->get_parameter(1).is_symbol(sym) && sym == "assign-bounds"; + } + return false; + } + + + + class linear_combinator { + struct scaled_lit { + bool is_pos; + app *lit; + rational coeff; + scaled_lit(bool is_pos, app *lit, const rational &coeff) : + is_pos(is_pos), lit(lit), coeff(coeff) {} + }; + ast_manager &m; + th_rewriter m_rw; + arith_util m_arith; + expr_ref m_sum; + bool m_is_strict; + rational m_lc; + vector m_lits; + public: + linear_combinator(ast_manager &m) : m(m), m_rw(m), m_arith(m), + m_sum(m), m_is_strict(false), + m_lc(1) {} + + void add_lit(app* lit, rational const &coeff, bool is_pos = true) { + m_lits.push_back(scaled_lit(is_pos, lit, coeff)); + } + + void normalize_coeff() { + for (auto &lit : m_lits) + m_lc = lcm(m_lc, denominator(lit.coeff)); + if (!m_lc.is_one()) { + for (auto &lit : m_lits) + lit.coeff *= m_lc; + } + } + + rational const &lc() const {return m_lc;} + + bool process_lit(scaled_lit &lit0) { + arith_util a(m); + app* lit = lit0.lit; + rational &coeff = lit0.coeff; + bool is_pos = lit0.is_pos; + + + if (m.is_not(lit)) { + lit = to_app(lit->get_arg(0)); + is_pos = !is_pos; + } + if (!m_arith.is_le(lit) && !m_arith.is_lt(lit) && + !m_arith.is_ge(lit) && !m_arith.is_gt(lit) && !m.is_eq(lit)) { + return false; + } + SASSERT(lit->get_num_args() == 2); + sort* s = m.get_sort(lit->get_arg(0)); + bool is_int = m_arith.is_int(s); + if (!is_int && m_arith.is_int_expr(lit->get_arg(0))) { + is_int = true; + s = m_arith.mk_int(); + } + + if (!is_int && is_pos && (m_arith.is_gt(lit) || m_arith.is_lt(lit))) { + m_is_strict = true; + } + if (!is_int && !is_pos && (m_arith.is_ge(lit) || m_arith.is_le(lit))) { + m_is_strict = true; + } + + + SASSERT(m_arith.is_int(s) || m_arith.is_real(s)); + expr_ref sign1(m), sign2(m), term(m); + sign1 = m_arith.mk_numeral(m.is_eq(lit)?coeff:abs(coeff), s); + sign2 = m_arith.mk_numeral(m.is_eq(lit)?-coeff:-abs(coeff), s); + if (!m_sum.get()) { + m_sum = m_arith.mk_numeral(rational(0), s); + } + + expr* a0 = lit->get_arg(0); + expr* a1 = lit->get_arg(1); + + if (is_pos && (m_arith.is_ge(lit) || m_arith.is_gt(lit))) { + std::swap(a0, a1); + } + if (!is_pos && (m_arith.is_le(lit) || m_arith.is_lt(lit))) { + std::swap(a0, a1); + } + + // + // Multiplying by coefficients over strict + // and non-strict inequalities: + // + // (a <= b) * 2 + // (a - b <= 0) * 2 + // (2a - 2b <= 0) + + // (a < b) * 2 <=> + // (a +1 <= b) * 2 <=> + // 2a + 2 <= 2b <=> + // 2a+2-2b <= 0 + + bool strict_ineq = + is_pos?(m_arith.is_gt(lit) || m_arith.is_lt(lit)):(m_arith.is_ge(lit) || m_arith.is_le(lit)); + + if (is_int && strict_ineq) { + m_sum = m_arith.mk_add(m_sum, sign1); + } + + term = m_arith.mk_mul(sign1, a0); + m_sum = m_arith.mk_add(m_sum, term); + term = m_arith.mk_mul(sign2, a1); + m_sum = m_arith.mk_add(m_sum, term); + + m_rw(m_sum); + return true; + } + + expr_ref operator()(){ + if (!m_sum) normalize_coeff(); + m_sum.reset(); + for (auto &lit : m_lits) { + if (!process_lit(lit)) return expr_ref(m); + } + return m_sum; + } + }; /* * ==================================== @@ -66,490 +202,639 @@ bool is_farkas_lemma(ast_manager& m, proof* pr) * ==================================== */ -void theory_axiom_reducer::reset() { - m_cache.reset(); - m_pinned.reset(); -} - -// -- rewrite theory axioms into theory lemmas -proof_ref theory_axiom_reducer::reduce(proof* pr) { - proof_post_order pit(pr, m); - while (pit.hasNext()) { - proof* p = pit.next(); - - if (m.get_num_parents(p) == 0 && is_arith_lemma(m, p)) { - // we have an arith-theory-axiom and want to get rid of it - // we need to replace the axiom with - // (a) corresponding hypothesis, - // (b) a theory lemma, and - // (c) a lemma. - // Furthermore update data-structures - app *fact = to_app(m.get_fact(p)); - ptr_buffer cls; - if (m.is_or(fact)) { - for (unsigned i = 0, sz = fact->get_num_args(); i < sz; ++i) - cls.push_back(fact->get_arg(i)); - } - else - cls.push_back(fact); - - // (a) create hypothesis - ptr_buffer hyps; - for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { - expr *c; - expr_ref hyp_fact(m); - if (m.is_not(cls[i], c)) - hyp_fact = c; - else - hyp_fact = m.mk_not (cls[i]); - - proof* hyp = m.mk_hypothesis(hyp_fact); - m_pinned.push_back(hyp); - hyps.push_back(hyp); - } - - // (b) create farkas lemma. Rebuild parameters since - // mk_th_lemma() adds tid as first parameter - unsigned num_params = p->get_decl()->get_num_parameters(); - parameter const* params = p->get_decl()->get_parameters(); - vector parameters; - for (unsigned i = 1; i < num_params; ++i) parameters.push_back(params[i]); - - SASSERT(params[0].is_symbol()); - family_id tid = m.mk_family_id(params[0].get_symbol()); - SASSERT(tid != null_family_id); - - proof* th_lemma = m.mk_th_lemma(tid, m.mk_false(), - hyps.size(), hyps.c_ptr(), - num_params-1, parameters.c_ptr()); - m_pinned.push_back(th_lemma); - SASSERT(is_arith_lemma(m, th_lemma)); - - // (c) create lemma - proof* res = m.mk_lemma(th_lemma, fact); - m_pinned.push_back(res); - m_cache.insert(p, res); - - SASSERT(m.get_fact(res) == m.get_fact(p)); - } - else { - // proof is dirty, if a sub-proof of one of its premises - // has been transformed - bool dirty = false; - - ptr_buffer args; - for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { - proof *pp, *tmp; - pp = m.get_parent(p, i); - VERIFY(m_cache.find(pp, tmp)); - args.push_back(tmp); - dirty |= (pp != tmp); - } - // if not dirty just use the old step - if (!dirty) m_cache.insert(p, p); - // otherwise create new proof with the corresponding proofs - // of the premises - else { - if (m.has_fact(p)) args.push_back(m.get_fact(p)); - - SASSERT(p->get_decl()->get_arity() == args.size()); - - proof* res = m.mk_app(p->get_decl(), - args.size(), (expr * const*)args.c_ptr()); - m_pinned.push_back(res); - m_cache.insert(p, res); - } - } + void theory_axiom_reducer::reset() { + m_cache.reset(); + m_pinned.reset(); } - proof* res; - VERIFY(m_cache.find(pr,res)); - DEBUG_CODE( - proof_checker pc(m); - expr_ref_vector side(m); - SASSERT(pc.check(res, side)); - ); + static proof_ref mk_th_lemma(ast_manager &m, ptr_buffer const &parents, + unsigned num_params, parameter const *params) { + buffer v; + for (unsigned i = 1; i < num_params; ++i) + v.push_back(params[i]); - return proof_ref(res, m); -} + SASSERT(params[0].is_symbol()); + family_id tid = m.mk_family_id(params[0].get_symbol()); + SASSERT(tid != null_family_id); + + proof_ref pf(m); + pf = m.mk_th_lemma(tid, m.mk_false(), + parents.size(), parents.c_ptr(), + v.size(), v.c_ptr()); + return pf; + } + + static bool match_mul(expr *e, expr_ref &var, expr_ref &val, arith_util &a) { + expr *e1 = nullptr, *e2 = nullptr; + if (!a.is_mul(e, e1, e2)) { + if (a.is_numeral(e)) return false; + if (!var || var == e) { + var = e; + val = a.mk_numeral(rational(1), get_sort(e)); + return true; + } + return false; + } + + if (!a.is_numeral(e1)) std::swap(e1, e2); + if (!a.is_numeral(e1)) return false; + + // if variable is given, match it as well + if (!var || var == e2) { + var = e2; + val = e1; + return true; + } + return false; + } + + static expr_ref get_coeff(expr *lit0, expr_ref &var) { + ast_manager &m = var.m(); + arith_util a(m); + + expr *lit = nullptr; + if (!m.is_not(lit0, lit)) lit = lit0; + + expr *e1 = nullptr, *e2 = nullptr; + // assume e2 is numeral and ignore it + if ((a.is_le(lit, e1, e2) || a.is_lt(lit, e1, e2) || + a.is_ge(lit, e1, e2) || a.is_gt(lit, e1, e2) || + m.is_eq(lit, e1, e2))) { + if (a.is_numeral(e1)) std::swap(e1, e2); + } + else { + e1 = lit; + } + + expr_ref val(m); + if (!a.is_add(e1)) { + if (match_mul(e1, var, val, a)) return val; + } + else { + for (auto *arg : *to_app(e1)) { + if (match_mul(arg, var, val, a)) return val; + } + } + return expr_ref(m); + } + + // convert assign-bounds lemma to a farkas lemma by adding missing coeff + // assume that missing coeff is for premise at position 0 + static proof_ref mk_fk_from_ab(ast_manager &m, + ptr_buffer const &parents, + unsigned num_params, + parameter const *params) { + SASSERT(num_params == parents.size() + 1 /* one param is missing */); + arith_util a(m); + th_rewriter rw(m); + + // compute missing coefficient + linear_combinator lcb(m); + for (unsigned i = 1, sz = parents.size(); i < sz; ++i) { + app *p = to_app(m.get_fact(parents.get(i))); + rational const &r = params[i+1].get_rational(); + lcb.add_lit(p, r); + } + + expr_ref lit0(m); + lit0 = m.get_fact(parents.get(0)); + // put lit0 into canonical form + rw(lit0); + TRACE("spacer.fkab", + tout << "lit0 is: " << lit0 << "\n" + << "LCB is: " << lcb() << "\n";); + + expr_ref var(m), val1(m), val2(m); + val1 = get_coeff(lit0, var); + val2 = get_coeff(lcb(), var); + TRACE("spacer.fkab", + tout << "var: " << var + << " val1: " << val1 << " val2: " << val2 << "\n";); + + rational rat1, rat2, coeff0; + CTRACE("spacer.fkab", !(val1 && val2), + tout << "Failed to match variables\n";); + if (val1 && val2 && + a.is_numeral(val1, rat1) && a.is_numeral(val2, rat2)) { + coeff0 = abs(rat2/rat1); + coeff0 = coeff0 / lcb.lc(); + TRACE("spacer.fkab", tout << "coeff0: " << coeff0 << "\n";); + } + else { + IF_VERBOSE(1, verbose_stream() + << "\n\n\nFAILED TO FIND COEFFICIENT\n\n\n";); + TRACE("spacer.fkab", tout << "FAILED TO FIND COEFFICIENT\n";); + // failed to find a coefficient + return proof_ref(m); + } + + + buffer v; + v.push_back(parameter(symbol("farkas"))); + v.push_back(parameter(coeff0)); + for (unsigned i = 2; i < num_params; ++i) + v.push_back(params[i]); + + SASSERT(params[0].is_symbol()); + family_id tid = m.mk_family_id(params[0].get_symbol()); + SASSERT(tid != null_family_id); + + proof_ref pf(m); + pf = m.mk_th_lemma(tid, m.mk_false(), + parents.size(), parents.c_ptr(), + v.size(), v.c_ptr()); + + SASSERT(is_arith_lemma(m, pf)); + + DEBUG_CODE( + proof_checker pc(m); + expr_ref_vector side(m); + ENSURE(pc.check(pf, side));); + return pf; + + } + + /// -- rewrite theory axioms into theory lemmas + proof_ref theory_axiom_reducer::reduce(proof* pr) { + proof_post_order pit(pr, m); + while (pit.hasNext()) { + proof* p = pit.next(); + + if (m.get_num_parents(p) == 0 && is_arith_lemma(m, p)) { + // we have an arith-theory-axiom and want to get rid of it + // we need to replace the axiom with + // (a) corresponding hypothesis, + // (b) a theory lemma, and + // (c) a lemma. + // Furthermore update data-structures + app *fact = to_app(m.get_fact(p)); + ptr_buffer cls; + if (m.is_or(fact)) { + for (unsigned i = 0, sz = fact->get_num_args(); i < sz; ++i) + cls.push_back(fact->get_arg(i)); + } + else + cls.push_back(fact); + + // (a) create hypothesis + ptr_buffer hyps; + for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { + expr *c; + expr_ref hyp_fact(m); + if (m.is_not(cls[i], c)) + hyp_fact = c; + else + hyp_fact = m.mk_not (cls[i]); + + proof* hyp = m.mk_hypothesis(hyp_fact); + m_pinned.push_back(hyp); + hyps.push_back(hyp); + } + + // (b) Create a theory lemma + proof_ref th_lemma(m); + func_decl *d = p->get_decl(); + if (is_assign_bounds_lemma(m, p)) { + + th_lemma = mk_fk_from_ab(m, hyps, + d->get_num_parameters(), + d->get_parameters()); + } + + // fall back to th-lemma + if (!th_lemma) { + th_lemma = mk_th_lemma(m, hyps, + d->get_num_parameters(), + d->get_parameters()); + } + m_pinned.push_back(th_lemma); + SASSERT(is_arith_lemma(m, th_lemma)); + + // (c) create lemma + proof* res = m.mk_lemma(th_lemma, fact); + m_pinned.push_back(res); + m_cache.insert(p, res); + + SASSERT(m.get_fact(res) == m.get_fact(p)); + } + else { + // proof is dirty, if a sub-proof of one of its premises + // has been transformed + bool dirty = false; + + ptr_buffer args; + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + proof *pp, *tmp; + pp = m.get_parent(p, i); + VERIFY(m_cache.find(pp, tmp)); + args.push_back(tmp); + dirty |= (pp != tmp); + } + // if not dirty just use the old step + if (!dirty) m_cache.insert(p, p); + // otherwise create new proof with the corresponding proofs + // of the premises + else { + if (m.has_fact(p)) args.push_back(m.get_fact(p)); + + SASSERT(p->get_decl()->get_arity() == args.size()); + + proof* res = m.mk_app(p->get_decl(), + args.size(), (expr * const*)args.c_ptr()); + m_pinned.push_back(res); + m_cache.insert(p, res); + } + } + } + + proof* res; + VERIFY(m_cache.find(pr,res)); + DEBUG_CODE( + proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(res, side)); + ); + + return proof_ref(res, m); + } /* ------------------------------------------------------------------------- */ /* hypothesis_reducer */ /* ------------------------------------------------------------------------- */ -proof_ref hypothesis_reducer::reduce(proof* pr) { - compute_hypsets(pr); - collect_units(pr); + proof_ref hypothesis_reducer::reduce(proof* pr) { + compute_hypsets(pr); + collect_units(pr); - proof_ref res(reduce_core(pr), m); - SASSERT(res); - reset(); + proof_ref res(reduce_core(pr), m); + SASSERT(res); + reset(); - DEBUG_CODE(proof_checker pc(m); - expr_ref_vector side(m); - SASSERT(pc.check(res, side));); - return res; -} + DEBUG_CODE(proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(res, side));); + return res; + } -void hypothesis_reducer::reset() { - m_active_hyps.reset(); - m_units.reset(); - m_cache.reset(); - for (auto t : m_pinned_active_hyps) dealloc(t); - m_pinned_active_hyps.reset(); - m_pinned.reset(); - m_hyp_mark.reset(); - m_open_mark.reset(); - m_visited.reset(); -} + void hypothesis_reducer::reset() { + m_active_hyps.reset(); + m_units.reset(); + m_cache.reset(); + for (auto t : m_pinned_active_hyps) dealloc(t); + m_pinned_active_hyps.reset(); + m_pinned.reset(); + m_hyp_mark.reset(); + m_open_mark.reset(); + m_visited.reset(); + } -void hypothesis_reducer::compute_hypsets(proof *pr) { - ptr_buffer todo; - todo.push_back(pr); + void hypothesis_reducer::compute_hypsets(proof *pr) { + ptr_buffer todo; + todo.push_back(pr); - while (!todo.empty()) { - proof* p = todo.back(); + while (!todo.empty()) { + proof* p = todo.back(); + + if (m_visited.is_marked(p)) { + todo.pop_back(); + continue; + } + + unsigned todo_sz = todo.size(); + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + SASSERT(m.is_proof(p->get_arg(i))); + proof *parent = to_app(p->get_arg(i)); + + if (!m_visited.is_marked(parent)) + todo.push_back(parent); + } + if (todo.size() > todo_sz) continue; - if (m_visited.is_marked(p)) { todo.pop_back(); - continue; - } - unsigned todo_sz = todo.size(); - for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { - SASSERT(m.is_proof(p->get_arg(i))); - proof *parent = to_app(p->get_arg(i)); - - if (!m_visited.is_marked(parent)) - todo.push_back(parent); - } - if (todo.size() > todo_sz) continue; - - todo.pop_back(); - - m_visited.mark(p); + m_visited.mark(p); - proof_ptr_vector* active_hyps = nullptr; - // fill both sets - if (m.is_hypothesis(p)) { - // create active_hyps-set for step p - proof_ptr_vector* active_hyps = alloc(proof_ptr_vector); - m_pinned_active_hyps.insert(active_hyps); - m_active_hyps.insert(p, active_hyps); - active_hyps->push_back(p); - m_open_mark.mark(p); - m_hyp_mark.mark(m.get_fact(p)); - continue; - } + proof_ptr_vector* active_hyps = nullptr; + // fill both sets + if (m.is_hypothesis(p)) { + // create active_hyps-set for step p + proof_ptr_vector* active_hyps = alloc(proof_ptr_vector); + m_pinned_active_hyps.insert(active_hyps); + m_active_hyps.insert(p, active_hyps); + active_hyps->push_back(p); + m_open_mark.mark(p); + m_hyp_mark.mark(m.get_fact(p)); + continue; + } - ast_fast_mark1 seen; + ast_fast_mark1 seen; - active_hyps = alloc(proof_ptr_vector); - for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { - proof* parent = m.get_parent(p, i); - // lemmas clear all hypotheses above them - if (m.is_lemma(p)) continue; - for (auto *x : *m_active_hyps.find(parent)) { - if (!seen.is_marked(x)) { - seen.mark(x); - active_hyps->push_back(x); - m_open_mark.mark(p); + active_hyps = alloc(proof_ptr_vector); + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + proof* parent = m.get_parent(p, i); + // lemmas clear all hypotheses above them + if (m.is_lemma(p)) continue; + for (auto *x : *m_active_hyps.find(parent)) { + if (!seen.is_marked(x)) { + seen.mark(x); + active_hyps->push_back(x); + m_open_mark.mark(p); + } } } - } - if (active_hyps->empty()) { - dealloc(active_hyps); - m_active_hyps.insert(p, &m_empty_vector); - } - else { - m_pinned_active_hyps.push_back(active_hyps); - m_active_hyps.insert(p, active_hyps); + if (active_hyps->empty()) { + dealloc(active_hyps); + m_active_hyps.insert(p, &m_empty_vector); + } + else { + m_pinned_active_hyps.push_back(active_hyps); + m_active_hyps.insert(p, active_hyps); + } } } -} // collect all units that are hyp-free and are used as hypotheses somewhere // requires that m_active_hyps has been computed -void hypothesis_reducer::collect_units(proof* pr) { + void hypothesis_reducer::collect_units(proof* pr) { - proof_post_order pit(pr, m); - while (pit.hasNext()) { - proof* p = pit.next(); - if (!m.is_hypothesis(p)) { - // collect units that are hyp-free and are used as - // hypotheses in the proof pr - if (!m_open_mark.is_marked(p) && m.has_fact(p) && - m_hyp_mark.is_marked(m.get_fact(p))) - m_units.insert(m.get_fact(p), p); + proof_post_order pit(pr, m); + while (pit.hasNext()) { + proof* p = pit.next(); + if (!m.is_hypothesis(p)) { + // collect units that are hyp-free and are used as + // hypotheses in the proof pr + if (!m_open_mark.is_marked(p) && m.has_fact(p) && + m_hyp_mark.is_marked(m.get_fact(p))) + m_units.insert(m.get_fact(p), p); + } } } -} /** \brief returns true if p is an ancestor of q - */ -bool hypothesis_reducer::is_ancestor(proof *p, proof *q) { - if (p == q) return true; - ptr_vector todo; - todo.push_back(q); +*/ + bool hypothesis_reducer::is_ancestor(proof *p, proof *q) { + if (p == q) return true; + ptr_vector todo; + todo.push_back(q); - expr_mark visited; - while (!todo.empty()) { - proof *cur; - cur = todo.back(); - todo.pop_back(); - - if (visited.is_marked(cur)) continue; - - if (cur == p) return true; - visited.mark(cur); - - for (unsigned i = 0, sz = m.get_num_parents(cur); i < sz; ++i) { - todo.push_back(m.get_parent(cur, i)); - } - } - return false; -} - -proof* hypothesis_reducer::reduce_core(proof* pf) { - SASSERT(m.is_false(m.get_fact(pf))); - - proof *res = NULL; - - ptr_vector todo; - todo.push_back(pf); - ptr_buffer args; - bool dirty = false; - - while (true) { - proof *p, *tmp, *pp; - unsigned todo_sz; - - p = todo.back(); - if (m_cache.find(p, tmp)) { + expr_mark visited; + while (!todo.empty()) { + proof *cur; + cur = todo.back(); todo.pop_back(); - continue; - } - dirty = false; - args.reset(); - todo_sz = todo.size(); - for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { - pp = m.get_parent(p, i); - if (m_cache.find(pp, tmp)) { - args.push_back(tmp); - dirty |= pp != tmp; - } else { - todo.push_back(pp); + if (visited.is_marked(cur)) continue; + + if (cur == p) return true; + visited.mark(cur); + + for (unsigned i = 0, sz = m.get_num_parents(cur); i < sz; ++i) { + todo.push_back(m.get_parent(cur, i)); } } + return false; + } - if (todo_sz < todo.size()) continue; + proof* hypothesis_reducer::reduce_core(proof* pf) { + SASSERT(m.is_false(m.get_fact(pf))); - todo.pop_back(); + proof *res = NULL; - // transform the current proof node + ptr_vector todo; + todo.push_back(pf); + ptr_buffer args; + bool dirty = false; - if (m.is_hypothesis(p)) { - // if possible, replace a hypothesis by a unit derivation - if (m_units.find(m.get_fact(p), tmp)) { - // use already transformed proof of the unit if it is available - proof* proof_of_unit; - if (!m_cache.find(tmp, proof_of_unit)) { - proof_of_unit = tmp; + while (true) { + proof *p, *tmp, *pp; + unsigned todo_sz; + + p = todo.back(); + if (m_cache.find(p, tmp)) { + todo.pop_back(); + continue; + } + + dirty = false; + args.reset(); + todo_sz = todo.size(); + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + pp = m.get_parent(p, i); + if (m_cache.find(pp, tmp)) { + args.push_back(tmp); + dirty |= pp != tmp; + } else { + todo.push_back(pp); } + } - // make sure hypsets for the unit are computed - // AG: is this needed? - compute_hypsets(proof_of_unit); + if (todo_sz < todo.size()) continue; - // if the transformation doesn't create a cycle, perform it - if (!is_ancestor(p, proof_of_unit)) { - res = proof_of_unit; + todo.pop_back(); + + // transform the current proof node + + if (m.is_hypothesis(p)) { + // if possible, replace a hypothesis by a unit derivation + if (m_units.find(m.get_fact(p), tmp)) { + // use already transformed proof of the unit if it is available + proof* proof_of_unit; + if (!m_cache.find(tmp, proof_of_unit)) { + proof_of_unit = tmp; + } + + // make sure hypsets for the unit are computed + // AG: is this needed? + compute_hypsets(proof_of_unit); + + // if the transformation doesn't create a cycle, perform it + if (!is_ancestor(p, proof_of_unit)) { + res = proof_of_unit; + } + else { + // -- failed to transform the proof, perhaps bad + // -- choice of the proof of unit + res = p; + } } else { - // -- failed to transform the proof, perhaps bad - // -- choice of the proof of unit + // -- no unit found to replace the hypothesis res = p; } } + + else if (!dirty) {res = p;} + + else if (m.is_lemma(p)) { + // lemma: reduce the premise; remove reduced consequences + // from conclusion + SASSERT(args.size() == 1); + res = mk_lemma_core(args[0], m.get_fact(p)); + // -- re-compute hypsets + compute_hypsets(res); + } + else if (m.is_unit_resolution(p)) { + // unit: reduce untis; reduce the first premise; rebuild + // unit resolution + res = mk_unit_resolution_core(p, args); + // -- re-compute hypsets + compute_hypsets(res); + } else { - // -- no unit found to replace the hypothesis - res = p; + res = mk_proof_core(p, args); + // -- re-compute hypsets + compute_hypsets(res); + } + + SASSERT(res); + m_cache.insert(p, res); + + // bail out as soon as found a sub-proof of false + if (!m_open_mark.is_marked(res) && m.has_fact(res) && m.is_false(m.get_fact(res))) + return res; + } + UNREACHABLE(); + return nullptr; + } + + proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) { + SASSERT(m.is_false(m.get_fact(premise))); + SASSERT(m_active_hyps.contains(premise)); + + proof_ptr_vector* active_hyps = m_active_hyps.find(premise); + + // if there is no active hypothesis return the premise + if (!m_open_mark.is_marked(premise)) { + // XXX just in case premise might go away + m_pinned.push_back(premise); + return premise; + } + + // add some stability + std::stable_sort(active_hyps->begin(), active_hyps->end(), ast_lt_proc()); + // otherwise, build a disjunction of the negated active hypotheses + // and add a lemma proof step + expr_ref_buffer args(m); + for (auto hyp : *active_hyps) { + expr *hyp_fact, *t; + hyp_fact = m.get_fact(hyp); + if (m.is_not(hyp_fact, t)) + args.push_back(t); + else + args.push_back(m.mk_not(hyp_fact)); + } + + expr_ref lemma(m); + lemma = mk_or(m, args.size(), args.c_ptr()); + + proof* res; + res = m.mk_lemma(premise, lemma); + m_pinned.push_back(res); + return res; + } + + proof* hypothesis_reducer::mk_unit_resolution_core(proof *ures, + ptr_buffer& args) { + // if any literal is false, we don't need a unit resolution step + // This can be the case due to some previous transformations + for (unsigned i = 1, sz = args.size(); i < sz; ++i) { + if (m.is_false(m.get_fact(args[i]))) { + // XXX pin just in case + m_pinned.push_back(args[i]); + return args[i]; } } - else if (!dirty) {res = p;} + proof* arg0 = args[0]; + app *fact0 = to_app(m.get_fact(arg0)); - else if (m.is_lemma(p)) { - // lemma: reduce the premise; remove reduced consequences - // from conclusion - SASSERT(args.size() == 1); - res = mk_lemma_core(args[0], m.get_fact(p)); - // -- re-compute hypsets - compute_hypsets(res); + + ptr_buffer pf_args; + ptr_buffer pf_fact; + pf_args.push_back(arg0); + + // compute literals to be resolved + ptr_buffer lits; + + // fact0 is a literal whenever the original resolution was a + // binary resolution to an empty clause + if (m.get_num_parents(ures) == 2 && m.is_false(m.get_fact(ures))) { + lits.push_back(fact0); } - else if (m.is_unit_resolution(p)) { - // unit: reduce untis; reduce the first premise; rebuild - // unit resolution - res = mk_unit_resolution_core(p, args); - // -- re-compute hypsets - compute_hypsets(res); + // fact0 is a literal unless it is a dijsunction + else if (!m.is_or(fact0)) { + lits.push_back(fact0); } + // fact0 is a literal only if it appears as a literal in the + // original resolution else { - res = mk_proof_core(p, args); - // -- re-compute hypsets - compute_hypsets(res); + lits.reset(); + app* ures_fact = to_app(m.get_fact(m.get_parent(ures, 0))); + for (unsigned i = 0, sz = ures_fact->get_num_args(); i < sz; ++i) { + if (ures_fact->get_arg(i) == fact0) { + lits.push_back(fact0); + break; + } + } + if (lits.empty()) { + lits.append(fact0->get_num_args(), fact0->get_args()); + } + } - SASSERT(res); - m_cache.insert(p, res); + // -- find all literals that are resolved on + for (unsigned i = 0, sz = lits.size(); i < sz; ++i) { + bool found = false; + for (unsigned j = 1; j < args.size(); ++j) { + if (m.is_complement(lits.get(i), m.get_fact(args[j]))) { + found = true; + pf_args.push_back(args[j]); + break; + } + } + if (!found) {pf_fact.push_back(lits.get(i));} + } - // bail out as soon as found a sub-proof of false - if (!m_open_mark.is_marked(res) && m.has_fact(res) && m.is_false(m.get_fact(res))) - return res; - } - UNREACHABLE(); - return nullptr; -} - -proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) { - SASSERT(m.is_false(m.get_fact(premise))); - SASSERT(m_active_hyps.contains(premise)); - - proof_ptr_vector* active_hyps = m_active_hyps.find(premise); - - // if there is no active hypothesis return the premise - if (!m_open_mark.is_marked(premise)) { - // XXX just in case premise might go away - m_pinned.push_back(premise); - return premise; - } - - // add some stability - std::stable_sort(active_hyps->begin(), active_hyps->end(), ast_lt_proc()); - // otherwise, build a disjunction of the negated active hypotheses - // and add a lemma proof step - expr_ref_buffer args(m); - for (auto hyp : *active_hyps) { - expr *hyp_fact, *t; - hyp_fact = m.get_fact(hyp); - if (m.is_not(hyp_fact, t)) - args.push_back(t); - else - args.push_back(m.mk_not(hyp_fact)); - } - - expr_ref lemma(m); - lemma = mk_or(m, args.size(), args.c_ptr()); - - proof* res; - res = m.mk_lemma(premise, lemma); - m_pinned.push_back(res); - return res; -} - -proof* hypothesis_reducer::mk_unit_resolution_core(proof *ures, - ptr_buffer& args) { - // if any literal is false, we don't need a unit resolution step - // This can be the case due to some previous transformations - for (unsigned i = 1, sz = args.size(); i < sz; ++i) { - if (m.is_false(m.get_fact(args[i]))) { + // unit resolution got reduced to noop + if (pf_args.size() == 1) { // XXX pin just in case - m_pinned.push_back(args[i]); - return args[i]; + m_pinned.push_back(arg0); + + return arg0; } + + // make unit resolution proof step + // expr_ref tmp(m); + // tmp = mk_or(m, pf_fact.size(), pf_fact.c_ptr()); + // proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), tmp); + proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr()); + m_pinned.push_back(res); + + return res; } - proof* arg0 = args[0]; - app *fact0 = to_app(m.get_fact(arg0)); - - - ptr_buffer pf_args; - ptr_buffer pf_fact; - pf_args.push_back(arg0); - - // compute literals to be resolved - ptr_buffer lits; - - // fact0 is a literal whenever the original resolution was a - // binary resolution to an empty clause - if (m.get_num_parents(ures) == 2 && m.is_false(m.get_fact(ures))) { - lits.push_back(fact0); - } - // fact0 is a literal unless it is a dijsunction - else if (!m.is_or(fact0)) { - lits.push_back(fact0); - } - // fact0 is a literal only if it appears as a literal in the - // original resolution - else { - lits.reset(); - app* ures_fact = to_app(m.get_fact(m.get_parent(ures, 0))); - for (unsigned i = 0, sz = ures_fact->get_num_args(); i < sz; ++i) { - if (ures_fact->get_arg(i) == fact0) { - lits.push_back(fact0); - break; + proof* hypothesis_reducer::mk_proof_core(proof* old, ptr_buffer& args) { + // if any of the literals are false, we don't need a step + for (unsigned i = 0; i < args.size(); ++i) { + if (m.is_false(m.get_fact(args[i]))) { + // XXX just in case + m_pinned.push_back(args[i]); + return args[i]; } } - if (lits.empty()) { - lits.append(fact0->get_num_args(), fact0->get_args()); - } + // otherwise build step + // BUG: I guess this doesn't work with quantifiers (since they are no apps) + args.push_back(to_app(m.get_fact(old))); + + SASSERT(old->get_decl()->get_arity() == args.size()); + + proof* res = m.mk_app(old->get_decl(), args.size(), + (expr * const*)args.c_ptr()); + m_pinned.push_back(res); + return res; } - // -- find all literals that are resolved on - for (unsigned i = 0, sz = lits.size(); i < sz; ++i) { - bool found = false; - for (unsigned j = 1; j < args.size(); ++j) { - if (m.is_complement(lits.get(i), m.get_fact(args[j]))) { - found = true; - pf_args.push_back(args[j]); - break; - } - } - if (!found) {pf_fact.push_back(lits.get(i));} - } - - // unit resolution got reduced to noop - if (pf_args.size() == 1) { - // XXX pin just in case - m_pinned.push_back(arg0); - - return arg0; - } - - // make unit resolution proof step - // expr_ref tmp(m); - // tmp = mk_or(m, pf_fact.size(), pf_fact.c_ptr()); - // proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), tmp); - proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr()); - m_pinned.push_back(res); - - return res; -} - -proof* hypothesis_reducer::mk_proof_core(proof* old, ptr_buffer& args) { - // if any of the literals are false, we don't need a step - for (unsigned i = 0; i < args.size(); ++i) { - if (m.is_false(m.get_fact(args[i]))) { - // XXX just in case - m_pinned.push_back(args[i]); - return args[i]; - } - } - - // otherwise build step - // BUG: I guess this doesn't work with quantifiers (since they are no apps) - args.push_back(to_app(m.get_fact(old))); - - SASSERT(old->get_decl()->get_arity() == args.size()); - - proof* res = m.mk_app(old->get_decl(), args.size(), - (expr * const*)args.c_ptr()); - m_pinned.push_back(res); - return res; -} - }; diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 7bd21a1b5..004ea6754 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -56,6 +56,7 @@ prop_solver::prop_solver(ast_manager &m, m_use_push_bg(p.spacer_keep_proxy()) { + m_random.set_seed(p.spacer_random_seed()); m_solvers[0] = solver0; m_solvers[1] = solver1; @@ -363,6 +364,8 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, hard.append(_hard.size(), _hard.c_ptr()); flatten_and(hard); + shuffle(hard.size(), hard.c_ptr(), m_random); + m_ctx = m_contexts [solver_id == 0 ? 0 : 0 /* 1 */].get(); // can be disabled if use_push_bg == true diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index 1db65dc3e..0eed969bd 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -61,6 +61,8 @@ private: bool m_use_push_bg; unsigned m_current_level; // set when m_in_level + random_gen m_random; + void assert_level_atoms(unsigned level); void ensure_level(unsigned lvl); diff --git a/src/muz/spacer/spacer_quant_generalizer.cpp b/src/muz/spacer/spacer_quant_generalizer.cpp index f66fe7b29..a11ab4d9e 100644 --- a/src/muz/spacer/spacer_quant_generalizer.cpp +++ b/src/muz/spacer/spacer_quant_generalizer.cpp @@ -80,6 +80,51 @@ struct index_lt_proc : public std::binary_function { } }; + + struct has_nlira_functor { + struct found{}; + + ast_manager &m; + arith_util u; + + has_nlira_functor(ast_manager &_m) : m(_m), u(m) {} + + void operator()(var *) {} + void operator()(quantifier *) {} + void operator()(app *n) { + family_id fid = n->get_family_id(); + if (fid != u.get_family_id()) return; + + switch(n->get_decl_kind()) { + case OP_MUL: + if (n->get_num_args() != 2) + throw found(); + if (!u.is_numeral(n->get_arg(0)) && !u.is_numeral(n->get_arg(1))) + throw found(); + return; + case OP_IDIV: case OP_DIV: case OP_REM: case OP_MOD: + if (!u.is_numeral(n->get_arg(1))) + throw found(); + return; + default: + return; + } + return; + } + }; + + bool has_nlira(expr_ref_vector &v) { + has_nlira_functor fn(v.m()); + expr_fast_mark1 visited; + try { + for (expr *e : v) + quick_for_each_expr(fn, visited, e); + } + catch (has_nlira_functor::found ) { + return true; + } + return false; + } } namespace spacer { @@ -114,7 +159,7 @@ void lemma_quantifier_generalizer::find_candidates(expr *e, if (!contains_selects(e, m)) return; app_ref_vector indices(m); - get_select_indices(e, indices, m); + get_select_indices(e, indices); app_ref_vector extra(m); expr_sparse_mark marked_args; @@ -155,7 +200,7 @@ bool lemma_quantifier_generalizer::match_sk_idx(expr *e, app_ref_vector const &z if (!contains_selects(e, m)) return false; app_ref_vector indicies(m); - get_select_indices(e, indicies, m); + get_select_indices(e, indicies); if (indicies.size() > 2) return false; unsigned i=0; @@ -191,9 +236,15 @@ expr* times_minus_one(expr *e, arith_util &arith) { Find sub-expression of the form (select A (+ sk!0 t)) and replaces (+ sk!0 t) --> sk!0 and sk!0 --> (+ sk!0 (* (- 1) t)) - Current implementation is an ugly hack for one special case + + rewrites bind to (+ bsk!0 t) where bsk!0 is the original binding for sk!0 + + Current implementation is an ugly hack for one special + case. Should be rewritten based on an equality solver from qe */ -void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, app_ref_vector const &zks, expr_ref &bind) { +void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, + app_ref_vector const &zks, + expr_ref &bind) { if (zks.size() != 1) return; arith_util arith(m); @@ -219,8 +270,8 @@ void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, app_ref_vector kids_bind.push_back(bind); } else { - kids.push_back (times_minus_one(arg, arith)); - kids_bind.push_back (times_minus_one(arg, arith)); + kids.push_back(times_minus_one(arg, arith)); + kids_bind.push_back(arg); } } if (!found) continue; @@ -228,7 +279,8 @@ void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, app_ref_vector rep = arith.mk_add(kids.size(), kids.c_ptr()); bind = arith.mk_add(kids_bind.size(), kids_bind.c_ptr()); TRACE("spacer_qgen", - tout << "replace " << mk_pp(idx, m) << " with " << mk_pp(rep, m) << "\n";); + tout << "replace " << mk_pp(idx, m) << " with " << mk_pp(rep, m) << "\n" + << "bind is: " << bind << "\n";); break; } } @@ -255,15 +307,17 @@ void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, app_ref_vector lb and ub are null if no bound was found */ -void lemma_quantifier_generalizer::mk_abs_cube(lemma_ref &lemma, app *term, var *var, +void lemma_quantifier_generalizer::mk_abs_cube(lemma_ref &lemma, app *term, + var *var, expr_ref_vector &gnd_cube, expr_ref_vector &abs_cube, - expr *&lb, expr *&ub, unsigned &stride) { + expr *&lb, expr *&ub, + unsigned &stride) { // create an abstraction function that maps candidate term to variables expr_safe_replace sub(m); // term -> var - sub.insert(term , var); + sub.insert(term, var); rational val; if (m_arith.is_numeral(term, val)) { bool is_int = val.is_int(); @@ -285,19 +339,17 @@ void lemma_quantifier_generalizer::mk_abs_cube(lemma_ref &lemma, app *term, var for (expr *lit : m_cube) { expr_ref abs_lit(m); - sub (lit, abs_lit); - if (lit == abs_lit) { - gnd_cube.push_back(lit); - } + sub(lit, abs_lit); + if (lit == abs_lit) {gnd_cube.push_back(lit);} else { expr *e1, *e2; // generalize v=num into v>=num if (m.is_eq(abs_lit, e1, e2) && (e1 == var || e2 == var)) { - if (m_arith.is_numeral(e1)) { - abs_lit = m_arith.mk_ge (var, e1); + if (m_arith.is_numeral(e1)) { + abs_lit = m_arith.mk_ge(var, e1); } else if (m_arith.is_numeral(e2)) { abs_lit = m_arith.mk_ge(var, e2); - } + } } abs_cube.push_back(abs_lit); if (contains_selects(abs_lit, m)) { @@ -454,9 +506,15 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { mk_abs_cube(lemma, term, var, gnd_cube, abs_cube, lb, ub, stride); if (abs_cube.empty()) {return false;} + if (has_nlira(abs_cube)) { + TRACE("spacer_qgen", + tout << "non-linear expression: " << abs_cube << "\n";); + return false; + } TRACE("spacer_qgen", tout << "abs_cube is: " << mk_and(abs_cube) << "\n"; + tout << "term: " << mk_pp(term, m) << "\n"; tout << "lb = "; if (lb) tout << mk_pp(lb, m); else tout << "none"; tout << "\n"; @@ -464,7 +522,7 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { if (ub) tout << mk_pp(ub, m); else tout << "none"; tout << "\n";); - if (!lb && !ub) + if (!lb && !ub) return false; // -- guess lower or upper bound if missing @@ -473,7 +531,7 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { lb = abs_cube.back(); } if (!ub) { - abs_cube.push_back (m_arith.mk_lt(var, term)); + abs_cube.push_back (m_arith.mk_le(var, term)); ub = abs_cube.back(); } @@ -489,10 +547,10 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { TRACE("spacer_qgen", tout << "mod=" << mod << " init=" << init << " stride=" << stride << "\n"; tout.flush();); - abs_cube.push_back(m.mk_eq( - m_arith.mk_mod(var, m_arith.mk_numeral(rational(stride), true)), - m_arith.mk_numeral(rational(mod), true))); - } + abs_cube.push_back + (m.mk_eq(m_arith.mk_mod(var, + m_arith.mk_numeral(rational(stride), true)), + m_arith.mk_numeral(rational(mod), true)));} // skolemize expr_ref gnd(m); @@ -512,21 +570,21 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { << "New CUBE is: " << gnd_cube << "\n";); SASSERT(zks.size() >= static_cast(m_offset)); - // lift quantified variables to top of select + // lift quantified variables to top of select expr_ref ext_bind(m); ext_bind = term; cleanup(gnd_cube, zks, ext_bind); - // XXX better do that check before changing bind in cleanup() - // XXX Or not because substitution might introduce _n variable into bind + // XXX better do that check before changing bind in cleanup() + // XXX Or not because substitution might introduce _n variable into bind if (m_ctx.get_manager().is_n_formula(ext_bind)) { - // XXX this creates an instance, but not necessarily the needed one + // XXX this creates an instance, but not necessarily the needed one - // XXX This is sound because any instance of - // XXX universal quantifier is sound + // XXX This is sound because any instance of + // XXX universal quantifier is sound - // XXX needs better long term solution. leave - // comment here for the future + // XXX needs better long term solution. leave + // comment here for the future m_ctx.get_manager().formula_n2o(ext_bind, ext_bind, 0); } @@ -542,46 +600,50 @@ bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { return false; } -bool lemma_quantifier_generalizer::find_stride(expr_ref_vector &c, expr_ref &pattern, unsigned &stride) { +bool lemma_quantifier_generalizer::find_stride(expr_ref_vector &cube, + expr_ref &pattern, + unsigned &stride) { expr_ref tmp(m); - tmp = mk_and(c); + tmp = mk_and(cube); normalize(tmp, tmp, false, true); - c.reset(); - flatten_and(tmp, c); + cube.reset(); + flatten_and(tmp, cube); app_ref_vector indices(m); - get_select_indices(pattern, indices, m); + get_select_indices(pattern, indices); - // TODO - if (indices.size() > 1) - return false; + CTRACE("spacer_qgen", indices.empty(), + tout << "Found no select indices in: " << pattern << "\n";); + + // TBD: handle multi-dimensional arrays and literals with multiple + // select terms + if (indices.size() != 1) return false; app *p_index = indices.get(0); - if (is_var(p_index)) return false; - std::vector instances; - for (expr* lit : c) { + unsigned_vector instances; + for (expr* lit : cube) { if (!contains_selects(lit, m)) continue; indices.reset(); - get_select_indices(lit, indices, m); + get_select_indices(lit, indices); - // TODO: - if (indices.size() > 1) + // TBD: handle multi-dimensional arrays + if (indices.size() != 1) continue; app *candidate = indices.get(0); unsigned size = p_index->get_num_args(); unsigned matched = 0; - for (unsigned p=0; p < size; p++) { + for (unsigned p = 0; p < size; p++) { expr *arg = p_index->get_arg(p); if (is_var(arg)) { rational val; - if (p < candidate->get_num_args() && - m_arith.is_numeral(candidate->get_arg(p), val) && + if (p < candidate->get_num_args() && + m_arith.is_numeral(candidate->get_arg(p), val) && val.is_unsigned()) { instances.push_back(val.get_unsigned()); } diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index da0d6ec34..730ef1629 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -36,28 +36,27 @@ void unsat_core_learner::register_plugin(unsat_core_plugin* plugin) { } void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) { - // traverse proof proof_post_order it(m_pr.get(), m); while (it.hasNext()) { - proof* currentNode = it.next(); + proof* curr = it.next(); - if (m.get_num_parents(currentNode) > 0) { - bool need_to_mark_closed = true; + bool done = is_closed(curr); + if (done) continue; - for (proof* premise : m.get_parents(currentNode)) { - need_to_mark_closed &= (!m_pr.is_b_marked(premise) || m_closed.is_marked(premise)); - } - - // save result - m_closed.mark(currentNode, need_to_mark_closed); + if (m.get_num_parents(curr) > 0) { + done = true; + for (proof* p : m.get_parents(curr)) done &= !is_b_open(p); + set_closed(curr, done); } - // we have now collected all necessary information, so we can visit the node - // if the node mixes A-reasoning and B-reasoning and contains non-closed premises - if (m_pr.is_a_marked(currentNode) && - m_pr.is_b_marked(currentNode) && - !m_closed.is_marked(currentNode)) { - compute_partial_core(currentNode); // then we need to compute a partial core + // we have now collected all necessary information, + // so we can visit the node + // if the node mixes A-reasoning and B-reasoning + // and contains non-closed premises + if (!done) { + if (is_a(curr) && is_b(curr)) { + compute_partial_core(curr); + } } } @@ -74,7 +73,7 @@ void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) { void unsat_core_learner::compute_partial_core(proof* step) { for (unsat_core_plugin* plugin : m_plugins) { - if (m_closed.is_marked(step)) break; + if (is_closed(step)) break; plugin->compute_partial_core(step); } } @@ -93,9 +92,6 @@ void unsat_core_learner::set_closed(proof* p, bool value) { m_closed.mark(p, value); } -bool unsat_core_learner::is_b_open(proof *p) { - return m_pr.is_b_marked(p) && !is_closed (p); -} void unsat_core_learner::add_lemma_to_core(expr* lemma) { m_unsat_core.push_back(lemma); diff --git a/src/muz/spacer/spacer_unsat_core_learner.h b/src/muz/spacer/spacer_unsat_core_learner.h index 327b12eb6..ead430ca0 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.h +++ b/src/muz/spacer/spacer_unsat_core_learner.h @@ -22,6 +22,7 @@ Revision History: #include "ast/ast.h" #include "muz/spacer/spacer_util.h" #include "muz/spacer/spacer_proof_utils.h" +#include "muz/spacer/spacer_iuc_proof.h" namespace spacer { @@ -31,13 +32,32 @@ namespace spacer { class unsat_core_learner { typedef obj_hashtable expr_set; + ast_manager& m; + iuc_proof& m_pr; + + ptr_vector m_plugins; + ast_mark m_closed; + + expr_ref_vector m_unsat_core; + public: unsat_core_learner(ast_manager& m, iuc_proof& pr) : m(m), m_pr(pr), m_unsat_core(m) {}; virtual ~unsat_core_learner(); - ast_manager& m; - iuc_proof& m_pr; + ast_manager& get_manager() {return m;} + + + bool is_a(proof *pr) { + // AG: treat hypotheses as A + // AG: assume that all B-hyp have been eliminated + // AG: this is not yet true in case of arithmetic eq_prop + return m_pr.is_a_marked(pr) || is_h(pr); + } + bool is_b(proof *pr) {return m_pr.is_b_marked(pr);} + bool is_h(proof *pr) {return m_pr.is_h_marked(pr);} + bool is_b_pure(proof *pr) { return m_pr.is_b_pure(pr);} + bool is_b_open(proof *p) {return m_pr.is_b_marked(p) && !is_closed (p);} /* * register a plugin for computation of partial unsat cores @@ -59,7 +79,6 @@ namespace spacer { bool is_closed(proof* p); void set_closed(proof* p, bool value); - bool is_b_open (proof *p); /* * adds a lemma to the unsat core @@ -67,14 +86,6 @@ namespace spacer { void add_lemma_to_core(expr* lemma); private: - ptr_vector m_plugins; - ast_mark m_closed; - - /* - * collects the lemmas of the unsat-core - * will at the end be inserted into unsat_core. - */ - expr_ref_vector m_unsat_core; /* * computes partial core for step by delegating computation to plugins diff --git a/src/muz/spacer/spacer_unsat_core_plugin.cpp b/src/muz/spacer/spacer_unsat_core_plugin.cpp index 8fb5feeef..aeb509c2e 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.cpp +++ b/src/muz/spacer/spacer_unsat_core_plugin.cpp @@ -34,82 +34,81 @@ Revision History: namespace spacer { - unsat_core_plugin::unsat_core_plugin(unsat_core_learner& learner): - m(learner.m), m_learner(learner) {}; - + unsat_core_plugin::unsat_core_plugin(unsat_core_learner& ctx): + m(ctx.get_manager()), m_ctx(ctx) {}; + void unsat_core_plugin_lemma::compute_partial_core(proof* step) { - SASSERT(m_learner.m_pr.is_a_marked(step)); - SASSERT(m_learner.m_pr.is_b_marked(step)); - - for (proof* premise : m.get_parents(step)) { - - if (m_learner.is_b_open (premise)) { + SASSERT(m_ctx.is_a(step)); + SASSERT(m_ctx.is_b(step)); + + for (auto* p : m.get_parents(step)) { + if (m_ctx.is_b_open (p)) { // by IH, premises that are AB marked are already closed - SASSERT(!m_learner.m_pr.is_a_marked(premise)); - add_lowest_split_to_core(premise); + SASSERT(!m_ctx.is_a(p)); + add_lowest_split_to_core(p); } } - m_learner.set_closed(step, true); + m_ctx.set_closed(step, true); } - - void unsat_core_plugin_lemma::add_lowest_split_to_core(proof* step) const - { - SASSERT(m_learner.is_b_open(step)); - + + void unsat_core_plugin_lemma::add_lowest_split_to_core(proof* step) const { + SASSERT(m_ctx.is_b_open(step)); + ptr_buffer todo; todo.push_back(step); - + while (!todo.empty()) { proof* pf = todo.back(); todo.pop_back(); - + // if current step hasn't been processed, - if (!m_learner.is_closed(pf)) { - m_learner.set_closed(pf, true); + if (!m_ctx.is_closed(pf)) { + m_ctx.set_closed(pf, true); // the step is b-marked and not closed. // by I.H. the step must be already visited // so if it is also a-marked, it must be closed - SASSERT(m_learner.m_pr.is_b_marked(pf)); - SASSERT(!m_learner.m_pr.is_a_marked(pf)); - + SASSERT(m_ctx.is_b(pf)); + SASSERT(!m_ctx.is_a(pf)); + // the current step needs to be interpolated: expr* fact = m.get_fact(pf); // if we trust the current step and we are able to use it - if (m_learner.m_pr.is_b_pure (pf) && - (m.is_asserted(pf) || is_literal(m, fact))) { + if (m_ctx.is_b_pure (pf) && (m.is_asserted(pf) || is_literal(m, fact))) { // just add it to the core - m_learner.add_lemma_to_core(fact); + m_ctx.add_lemma_to_core(fact); } // otherwise recurse on premises else { - for (proof* premise : m.get_parents(pf)) - if (m_learner.is_b_open(premise)) + for (proof* premise : m.get_parents(pf)) + if (m_ctx.is_b_open(premise)) todo.push_back(premise); } - } } } - void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step) - { - SASSERT(m_learner.m_pr.is_a_marked(step)); - SASSERT(m_learner.m_pr.is_b_marked(step)); + /*** + * FARKAS + */ + void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step) { + SASSERT(m_ctx.is_a(step)); + SASSERT(m_ctx.is_b(step)); // XXX this assertion should be true so there is no need to check for it - SASSERT (!m_learner.is_closed (step)); + SASSERT (!m_ctx.is_closed (step)); func_decl* d = step->get_decl(); symbol sym; - if (!m_learner.is_closed(step) && // if step is not already interpolated - is_farkas_lemma(m, step)) { - // weaker check: d->get_num_parameters() >= m.get_num_parents(step) + 2 - + TRACE("spacer.farkas", + tout << "looking at: " << mk_pp(step, m) << "\n";); + if (!m_ctx.is_closed(step) && is_farkas_lemma(m, step)) { + // weaker check : d->get_num_parameters() >= m.get_num_parents(step) + 2 + SASSERT(d->get_num_parameters() == m.get_num_parents(step) + 2); SASSERT(m.has_fact(step)); - + coeff_lits_t coeff_lits; expr_ref_vector pinned(m); - + /* The farkas lemma represents a subproof starting from premise(-set)s A, BNP and BP(ure) and * ending in a disjunction D. We need to compute the contribution of BP, i.e. a formula, which * is entailed by BP and together with A and BNP entails D. @@ -134,34 +133,35 @@ namespace spacer { * as workaround we take the absolute value of the provided coefficients. */ parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient - - STRACE("spacer.farkas", - verbose_stream() << "Farkas input: "<< "\n"; - for (unsigned i = 0; i < m.get_num_parents(step); ++i) { - proof * prem = m.get_parent(step, i); - rational coef = params[i].get_rational(); - bool b_pure = m_learner.m_pr.is_b_pure (prem); - verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; - } - ); - - bool can_be_closed = true; - + + TRACE("spacer.farkas", + tout << "Farkas input: "<< "\n"; + for (unsigned i = 0; i < m.get_num_parents(step); ++i) { + proof * prem = m.get_parent(step, i); + rational coef = params[i].get_rational(); + bool b_pure = m_ctx.is_b_pure (prem); + tout << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; + } + ); + + bool done = true; + for (unsigned i = 0; i < m.get_num_parents(step); ++i) { proof * premise = m.get_parent(step, i); - - if (m_learner.is_b_open (premise)) { - SASSERT(!m_learner.m_pr.is_a_marked(premise)); - - if (m_learner.m_pr.is_b_pure (step)) { + + if (m_ctx.is_b_open (premise)) { + SASSERT(!m_ctx.is_a(premise)); + + if (m_ctx.is_b_pure (premise)) { if (!m_use_constant_from_a) { rational coefficient = params[i].get_rational(); coeff_lits.push_back(std::make_pair(abs(coefficient), (app*)m.get_fact(premise))); } } else { - can_be_closed = false; - + // -- mixed premise, won't be able to close this proof step + done = false; + if (m_use_constant_from_a) { rational coefficient = params[i].get_rational(); coeff_lits.push_back(std::make_pair(abs(coefficient), (app*)m.get_fact(premise))); @@ -175,10 +175,11 @@ namespace spacer { } } } - + + // TBD: factor into another method if (m_use_constant_from_a) { params += m.get_num_parents(step); // point to the first Farkas coefficient, which corresponds to a formula in the conclusion - + // the conclusion can either be a single formula or a disjunction of several formulas, we have to deal with both situations if (m.get_num_parents(step) + 2 < d->get_num_parameters()) { unsigned num_args = 1; @@ -190,7 +191,7 @@ namespace spacer { args = _or->get_args(); } SASSERT(m.get_num_parents(step) + 2 + num_args == d->get_num_parameters()); - + bool_rewriter brw(m); for (unsigned i = 0; i < num_args; ++i) { expr* premise = args[i]; @@ -205,13 +206,12 @@ namespace spacer { } // only if all b-premises can be used directly, add the farkas core and close the step - if (can_be_closed) { - m_learner.set_closed(step, true); - - expr_ref res = compute_linear_combination(coeff_lits); - - m_learner.add_lemma_to_core(res); - } + // AG: this decision needs to be re-evaluated. If the proof cannot be closed, literals above + // AG: it will go into the core. However, it does not mean that this literal should/could not be added. + m_ctx.set_closed(step, done); + expr_ref res = compute_linear_combination(coeff_lits); + TRACE("spacer.farkas", tout << "Farkas core: " << res << "\n";); + m_ctx.add_lemma_to_core(res); } } @@ -235,12 +235,12 @@ namespace spacer { void unsat_core_plugin_farkas_lemma_optimized::compute_partial_core(proof* step) { - SASSERT(m_learner.m_pr.is_a_marked(step)); - SASSERT(m_learner.m_pr.is_b_marked(step)); + SASSERT(m_ctx.is_a(step)); + SASSERT(m_ctx.is_b(step)); func_decl* d = step->get_decl(); symbol sym; - if (!m_learner.is_closed(step) && // if step is not already interpolated + if (!m_ctx.is_closed(step) && // if step is not already interpolated is_farkas_lemma(m, step)) { SASSERT(d->get_num_parameters() == m.get_num_parents(step) + 2); SASSERT(m.has_fact(step)); @@ -249,25 +249,25 @@ namespace spacer { parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient - STRACE("spacer.farkas", - verbose_stream() << "Farkas input: "<< "\n"; - for (unsigned i = 0; i < m.get_num_parents(step); ++i) { - proof * prem = m.get_parent(step, i); - rational coef = params[i].get_rational(); - bool b_pure = m_learner.m_pr.is_b_pure (prem); - verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m_learner.m) << "\n"; - } - ); + TRACE("spacer.farkas", + tout << "Farkas input: "<< "\n"; + for (unsigned i = 0; i < m.get_num_parents(step); ++i) { + proof * prem = m.get_parent(step, i); + rational coef = params[i].get_rational(); + bool b_pure = m_ctx.is_b_pure (prem); + tout << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; + } + ); bool can_be_closed = true; for (unsigned i = 0; i < m.get_num_parents(step); ++i) { proof * premise = m.get_parent(step, i); - if (m_learner.m_pr.is_b_marked(premise) && !m_learner.is_closed(premise)) + if (m_ctx.is_b(premise) && !m_ctx.is_closed(premise)) { - SASSERT(!m_learner.m_pr.is_a_marked(premise)); + SASSERT(!m_ctx.is_a(premise)); - if (m_learner.m_pr.is_b_pure(premise)) + if (m_ctx.is_b_pure(premise)) { rational coefficient = params[i].get_rational(); linear_combination.push_back @@ -283,7 +283,7 @@ namespace spacer { // only if all b-premises can be used directly, close the step and add linear combinations for later processing if (can_be_closed) { - m_learner.set_closed(step, true); + m_ctx.set_closed(step, true); if (!linear_combination.empty()) { m_linear_combinations.push_back(linear_combination); @@ -340,7 +340,7 @@ namespace spacer { // 4. extract linear combinations from matrix and add result to core for (unsigned k = 0; k < i; ++k)// i points to the row after the last row which is non-zero { - coeff_lits_t coeff_lits; + coeff_lits_t coeff_lits; for (unsigned l = 0; l < matrix.num_cols(); ++l) { if (!matrix.get(k,l).is_zero()) { coeff_lits.push_back(std::make_pair(matrix.get(k, l), ordered_basis[l])); @@ -349,19 +349,19 @@ namespace spacer { SASSERT(!coeff_lits.empty()); expr_ref linear_combination = compute_linear_combination(coeff_lits); - m_learner.add_lemma_to_core(linear_combination); + m_ctx.add_lemma_to_core(linear_combination); } } - expr_ref unsat_core_plugin_farkas_lemma_optimized::compute_linear_combination(const coeff_lits_t& coeff_lits) { + expr_ref unsat_core_plugin_farkas_lemma_optimized::compute_linear_combination(const coeff_lits_t& coeff_lits) { smt::farkas_util util(m); for (auto const & p : coeff_lits) { util.add(p.first, p.second); } expr_ref negated_linear_combination = util.get(); SASSERT(m.is_not(negated_linear_combination)); - return expr_ref(mk_not(m, negated_linear_combination), m); + return expr_ref(mk_not(m, negated_linear_combination), m); //TODO: rewrite the get-method to return nonnegated stuff? } @@ -449,7 +449,7 @@ namespace spacer { for (unsigned j = 0; j < matrix.num_cols(); ++j) { SASSERT(matrix.get(i, j).is_int()); app_ref a_ij(util.mk_numeral(matrix.get(i,j), true), m); - + app_ref sum(util.mk_int(0), m); for (unsigned k = 0; k < n; ++k) { sum = util.mk_add(sum, util.mk_mul(coeffs[i][k].get(), bounded_vectors[j][k].get())); @@ -458,7 +458,7 @@ namespace spacer { s->assert_expr(eq); } } - + // check result lbool res = s->check_sat(0, nullptr); @@ -480,7 +480,7 @@ namespace spacer { SASSERT(!coeff_lits.empty()); // since then previous outer loop would have found solution already expr_ref linear_combination = compute_linear_combination(coeff_lits); - m_learner.add_lemma_to_core(linear_combination); + m_ctx.add_lemma_to_core(linear_combination); } return; } @@ -504,10 +504,10 @@ namespace spacer { { ptr_vector todo; - SASSERT(m_learner.m_pr.is_a_marked(step)); - SASSERT(m_learner.m_pr.is_b_marked(step)); + SASSERT(m_ctx.is_a(step)); + SASSERT(m_ctx.is_b(step)); SASSERT(m.get_num_parents(step) > 0); - SASSERT(!m_learner.is_closed(step)); + SASSERT(!m_ctx.is_closed(step)); todo.push_back(step); while (!todo.empty()) @@ -516,7 +516,7 @@ namespace spacer { todo.pop_back(); // if we need to deal with the node and if we haven't added the corresponding edges so far - if (!m_learner.is_closed(current) && !m_visited.is_marked(current)) + if (!m_ctx.is_closed(current) && !m_visited.is_marked(current)) { // compute smallest subproof rooted in current, which has only good edges // add an edge from current to each leaf of that subproof @@ -527,7 +527,7 @@ namespace spacer { } } - m_learner.set_closed(step, true); + m_ctx.set_closed(step, true); } @@ -538,7 +538,7 @@ namespace spacer { ptr_buffer todo_subproof; for (proof* premise : m.get_parents(step)) { - if (m_learner.m_pr.is_b_marked(premise)) { + if (m_ctx.is_b(premise)) { todo_subproof.push_back(premise); } } @@ -548,21 +548,21 @@ namespace spacer { todo_subproof.pop_back(); // if we need to deal with the node - if (!m_learner.is_closed(current)) + if (!m_ctx.is_closed(current)) { - SASSERT(!m_learner.m_pr.is_a_marked(current)); // by I.H. the step must be already visited + SASSERT(!m_ctx.is_a(current)); // by I.H. the step must be already visited // and the current step needs to be interpolated: - if (m_learner.m_pr.is_b_marked(current)) + if (m_ctx.is_b(current)) { // if we trust the current step and we are able to use it - if (m_learner.m_pr.is_b_pure (current) && + if (m_ctx.is_b_pure (current) && (m.is_asserted(current) || is_literal(m, m.get_fact(current)))) { // we found a leaf of the subproof, so // 1) we add corresponding edges - if (m_learner.m_pr.is_a_marked(step)) + if (m_ctx.is_a(step)) { add_edge(nullptr, current); // current is sink } @@ -682,9 +682,9 @@ namespace spacer { void unsat_core_plugin_min_cut::finalize() { unsigned_vector cut_nodes; m_min_cut.compute_min_cut(cut_nodes); - + for (unsigned cut_node : cut_nodes) { - m_learner.add_lemma_to_core(m_node_to_formula[cut_node]); + m_ctx.add_lemma_to_core(m_node_to_formula[cut_node]); } } } diff --git a/src/muz/spacer/spacer_unsat_core_plugin.h b/src/muz/spacer/spacer_unsat_core_plugin.h index c05bcc5b9..746f21b19 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.h +++ b/src/muz/spacer/spacer_unsat_core_plugin.h @@ -35,14 +35,14 @@ namespace spacer { virtual ~unsat_core_plugin() {}; virtual void compute_partial_core(proof* step) = 0; virtual void finalize(){}; - - unsat_core_learner& m_learner; + + unsat_core_learner& m_ctx; }; - class unsat_core_plugin_lemma : public unsat_core_plugin { + class unsat_core_plugin_lemma : public unsat_core_plugin { public: - unsat_core_plugin_lemma(unsat_core_learner& learner) : unsat_core_plugin(learner){}; - void compute_partial_core(proof* step) override; + unsat_core_plugin_lemma(unsat_core_learner& learner) : unsat_core_plugin(learner){}; + void compute_partial_core(proof* step) override; private: void add_lowest_split_to_core(proof* step) const; }; @@ -54,7 +54,7 @@ namespace spacer { bool use_constant_from_a=true) : unsat_core_plugin(learner), m_split_literals(split_literals), - m_use_constant_from_a(use_constant_from_a) {}; + m_use_constant_from_a(use_constant_from_a) {}; void compute_partial_core(proof* step) override; private: bool m_split_literals; @@ -64,8 +64,8 @@ namespace spacer { */ expr_ref compute_linear_combination(const coeff_lits_t& coeff_lits); }; - - class unsat_core_plugin_farkas_lemma_optimized : public unsat_core_plugin { + + class unsat_core_plugin_farkas_lemma_optimized : public unsat_core_plugin { public: unsat_core_plugin_farkas_lemma_optimized(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner) {}; void compute_partial_core(proof* step) override; diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index aaa203c5e..ec01218f1 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -666,9 +666,6 @@ namespace { flatten_and(out, v); if (v.size() > 1) { - // sort arguments of the top-level and - std::stable_sort(v.c_ptr(), v.c_ptr() + v.size(), ast_lt_proc()); - if (use_simplify_bounds) { // remove redundant inequalities simplify_bounds(v); @@ -680,6 +677,8 @@ namespace { v.reset(); egraph.to_lits(v); } + // sort arguments of the top-level and + std::stable_sort(v.c_ptr(), v.c_ptr() + v.size(), ast_lt_proc()); TRACE("spacer_normalize", tout << "Normalized:\n" @@ -690,7 +689,7 @@ namespace { qe::term_graph egraph(out.m()); for (expr* e : v) egraph.add_lit(to_app(e)); tout << "Reduced app:\n" - << mk_pp(egraph.to_app(), out.m()) << "\n";); + << mk_pp(egraph.to_expr(), out.m()) << "\n";); out = mk_and(v); } } @@ -785,7 +784,7 @@ namespace { void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { expr_free_vars fv; - ast_manager &m = out.get_manager(); + ast_manager &m = out.m(); fv(e); if (vars.size() < fv.size()) { @@ -795,7 +794,7 @@ namespace { sort *s = fv[i] ? fv[i] : m.mk_bool_sort(); vars[i] = mk_zk_const(m, i, s); var_subst vs(m, false); - vs(e, vars.size(),(expr * *) vars.c_ptr(), out); + out = vs(e, vars.size(),(expr * *) vars.c_ptr()); } } @@ -900,17 +899,22 @@ namespace { struct collect_indices { app_ref_vector& m_indices; array_util a; - collect_indices(app_ref_vector& indices): m_indices(indices), a(indices.get_manager()) {} + collect_indices(app_ref_vector& indices): m_indices(indices), + a(indices.get_manager()) {} void operator()(expr* n) {} void operator()(app* n) { - if (a.is_select(n)) - for (unsigned i = 1; i < n->get_num_args(); ++i) - if (is_app(n->get_arg(i))) - m_indices.push_back(to_app(n->get_arg(i))); + if (a.is_select(n)) { + // for all but first argument + for (unsigned i = 1; i < n->get_num_args(); ++i) { + expr *arg = n->get_arg(i); + if (is_app(arg)) + m_indices.push_back(to_app(arg)); + } + } } }; - void get_select_indices(expr* fml, app_ref_vector &indices, ast_manager& m) { + void get_select_indices(expr* fml, app_ref_vector &indices) { collect_indices ci(indices); for_each_expr(ci, fml); } diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h index 9912769c5..8da574d0e 100644 --- a/src/muz/spacer/spacer_util.h +++ b/src/muz/spacer/spacer_util.h @@ -121,7 +121,7 @@ namespace spacer { void mbqi_project(model &mdl, app_ref_vector &vars, expr_ref &fml); bool contains_selects (expr* fml, ast_manager& m); - void get_select_indices (expr* fml, app_ref_vector& indices, ast_manager& m); + void get_select_indices (expr* fml, app_ref_vector& indices); void find_decls (expr* fml, app_ref_vector& decls, std::string& prefix); diff --git a/src/muz/tab/tab_context.cpp b/src/muz/tab/tab_context.cpp index 7200b3522..c60c770bf 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -561,13 +561,13 @@ namespace tb { } vars.push_back(m.mk_const(symbol(i), sorts[i])); } - vs(g.get_head(), vars.size(), vars.c_ptr(), fml); + fml = vs(g.get_head(), vars.size(), vars.c_ptr()); m_head = to_app(fml); for (unsigned i = 0; i < g.get_num_predicates(); ++i) { - vs(g.get_predicate(i), vars.size(), vars.c_ptr(), fml); + fml = vs(g.get_predicate(i), vars.size(), vars.c_ptr()); m_preds.push_back(to_app(fml)); } - vs(g.get_constraint(), vars.size(), vars.c_ptr(), fml); + fml = vs(g.get_constraint(), vars.size(), vars.c_ptr()); fmls.push_back(fml); m_precond = m.mk_and(fmls.size(), fmls.c_ptr()); IF_VERBOSE(2, @@ -1132,12 +1132,12 @@ namespace tb { } } if (change) { - m_S2(result->get_constraint(), m_rename.size(), m_rename.c_ptr(), constraint); + constraint = m_S2(result->get_constraint(), m_rename.size(), m_rename.c_ptr()); for (unsigned i = 0; i < result->get_num_predicates(); ++i) { - m_S2(result->get_predicate(i), m_rename.size(), m_rename.c_ptr(), tmp); + tmp = m_S2(result->get_predicate(i), m_rename.size(), m_rename.c_ptr()); predicates[i] = to_app(tmp); } - m_S2(result->get_head(), m_rename.size(), m_rename.c_ptr(), tmp); + tmp = m_S2(result->get_head(), m_rename.size(), m_rename.c_ptr()); head = to_app(tmp); result->init(head, predicates, constraint); } @@ -1168,7 +1168,7 @@ namespace tb { if (vars[i]) { v = m.mk_var(i, vars[i]); m_S1.apply(2, delta, expr_offset(v, offset), tmp); - m_S2(tmp, m_rename.size(), m_rename.c_ptr(), tmp); + tmp = m_S2(tmp, m_rename.size(), m_rename.c_ptr()); insert_subst(offset, tmp); } else { @@ -1613,8 +1613,8 @@ namespace datalog { } expr_ref body = clause.get_body(); var_subst vs(m, false); - vs(body, subst.size(), subst.c_ptr(), body); - out << mk_pp(body, m) << "\n"; + body = vs(body, subst.size(), subst.c_ptr()); + out << body << "\n"; } void resolve_rule(replace_proof_converter& pc, tb::clause const& r1, tb::clause const& r2, diff --git a/src/muz/transforms/CMakeLists.txt b/src/muz/transforms/CMakeLists.txt index 5e8829d42..e92aa1c2f 100644 --- a/src/muz/transforms/CMakeLists.txt +++ b/src/muz/transforms/CMakeLists.txt @@ -1,3 +1,4 @@ + z3_add_component(transforms SOURCES dl_mk_array_blast.cpp @@ -23,6 +24,7 @@ z3_add_component(transforms dl_transforms.cpp dl_mk_array_eq_rewrite.cpp dl_mk_array_instantiation.cpp + dl_mk_elim_term_ite.cpp COMPONENT_DEPENDENCIES dataflow hilbert diff --git a/src/muz/transforms/dl_mk_coalesce.cpp b/src/muz/transforms/dl_mk_coalesce.cpp index 670a65e21..06eea7fce 100644 --- a/src/muz/transforms/dl_mk_coalesce.cpp +++ b/src/muz/transforms/dl_mk_coalesce.cpp @@ -98,7 +98,7 @@ namespace datalog { } var_subst vs(m, false); for (unsigned i = r->get_uninterpreted_tail_size(); i < r->get_tail_size(); ++i) { - vs(r->get_tail(i), revsub.size(), revsub.c_ptr(), result); + result = vs(r->get_tail(i), revsub.size(), revsub.c_ptr()); conjs.push_back(result); } bwr.mk_and(conjs.size(), conjs.c_ptr(), result); diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index e37444c5e..457905a56 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -23,7 +23,6 @@ Author: #include "ast/ast_pp.h" #include "tactic/generic_model_converter.h" #include "ast/ast_util.h" -#include "tactic/extension_model_converter.h" namespace datalog { rule_set * mk_coi_filter::operator()(rule_set const & source) { diff --git a/src/muz/transforms/dl_mk_elim_term_ite.cpp b/src/muz/transforms/dl_mk_elim_term_ite.cpp new file mode 100644 index 000000000..f0d9a8843 --- /dev/null +++ b/src/muz/transforms/dl_mk_elim_term_ite.cpp @@ -0,0 +1,203 @@ +/*++ +Copyright (c) 2018 Arie Gurfinkel + +Module Name: + + dl_mk_elim_term_ite.h + +Abstract: + + Remove term-ite expressions from the rules + +Author: + + Arie Gurfinkel (agurfinkel) + +Revision History: + +--*/ + +#include "muz/transforms/dl_mk_elim_term_ite.h" +#include "tactic/core/blast_term_ite_tactic.h" +#include "ast/ast_util.h" +#include "ast/expr_abstract.h" +#include "tactic/tactic.h" +#include "tactic/core/elim_term_ite_tactic.h" + +namespace { + struct uninterp_const_collector { + app_ref_vector &m_consts; + uninterp_const_collector(app_ref_vector &v) : m_consts(v) {} + void operator()(var *v) {} + void operator()(quantifier *n) {} + void operator()(expr *a){} + void operator()(app *a){ + if (is_uninterp_const(a)) {m_consts.push_back(a);}; + } + }; + + void collect_uninterp_consts(expr *e, app_ref_vector &v) { + uninterp_const_collector c(v); + quick_for_each_expr(c, e); + } + + struct term_ite_proc { + class found{}; + ast_manager &m; + term_ite_proc(ast_manager &m) : m(m) {} + void operator()(var *v) {} + void operator()(quantifier *n) {} + void operator()(expr *a){} + void operator()(app *a){ + if (m.is_term_ite(a)) throw found(); + } + }; + + bool has_term_ite(expr *e, ast_manager& m) { + term_ite_proc f(m); + try { + quick_for_each_expr(f, e); + } catch (term_ite_proc::found) { + return true; + } + return false; + } + bool has_term_ite(expr_ref &e) { return has_term_ite(e, e.m());} + +} +namespace datalog { + mk_elim_term_ite::mk_elim_term_ite(context & ctx, unsigned priority) : + rule_transformer::plugin(priority, false), + m_ctx(ctx), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()), + m_ground(m) {} + + mk_elim_term_ite::~mk_elim_term_ite() {} + + expr_ref mk_elim_term_ite::ground(expr_ref &e) { + expr_free_vars fv; + fv(e); + if (m_ground.size() < fv.size()) + m_ground.resize(fv.size()); + for (unsigned i = 0, sz = fv.size(); i < sz; ++i) { + if (fv[i] && !m_ground.get(i)) + m_ground[i] = m.mk_fresh_const("c", fv[i]); + } + + var_subst vsub(m, false); + return vsub(e, m_ground.size(), m_ground.c_ptr()); + } + + bool mk_elim_term_ite::elim(rule &r, rule_set &new_rules){ + m_ground.reset(); + + th_rewriter rw(m); + unsigned utsz = r.get_uninterpreted_tail_size(); + unsigned tsz = r.get_tail_size(); + expr_ref_vector new_conjs(m); + expr_ref tmp(m); + + + for (unsigned i = utsz; i < tsz; ++i) + new_conjs.push_back(r.get_tail(i)); + flatten_and(new_conjs); + + expr_ref fml1(m), fml2(m), body(m), head(m); + + // blast ite + body = m.mk_and(new_conjs.size(), new_conjs.c_ptr()); + if (!has_term_ite(body)) { + TRACE("dl_term_ite", tout << "No term-ite, skipping a rule\n";); + new_rules.add_rule(&r); + return false; + } + new_conjs.reset(); + blast_term_ite(body, m_ctx.blast_term_ite_inflation()); + // simplify body + rw(body); + + if (!has_term_ite(body)) { + head = r.get_head(); + + fml2 = m.mk_implies(body, head); + proof_ref p(m); + rm.mk_rule(fml2, p, new_rules, r.name()); + rm.mk_rule_rewrite_proof(r, *new_rules.last()); + TRACE("dl_term_ite", tout << "No term-ite after blast_term_ite\n";); + return true; + } + + TRACE("dl_term_ite", + tout << "Rule has term-ite after blasting, starting elimination\n";); + body = ground(body); + // elim ite + tactic_ref elim_term_ite = mk_elim_term_ite_tactic(m); + goal_ref goal = alloc(class goal, m); + goal_ref_buffer result; + flatten_and(body, new_conjs); + for (auto *e : new_conjs) { + goal->assert_expr(e); + } + unsigned sz = goal->num_exprs(); + (*elim_term_ite)(goal, result); + if (result.size() == 1) { + goal_ref new_goal = result[0]; + if (new_goal->num_exprs() != sz) { + new_conjs.reset(); + new_goal->get_formulas(new_conjs); + flatten_and(new_conjs); + } + } + + expr_ref_vector conjs(m); + for (unsigned i = 0; i < utsz; ++i) { + tmp = r.get_tail(i); + tmp = ground(tmp); + conjs.push_back(tmp); + } + conjs.append(new_conjs); + + body = mk_and(conjs); + rw(body); + + head = r.get_head(); + head = ground(head); + + fml2 = m.mk_implies(body, head); + SASSERT(!has_term_ite(fml2)); + app_ref_vector consts(m); + collect_uninterp_consts(fml2, consts); + fml2 = mk_forall(m, consts.size(), consts.c_ptr(), fml2); + proof_ref p(m); + rm.mk_rule(fml2, p, new_rules, r.name()); + rm.mk_rule_rewrite_proof(r, *new_rules.last()); + TRACE("dl_term_ite", tout << "New rule: " << fml2 << "\n";); + + return true; + } + + + + rule_set * mk_elim_term_ite::operator()(rule_set const & source) { + if (!m_ctx.elim_term_ite ()) {return nullptr;} + + rule_set* rules = alloc(rule_set, m_ctx); + rules->inherit_predicates(source); + bool change = false; + for (auto *rule : source) { + if (m_ctx.canceled()) { + change = false; + break; + } + change |= elim(*rule, *rules); + } + if (!change) { + dealloc(rules); + rules = nullptr; + } + return rules; + } + + +} diff --git a/src/muz/transforms/dl_mk_elim_term_ite.h b/src/muz/transforms/dl_mk_elim_term_ite.h new file mode 100644 index 000000000..231697a0b --- /dev/null +++ b/src/muz/transforms/dl_mk_elim_term_ite.h @@ -0,0 +1,41 @@ +/*++ +Copyright (c) 2018 Arie Gurfinkel + +Module Name: + + dl_mk_elim_term_ite.h + +Abstract: + + Remove term-ite expressions from the rules + +Author: + + Arie Gurfinkel (agurfinkel) + +Revision History: + +--*/ +#pragma once + +#include "muz/base/dl_context.h" +#include "muz/base/dl_rule_set.h" +#include "muz/base/dl_rule_transformer.h" +#include "tactic/equiv_proof_converter.h" + +namespace datalog { + class mk_elim_term_ite : public rule_transformer::plugin { + context &m_ctx; + ast_manager &m; + rule_manager &rm; + + expr_ref_vector m_ground; + + bool elim(rule &r, rule_set &new_rules); + expr_ref ground(expr_ref &e); + public: + mk_elim_term_ite(context &ctx, unsigned priority); + ~mk_elim_term_ite() override; + rule_set * operator()(const rule_set &source) override; + }; +} diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp index 9f6302e05..058d9dca8 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp @@ -156,8 +156,7 @@ namespace datalog { SASSERT(m_binding[i]); }); m_binding.reverse(); - expr_ref res(m); - instantiate(m, q, m_binding.c_ptr(), res); + expr_ref res = instantiate(m, q, m_binding.c_ptr()); m_binding.reverse(); m_cnst2var(res); conjs.push_back(res); @@ -222,10 +221,7 @@ namespace datalog { for (unsigned i = 0; i < qs.size(); ++i) { instantiate_quantifier(qs[i].get(), conjs); } - obj_map*>::iterator it = m_funs.begin(), end = m_funs.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } + for (auto & kv : m_funs) dealloc(kv.m_value); m_funs.reset(); fml = m.mk_and(conjs.size(), conjs.c_ptr()); diff --git a/src/muz/transforms/dl_mk_scale.cpp b/src/muz/transforms/dl_mk_scale.cpp index 9ceaeeab3..978a22eda 100644 --- a/src/muz/transforms/dl_mk_scale.cpp +++ b/src/muz/transforms/dl_mk_scale.cpp @@ -69,7 +69,7 @@ namespace datalog { // Hedge that we don't have to handle the general case for models produced // by Horn clause solvers. SASSERT(!new_fi->is_partial() && new_fi->num_entries() == 0); - vs(new_fi->get_else(), subst.size(), subst.c_ptr(), tmp); + tmp = vs(new_fi->get_else(), subst.size(), subst.c_ptr()); old_fi->set_else(tmp); old_model->register_decl(old_p, old_fi); } diff --git a/src/muz/transforms/dl_mk_slice.cpp b/src/muz/transforms/dl_mk_slice.cpp index 8f3d5c220..6e6f7dd43 100644 --- a/src/muz/transforms/dl_mk_slice.cpp +++ b/src/muz/transforms/dl_mk_slice.cpp @@ -352,7 +352,7 @@ namespace datalog { } if (!new_fi->is_partial()) { TRACE("dl", tout << mk_pp(new_fi->get_else(), m) << "\n";); - vs(new_fi->get_else(), subst.size(), subst.c_ptr(), tmp); + tmp = vs(new_fi->get_else(), subst.size(), subst.c_ptr()); old_fi->set_else(tmp); } unsigned num_entries = new_fi->num_entries(); @@ -362,7 +362,7 @@ namespace datalog { func_entry const* e = new_fi->get_entry(j); for (unsigned k = 0, l = 0; k < old_p->get_arity(); ++k) { if (!is_sliced.get(k)) { - vs(e->get_arg(l++), subst.size(), subst.c_ptr(), tmp); + tmp = vs(e->get_arg(l++), subst.size(), subst.c_ptr()); args.push_back(tmp); } else { @@ -370,7 +370,7 @@ namespace datalog { } SASSERT(l <= new_p->get_arity()); } - vs(e->get_result(), subst.size(), subst.c_ptr(), res); + res = vs(e->get_result(), subst.size(), subst.c_ptr()); old_fi->insert_entry(args.c_ptr(), res.get()); } old_model->register_decl(old_p, old_fi); diff --git a/src/muz/transforms/dl_transforms.cpp b/src/muz/transforms/dl_transforms.cpp index d4b9506e8..3ce2e6ee4 100644 --- a/src/muz/transforms/dl_transforms.cpp +++ b/src/muz/transforms/dl_transforms.cpp @@ -35,6 +35,7 @@ Revision History: #include "muz/transforms/dl_mk_scale.h" #include "muz/transforms/dl_mk_array_eq_rewrite.h" #include "muz/transforms/dl_mk_array_instantiation.h" +#include "muz/transforms/dl_mk_elim_term_ite.h" #include "muz/base/fp_params.hpp" namespace datalog { @@ -95,6 +96,8 @@ namespace datalog { if (ctx.get_params().xform_magic()) { transf.register_plugin(alloc(datalog::mk_magic_symbolic, ctx, 36020)); } + + transf.register_plugin(alloc(datalog::mk_elim_term_ite, ctx, 35010)); ctx.transform_rules(transf); } } diff --git a/src/nlsat/tactic/qfnra_nlsat_tactic.h b/src/nlsat/tactic/qfnra_nlsat_tactic.h index b1a4d8392..29dbadb9e 100644 --- a/src/nlsat/tactic/qfnra_nlsat_tactic.h +++ b/src/nlsat/tactic/qfnra_nlsat_tactic.h @@ -25,8 +25,6 @@ class tactic; tactic * mk_qfnra_nlsat_tactic(ast_manager & m, params_ref const & p = params_ref()); -MK_SIMPLE_TACTIC_FACTORY(qfnra_nlsat_fct, mk_qfnra_nlsat_tactic(m, p)); - /* ADD_TACTIC("qfnra-nlsat", "builtin strategy for solving QF_NRA problems using only nlsat.", "mk_qfnra_nlsat_tactic(m, p)") */ diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index 1b44b578b..dc4451b6c 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -449,7 +449,6 @@ namespace opt { } lbool r = maxsmt(); if (r == l_true) { - ast_manager& m = m_solver->get_manager(); svector labels; maxsmt.get_model(m_model, labels); // TBD: is m_fm applied or not? diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 016c6f4e5..403ea4c85 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -78,6 +78,7 @@ namespace smt2 { symbol m_bang; symbol m_forall; symbol m_exists; + symbol m_lambda; symbol m_as; symbol m_not; symbol m_root_obj; @@ -161,7 +162,7 @@ namespace smt2 { }; struct quant_frame : public expr_frame { - bool m_forall; + quantifier_kind m_kind; symbol m_qid; symbol m_skid; unsigned m_weight; @@ -170,8 +171,8 @@ namespace smt2 { unsigned m_sym_spos; unsigned m_sort_spos; unsigned m_expr_spos; - quant_frame(bool forall, unsigned pat_spos, unsigned nopat_spos, unsigned sym_spos, unsigned sort_spos, unsigned expr_spos): - expr_frame(EF_QUANT), m_forall(forall), m_weight(1), + quant_frame(quantifier_kind k, unsigned pat_spos, unsigned nopat_spos, unsigned sym_spos, unsigned sort_spos, unsigned expr_spos): + expr_frame(EF_QUANT), m_kind(k), m_weight(1), m_pat_spos(pat_spos), m_nopat_spos(nopat_spos), m_sym_spos(sym_spos), m_sort_spos(sort_spos), m_expr_spos(expr_spos) {} @@ -407,6 +408,7 @@ namespace smt2 { bool curr_id_is_match() const { SASSERT(curr_is_identifier()); return curr_id() == m_match; } bool curr_id_is_forall() const { SASSERT(curr_is_identifier()); return curr_id() == m_forall; } bool curr_id_is_exists() const { SASSERT(curr_is_identifier()); return curr_id() == m_exists; } + bool curr_id_is_lambda() const { SASSERT(curr_is_identifier()); return curr_id() == m_lambda; } bool curr_id_is_bang() const { SASSERT(curr_is_identifier()); return curr_id() == m_bang; } bool curr_id_is_let() const { SASSERT(curr_is_identifier()); return curr_id() == m_let; } bool curr_id_is_root_obj() const { SASSERT(curr_is_identifier()); return curr_id() == m_root_obj; } @@ -907,7 +909,7 @@ namespace smt2 { std::string err_msg = "invalid datatype declaration, unknown sort '"; err_msg += missing.str(); err_msg += "'"; - throw parser_exception(err_msg, line, pos); + throw parser_exception(std::move(err_msg), line, pos); } dts->commit(pm()); m_ctx.insert_aux_pdecl(dts.get()); @@ -987,7 +989,7 @@ namespace smt2 { std::string err_msg = "invalid datatype declaration, unknown sort '"; err_msg += missing.str(); err_msg += "'"; - throw parser_exception(err_msg, line, pos); + throw parser_exception(std::move(err_msg), line, pos); } } @@ -997,7 +999,7 @@ namespace smt2 { std::string err_msg = "invalid datatype declaration, repeated accessor identifier '"; err_msg += duplicated.str(); err_msg += "'"; - throw parser_exception(err_msg, line, pos); + throw parser_exception(std::move(err_msg), line, pos); } } @@ -1301,15 +1303,15 @@ namespace smt2 { m_num_expr_frames++; } - - void push_quant_frame(bool is_forall) { + void push_quant_frame(quantifier_kind k) { SASSERT(curr_is_identifier()); - SASSERT(curr_id_is_forall() || curr_id_is_exists()); - SASSERT(!is_forall || curr_id_is_forall()); - SASSERT(is_forall || curr_id_is_exists()); + SASSERT(curr_id_is_forall() || curr_id_is_exists() || curr_id_is_lambda()); + SASSERT((k == forall_k) == curr_id_is_forall()); + SASSERT((k == exists_k) == curr_id_is_exists()); + SASSERT((k == lambda_k) == curr_id_is_lambda()); next(); void * mem = m_stack.allocate(sizeof(quant_frame)); - new (mem) quant_frame(is_forall, pattern_stack().size(), nopattern_stack().size(), symbol_stack().size(), + new (mem) quant_frame(k, pattern_stack().size(), nopattern_stack().size(), symbol_stack().size(), sort_stack().size(), expr_stack().size()); m_num_expr_frames++; unsigned num_vars = parse_sorted_vars(); @@ -1374,7 +1376,7 @@ namespace smt2 { new_case = cases[i]; } else { - sub(cases[i], subst.size(), subst.c_ptr(), new_case); + new_case = sub(cases[i], subst.size(), subst.c_ptr()); inv_var_shifter inv(m()); inv(new_case, subst.size(), new_case); } @@ -1804,7 +1806,7 @@ namespace smt2 { new (mem) app_frame(f, expr_spos, param_spos, has_as); m_num_expr_frames++; } - + void push_expr_frame(expr_frame * curr) { SASSERT(curr_is_lparen()); next(); @@ -1815,10 +1817,13 @@ namespace smt2 { push_let_frame(); } else if (curr_id_is_forall()) { - push_quant_frame(true); + push_quant_frame(forall_k); } else if (curr_id_is_exists()) { - push_quant_frame(false); + push_quant_frame(exists_k); + } + else if (curr_id_is_lambda()) { + push_quant_frame(lambda_k); } else if (curr_id_is_bang()) { push_bang_frame(curr); @@ -1915,6 +1920,7 @@ namespace smt2 { SASSERT(expr_stack().size() >= fr->m_expr_spos); unsigned num_decls = sort_stack().size() - fr->m_sort_spos; if (expr_stack().size() - fr->m_expr_spos != num_decls /* variables */ + 1 /* result */) + //throw parser_exception("invalid quantified expression, syntax error: (forall|exists|lambda (( )*) ) expected"); throw parser_exception("invalid quantified expression, syntax error: (forall|exists (( )*) ) expected"); unsigned begin_pats = fr->m_pat_spos; unsigned end_pats = pattern_stack().size(); @@ -1938,19 +1944,19 @@ namespace smt2 { TRACE("parse_quantifier", tout << "body:\n" << mk_pp(expr_stack().back(), m()) << "\n";); if (fr->m_qid == symbol::null) fr->m_qid = symbol(m_scanner.get_line()); - if (!m().is_bool(expr_stack().back())) + if (fr->m_kind != lambda_k && !m().is_bool(expr_stack().back())) throw parser_exception("quantifier body must be a Boolean expression"); - quantifier * new_q = m().mk_quantifier(fr->m_forall, - num_decls, - sort_stack().c_ptr() + fr->m_sort_spos, - symbol_stack().c_ptr() + fr->m_sym_spos, - expr_stack().back(), - fr->m_weight, - fr->m_qid, - fr->m_skid, - num_pats, pattern_stack().c_ptr() + fr->m_pat_spos, - num_nopats, nopattern_stack().c_ptr() + fr->m_nopat_spos - ); + quantifier* new_q = m().mk_quantifier(fr->m_kind, + num_decls, + sort_stack().c_ptr() + fr->m_sort_spos, + symbol_stack().c_ptr() + fr->m_sym_spos, + expr_stack().back(), + fr->m_weight, + fr->m_qid, + fr->m_skid, + num_pats, pattern_stack().c_ptr() + fr->m_pat_spos, + num_nopats, nopattern_stack().c_ptr() + fr->m_nopat_spos + ); TRACE("mk_quantifier", tout << "id: " << new_q->get_id() << "\n" << mk_ismt2_pp(new_q, m()) << "\n";); TRACE("skid", tout << "new_q->skid: " << new_q->get_skid() << "\n";); expr_stack().shrink(fr->m_expr_spos); @@ -2965,6 +2971,7 @@ namespace smt2 { m_bang("!"), m_forall("forall"), m_exists("exists"), + m_lambda("lambda"), m_as("as"), m_not("not"), m_root_obj("root-obj"), diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp index 1ae9723c9..695bac07d 100644 --- a/src/qe/nlqsat.cpp +++ b/src/qe/nlqsat.cpp @@ -844,8 +844,7 @@ namespace qe { break; case l_undef: result.push_back(in.get()); - std::string s = "search failed"; - throw tactic_exception(s.c_str()); + throw tactic_exception("search failed"); } } diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index 3916e547c..1b6639e8b 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -2269,8 +2269,7 @@ namespace qe { bound.push_back(m.mk_fresh_const("bound", fv[i])); } var_subst subst(m); - subst(fml, bound.size(), bound.c_ptr(), tmp); - fml = tmp; + fml = subst(fml, bound.size(), bound.c_ptr()); } } @@ -2291,7 +2290,7 @@ namespace qe { } expr* const* exprs = (expr* const*)(vars.c_ptr()); var_subst subst(m); - subst(new_body, vars.size(), exprs, tmp); + tmp = subst(new_body, vars.size(), exprs); inv_var_shifter shift(m); shift(tmp, vars.size(), new_body); } @@ -2337,13 +2336,18 @@ namespace qe { case AST_QUANTIFIER: { app_ref_vector vars(m); quantifier* q = to_quantifier(e); - bool is_fa = q->is_forall(); - tmp = q->get_expr(); - extract_vars(q, tmp, vars); - elim(tmp); - init_qe(); - m_qe->set_assumption(m_assumption); - m_qe->eliminate(is_fa, vars.size(), vars.c_ptr(), tmp); + if (is_lambda(q)) { + tmp = e; + } + else { + bool is_fa = is_forall(q); + tmp = q->get_expr(); + extract_vars(q, tmp, vars); + elim(tmp); + init_qe(); + m_qe->set_assumption(m_assumption); + m_qe->eliminate(is_fa, vars.size(), vars.c_ptr(), tmp); + } m_trail.push_back(tmp); m_visited.insert(e, tmp); todo.pop_back(); @@ -2593,6 +2597,9 @@ namespace qe { ) { + if (is_lambda(old_q)) { + return false; + } // bool is_forall = old_q->is_forall(); app_ref_vector vars(m); TRACE("qe", tout << "simplifying" << mk_pp(new_body, m) << "\n";); @@ -2600,11 +2607,11 @@ namespace qe { extract_vars(old_q, result, vars); TRACE("qe", tout << "variables extracted" << mk_pp(result, m) << "\n";); - if (old_q->is_forall()) { + if (is_forall(old_q)) { result = mk_not(m, result); } m_ctx.solve(result, vars); - if (old_q->is_forall()) { + if (is_forall(old_q)) { expr* e = nullptr; result = m.is_not(result, e)?e:mk_not(m, result); } @@ -2619,7 +2626,7 @@ namespace qe { names.push_back(vars[i]->get_decl()->get_name()); } if (!vars.empty()) { - result = m.mk_quantifier(old_q->is_forall(), vars.size(), sorts.c_ptr(), names.c_ptr(), result, 1); + result = m.mk_quantifier(old_q->get_kind(), vars.size(), sorts.c_ptr(), names.c_ptr(), result, 1); } result_pr = nullptr; return true; diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index ff4c7dc52..cd81167e3 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -41,7 +41,7 @@ namespace qe { bool m_check_purified; // check that variables are properly pure void insert_mul(expr* x, rational const& v, obj_map& ts) { - TRACE("qe", tout << "Adding variable " << mk_pp(x, m) << " " << v << "\n";); + // TRACE("qe", tout << "Adding variable " << mk_pp(x, m) << " " << v << "\n";); rational w; if (ts.find(x, w)) { ts.insert(x, w + v); @@ -92,8 +92,8 @@ namespace qe { rational r1, r2; expr_ref val1 = eval(e1); expr_ref val2 = eval(e2); - TRACE("qe", tout << mk_pp(e1, m) << " " << val1 << "\n";); - TRACE("qe", tout << mk_pp(e2, m) << " " << val2 << "\n";); + //TRACE("qe", tout << mk_pp(e1, m) << " " << val1 << "\n";); + //TRACE("qe", tout << mk_pp(e2, m) << " " << val2 << "\n";); if (!a.is_numeral(val1, r1)) return false; if (!a.is_numeral(val2, r2)) return false; SASSERT(r1 != r2); @@ -306,14 +306,14 @@ namespace qe { return vector(); } model_evaluator eval(model); - TRACE("qe", model_smt2_pp(tout, m, model, 0);); + TRACE("qe", tout << model;); // eval.set_model_completion(true); opt::model_based_opt mbo; obj_map tids; expr_ref_vector pinned(m); unsigned j = 0; - TRACE("qe", tout << "fmls: " << fmls << "\n";); + TRACE("qe", tout << "vars: " << vars << "\nfmls: " << fmls << "\n";); for (unsigned i = 0; i < fmls.size(); ++i) { expr * fml = fmls.get(i); if (!linearize(mbo, eval, fml, fmls, tids)) { @@ -325,7 +325,6 @@ namespace qe { } } fmls.shrink(j); - TRACE("qe", tout << "linearized: " << fmls << "\n";); // fmls holds residue, // mbo holds linear inequalities that are in scope @@ -454,31 +453,13 @@ namespace qe { else if (!d.m_div.is_one() && !is_int) { t = a.mk_div(t, a.mk_numeral(d.m_div, is_int)); } - update_model(model, to_app(x), eval(t)); - SASSERT(eval(t) == eval(x)); result.push_back(def(expr_ref(x, m), t)); } } return result; } - void update_model(model& mdl, app* x, expr_ref const& val) { - if (is_uninterp_const(x)) { - mdl.register_decl(x->get_decl(), val); - } - else { - func_interp* fi = mdl.get_func_interp(x->get_decl()); - if (!fi) return; - model_evaluator eval(mdl); - expr_ref_vector args(m); - for (expr* arg : *x) { - args.push_back(eval(arg)); - } - fi->insert_entry(args.c_ptr(), val); - } - } - expr_ref mk_add(expr_ref_vector const& ts) { switch (ts.size()) { case 0: diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index 3b8d41316..da45d17ca 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -320,15 +320,15 @@ namespace eq { << "sz is " << sz << "\n" << "subst_map[inx]: " << mk_pp(m_subst_map.get(inx), m) << "\n";); SASSERT(m_subst_map.get(inx) == nullptr); - m_subst_map[inx] = r; m_subst.update_inv_binding_at(inx, r); + m_subst_map[inx] = std::move(r); } } void flatten_args(quantifier* q, unsigned& num_args, expr*const*& args) { expr * e = q->get_expr(); - if ((q->is_forall() && m.is_or(e)) || - (q->is_exists() && m.is_and(e))) { + if ((is_forall(q) && m.is_or(e)) || + (is_exists(q) && m.is_and(e))) { num_args = to_app(e)->get_num_args(); args = to_app(e)->get_args(); } @@ -356,27 +356,29 @@ namespace eq { } expr_ref t(m); - if (q->is_forall()) { + switch (q->get_kind()) { + case forall_k: rw.mk_or(m_new_args.size(), m_new_args.c_ptr(), t); - } - else { + break; + case exists_k: rw.mk_and(m_new_args.size(), m_new_args.c_ptr(), t); + break; + default: + t = e; + break; } - expr_ref new_e(m); - m_subst(t, new_e); + expr_ref new_e = m_subst(t, m_subst_map.size(), m_subst_map.c_ptr()); // don't forget to update the quantifier patterns expr_ref_buffer new_patterns(m); expr_ref_buffer new_no_patterns(m); for (unsigned j = 0; j < q->get_num_patterns(); j++) { - expr_ref new_pat(m); - m_subst(q->get_pattern(j), new_pat); + expr_ref new_pat = m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr()); new_patterns.push_back(new_pat); } for (unsigned j = 0; j < q->get_num_no_patterns(); j++) { - expr_ref new_nopat(m); - m_subst(q->get_no_pattern(j), new_nopat); + expr_ref new_nopat = m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr()); new_no_patterns.push_back(new_nopat); } @@ -390,12 +392,17 @@ namespace eq { set_is_variable_proc(is_v); unsigned num_args = 1; expr* const* args = &e; + if (is_lambda(q)) { + r = q; + pr = 0; + return; + } flatten_args(q, num_args, args); unsigned def_count = 0; unsigned largest_vinx = 0; - find_definitions(num_args, args, q->is_exists(), def_count, largest_vinx); + find_definitions(num_args, args, is_exists(q), def_count, largest_vinx); if (def_count > 0) { get_elimination_order(); @@ -423,7 +430,7 @@ namespace eq { if (is_quantifier(r)) { quantifier * q = to_quantifier(r); - ::elim_unused_vars(m, q, m_params, r); + r = ::elim_unused_vars(m, q, m_params); if (m.proofs_enabled()) { proof * p1 = m.mk_elim_unused_vars(q, r); pr = m.mk_transitivity(pr, p1); @@ -463,7 +470,7 @@ namespace eq { m_var2pos[idx] = i; def_count++; largest_vinx = std::max(idx, largest_vinx); - m_new_exprs.push_back(t); + m_new_exprs.push_back(std::move(t)); } else if (!m.is_value(m_map[idx])) { // check if the new definition is simpler @@ -475,7 +482,7 @@ namespace eq { m_pos2var[i] = idx; m_var2pos[idx] = i; m_map[idx] = t; - m_new_exprs.push_back(t); + m_new_exprs.push_back(std::move(t)); } // -- prefer ground else if (is_app(t) && to_app(t)->is_ground() && @@ -485,7 +492,7 @@ namespace eq { m_pos2var[i] = idx; m_var2pos[idx] = i; m_map[idx] = t; - m_new_exprs.push_back(t); + m_new_exprs.push_back(std::move(t)); } // -- prefer constants else if (is_uninterp_const(t) @@ -494,7 +501,7 @@ namespace eq { m_pos2var[i] = idx; m_var2pos[idx] = i; m_map[idx] = t; - m_new_exprs.push_back(t); + m_new_exprs.push_back(std::move(t)); } TRACE ("qe_def", tout << "Replacing definition of VAR " << idx << " from " @@ -624,7 +631,7 @@ namespace eq { expr_ref r(m), new_r(m); r = m.mk_and(conjs.size(), conjs.c_ptr()); create_substitution(largest_vinx + 1); - m_subst(r, new_r); + new_r = m_subst(r, m_subst_map.size(), m_subst_map.c_ptr()); m_rewriter(new_r); conjs.reset(); flatten_and(new_r, conjs); @@ -1756,16 +1763,12 @@ namespace fm { // x_cost_lt is not a total order on variables std::stable_sort(x_cost_vector.begin(), x_cost_vector.end(), x_cost_lt(m_is_int)); TRACE("qe_lite", - svector::iterator it2 = x_cost_vector.begin(); - svector::iterator end2 = x_cost_vector.end(); - for (; it2 != end2; ++it2) { - tout << "(" << mk_ismt2_pp(m_var2expr.get(it2->first), m) << " " << it2->second << ") "; + for (auto const& kv : x_cost_vector) { + tout << "(" << mk_ismt2_pp(m_var2expr.get(kv.first), m) << " " << kv.second << ") "; } tout << "\n";); - svector::iterator it2 = x_cost_vector.begin(); - svector::iterator end2 = x_cost_vector.end(); - for (; it2 != end2; ++it2) { - xs.push_back(it2->first); + for (auto const& kv : x_cost_vector) { + xs.push_back(kv.first); } } @@ -1802,11 +1805,9 @@ namespace fm { void analyze(constraints const & cs, var x, bool & all_int, bool & unit_coeff) const { all_int = true; unit_coeff = true; - constraints::const_iterator it = cs.begin(); - constraints::const_iterator end = cs.end(); - for (; it != end; ++it) { + for (constraint const* c : cs) { bool curr_unit_coeff; - analyze(*(*it), x, all_int, curr_unit_coeff); + analyze(*c, x, all_int, curr_unit_coeff); if (!all_int) return; if (!curr_unit_coeff) @@ -1830,10 +1831,8 @@ namespace fm { } void copy_constraints(constraints const & s, clauses & t) { - constraints::const_iterator it = s.begin(); - constraints::const_iterator end = s.end(); - for (; it != end; ++it) { - app * c = to_expr(*(*it)); + for (constraint const* cns : s) { + app * c = to_expr(*cns); t.push_back(c); } } @@ -1842,10 +1841,8 @@ namespace fm { void save_constraints(var x) { } void mark_constraints_dead(constraints const & cs) { - constraints::const_iterator it = cs.begin(); - constraints::const_iterator end = cs.end(); - for (; it != end; ++it) - (*it)->m_dead = true; + for (constraint* c : cs) + c->m_dead = true; } void mark_constraints_dead(var x) { @@ -2094,14 +2091,8 @@ namespace fm { } void copy_remaining(vector & v2cs) { - vector::iterator it = v2cs.begin(); - vector::iterator end = v2cs.end(); - for (; it != end; ++it) { - constraints & cs = *it; - constraints::iterator it2 = cs.begin(); - constraints::iterator end2 = cs.end(); - for (; it2 != end2; ++it2) { - constraint * c = *it2; + for (constraints& cs : v2cs) { + for (constraint* c : cs) { if (!c->m_dead) { c->m_dead = true; expr * new_f = to_expr(*c); @@ -2170,11 +2161,8 @@ namespace fm { } void display_constraints(std::ostream & out, constraints const & cs) const { - constraints::const_iterator it = cs.begin(); - constraints::const_iterator end = cs.end(); - for (; it != end; ++it) { - out << " "; - display(out, *(*it)); + for (constraint const* c : cs) { + display(out << " ", *c); out << "\n"; } } @@ -2215,7 +2203,9 @@ public: for (unsigned i = 0; i < q->get_num_decls(); ++i) { indices.insert(i); } - m_imp(indices, true, result); + if (q->get_kind() != lambda_k) { + m_imp(indices, true, result); + } if (is_forall(q)) { result = push_not(result); } @@ -2295,7 +2285,7 @@ public: tmp = to_quantifier(tmp)->get_expr(); used.process(tmp); var_subst vs(m, true); - vs(tmp, vars.size(), (expr*const*)vars.c_ptr(), fml); + fml = vs(tmp, vars.size(), (expr*const*)vars.c_ptr()); // collect set of variables that were used. unsigned j = 0; for (unsigned i = 0; i < vars.size(); ++i) { @@ -2307,14 +2297,14 @@ public: vars.resize(j); } else { - fml = tmp; + fml = std::move(tmp); } } void operator()(expr_ref& fml, proof_ref& pr) { expr_ref tmp(m); m_elim_star(fml, tmp, pr); - fml = tmp; + fml = std::move(tmp); } void operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml) { diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp index 86fc1ac3c..b7f0d0d49 100644 --- a/src/qe/qe_mbi.cpp +++ b/src/qe/qe_mbi.cpp @@ -25,52 +25,6 @@ Notes: Other theories: DT, ARR reduced to EUF BV is EUF/Boolean. - Purify EUF1 & LIRA1 & EUF2 & LIRA2 - - Then EUF1 & EUF2 |- false - LIRA1 & LIRA2 |- false - - Sketch of approach by example: - - A: s <= 2a <= t & f(a) = q - - B: t <= 2b <= s + 1 & f(b) != q - - 1. Extract arithmetic consequences of A over shared vocabulary. - - A -> s <= t & (even(t) | s < t) - - 2a. Send to B, have B solve shared variables with EUF_B. - epsilon b . B & A_pure - epsilon b . t <= 2b <= s + 1 & s <= t & (even(t) | s < t) - = t <= s + 1 & (even(t) | t <= s) & s <= t & (even(t) | s < t) - = even(t) & t = s - b := t div 2 - - B & A_pure -> B[b/t div 2] = f(t div 2) != q & t <= s + 1 - - 3a. Send purified result to A - A & B_pure -> false - - Invoke the ping-pong principle to extract interpolant. - - 2b. Solve for shared variables with EUF. - - epsilon a . A - = a := (s + 1) div 2 & s < t & f((s + 1) div 2) = q - - 3b. Send to B. Produces core - s < t & f((s + 1) div 2) = q - - 4b Solve again in arithmetic for shared variables with EUF. - - epsion a . A & (s >= t | f((s + 1) div 2) != q) - - a := t div 2 | s = t & f(t div 2) = q & even(t) - - Send to B, produces core (s != t | f(t div 2) != q) - - 5b. There is no longer a solution for A. A is unsat. --*/ @@ -240,15 +194,24 @@ namespace qe { // euf_arith_mbi struct euf_arith_mbi_plugin::is_atom_proc { - ast_manager& m; - expr_ref_vector& m_atoms; - is_atom_proc(expr_ref_vector& atoms): m(atoms.m()), m_atoms(atoms) {} + ast_manager& m; + expr_ref_vector& m_atoms; + obj_hashtable& m_atom_set; + + is_atom_proc(expr_ref_vector& atoms, obj_hashtable& atom_set): + m(atoms.m()), m_atoms(atoms), m_atom_set(atom_set) {} + void operator()(app* a) { - if (m.is_eq(a)) { + if (m_atom_set.contains(a)) { + // continue + } + else if (m.is_eq(a)) { m_atoms.push_back(a); + m_atom_set.insert(a); } else if (m.is_bool(a) && a->get_family_id() != m.get_basic_family_id()) { m_atoms.push_back(a); + m_atom_set.insert(a); } } void operator()(expr*) {} @@ -256,16 +219,23 @@ namespace qe { struct euf_arith_mbi_plugin::is_arith_var_proc { ast_manager& m; - app_ref_vector& m_vars; + app_ref_vector& m_pvars; + app_ref_vector& m_svars; arith_util arith; - obj_hashtable m_exclude; - is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): - m(vars.m()), m_vars(vars), arith(m) { - for (func_decl* f : shared) m_exclude.insert(f); + obj_hashtable m_shared; + is_arith_var_proc(func_decl_ref_vector const& shared, app_ref_vector& pvars, app_ref_vector& svars): + m(pvars.m()), m_pvars(pvars), m_svars(svars), arith(m) { + for (func_decl* f : shared) m_shared.insert(f); } void operator()(app* a) { - if (arith.is_int_real(a) && a->get_family_id() != arith.get_family_id() && !m_exclude.contains(a->get_decl())) { - m_vars.push_back(a); + if (!arith.is_int_real(a) || a->get_family_id() == arith.get_family_id()) { + // no-op + } + else if (m_shared.contains(a->get_decl())) { + m_svars.push_back(a); + } + else { + m_pvars.push_back(a); } } void operator()(expr*) {} @@ -275,38 +245,44 @@ namespace qe { euf_arith_mbi_plugin::euf_arith_mbi_plugin(solver* s, solver* sNot): mbi_plugin(s->get_manager()), m_atoms(m), + m_fmls(m), m_solver(s), m_dual_solver(sNot) { params_ref p; p.set_bool("core.minimize", true); m_solver->updt_params(p); m_dual_solver->updt_params(p); - expr_ref_vector fmls(m); - m_solver->get_assertions(fmls); + m_solver->get_assertions(m_fmls); + collect_atoms(m_fmls); + } + + void euf_arith_mbi_plugin::collect_atoms(expr_ref_vector const& fmls) { expr_fast_mark1 marks; - is_atom_proc proc(m_atoms); + is_atom_proc proc(m_atoms, m_atom_set); for (expr* e : fmls) { quick_for_each_expr(proc, marks, e); } } bool euf_arith_mbi_plugin::get_literals(model_ref& mdl, expr_ref_vector& lits) { - model_evaluator mev(*mdl.get()); - lits.reset(); - for (expr* e : m_atoms) { - if (mev.is_true(e)) { - lits.push_back(e); - } - else if (mev.is_false(e)) { - lits.push_back(m.mk_not(e)); - } + lits.reset(); + for (expr* e : m_atoms) { + if (mdl->is_true(e)) { + lits.push_back(e); } - TRACE("qe", tout << "atoms from model: " << lits << "\n";); - lbool r = m_dual_solver->check_sat(lits); + else if (mdl->is_false(e)) { + lits.push_back(m.mk_not(e)); + } + } + TRACE("qe", tout << "atoms from model: " << lits << "\n";); + solver_ref dual = m_dual_solver->translate(m, m_dual_solver->get_params()); + dual->assert_expr(mk_not(mk_and(m_fmls))); + lbool r = dual->check_sat(lits); + TRACE("qe", dual->display(tout << "dual result " << r << "\n");); if (l_false == r) { - // use the dual solver to find a 'small' implicant - lits.reset(); - m_dual_solver->get_unsat_core(lits); + // use the dual solver to find a 'small' implicant + lits.reset(); + dual->get_unsat_core(lits); return true; } else { @@ -314,12 +290,28 @@ namespace qe { } } - app_ref_vector euf_arith_mbi_plugin::get_arith_vars(expr_ref_vector const& lits) { + app_ref_vector euf_arith_mbi_plugin::get_arith_vars(model_ref& mdl, expr_ref_vector& lits) { arith_util a(m); - app_ref_vector avars(m); - is_arith_var_proc _proc(avars, m_shared); + app_ref_vector pvars(m), svars(m); // private and shared arithmetic variables. + is_arith_var_proc _proc(m_shared, pvars, svars); for_each_expr(_proc, lits); - return avars; + rational v1, v2; + for (expr* p : pvars) { + VERIFY(a.is_numeral((*mdl)(p), v1)); + for (expr* s : svars) { + VERIFY(a.is_numeral((*mdl)(s), v2)); + if (v1 < v2) { + lits.push_back(a.mk_lt(p, s)); + } + else if (v2 < v1) { + lits.push_back(a.mk_lt(s, p)); + } + else { + lits.push_back(m.mk_eq(s, p)); + } + } + } + return pvars; } mbi_result euf_arith_mbi_plugin::operator()(expr_ref_vector& lits, model_ref& mdl) { @@ -337,29 +329,85 @@ namespace qe { if (!get_literals(mdl, lits)) { return mbi_undef; } - app_ref_vector avars = get_arith_vars(lits); + TRACE("qe", tout << lits << "\n";); + // 1. Extract projected variables, add inequalities between + // projected variables and non-projected terms according to model. + // 2. Extract disequalities implied by congruence closure. + // 3. project arithmetic variables from pure literals. + // 4. Add projected definitions as equalities to EUF. + // 5. project remaining literals with respect to EUF. + + app_ref_vector avars = get_arith_vars(mdl, lits); TRACE("qe", tout << "vars: " << avars << " lits: " << lits << "\n";); - // 1. project arithmetic variables using mdl that satisfies core. - // ground any remaining arithmetic variables using model. + // 2. + term_graph tg1(m); + func_decl_ref_vector no_shared(m); + tg1.set_vars(no_shared, false); + tg1.add_lits(lits); + arith_util a(m); + expr_ref_vector foreign = tg1.shared_occurrences(a.get_family_id()); + obj_hashtable _foreign; + for (expr* e : foreign) _foreign.insert(e); + vector partition = tg1.get_partition(*mdl); + expr_ref_vector diseq = tg1.get_ackerman_disequalities(); + lits.append(diseq); + TRACE("qe", tout << "diseq: " << diseq << "\n"; + tout << "foreign: " << foreign << "\n"; + for (auto const& v : partition) { + tout << "partition: {"; + bool first = true; + for (expr* e : v) { + if (first) first = false; else tout << ", "; + tout << expr_ref(e, m); + } + tout << "}\n"; + } + ); + vector refined_partition; + for (auto & p : partition) { + unsigned j = 0; + for (expr* e : p) { + if (_foreign.contains(e) || + (is_app(e) && m_shared.contains(to_app(e)->get_decl()))) { + p[j++] = e; + } + } + p.shrink(j); + if (!p.empty()) refined_partition.push_back(p); + } + TRACE("qe", + for (auto const& v : refined_partition) { + tout << "partition: {"; + bool first = true; + for (expr* e : v) { + if (first) first = false; else tout << ", "; + tout << expr_ref(e, m); + } + tout << "}\n"; + }); + + + arith_project_plugin ap(m); ap.set_check_purified(false); + // 3. auto defs = ap.project(*mdl.get(), avars, lits); - // 2. Add the projected definitions to the remaining (euf) literals + + // 4. for (auto const& def : defs) { lits.push_back(m.mk_eq(def.var, def.term)); } - TRACE("qe", tout << "# arith defs" << defs.size() << " avars: " << avars << " " << lits << "\n";); + TRACE("qe", tout << "# arith defs " << defs.size() << " avars: " << avars << " " << lits << "\n";); - // 3. Project the remaining literals with respect to EUF. - term_graph tg(m); - tg.set_vars(m_shared, false); - tg.add_lits(lits); + // 5. + term_graph tg2(m); + tg2.set_vars(m_shared, false); + tg2.add_lits(lits); lits.reset(); - lits.append(tg.project(*mdl)); - //lits.append(tg.project()); + lits.append(tg2.project()); TRACE("qe", tout << "project: " << lits << "\n";); return mbi_sat; } @@ -374,7 +422,9 @@ namespace qe { } void euf_arith_mbi_plugin::block(expr_ref_vector const& lits) { - m_solver->assert_expr(mk_not(mk_and(lits))); + collect_atoms(lits); + m_fmls.push_back(mk_not(mk_and(lits))); + m_solver->assert_expr(m_fmls.back()); } diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h index 22a0864ba..fdc4c3c6a 100644 --- a/src/qe/qe_mbi.h +++ b/src/qe/qe_mbi.h @@ -104,17 +104,19 @@ namespace qe { }; class euf_arith_mbi_plugin : public mbi_plugin { - expr_ref_vector m_atoms; - solver_ref m_solver; - solver_ref m_dual_solver; + expr_ref_vector m_atoms; + obj_hashtable m_atom_set; + expr_ref_vector m_fmls; + solver_ref m_solver; + solver_ref m_dual_solver; struct is_atom_proc; struct is_arith_var_proc; - app_ref_vector get_arith_vars(expr_ref_vector const& lits); + app_ref_vector get_arith_vars(model_ref& mdl, expr_ref_vector& lits); bool get_literals(model_ref& mdl, expr_ref_vector& lits); - + void collect_atoms(expr_ref_vector const& fmls); public: - euf_arith_mbi_plugin(solver* s, solver* sNot); + euf_arith_mbi_plugin(solver* s, solver* emptySolver); ~euf_arith_mbi_plugin() override {} mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) override; void block(expr_ref_vector const& lits) override; diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 78dcbfd16..a0e16727e 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -621,22 +621,21 @@ public: SASSERT (!m.is_false (fml)); TRACE ("qe", - tout << "extended model:\n"; - model_pp (tout, mdl); + tout << "extended model:\n" << mdl; tout << "Vars: " << vars << "\n"; ); } // project reals, ints and other variables. if (!other_vars.empty ()) { - TRACE ("qe", tout << "Other vars: " << other_vars << "\n"; - model_pp(tout, mdl);); + TRACE ("qe", tout << "Other vars: " << other_vars << "\n" << mdl;); expr_ref_vector fmls(m); flatten_and (fml, fmls); (*this)(false, other_vars, mdl, fmls); fml = mk_and (fmls); + m_rw(fml); TRACE ("qe", tout << "Projected other vars:\n" << fml << "\n"; @@ -646,6 +645,7 @@ public: if (!other_vars.empty ()) { project_vars (mdl, other_vars, fml); + m_rw(fml); } // substitute any remaining other vars diff --git a/src/qe/qe_solve_plugin.cpp b/src/qe/qe_solve_plugin.cpp index 1f314848f..6ec840de1 100644 --- a/src/qe/qe_solve_plugin.cpp +++ b/src/qe/qe_solve_plugin.cpp @@ -99,7 +99,7 @@ namespace qe { v = e; a_val = rational(1)/a_val; t = mk_term(is_int, a_val, sign, done); - TRACE("qe", tout << mk_pp(lhs, m) << " " << mk_pp(rhs, m) << " " << e << " := " << t << "\n";); + TRACE("qe", tout << mk_pp(lhs, m) << " " << mk_pp(rhs, m) << " " << mk_pp(e, m) << " := " << t << "\n";); return true; } else { diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp index 510868366..faa9cfed8 100644 --- a/src/qe/qe_term_graph.cpp +++ b/src/qe/qe_term_graph.cpp @@ -99,7 +99,7 @@ namespace qe { m_mark(false), m_mark2(false), m_interpreted(false) { - if (!is_app()) return; + if (!is_app(m_expr)) return; for (expr* e : *to_app(m_expr)) { term* t = app2term[e->get_id()]; t->get_root().m_parents.push_back(this); @@ -125,7 +125,7 @@ namespace qe { children(term const* _t):t(*_t) {} ptr_vector::const_iterator begin() const { return t.m_children.begin(); } ptr_vector::const_iterator end() const { return t.m_children.end(); } - }; + }; // Congruence table hash function is based on // roots of children and function declaration. @@ -151,7 +151,7 @@ namespace qe { unsigned get_id() const { return m_expr->get_id();} - unsigned get_decl_id() const { return is_app() ? get_app()->get_decl()->get_id() : m_expr->get_id(); } + unsigned get_decl_id() const { return is_app(m_expr) ? to_app(m_expr)->get_decl()->get_id() : m_expr->get_id(); } bool is_marked() const {return m_mark;} void set_mark(bool v){m_mark = v;} @@ -159,12 +159,10 @@ namespace qe { void set_mark2(bool v){m_mark2 = v;} // NSB: where is this used? bool is_interpreted() const {return m_interpreted;} - bool is_theory() const { return !is_app() || get_app()->get_family_id() != null_family_id; } + bool is_theory() const { return !is_app(m_expr) || to_app(m_expr)->get_family_id() != null_family_id; } void mark_as_interpreted() {m_interpreted=true;} expr* get_expr() const {return m_expr;} - bool is_app() const {return ::is_app(m_expr);} - app *get_app() const {return is_app() ? to_app(m_expr) : nullptr;} - unsigned get_num_args() const { return is_app() ? get_app()->get_num_args() : 0; } + unsigned get_num_args() const { return is_app(m_expr) ? to_app(m_expr)->get_num_args() : 0; } term &get_root() const {return *m_root;} bool is_root() const {return m_root == this;} @@ -198,8 +196,23 @@ namespace qe { } while (curr != this); } + + std::ostream& display(std::ostream& out) const { + out << get_id() << ": " << m_expr + << (is_root() ? " R" : "") << " - "; + term const* r = &this->get_next(); + while (r != this) { + out << r->get_id() << " "; + r = &r->get_next(); + } + out << "\n"; + return out; + } }; + static std::ostream& operator<<(std::ostream& out, term const& t) { + return t.display(out); + } bool term_graph::is_variable_proc::operator()(const expr * e) const { if (!is_app(e)) return false; @@ -211,7 +224,7 @@ namespace qe { } bool term_graph::is_variable_proc::operator()(const term &t) const { - return (*this)(t.get_app()); + return (*this)(t.get_expr()); } void term_graph::is_variable_proc::set_decls(const func_decl_ref_vector &decls, bool exclude) { @@ -357,20 +370,20 @@ namespace qe { } void term_graph::merge(term &t1, term &t2) { - // -- merge might invalidate term2app cache - m_term2app.reset(); - m_pinned.reset(); - term *a = &t1.get_root(); term *b = &t2.get_root(); if (a == b) return; + // -- merge might invalidate term2app cache + m_term2app.reset(); + m_pinned.reset(); + if (a->get_class_size() > b->get_class_size()) { std::swap(a, b); } - // Remove parents of it from the cg table. + // Remove parents of b from the cg table. for (term* p : term::parents(b)) { if (!p->is_marked()) { p->set_mark(true); @@ -387,7 +400,7 @@ namespace qe { a->merge_eq_class(*b); // Insert parents of b's old equilvalence class into the cg table - for (term* p : term::parents(a)) { + for (term* p : term::parents(b)) { if (p->is_marked()) { term* p_old = m_cg_table.insert_if_not_there(p); p->set_mark(false); @@ -398,6 +411,7 @@ namespace qe { } } } + SASSERT(marks_are_clear()); } expr* term_graph::mk_app_core (expr *e) { @@ -428,7 +442,7 @@ namespace qe { return expr_ref(res, m); } - res = mk_app_core (r.get_app()); + res = mk_app_core (r.get_expr()); m_term2app.insert(r.get_id(), res); return expr_ref(res, m); @@ -447,7 +461,7 @@ namespace qe { SASSERT(t.is_root()); expr_ref rep(mk_app(t), m); for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { - expr* mem = mk_app_core(it->get_app()); + expr* mem = mk_app_core(it->get_expr()); out.push_back (m.mk_eq (rep, mem)); } } @@ -456,9 +470,9 @@ namespace qe { mk_equalities(t, out); for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { - expr* a1 = mk_app_core (it->get_app()); + expr* a1 = mk_app_core (it->get_expr()); for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { - expr* a2 = mk_app_core(it2->get_app()); + expr* a2 = mk_app_core(it2->get_expr()); out.push_back (m.mk_eq (a1, a2)); } } @@ -470,10 +484,16 @@ namespace qe { } } + bool term_graph::marks_are_clear() { + for (term * t : m_terms) { + if (t->is_marked()) return false; + } + return true; + } + /// Order of preference for roots of equivalence classes /// XXX This should be factored out to let clients control the preference bool term_graph::term_lt(term const &t1, term const &t2) { - // prefer constants over applications // prefer uninterpreted constants over values // prefer smaller expressions over larger ones @@ -507,6 +527,7 @@ namespace qe { /// Choose better roots for equivalence classes void term_graph::pick_roots() { + SASSERT(marks_are_clear()); for (term* t : m_terms) { if (!t->is_marked() && t->is_root()) pick_root(*t); @@ -516,10 +537,7 @@ namespace qe { void term_graph::display(std::ostream &out) { for (term * t : m_terms) { - out << mk_pp(t->get_expr(), m) << " is root " << t->is_root() - << " cls sz " << t->get_class_size() - << " term " << t - << "\n"; + out << *t; } } @@ -542,7 +560,7 @@ namespace qe { } } - expr_ref term_graph::to_app() { + expr_ref term_graph::to_expr() { expr_ref_vector lits(m); to_lits(lits); return mk_and(lits); @@ -575,8 +593,15 @@ namespace qe { app* a = ::to_app(e); expr_ref_buffer kids(m); for (term* ch : term::children(t)) { - if (!m_root2rep.find(ch->get_root().get_id(), e)) return nullptr; + // prefer a node that resembles current child, + // otherwise, pick a root representative, if present. + if (m_term2app.find(ch->get_id(), e)) kids.push_back(e); + else if (m_root2rep.find(ch->get_root().get_id(), e)) + kids.push_back(e); + else + return nullptr; + TRACE("qe_verbose", tout << *ch << " -> " << mk_pp(e, m) << "\n";); } expr* pure = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); m_pinned.push_back(pure); @@ -590,6 +615,12 @@ namespace qe { return m.is_unique_value(t1) && !m.is_unique_value(t2); } + struct term_depth { + bool operator()(term const* t1, term const* t2) const { + return get_depth(t1->get_expr()) < get_depth(t2->get_expr()); + } + }; + void purify() { // - propagate representatives up over parents. // use work-list + marking to propagate. @@ -603,12 +634,14 @@ namespace qe { worklist.push_back(t); t->set_mark(true); } + // traverse worklist in order of depth. + term_depth td; + std::sort(worklist.begin(), worklist.end(), td); - while (!worklist.empty()) { - term* t = worklist.back(); - worklist.pop_back(); + for (unsigned i = 0; i < worklist.size(); ++i) { + term* t = worklist[i]; t->set_mark(false); - if (m_term2app.contains(t->get_id())) + if (m_term2app.contains(t->get_id())) continue; if (!t->is_theory() && is_projected(*t)) continue; @@ -617,8 +650,8 @@ namespace qe { if (!pure) continue; m_term2app.insert(t->get_id(), pure); - expr* rep = nullptr; - // ensure that the root has a representative + TRACE("qe_verbose", tout << "purified " << *t << " " << mk_pp(pure, m) << "\n";); + expr* rep = nullptr; // ensure that the root has a representative m_root2rep.find(t->get_root().get_id(), rep); // update rep with pure if it is better @@ -641,6 +674,7 @@ namespace qe { // and can be mined using other means, such as theory // aware core minimization m_tg.reset_marks(); + TRACE("qe", display(tout << "after purify\n");); } void solve_core() { @@ -651,10 +685,11 @@ namespace qe { worklist.push_back(t); t->set_mark(true); } + term_depth td; + std::sort(worklist.begin(), worklist.end(), td); - while (!worklist.empty()) { - term* t = worklist.back(); - worklist.pop_back(); + for (unsigned i = 0; i < worklist.size(); ++i) { + term* t = worklist[i]; t->set_mark(false); if (m_term2app.contains(t->get_id())) continue; @@ -682,11 +717,16 @@ namespace qe { } bool find_app(term &t, expr *&res) { - return m_root2rep.find(t.get_root().get_id(), res); + return + m_term2app.find(t.get_id(), res) || + m_root2rep.find(t.get_root().get_id(), res); } bool find_app(expr *lit, expr *&res) { - return m_root2rep.find(m_tg.get_term(lit)->get_root().get_id(), res); + term const* t = m_tg.get_term(lit); + return + m_term2app.find(t->get_id(), res) || + m_root2rep.find(t->get_root().get_id(), res); } void mk_lits(expr_ref_vector &res) { @@ -695,6 +735,91 @@ namespace qe { if (!m.is_eq(lit) && find_app(lit, e)) res.push_back(e); } + TRACE("qe", tout << "literals: " << res << "\n";); + } + + void lits2pure(expr_ref_vector& res) { + expr *e1 = nullptr, *e2 = nullptr, *p1 = nullptr, *p2 = nullptr; + for (auto *lit : m_tg.m_lits) { + if (m.is_eq(lit, e1, e2)) { + if (find_app(e1, p1) && find_app(e2, p2)) { + if (p1 != p2) + res.push_back(m.mk_eq(p1, p2)); + } + else { + TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); + } + } + else if (m.is_distinct(lit)) { + ptr_buffer diff; + for (expr* arg : *to_app(lit)) { + if (find_app(arg, p1)) { + diff.push_back(p1); + } + } + if (diff.size() > 1) { + res.push_back(m.mk_distinct(diff.size(), diff.c_ptr())); + } + else { + TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); + } + } + else if (find_app(lit, p1)) { + res.push_back(p1); + } + else { + TRACE("qe", tout << "skipping " << mk_pp(lit, m) << "\n";); + } + } + TRACE("qe", tout << "literals: " << res << "\n";); + } + + void mk_distinct(expr_ref_vector& res) { + vector> decl2terms; // terms that use function f + ptr_vector decls; + decl2terms.reset(); + // Collect the projected function symbols. + for (term *t : m_tg.m_terms) { + expr* e = t->get_expr(); + if (!is_app(e)) continue; + if (!is_projected(*t)) continue; + app* a = to_app(e); + func_decl* d = a->get_decl(); + if (d->get_arity() == 0) continue; + unsigned id = d->get_decl_id(); + decl2terms.reserve(id+1); + if (decl2terms[id].empty()) decls.push_back(d); + decl2terms[id].push_back(t); + } + + // + // for each projected function that occurs + // (may occur) in multiple congruence classes, + // produce assertions that non-congruent arguments + // are forced distinct. + // + for (func_decl* d : decls) { + unsigned id = d->get_decl_id(); + ptr_vector const& terms = decl2terms[id]; + if (terms.size() <= 1) continue; + unsigned arity = d->get_arity(); + for (unsigned i = 0; i < arity; ++i) { + obj_hashtable roots; + for (term* t : terms) { + expr* arg = to_app(t->get_expr())->get_arg(i); + term const& root = m_tg.get_term(arg)->get_root(); + roots.insert(root.get_expr()); + } + if (roots.size() > 1) { + ptr_buffer args; + for (expr* r : roots) { + args.push_back(r); + } + res.push_back(m.mk_distinct(args.size(), args.c_ptr())); + } + } + } + TRACE("qe", tout << res << "\n";); } void mk_pure_equalities(const term &t, expr_ref_vector &res) { @@ -736,7 +861,8 @@ namespace qe { while (r != &t); } - void mk_equalities(bool pure, expr_ref_vector &res) { + template + void mk_equalities(expr_ref_vector &res) { for (term *t : m_tg.m_terms) { if (!t->is_root()) continue; if (!m_root2rep.contains(t->get_id())) continue; @@ -745,14 +871,15 @@ namespace qe { else mk_unpure_equalities(*t, res); } + TRACE("qe", tout << "literals: " << res << "\n";); } void mk_pure_equalities(expr_ref_vector &res) { - return mk_equalities(true, res); + mk_equalities(res); } void mk_unpure_equalities(expr_ref_vector &res) { - return mk_equalities(false, res); + mk_equalities(res); } // TBD: generalize for also the case of a (:var n) @@ -813,6 +940,19 @@ namespace qe { TRACE("qe", tout << "after distinct: " << res << "\n";); } + std::ostream& display(std::ostream& out) const { + m_tg.display(out); + out << "term2app:\n"; + for (auto const& kv : m_term2app) { + out << kv.m_key << " |-> " << mk_pp(kv.m_value, m) << "\n"; + } + out << "root2rep:\n"; + for (auto const& kv : m_root2rep) { + out << kv.m_key << " |-> " << mk_pp(kv.m_value, m) << "\n"; + } + return out; + } + public: projector(term_graph &tg) : m_tg(tg), m(m_tg.m), m_pinned(m) {} @@ -828,12 +968,27 @@ namespace qe { expr_ref_vector project() { expr_ref_vector res(m); purify(); - mk_lits(res); - mk_pure_equalities(res); - model_complete(res); + lits2pure(res); + mk_distinct(res); reset(); return res; } + + expr_ref_vector get_ackerman_disequalities() { + expr_ref_vector res(m); + purify(); + lits2pure(res); + unsigned sz = res.size(); + mk_distinct(res); + reset(); + unsigned j = 0; + for (unsigned i = sz; i < res.size(); ++i) { + res[j++] = res.get(i); + } + res.shrink(j); + return res; + } + expr_ref_vector solve() { expr_ref_vector res(m); purify(); @@ -843,6 +998,47 @@ namespace qe { reset(); return res; } + + vector get_partition(model& mdl) { + vector result; + expr_ref_vector pinned(m); + obj_map pid; + model::scoped_model_completion _smc(mdl, true); + for (term *t : m_tg.m_terms) { + expr* a = t->get_expr(); + if (!is_app(a)) continue; + if (m.is_bool(a)) continue; + expr_ref val = mdl(a); + unsigned p = 0; + // NB. works for simple domains Integers, Rationals, + // but not for algebraic numerals. + if (!pid.find(val, p)) { + p = pid.size(); + pid.insert(val, p); + pinned.push_back(val); + result.push_back(expr_ref_vector(m)); + } + result[p].push_back(a); + } + return result; + } + + expr_ref_vector shared_occurrences(family_id fid) { + expr_ref_vector result(m); + for (term *t : m_tg.m_terms) { + expr* e = t->get_expr(); + if (m.get_sort(e)->get_family_id() != fid) continue; + for (term * p : term::parents(t->get_root())) { + expr* pe = p->get_expr(); + if (!is_app(pe)) continue; + if (to_app(pe)->get_family_id() == fid) continue; + if (to_app(pe)->get_family_id() == m.get_basic_family_id()) continue; + result.push_back(e); + break; + } + } + return result; + } }; void term_graph::set_vars(func_decl_ref_vector const& decls, bool exclude) { @@ -870,4 +1066,20 @@ namespace qe { return p.solve(); } + expr_ref_vector term_graph::get_ackerman_disequalities() { + m_is_var.reset_solved(); + term_graph::projector p(*this); + return p.get_ackerman_disequalities(); + } + + vector term_graph::get_partition(model& mdl) { + term_graph::projector p(*this); + return p.get_partition(mdl); + } + + expr_ref_vector term_graph::shared_occurrences(family_id fid) { + term_graph::projector p(*this); + return p.shared_occurrences(fid); + } + } diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h index 19b694a76..855a0f2bc 100644 --- a/src/qe/qe_term_graph.h +++ b/src/qe/qe_term_graph.h @@ -75,6 +75,7 @@ namespace qe { void pick_roots(); void reset_marks(); + bool marks_are_clear(); expr* mk_app_core(expr* a); expr_ref mk_app(term const &t); @@ -102,7 +103,7 @@ namespace qe { // deprecate? void to_lits(expr_ref_vector &lits, bool all_equalities = false); - expr_ref to_app(); + expr_ref to_expr(); /** * Return literals obtained by projecting added literals @@ -113,6 +114,27 @@ namespace qe { expr_ref_vector solve(); expr_ref_vector project(model &mdl); + /** + * Return disequalities to ensure that disequalities between + * excluded functions are preserved. + * For example if f(a) = b, f(c) = d, and b and d are not + * congruent, then produce the disequality a != c. + */ + expr_ref_vector get_ackerman_disequalities(); + + /** + * Produce a model-based partition. + */ + vector get_partition(model& mdl); + + /** + * Extract shared occurrences of terms whose sort are + * fid, but appear in a context that is not fid. + * for example f(x + y) produces the shared occurrence + * x + y when f is uninterpreted and x + y has sort Int or Real. + */ + expr_ref_vector shared_occurrences(family_id fid); + }; } diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index a2e8fd8b1..c87b1c2eb 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -989,9 +989,10 @@ namespace qe { break; } case AST_QUANTIFIER: { + SASSERT(!is_lambda(e)); app_ref_vector vars(m); quantifier* q = to_quantifier(e); - bool is_fa = q->is_forall(); + bool is_fa = ::is_forall(q); tmp = q->get_expr(); extract_vars(q, tmp, vars); TRACE("qe", tout << vars << " " << mk_pp(q, m) << " " << tmp << "\n";); @@ -1235,6 +1236,9 @@ namespace qe { fml = push_not(fml); } hoist(fml); + if (!is_ground(fml)) { + throw tactic_exception("formula is not hoistable"); + } m_pred_abs.abstract_atoms(fml, defs); fml = m_pred_abs.mk_abstract(fml); m_ex.assert_expr(mk_and(defs)); @@ -1275,7 +1279,7 @@ namespace qe { if (s == "ok" || s == "unknown") { s = m_fa.s().reason_unknown(); } - throw tactic_exception(s.c_str()); + throw tactic_exception(std::move(s)); } } @@ -1340,7 +1344,7 @@ namespace qe { s = m_fa.s().reason_unknown(); } - throw tactic_exception(s.c_str()); + throw tactic_exception(std::move(s)); } return l_true; } diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index a59dd2b46..04a0274c4 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -17,6 +17,7 @@ Revision History: --*/ + #include #include "sat/sat_solver.h" #include "sat/sat_integrity_checker.h" @@ -298,6 +299,9 @@ namespace sat { if (!c.is_learned()) { m_stats.m_non_learned_generation++; } + if (c.frozen()) { + --m_num_frozen; + } if (m_config.m_drat && !m_drat.is_cleaned(c)) { m_drat.del(c); } @@ -481,9 +485,10 @@ 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]); + VERIFY(!c.frozen()); + DEBUG_CODE(for (auto const& w : m_watches[(~c[0]).index()]) SASSERT(!w.is_clause() || w.get_clause_offset() != cls_off);); + DEBUG_CODE(for (auto const& w : m_watches[(~c[1]).index()]) SASSERT(!w.is_clause() || w.get_clause_offset() != cls_off);); + SASSERT(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; @@ -2139,7 +2144,6 @@ namespace sat { else { c.inc_inact_rounds(); if (c.inact_rounds() > m_config.m_gc_k) { - m_num_frozen--; del_clause(c); m_stats.m_gc_clause++; deleted++; diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index b44c04604..ad972b2af 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -682,10 +682,10 @@ namespace sat { bool m_deleted; public: scoped_detach(solver& s, clause& c): s(s), c(c), m_deleted(false) { - s.detach_clause(c); + if (!c.frozen()) s.detach_clause(c); } ~scoped_detach() { - if (!m_deleted) s.attach_clause(c); + if (!m_deleted && !c.frozen()) s.attach_clause(c); } void del_clause() { diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 5f0fbec16..029ddf924 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -92,7 +92,7 @@ struct goal2sat::imp { void throw_op_not_handled(std::string const& s) { std::string s0 = "operator " + s + " not supported, apply simplifier before invoking translator"; - throw tactic_exception(s0.c_str()); + throw tactic_exception(std::move(s0)); } void mk_clause(sat::literal l) { diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp index ad9bcbd54..718f59854 100644 --- a/src/shell/lp_frontend.cpp +++ b/src/shell/lp_frontend.cpp @@ -85,7 +85,7 @@ void run_solver(lp_params & params, char const * mps_file_name) { solver->find_maximal_solution(); *(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl; - if (solver->get_status() == lp::OPTIMAL) { + if (solver->get_status() == lp::lp_status::OPTIMAL) { if (params.min()) { solver->flip_costs(); } diff --git a/src/smt/cached_var_subst.cpp b/src/smt/cached_var_subst.cpp index 1f184c39c..e096e00a3 100644 --- a/src/smt/cached_var_subst.cpp +++ b/src/smt/cached_var_subst.cpp @@ -63,7 +63,7 @@ void cached_var_subst::operator()(quantifier * qa, unsigned num_bindings, smt::e SASSERT(entry->get_data().m_value == 0); try { - m_proc(qa->get_expr(), new_key->m_num_bindings, new_key->m_bindings, result); + result = m_proc(qa->get_expr(), new_key->m_num_bindings, new_key->m_bindings); } catch (...) { // CMW: The var_subst reducer was interrupted and m_instances is diff --git a/src/smt/fingerprints.cpp b/src/smt/fingerprints.cpp index 832f539df..b9d4571da 100644 --- a/src/smt/fingerprints.cpp +++ b/src/smt/fingerprints.cpp @@ -20,9 +20,10 @@ Revision History: namespace smt { - fingerprint::fingerprint(region & r, void * d, unsigned d_h, unsigned n, enode * const * args): + fingerprint::fingerprint(region & r, void * d, unsigned d_h, expr* def, unsigned n, enode * const * args): m_data(d), m_data_hash(d_h), + m_def(def), m_num_args(n), m_args(nullptr) { m_args = new (r) enode*[n]; @@ -50,8 +51,18 @@ namespace smt { m_dummy.m_args = m_tmp.c_ptr(); return &m_dummy; } + + std::ostream& operator<<(std::ostream& out, fingerprint const& f) { + out << f.get_data_hash() << " " << " num_args " << f.get_num_args() << " "; + for (enode const * arg : f) { + out << " " << arg->get_owner_id(); + } + out << "\n"; + return out; + } + - fingerprint * fingerprint_set::insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args) { + fingerprint * fingerprint_set::insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def) { fingerprint * d = mk_dummy(data, data_hash, num_args, args); if (m_set.contains(d)) return nullptr; @@ -66,11 +77,10 @@ namespace smt { tout << "\n";); return nullptr; } - TRACE("fingerprint_bug", tout << "2) inserting: " << data_hash << " num_args: " << num_args; - for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_owner_id(); - tout << "\n";); - fingerprint * f = new (m_region) fingerprint(m_region, data, data_hash, num_args, d->m_args); + TRACE("fingerprint_bug", tout << "2) inserting: " << *d;); + fingerprint * f = new (m_region) fingerprint(m_region, data, data_hash, def, num_args, d->m_args); m_fingerprints.push_back(f); + m_defs.push_back(def); m_set.insert(f); return f; } @@ -89,6 +99,7 @@ namespace smt { void fingerprint_set::reset() { m_set.reset(); m_fingerprints.reset(); + m_defs.reset(); } void fingerprint_set::push_scope() { @@ -104,20 +115,15 @@ namespace smt { for (unsigned i = old_size; i < size; i++) m_set.erase(m_fingerprints[i]); m_fingerprints.shrink(old_size); + m_defs.shrink(old_size); m_scopes.shrink(new_lvl); } void fingerprint_set::display(std::ostream & out) const { out << "fingerprints:\n"; SASSERT(m_set.size() == m_fingerprints.size()); - ptr_vector::const_iterator it = m_fingerprints.begin(); - ptr_vector::const_iterator end = m_fingerprints.end(); - for (; it != end; ++it) { - fingerprint const * f = *it; - out << f->get_data() << " #" << f->get_data_hash(); - for (unsigned i = 0; i < f->get_num_args(); i++) - out << " #" << f->get_arg(i)->get_owner_id(); - out << "\n"; + for (fingerprint const * f : m_fingerprints) { + out << f->get_data() << " " << *f; } } @@ -126,10 +132,7 @@ namespace smt { \brief Slow function for checking if there is a fingerprint congruent to (data args[0] ... args[num_args-1]) */ bool fingerprint_set::slow_contains(void const * data, unsigned data_hash, unsigned num_args, enode * const * args) const { - ptr_vector::const_iterator it = m_fingerprints.begin(); - ptr_vector::const_iterator end = m_fingerprints.end(); - for (; it != end; ++it) { - fingerprint const * f = *it; + for (fingerprint const* f : m_fingerprints) { if (f->get_data() != data) continue; if (f->get_num_args() != num_args) @@ -139,12 +142,7 @@ namespace smt { if (f->get_arg(i)->get_root() != args[i]->get_root()) break; if (i == num_args) { - TRACE("missing_instance_detail", tout << "found instance data: " << data << "=" << f->get_data() << " hash: " << f->get_data_hash(); - for (unsigned i = 0; i < num_args; i++) { - tout << " " << f->get_arg(i)->get_owner_id() << ":" << f->get_arg(i)->get_root()->get_owner_id() << "=" - << args[i]->get_owner_id() << ":" << args[i]->get_root()->get_owner_id(); - } - tout << "\n";); + TRACE("missing_instance_detail", tout << "found instance data: " << data << "=" << *f;); return true; } } diff --git a/src/smt/fingerprints.h b/src/smt/fingerprints.h index fc3259ca7..a5b993470 100644 --- a/src/smt/fingerprints.h +++ b/src/smt/fingerprints.h @@ -20,6 +20,7 @@ Revision History: #define FINGERPRINTS_H_ #include "smt/smt_enode.h" +#include "util/util.h" namespace smt { @@ -27,18 +28,23 @@ namespace smt { protected: void * m_data; unsigned m_data_hash; + expr* m_def; unsigned m_num_args; enode * * m_args; friend class fingerprint_set; fingerprint() {} public: - fingerprint(region & r, void * d, unsigned d_hash, unsigned n, enode * const * args); + fingerprint(region & r, void * d, unsigned d_hash, expr* def, unsigned n, enode * const * args); void * get_data() const { return m_data; } + expr * get_def() const { return m_def; } unsigned get_data_hash() const { return m_data_hash; } unsigned get_num_args() const { return m_num_args; } enode * const * get_args() const { return m_args; } enode * get_arg(unsigned idx) const { SASSERT(idx < m_num_args); return m_args[idx]; } + enode * const * begin() const { return m_args; } + enode * const * end() const { return begin() + get_num_args(); } + friend std::ostream& operator<<(std::ostream& out, fingerprint const& f); }; class fingerprint_set { @@ -60,6 +66,7 @@ namespace smt { region & m_region; set m_set; ptr_vector m_fingerprints; + expr_ref_vector m_defs; unsigned_vector m_scopes; ptr_vector m_tmp; fingerprint m_dummy; @@ -67,8 +74,8 @@ namespace smt { fingerprint * mk_dummy(void * data, unsigned data_hash, unsigned num_args, enode * const * args); public: - fingerprint_set(region & r):m_region(r) {} - fingerprint * insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args); + fingerprint_set(ast_manager& m, region & r): m_region(r), m_defs(m) {} + fingerprint * insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def); unsigned size() const { return m_fingerprints.size(); } bool contains(void * data, unsigned data_hash, unsigned num_args, enode * const * args); void reset(); diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index a39fe96ed..c71a6e21d 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -16,15 +16,16 @@ Author: Revision History: --*/ -#include "smt/mam.h" -#include "smt/smt_context.h" +#include + #include "util/pool.h" -#include "ast/ast_pp.h" -#include "ast/ast_ll_pp.h" #include "util/trail.h" #include "util/stopwatch.h" +#include "ast/ast_pp.h" +#include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" -#include +#include "smt/mam.h" +#include "smt/smt_context.h" // #define _PROFILE_MAM @@ -569,9 +570,8 @@ namespace smt { if (m_context) { ast_manager & m = m_context->get_manager(); out << "patterns:\n"; - for (expr* p : m_patterns) { - out << mk_pp(p, m) << "\n"; - } + for (app* a : m_patterns) + out << mk_pp(a, m) << "\n"; } #endif out << "function: " << m_root_lbl->get_name(); @@ -1482,8 +1482,8 @@ namespace smt { } if (num_instr > SIMPLE_SEQ_THRESHOLD || (curr != nullptr && curr->m_opcode == CHOOSE)) simple = false; - for (unsigned reg : m_to_reset) - m_registers[reg] = 0; + for (unsigned r : m_to_reset) + m_registers[r] = 0; return weight; } @@ -2001,30 +2001,32 @@ namespace smt { TRACE("trigger_bug", tout << "execute for code tree:\n"; t->display(tout);); init(t); if (t->filter_candidates()) { - for (enode * app : t->get_candidates()) { + for (enode* app : t->get_candidates()) { if (!app->is_marked() && app->is_cgr()) { - execute_core(t, app); + if (m_context.resource_limits_exceeded() || !execute_core(t, app)) + return; app->set_mark(); } } - for (enode * app : t->get_candidates()) { + for (enode* app : t->get_candidates()) { if (app->is_marked()) app->unset_mark(); } } else { - for (enode * app : t->get_candidates()) { + for (enode* app : t->get_candidates()) { TRACE("trigger_bug", tout << "candidate\n" << mk_ismt2_pp(app->get_owner(), m_ast_manager) << "\n";); if (app->is_cgr()) { TRACE("trigger_bug", tout << "is_cgr\n";); - execute_core(t, app); + if (m_context.resource_limits_exceeded() || !execute_core(t, app)) + return; } } } } // init(t) must be invoked before execute_core - void execute_core(code_tree * t, enode * n); + bool execute_core(code_tree * t, enode * n); // Return the min, max generation of the enodes in m_pattern_instances. @@ -2253,7 +2255,7 @@ namespace smt { display_instr_input_reg(out, m_pc); } - void interpreter::execute_core(code_tree * t, enode * n) { + bool interpreter::execute_core(code_tree * t, enode * n) { TRACE("trigger_bug", tout << "interpreter::execute_core\n"; t->display(tout); tout << "\nenode\n" << mk_ismt2_pp(n->get_owner(), m_ast_manager) << "\n";); unsigned since_last_check = 0; @@ -2501,7 +2503,7 @@ namespace smt { #define ON_MATCH(NUM) \ m_max_generation = std::max(m_max_generation, get_max_generation(NUM, m_bindings.begin())); \ if (m_context.get_cancel_flag()) { \ - return; \ + return false; \ } \ m_mam.on_match(static_cast(m_pc)->m_qa, \ static_cast(m_pc)->m_pat, \ @@ -2654,7 +2656,7 @@ namespace smt { #ifdef _PROFILE_MAM t->get_watch().stop(); #endif - return; // no more alternatives + return true; // no more alternatives } backtrack_point & bp = m_backtrack_stack[m_top - 1]; m_max_generation = bp.m_old_max_generation; @@ -2682,7 +2684,7 @@ namespace smt { #ifdef _PROFILE_MAM t->get_watch().stop(); #endif - return; + return false; } } @@ -2800,11 +2802,12 @@ namespace smt { default: UNREACHABLE(); } + return false; } // end of execute_core void display_trees(std::ostream & out, const ptr_vector & trees) { unsigned lbl = 0; - for (code_tree* tree : trees) { + for (code_tree * tree : trees) { if (tree) { out << "tree for f" << lbl << "\n"; out << *tree; @@ -3150,10 +3153,7 @@ namespace smt { m_trail_stack.push(set_bitvector_trail(m_is_clbl, lbl_id)); SASSERT(m_is_clbl[lbl_id]); unsigned h = m_lbl_hasher(lbl); - enode_vector::const_iterator it = m_context.begin_enodes_of(lbl); - enode_vector::const_iterator end = m_context.end_enodes_of(lbl); - for (; it != end; ++it) { - enode * app = *it; + for (enode* app : m_context.enodes_of(lbl)) { if (m_context.is_relevant(app)) { update_lbls(app, h); TRACE("mam_bug", tout << "updating labels of: #" << app->get_owner_id() << "\n"; @@ -3195,10 +3195,7 @@ namespace smt { SASSERT(m_is_plbl[lbl_id]); SASSERT(is_plbl(lbl)); unsigned h = m_lbl_hasher(lbl); - enode_vector::const_iterator it = m_context.begin_enodes_of(lbl); - enode_vector::const_iterator end = m_context.end_enodes_of(lbl); - for (; it != end; ++it) { - enode * app = *it; + for (enode * app : m_context.enodes_of(lbl)) { if (m_context.is_relevant(app)) update_children_plbls(app, h); } @@ -3684,18 +3681,12 @@ namespace smt { approx_set & plbls = r1->get_plbls(); approx_set & clbls = r2->get_lbls(); if (!plbls.empty() && !clbls.empty()) { - approx_set::iterator it1 = plbls.begin(); - approx_set::iterator end1 = plbls.end(); - for (; it1 != end1; ++it1) { + for (unsigned plbl1 : plbls) { if (m_context.get_cancel_flag()) { break; } - unsigned plbl1 = *it1; SASSERT(plbls.may_contain(plbl1)); - approx_set::iterator it2 = clbls.begin(); - approx_set::iterator end2 = clbls.end(); - for (; it2 != end2; ++it2) { - unsigned lbl2 = *it2; + for (unsigned lbl2 : clbls) { SASSERT(clbls.may_contain(lbl2)); collect_parents(r1, m_pc[plbl1][lbl2]); } @@ -3706,14 +3697,12 @@ namespace smt { void match_new_patterns() { TRACE("mam_new_pat", tout << "matching new patterns:\n";); m_tmp_trees_to_delete.reset(); - svector::iterator it1 = m_new_patterns.begin(); - svector::iterator end1 = m_new_patterns.end(); - for (; it1 != end1; ++it1) { + for (auto const& kv : m_new_patterns) { if (m_context.get_cancel_flag()) { break; } - quantifier * qa = it1->first; - app * mp = it1->second; + quantifier * qa = kv.first; + app * mp = kv.second; SASSERT(m_ast_manager.is_pattern(mp)); app * p = to_app(mp->get_arg(0)); func_decl * lbl = p->get_decl(); @@ -3730,19 +3719,13 @@ namespace smt { } } - ptr_vector::iterator it2 = m_tmp_trees_to_delete.begin(); - ptr_vector::iterator end2 = m_tmp_trees_to_delete.end(); - for (; it2 != end2; ++it2) { - func_decl * lbl = *it2; + for (func_decl * lbl : m_tmp_trees_to_delete) { unsigned lbl_id = lbl->get_decl_id(); code_tree * tmp_tree = m_tmp_trees[lbl_id]; SASSERT(tmp_tree != 0); SASSERT(m_context.get_num_enodes_of(lbl) > 0); m_interpreter.init(tmp_tree); - enode_vector::const_iterator it3 = m_context.begin_enodes_of(lbl); - enode_vector::const_iterator end3 = m_context.end_enodes_of(lbl); - for (; it3 != end3; ++it3) { - enode * app = *it3; + for (enode * app : m_context.enodes_of(lbl)) { if (m_context.is_relevant(app)) m_interpreter.execute_core(tmp_tree, app); } @@ -3831,10 +3814,7 @@ namespace smt { void pop_scope(unsigned num_scopes) override { if (!m_to_match.empty()) { - ptr_vector::iterator it = m_to_match.begin(); - ptr_vector::iterator end = m_to_match.end(); - for (; it != end; ++it) { - code_tree * t = *it; + for (code_tree* t : m_to_match) { t->reset_candidates(); } m_to_match.reset(); @@ -3867,10 +3847,7 @@ namespace smt { void match() override { TRACE("trigger_bug", tout << "match\n"; display(tout);); - ptr_vector::iterator it = m_to_match.begin(); - ptr_vector::iterator end = m_to_match.end(); - for (; it != end; ++it) { - code_tree * t = *it; + for (code_tree* t : m_to_match) { SASSERT(t->has_candidates()); m_interpreter.execute(t); t->reset_candidates(); @@ -3891,10 +3868,7 @@ namespace smt { if (t) { m_interpreter.init(t); func_decl * lbl = t->get_root_lbl(); - enode_vector::const_iterator it2 = m_context.begin_enodes_of(lbl); - enode_vector::const_iterator end2 = m_context.end_enodes_of(lbl); - for (; it2 != end2; ++it2) { - enode * curr = *it2; + for (enode * curr : m_context.enodes_of(lbl)) { if (use_irrelevant || m_context.is_relevant(curr)) m_interpreter.execute_core(t, curr); } @@ -3931,7 +3905,7 @@ namespace smt { #endif unsigned min_gen, max_gen; m_interpreter.get_min_max_top_generation(min_gen, max_gen); - m_context.add_instance(qa, pat, num_bindings, bindings, max_generation, min_gen, max_gen, used_enodes); + m_context.add_instance(qa, pat, num_bindings, bindings, nullptr, max_generation, min_gen, max_gen, used_enodes); } bool is_shared(enode * n) const override { diff --git a/src/smt/params/CMakeLists.txt b/src/smt/params/CMakeLists.txt index c965f0a62..4beec80f0 100644 --- a/src/smt/params/CMakeLists.txt +++ b/src/smt/params/CMakeLists.txt @@ -8,6 +8,7 @@ z3_add_component(smt_params theory_array_params.cpp theory_bv_params.cpp theory_pb_params.cpp + theory_seq_params.cpp theory_str_params.cpp COMPONENT_DEPENDENCIES ast diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index 32b634626..8901697b7 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -26,6 +26,7 @@ Revision History: #include "smt/params/theory_array_params.h" #include "smt/params/theory_bv_params.h" #include "smt/params/theory_str_params.h" +#include "smt/params/theory_seq_params.h" #include "smt/params/theory_pb_params.h" #include "smt/params/theory_datatype_params.h" #include "smt/params/preprocessor_params.h" @@ -79,6 +80,7 @@ struct smt_params : public preprocessor_params, public theory_array_params, public theory_bv_params, public theory_str_params, + public theory_seq_params, public theory_pb_params, public theory_datatype_params { bool m_display_proof; diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 816764896..3f4105c34 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -71,6 +71,7 @@ def_module_params(module_name='smt', ('theory_case_split', BOOL, False, 'Allow the context to use heuristics involving theory case splits, which are a set of literals of which exactly one can be assigned True. If this option is false, the context will generate extra axioms to enforce this instead.'), ('string_solver', SYMBOL, 'seq', 'solver for string/sequence theories. options are: \'z3str3\' (specialized string solver), \'seq\' (sequence solver), \'auto\' (use static features to choose best solver)'), ('core.validate', BOOL, False, 'validate unsat core produced by SMT context'), + ('seq.split_w_len', BOOL, True, 'enable splitting guided by length constraints'), ('str.strong_arrangements', BOOL, True, 'assert equivalences instead of implications when generating string arrangement axioms'), ('str.aggressive_length_testing', BOOL, False, 'prioritize testing concrete length values over generating more options'), ('str.aggressive_value_testing', BOOL, False, 'prioritize testing concrete string constant values over generating more options'), diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index eb1459058..9d09b4c38 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -25,11 +25,11 @@ Revision History: enum arith_solver_id { AS_NO_ARITH, // 0 AS_DIFF_LOGIC, // 1 - AS_ARITH, // 2 + AS_OLD_ARITH, // 2 AS_DENSE_DIFF_LOGIC, // 3 AS_UTVPI, // 4 AS_OPTINF, // 5 - AS_LRA // 6 + AS_NEW_ARITH // 6 }; enum bound_prop_mode { @@ -113,7 +113,7 @@ struct theory_arith_params { theory_arith_params(params_ref const & p = params_ref()): m_arith_eq2ineq(false), m_arith_process_all_eqs(false), - m_arith_mode(AS_ARITH), + m_arith_mode(AS_NEW_ARITH), m_arith_auto_config_simplex(false), m_arith_blands_rule_threshold(1000), m_arith_propagate_eqs(true), diff --git a/src/smt/params/theory_array_params.h b/src/smt/params/theory_array_params.h index edda4d7cf..a3be083d7 100644 --- a/src/smt/params/theory_array_params.h +++ b/src/smt/params/theory_array_params.h @@ -40,6 +40,7 @@ struct theory_array_params { bool m_array_always_prop_upward; bool m_array_lazy_ieq; unsigned m_array_lazy_ieq_delay; + bool m_array_fake_support; // fake support for all array operations to pretend they are satisfiable. theory_array_params(): m_array_canonize_simplify(false), @@ -52,7 +53,8 @@ struct theory_array_params { m_array_cg(false), m_array_always_prop_upward(true), // UPWARDs filter is broken... TODO: fix it m_array_lazy_ieq(false), - m_array_lazy_ieq_delay(10) { + m_array_lazy_ieq_delay(10), + m_array_fake_support(false) { } diff --git a/src/smt/params/theory_seq_params.cpp b/src/smt/params/theory_seq_params.cpp new file mode 100644 index 000000000..e521298d3 --- /dev/null +++ b/src/smt/params/theory_seq_params.cpp @@ -0,0 +1,23 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + theory_seq_params.cpp + +Abstract: + + Parameters for sequence theory plugin + +Revision History: + + +--*/ + +#include "smt/params/theory_seq_params.h" +#include "smt/params/smt_params_helper.hpp" + +void theory_seq_params::updt_params(params_ref const & _p) { + smt_params_helper p(_p); + m_split_w_len = p.seq_split_w_len(); +} diff --git a/src/smt/params/theory_seq_params.h b/src/smt/params/theory_seq_params.h new file mode 100644 index 000000000..24e67b8ff --- /dev/null +++ b/src/smt/params/theory_seq_params.h @@ -0,0 +1,38 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + theory_seq_params.h + +Abstract: + + Parameters for sequence theory plugin + +Revision History: + + +--*/ + +#ifndef THEORY_SEQ_PARAMS_H +#define THEORY_SEQ_PARAMS_H + +#include "util/params.h" + +struct theory_seq_params { + /* + * Enable splitting guided by length constraints + */ + bool m_split_w_len; + + + theory_seq_params(params_ref const & p = params_ref()): + m_split_w_len(true) + { + updt_params(p); + } + + void updt_params(params_ref const & p); +}; + +#endif /* THEORY_SEQ_PARAMS_H */ diff --git a/src/smt/proto_model/array_factory.cpp b/src/smt/proto_model/array_factory.cpp index 919f18dc0..dc3e0f89c 100644 --- a/src/smt/proto_model/array_factory.cpp +++ b/src/smt/proto_model/array_factory.cpp @@ -17,11 +17,11 @@ Revision History: --*/ -#include "smt/proto_model/array_factory.h" #include "ast/array_decl_plugin.h" -#include "smt/proto_model/proto_model.h" -#include "model/func_interp.h" #include "ast/ast_pp.h" +#include "model/func_interp.h" +#include "smt/proto_model/array_factory.h" +#include "smt/proto_model/proto_model.h" func_decl * mk_aux_decl_for_array_sort(ast_manager & m, sort * s) { ptr_buffer domain; @@ -67,9 +67,13 @@ expr * array_factory::get_some_value(sort * s) { return *(set->begin()); func_interp * fi; expr * val = mk_array_interp(s, fi); +#if 0 ptr_buffer args; get_some_args_for(s, args); fi->insert_entry(args.c_ptr(), m_model.get_some_value(get_array_range(s))); +#else + fi->set_else(m_model.get_some_value(get_array_range(s))); +#endif return val; } @@ -143,9 +147,13 @@ expr * array_factory::get_fresh_value(sort * s) { // easy case func_interp * fi; expr * val = mk_array_interp(s, fi); +#if 0 ptr_buffer args; get_some_args_for(s, args); fi->insert_entry(args.c_ptr(), range_val); +#else + fi->set_else(range_val); +#endif return val; } else { diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index 8af3af277..654925f72 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -18,13 +18,15 @@ Revision History: --*/ #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" -#include "ast/rewriter/var_subst.h" #include "ast/well_sorted.h" #include "ast/used_symbols.h" +#include "ast/for_each_expr.h" +#include "ast/rewriter/var_subst.h" #include "model/model_params.hpp" #include "model/model_v2_pp.h" #include "smt/proto_model/proto_model.h" + proto_model::proto_model(ast_manager & m, params_ref const & p): model_core(m), m_eval(*this), @@ -75,7 +77,7 @@ expr * proto_model::mk_some_interp_for(func_decl * d) { register_decl(d, r); } else { - func_interp * new_fi = alloc(func_interp, m_manager, d->get_arity()); + func_interp * new_fi = alloc(func_interp, m, d->get_arity()); new_fi->set_else(r); register_decl(d, new_fi); } @@ -86,6 +88,7 @@ expr * proto_model::mk_some_interp_for(func_decl * d) { bool proto_model::eval(expr * e, expr_ref & result, bool model_completion) { m_eval.set_model_completion(model_completion); m_eval.set_expand_array_equalities(false); + TRACE("model_evaluator", model_v2_pp(tout, *this, true);); try { m_eval(e, result); return true; @@ -120,10 +123,10 @@ void proto_model::cleanup_func_interp(func_interp * fi, func_decl_set & found_au if (fi->is_partial()) return; expr * fi_else = fi->get_else(); - TRACE("model_bug", tout << "cleaning up:\n" << mk_pp(fi_else, m_manager) << "\n";); + TRACE("model_bug", tout << "cleaning up:\n" << mk_pp(fi_else, m) << "\n";); obj_map cache; - expr_ref_vector trail(m_manager); + expr_ref_vector trail(m); ptr_buffer todo; ptr_buffer args; todo.push_back(fi_else); @@ -165,7 +168,7 @@ void proto_model::cleanup_func_interp(func_interp * fi, func_decl_set & found_au TRACE("model_bug", tout << f->get_name() << "\n";); found_aux_fs.insert(f); } - expr_ref new_t(m_manager); + expr_ref new_t(m); new_t = m_rewrite.mk_app(f, args.size(), args.c_ptr()); if (t != new_t.get()) trail.push_back(new_t); @@ -235,7 +238,7 @@ value_factory * proto_model::get_factory(family_id fid) { } void proto_model::freeze_universe(sort * s) { - SASSERT(m_manager.is_uninterp(s)); + SASSERT(m.is_uninterp(s)); m_user_sort_factory->freeze_universe(s); } @@ -270,11 +273,11 @@ sort * proto_model::get_uninterpreted_sort(unsigned idx) const { in the model. */ bool proto_model::is_finite(sort * s) const { - return m_manager.is_uninterp(s) && m_user_sort_factory->is_finite(s); + return m.is_uninterp(s) && m_user_sort_factory->is_finite(s); } expr * proto_model::get_some_value(sort * s) { - if (m_manager.is_uninterp(s)) { + if (m.is_uninterp(s)) { return m_user_sort_factory->get_some_value(s); } else { @@ -288,7 +291,7 @@ expr * proto_model::get_some_value(sort * s) { } bool proto_model::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { - if (m_manager.is_uninterp(s)) { + if (m.is_uninterp(s)) { return m_user_sort_factory->get_some_values(s, v1, v2); } else { @@ -302,7 +305,7 @@ bool proto_model::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { } expr * proto_model::get_fresh_value(sort * s) { - if (m_manager.is_uninterp(s)) { + if (m.is_uninterp(s)) { return m_user_sort_factory->get_fresh_value(s); } else { @@ -318,8 +321,8 @@ expr * proto_model::get_fresh_value(sort * s) { } void proto_model::register_value(expr * n) { - sort * s = m_manager.get_sort(n); - if (m_manager.is_uninterp(s)) { + sort * s = m.get_sort(n); + if (m.is_uninterp(s)) { m_user_sort_factory->register_value(n); } else { @@ -374,15 +377,15 @@ void proto_model::complete_partial_funcs(bool use_fresh) { model * proto_model::mk_model() { TRACE("proto_model", model_v2_pp(tout << "mk_model\n", *this);); - model * m = alloc(model, m_manager); + model * mdl = alloc(model, m); for (auto const& kv : m_interp) { - m->register_decl(kv.m_key, kv.m_value); + mdl->register_decl(kv.m_key, kv.m_value); } for (auto const& kv : m_finterp) { - m->register_decl(kv.m_key, kv.m_value); - m_manager.dec_ref(kv.m_key); + mdl->register_decl(kv.m_key, kv.m_value); + m.dec_ref(kv.m_key); } m_finterp.reset(); // m took the ownership of the func_interp's @@ -390,10 +393,11 @@ model * proto_model::mk_model() { unsigned sz = get_num_uninterpreted_sorts(); for (unsigned i = 0; i < sz; i++) { sort * s = get_uninterpreted_sort(i); - TRACE("proto_model", tout << "copying uninterpreted sorts...\n" << mk_pp(s, m_manager) << "\n";); + TRACE("proto_model", tout << "copying uninterpreted sorts...\n" << mk_pp(s, m) << "\n";); ptr_vector const& buf = get_universe(s); - m->register_usort(s, buf.size(), buf.c_ptr()); + mdl->register_usort(s, buf.size(), buf.c_ptr()); } - return m; + return mdl; } + diff --git a/src/smt/proto_model/proto_model.h b/src/smt/proto_model/proto_model.h index 04e3a90fe..b9ed65fb1 100644 --- a/src/smt/proto_model/proto_model.h +++ b/src/smt/proto_model/proto_model.h @@ -38,6 +38,7 @@ Revision History: #include "util/params.h" #include "ast/rewriter/th_rewriter.h" + class proto_model : public model_core { plugin_manager m_factories; user_sort_factory * m_user_sort_factory; diff --git a/src/smt/qi_queue.cpp b/src/smt/qi_queue.cpp index 4e639a7b2..94868ef6e 100644 --- a/src/smt/qi_queue.cpp +++ b/src/smt/qi_queue.cpp @@ -148,7 +148,7 @@ namespace smt { } void qi_queue::instantiate() { - unsigned since_last_check = 0; + unsigned since_last_check = 0; for (entry & curr : m_new_entries) { fingerprint * f = curr.m_qb; quantifier * qa = static_cast(f->get_data()); @@ -169,7 +169,6 @@ namespace smt { // Periodically check if we didn't run out of time/memory. if (since_last_check++ > 100) { if (m_context.resource_limits_exceeded()) { - // verbose_stream() << "EXCEEDED...\n"; break; } since_last_check = 0; @@ -182,16 +181,7 @@ namespace smt { void qi_queue::display_instance_profile(fingerprint * f, quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned proof_id, unsigned generation) { if (m_manager.has_trace_stream()) { m_manager.trace_stream() << "[instance] "; -#if 1 m_manager.trace_stream() << static_cast(f); -#else - for (unsigned i = 0; i < num_bindings; i++) { - // I don't want to use mk_pp because it creates expressions for pretty printing. - // This nasty side-effect may change the behavior of Z3. - m_manager.trace_stream() << " #" << bindings[i]->get_owner_id(); - } - -#endif if (m_manager.proofs_enabled()) m_manager.trace_stream() << " #" << proof_id; m_manager.trace_stream() << " ; " << generation; @@ -208,10 +198,7 @@ namespace smt { ent.m_instantiated = true; - TRACE("qi_queue_profile", - tout << q->get_qid() << ", gen: " << generation; - for (unsigned i = 0; i < num_bindings; i++) tout << " #" << bindings[i]->get_owner_id(); - tout << "\n";); + TRACE("qi_queue_profile", tout << q->get_qid() << ", gen: " << generation << " " << *f;); if (m_checker.is_sat(q->get_expr(), num_bindings, bindings)) { TRACE("checker", tout << "instance already satisfied\n";); @@ -288,6 +275,9 @@ namespace smt { unsigned gen = get_new_gen(q, generation, ent.m_cost); display_instance_profile(f, q, num_bindings, bindings, proof_id, gen); m_context.internalize_instance(lemma, pr1, gen); + if (f->get_def()) { + m_context.internalize(f->get_def(), true); + } TRACE_CODE({ static unsigned num_useless = 0; if (m_manager.is_or(lemma)) { @@ -412,10 +402,7 @@ namespace smt { void qi_queue::display_delayed_instances_stats(std::ostream & out) const { obj_map qa2info; ptr_vector qas; - svector::const_iterator it = m_delayed_entries.begin(); - svector::const_iterator end = m_delayed_entries.end(); - for (; it != end; ++it) { - entry const & e = *it; + for (entry const & e : m_delayed_entries) { if (e.m_instantiated) continue; quantifier * qa = static_cast(e.m_qb->get_data()); @@ -433,10 +420,7 @@ namespace smt { } qa2info.insert(qa, info); } - ptr_vector::iterator it2 = qas.begin(); - ptr_vector::iterator end2 = qas.end(); - for (; it2 != end2; ++it2) { - quantifier * qa = *it2; + for (quantifier * qa : qas) { delayed_qa_info info; qa2info.find(qa, info); out << qa->get_qid() << ": " << info.m_num << " [" << info.m_min_cost << ", " << info.m_max_cost << "]\n"; diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index bf1f789c4..70b55acee 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -53,7 +53,7 @@ namespace smt { m_flushing(false), m_progress_callback(nullptr), m_next_progress_sample(0), - m_fingerprints(m_region), + m_fingerprints(m, m_region), m_b_internalized_stack(m), m_e_internalized_stack(m), m_final_check_idx(0), @@ -1598,7 +1598,7 @@ namespace smt { for (literal lit : m_assigned_literals) { expr_ref e(m_manager); literal2expr(lit, e); - assignments.push_back(e); + assignments.push_back(std::move(e)); } } @@ -1789,9 +1789,9 @@ namespace smt { return m_fingerprints.contains(q, q->get_id(), num_bindings, bindings); } - bool context::add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, + bool context::add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, expr* def, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, vector> & used_enodes) { - return m_qmanager->add_instance(q, pat, num_bindings, bindings, max_generation, min_top_generation, max_top_generation, used_enodes); + return m_qmanager->add_instance(q, pat, num_bindings, bindings, def, max_generation, min_top_generation, max_top_generation, used_enodes); } void context::rescale_bool_var_activity() { @@ -2936,7 +2936,7 @@ namespace smt { void context::assert_expr_core(expr * e, proof * pr) { if (get_cancel_flag()) return; SASSERT(is_well_sorted(m_manager, e)); - TRACE("begin_assert_expr", tout << mk_pp(e, m_manager) << "\n";); + TRACE("begin_assert_expr", tout << this << " " << mk_pp(e, m_manager) << "\n";); TRACE("begin_assert_expr_ll", tout << mk_ll_pp(e, m_manager) << "\n";); pop_to_base_lvl(); if (pr == nullptr) @@ -3767,6 +3767,7 @@ namespace smt { } m_stats.m_num_final_checks++; + TRACE("final_check_stats", tout << "m_stats.m_num_final_checks = " << m_stats.m_num_final_checks << "\n";); final_check_status ok = m_qmanager->final_check_eh(false); if (ok != FC_DONE) @@ -4184,7 +4185,7 @@ namespace smt { SASSERT(get_justification(guess.var()).get_kind() == b_justification::AXIOM); expr_ref lit(m_manager); literal2expr(guess, lit); - result.push_back(lit); + result.push_back(std::move(lit)); } } @@ -4405,9 +4406,9 @@ namespace smt { for (unsigned i = 0; !get_cancel_flag() && i < m_asserted_formulas.get_num_formulas(); ++i) { expr* e = m_asserted_formulas.get_formula(i); if (is_quantifier(e)) { - TRACE("context", tout << mk_pp(e, m) << "\n";); quantifier* q = to_quantifier(e); if (!m.is_rec_fun_def(q)) continue; + TRACE("context", tout << mk_pp(e, m) << "\n";); SASSERT(q->get_num_patterns() == 2); expr* fn = to_app(q->get_pattern(0))->get_arg(0); expr* body = to_app(q->get_pattern(1))->get_arg(0); @@ -4421,7 +4422,7 @@ namespace smt { expr_ref bodyr(m); var_subst sub(m, true); TRACE("context", tout << expr_ref(q, m) << " " << subst << "\n";); - sub(body, subst.size(), subst.c_ptr(), bodyr); + bodyr = sub(body, subst.size(), subst.c_ptr()); func_decl* f = to_app(fn)->get_decl(); func_interp* fi = alloc(func_interp, m, f->get_arity()); fi->set_else(bodyr); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index f3c3fc69f..77b4ca79c 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -117,6 +117,7 @@ namespace smt { plugin_manager m_theories; // mapping from theory_id -> theory ptr_vector m_theory_set; // set of theories for fast traversal vector m_decl2enodes; // decl -> enode (for decls with arity > 0) + enode_vector m_empty_vector; cg_table m_cg_table; dyn_ack_manager m_dyn_ack_manager; struct new_eq { @@ -457,6 +458,8 @@ namespace smt { theory * get_theory(theory_id th_id) const { return m_theories.get_plugin(th_id); } + + ptr_vector const& theories() const { return m_theories.plugins(); } ptr_vector::const_iterator begin_theories() const { return m_theories.begin(); @@ -518,6 +521,11 @@ namespace smt { return id < m_decl2enodes.size() ? m_decl2enodes[id].size() : 0; } + enode_vector const& enodes_of(func_decl const * d) const { + unsigned id = d->get_decl_id(); + return id < m_decl2enodes.size() ? m_decl2enodes[id] : m_empty_vector; + } + enode_vector::const_iterator begin_enodes_of(func_decl const * decl) const { unsigned id = decl->get_decl_id(); return id < m_decl2enodes.size() ? m_decl2enodes[id].begin() : nullptr; @@ -528,6 +536,8 @@ namespace smt { return id < m_decl2enodes.size() ? m_decl2enodes[id].end() : nullptr; } + ptr_vector const& enodes() const { return m_enodes; } + ptr_vector::const_iterator begin_enodes() const { return m_enodes.begin(); } @@ -554,8 +564,8 @@ namespace smt { return m_asserted_formulas.has_quantifiers(); } - fingerprint * add_fingerprint(void * data, unsigned data_hash, unsigned num_args, enode * const * args) { - return m_fingerprints.insert(data, data_hash, num_args, args); + fingerprint * add_fingerprint(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def = 0) { + return m_fingerprints.insert(data, data_hash, num_args, args, def); } theory_id get_var_theory(bool_var v) const { @@ -737,6 +747,8 @@ namespace smt { void internalize_quantifier(quantifier * q, bool gate_ctx); + void internalize_lambda(quantifier * q); + void internalize_formula_core(app * n, bool gate_ctx); void set_merge_tf(enode * n, bool_var v, bool is_new_var); @@ -958,7 +970,7 @@ namespace smt { bool contains_instance(quantifier * q, unsigned num_bindings, enode * const * bindings); - bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, + bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, expr* def, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, vector> & used_enodes /*gives the equalities used for the pattern match, see mam.cpp for more info*/); void set_global_generation(unsigned generation) { m_generation = generation; } @@ -1104,8 +1116,6 @@ namespace smt { void internalize_assertions(); - void assert_assumption(expr * a); - bool validate_assumptions(expr_ref_vector const& asms); void init_assumptions(expr_ref_vector const& asms); @@ -1118,8 +1128,6 @@ namespace smt { void reset_assumptions(); - void reset_clause(); - void add_theory_assumptions(expr_ref_vector & theory_assumptions); lbool mk_unsat_core(lbool result); @@ -1574,8 +1582,6 @@ namespace smt { //proof * const * get_asserted_formula_proofs() const { return m_asserted_formulas.get_formula_proofs(); } - void get_assumptions_core(ptr_vector & result); - void get_assertions(ptr_vector & result) { m_asserted_formulas.get_assertions(result); } void display(std::ostream & out) const; diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 6f971acc8..3a2aacd26 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -409,11 +409,11 @@ namespace smt { for (unsigned i = 0; i < num_antecedents; i++) { literal l = antecedents[i]; literal2expr(l, n); - fmls.push_back(n); + fmls.push_back(std::move(n)); } if (consequent != false_literal) { literal2expr(~consequent, n); - fmls.push_back(n); + fmls.push_back(std::move(n)); } if (logic != symbol::null) out << "(set-logic " << logic << ")\n"; visitor.collect(fmls); diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index f9ee900ff..ae023078a 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -329,6 +329,9 @@ namespace smt { SASSERT(is_quantifier(n) || is_app(n)); internalize_formula(n, gate_ctx); } + else if (is_lambda(n)) { + internalize_lambda(to_quantifier(n)); + } else { SASSERT(is_app(n)); SASSERT(!gate_ctx); @@ -514,7 +517,7 @@ namespace smt { CTRACE("internalize_quantifier_zero", q->get_weight() == 0, tout << mk_pp(q, m_manager) << "\n";); SASSERT(gate_ctx); // limitation of the current implementation SASSERT(!b_internalized(q)); - SASSERT(q->is_forall()); + SASSERT(is_forall(q)); SASSERT(check_patterns(q)); bool_var v = mk_bool_var(q); unsigned generation = m_generation; @@ -528,6 +531,31 @@ namespace smt { m_qmanager->add(q, generation); } + void context::internalize_lambda(quantifier * q) { + UNREACHABLE(); + +#if 0 + TRACE("internalize_quantifier", tout << mk_pp(q, m_manager) << "\n";); + SASSERT(is_lambda(q)); + app_ref lam_name(m_manager.mk_fresh_const("lambda", m_manager.get_sort(q)), m_manager); + enode * e = mk_enode(lam_name, true, false, false); + expr_ref eq(m_manager), lam_app(m_manager); + expr_ref_vector vars(m_manager); + vars.push_back(lam_name); + unsigned sz = q->get_num_decls(); + for (unsigned i = 0; i < sz; ++i) { + vars.push_back(m_manager.mk_var(sz - i - 1, q->get_decl_sort(i))); + } + array_util autil(m_manager); + lam_app = autil.mk_select(vars.size(), vars.c_ptr()); + eq = m_manager.mk_eq(lam_app, q->get_expr()); + quantifier_ref fa(m_manager); + expr * patterns[1] = { m_manager.mk_pattern(lam_name) }; + fa = m_manager.mk_forall(sz, q->get_decl_sorts(), q->get_decl_names(), eq, 0, m_manager.lambda_def_qid(), symbol::null, 1, patterns); + internalize_quantifier(fa, true); +#endif + } + /** \brief Internalize gates and (uninterpreted and equality) predicates. */ @@ -821,7 +849,7 @@ namespace smt { std::cerr << v << " ::=\n" << mk_ll_pp(n, m_manager) << "\n"; } #endif - TRACE("mk_bool_var", tout << "creating boolean variable: " << v << " for:\n" << mk_pp(n, m_manager) << "\n";); + TRACE("mk_bool_var", tout << "creating boolean variable: " << v << " for:\n" << mk_pp(n, m_manager) << " " << n->get_id() << "\n";); TRACE("mk_var_bug", tout << "mk_bool: " << v << "\n";); set_bool_var(id, v); m_bdata.reserve(v+1); diff --git a/src/smt/smt_justification.cpp b/src/smt/smt_justification.cpp index 4ac176a2b..fc9307792 100644 --- a/src/smt/smt_justification.cpp +++ b/src/smt/smt_justification.cpp @@ -281,7 +281,7 @@ namespace smt { for (unsigned i = 0; i < m_num_literals; i++) { expr_ref l(m); ctx.literal2expr(m_literals[i], l); - lits.push_back(l); + lits.push_back(std::move(l)); } if (lits.size() == 1) return m.mk_th_lemma(m_th_id, lits.get(0), 0, nullptr, m_params.size(), m_params.c_ptr()); @@ -407,12 +407,7 @@ namespace smt { for (unsigned i = 0; i < m_num_literals; i++) { bool sign = GET_TAG(m_literals[i]) != 0; expr * v = UNTAG(expr*, m_literals[i]); - expr_ref l(m); - if (sign) - l = m.mk_not(v); - else - l = v; - lits.push_back(l); + lits.push_back(sign ? m.mk_not(v) : v); } if (lits.size() == 1) return m.mk_th_lemma(m_th_id, lits.get(0), 0, nullptr, m_params.size(), m_params.c_ptr()); diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index 7dde3bb00..f4c5c09d9 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -15,12 +15,19 @@ Author: Revision History: +- to support lambdas/array models: + binding sk -> (as-array k!0) + then include definition for k!0 as part of binding. + Binding instance can be a pointer into m_pinned expressions. + --*/ #include "ast/normal_forms/pull_quant.h" #include "ast/for_each_expr.h" #include "ast/rewriter/var_subst.h" +#include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" +#include "ast/array_decl_plugin.h" #include "ast/ast_smt2_pp.h" #include "smt/smt_model_checker.h" #include "smt/smt_context.h" @@ -54,8 +61,8 @@ namespace smt { } void model_checker::set_qm(quantifier_manager & qm) { - SASSERT(m_qm == 0); - SASSERT(m_context == 0); + SASSERT(m_qm == nullptr); + SASSERT(m_context == nullptr); m_qm = &qm; m_context = &(m_qm->get_context()); } @@ -64,6 +71,13 @@ namespace smt { \brief Return a term in the context that evaluates to val. */ expr * model_checker::get_term_from_ctx(expr * val) { + init_value2expr(); + expr * t = nullptr; + m_value2expr.find(val, t); + return t; + } + + void model_checker::init_value2expr() { if (m_value2expr.empty()) { // populate m_value2expr for (auto const& kv : *m_root2value) { @@ -73,9 +87,28 @@ namespace smt { m_value2expr.insert(val, n->get_owner()); } } - expr * t = nullptr; - m_value2expr.find(val, t); - return t; + } + + expr_ref model_checker::replace_value_from_ctx(expr * e) { + init_value2expr(); + struct beta_reducer_cfg : default_rewriter_cfg { + model_checker& mc; + beta_reducer_cfg(model_checker& mc):mc(mc) {} + bool get_subst(expr * e, expr* & t, proof *& pr) { + t = nullptr; pr = nullptr; + mc.m_value2expr.find(e, t); + return t != nullptr; + } + }; + struct beta_reducer : public rewriter_tpl { + beta_reducer_cfg m_cfg; + beta_reducer(model_checker& m): + rewriter_tpl(m.m, false, m_cfg), m_cfg(m) {} + }; + beta_reducer br(*this); + expr_ref result(m); + br(e, result); + return result; } /** @@ -95,8 +128,6 @@ namespace smt { m_aux_context->assert_expr(fml); } -#define PP_DEPTH 8 - /** \brief Assert the negation of q after applying the interpretation in m_curr_model to the uninterpreted symbols in q. @@ -123,9 +154,8 @@ namespace smt { } } - expr_ref sk_body(m); var_subst s(m); - s(tmp, subst_args.size(), subst_args.c_ptr(), sk_body); + expr_ref sk_body = s(tmp, subst_args.size(), subst_args.c_ptr()); expr_ref r(m); r = m.mk_not(sk_body); TRACE("model_checker", tout << "mk_neg_q_m:\n" << mk_ismt2_pp(r, m) << "\n";); @@ -133,37 +163,35 @@ namespace smt { } bool model_checker::add_instance(quantifier * q, model * cex, expr_ref_vector & sks, bool use_inv) { - if (cex == nullptr) { + if (cex == nullptr || sks.empty()) { TRACE("model_checker", tout << "no model is available\n";); return false; } + array_util autil(m); unsigned num_decls = q->get_num_decls(); // Remark: sks were created for the flat version of q. SASSERT(sks.size() >= num_decls); - expr_ref_vector bindings(m); + expr_ref_vector bindings(m), defs(m); + expr_ref def(m); bindings.resize(num_decls); unsigned max_generation = 0; for (unsigned i = 0; i < num_decls; i++) { expr * sk = sks.get(num_decls - i - 1); func_decl * sk_d = to_app(sk)->get_decl(); - expr_ref sk_value(m); - sk_value = cex->get_const_interp(sk_d); - if (sk_value == 0) { - sk_value = cex->get_some_value(sk_d->get_range()); - if (sk_value == 0) { - TRACE("model_checker", tout << "Could not get value for " << sk_d->get_name() << "\n";); - return false; // get_some_value failed... giving up - } - TRACE("model_checker", tout << "Got some value " << sk_value << "\n";); + expr_ref sk_value(cex->get_some_const_interp(sk_d), m); + if (!sk_value) { + TRACE("model_checker", tout << "Could not get value for " << sk_d->get_name() << "\n";); + return false; // get_some_value failed... giving up } + TRACE("model_checker", tout << "Got some value " << sk_value << "\n";); + if (use_inv) { unsigned sk_term_gen; expr * sk_term = m_model_finder.get_inv(q, i, sk_value, sk_term_gen); if (sk_term != nullptr) { TRACE("model_checker", tout << "Found inverse " << mk_pp(sk_term, m) << "\n";); SASSERT(!m.is_model_value(sk_term)); - if (sk_term_gen > max_generation) - max_generation = sk_term_gen; + max_generation = std::max(sk_term_gen, max_generation); sk_value = sk_term; } else { @@ -181,39 +209,48 @@ namespace smt { TRACE("model_checker", tout << "value is private to model: " << sk_value << "\n";); return false; } + func_decl * f = nullptr; + if (autil.is_as_array(sk_value, f) && cex->get_func_interp(f)) { + expr_ref body(cex->get_func_interp(f)->get_interp(), m); + ptr_vector sorts(f->get_arity(), f->get_domain()); + svector names; + for (unsigned i = 0; i < f->get_arity(); ++i) { + names.push_back(symbol(i)); + } + defined_names dn(m); + body = replace_value_from_ctx(body); + body = m.mk_lambda(sorts.size(), sorts.c_ptr(), names.c_ptr(), body); + // sk_value = m.mk_fresh_const(0, m.get_sort(sk_value)); // get rid of as-array + body = dn.mk_definition(body, to_app(sk_value)); + defs.push_back(body); + } bindings.set(num_decls - i - 1, sk_value); } - TRACE("model_checker", tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance: "; - for (unsigned i = 0; i < num_decls; i++) { - tout << mk_ismt2_pp(bindings[i].get(), m) << " "; - } - tout << "\n";); - + TRACE("model_checker", tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance: " << bindings << "\n" << defs << "\n";); + if (!defs.empty()) def = mk_and(defs); max_generation = std::max(m_qm->get_generation(q), max_generation); - add_instance(q, bindings, max_generation); + add_instance(q, bindings, max_generation, def.get()); return true; } - void model_checker::add_instance(quantifier* q, expr_ref_vector const& bindings, unsigned max_generation) { + void model_checker::add_instance(quantifier* q, expr_ref_vector const& bindings, unsigned max_generation, expr* def) { SASSERT(q->get_num_decls() == bindings.size()); - for (expr* b : bindings) - m_pinned_exprs.push_back(b); + unsigned offset = m_pinned_exprs.size(); + m_pinned_exprs.append(bindings); m_pinned_exprs.push_back(q); - - void * mem = m_new_instances_region.allocate(instance::get_obj_size(q->get_num_decls())); - instance * new_inst = new (mem) instance(q, bindings.c_ptr(), max_generation); - m_new_instances.push_back(new_inst); + m_pinned_exprs.push_back(def); + m_new_instances.push_back(instance(q, offset, def, max_generation)); } void model_checker::operator()(expr *n) { - if (m.is_model_value(n) || m_autil.is_as_array(n)) { + if (m.is_model_value(n) /*|| m_autil.is_as_array(n)*/) { throw is_model_value(); } } bool model_checker::contains_model_value(expr* n) { - if (m.is_model_value(n) || m_autil.is_as_array(n)) { + if (m.is_model_value(n) /*|| m_autil.is_as_array(n)*/) { return true; } if (is_app(n) && to_app(n)->get_num_args() == 0) { @@ -231,16 +268,13 @@ namespace smt { bool model_checker::add_blocking_clause(model * cex, expr_ref_vector & sks) { - SASSERT(cex != 0); + SASSERT(cex != nullptr); expr_ref_buffer diseqs(m); for (expr * sk : sks) { func_decl * sk_d = to_app(sk)->get_decl(); - expr_ref sk_value(m); - sk_value = cex->get_const_interp(sk_d); - if (sk_value == 0) { - sk_value = cex->get_some_value(sk_d->get_range()); - if (sk_value == 0) - return false; // get_some_value failed... aborting add_blocking_clause + expr_ref sk_value(cex->get_some_const_interp(sk_d), m); + if (!sk_value) { + return false; // get_some_value failed... aborting add_blocking_clause } diseqs.push_back(m.mk_not(m.mk_eq(sk, sk_value))); } @@ -251,40 +285,43 @@ namespace smt { return true; } + struct scoped_ctx_push { + context* c; + scoped_ctx_push(context* c): c(c) { c->push(); } + ~scoped_ctx_push() { c->pop(1); } + }; + /** \brief Return true if q is satisfied by m_curr_model. */ bool model_checker::check(quantifier * q) { SASSERT(!m_aux_context->relevancy()); - m_aux_context->push(); + scoped_ctx_push _push(m_aux_context.get()); quantifier * flat_q = get_flat_quantifier(q); - TRACE("model_checker", tout << "model checking:\n" << mk_ismt2_pp(q->get_expr(), m) << "\n" << - mk_ismt2_pp(flat_q->get_expr(), m) << "\n";); + TRACE("model_checker", tout << "model checking:\n" << expr_ref(q->get_expr(), m) << "\n" << expr_ref(flat_q->get_expr(), m) << "\n";); expr_ref_vector sks(m); assert_neg_q_m(flat_q, sks); - TRACE("model_checker", tout << "skolems:\n"; - for (expr* sk : sks) { - tout << mk_ismt2_pp(sk, m) << " " << mk_pp(m.get_sort(sk), m) << "\n"; - }); + TRACE("model_checker", tout << "skolems:\n" << sks << "\n";); + flet l(m_aux_context->get_fparams().m_array_fake_support, true); lbool r = m_aux_context->check(); TRACE("model_checker", tout << "[complete] model-checker result: " << to_sat_str(r) << "\n";); - if (r != l_true) { - m_aux_context->pop(1); + if (r != l_true) { return r == l_false; // quantifier is satisfied by m_curr_model } model_ref complete_cex; m_aux_context->get_model(complete_cex); - + // try to find new instances using instantiation sets. m_model_finder.restrict_sks_to_inst_set(m_aux_context.get(), q, sks); - + unsigned num_new_instances = 0; - + while (true) { + flet l(m_aux_context->get_fparams().m_array_fake_support, true); lbool r = m_aux_context->check(); TRACE("model_checker", tout << "[restricted] model-checker (" << (num_new_instances+1) << ") result: " << to_sat_str(r) << "\n";); if (r != l_true) @@ -308,7 +345,6 @@ namespace smt { add_instance(q, complete_cex.get(), sks, false); } - m_aux_context->pop(1); return false; } @@ -318,33 +354,29 @@ namespace smt { expr* fn = to_app(q->get_pattern(0))->get_arg(0); SASSERT(is_app(fn)); func_decl* f = to_app(fn)->get_decl(); - enode_vector::const_iterator it = m_context->begin_enodes_of(f); - enode_vector::const_iterator end = m_context->end_enodes_of(f); - bool is_undef = false; expr_ref_vector args(m); unsigned num_decls = q->get_num_decls(); args.resize(num_decls, nullptr); var_subst sub(m); expr_ref tmp(m), result(m); - for (; it != end; ++it) { - if (m_context->is_relevant(*it)) { - app* e = (*it)->get_owner(); + for (enode* n : m_context->enodes_of(f)) { + if (m_context->is_relevant(n)) { + app* e = n->get_owner(); SASSERT(e->get_num_args() == num_decls); for (unsigned i = 0; i < num_decls; ++i) { args[i] = e->get_arg(i); } - sub(q->get_expr(), num_decls, args.c_ptr(), tmp); + tmp = sub(q->get_expr(), num_decls, args.c_ptr()); m_curr_model->eval(tmp, result, true); if (strict_rec_fun ? !m.is_true(result) : m.is_false(result)) { - add_instance(q, args, 0); + add_instance(q, args, 0, nullptr); return false; } - TRACE("model_checker", tout << tmp << "\nevaluates to:\n" << result << "\n";); - // if (!m.is_true(result)) is_undef = true; + TRACE("model_checker", tout << tmp << "\nevaluates to:\n" << result << "\n";); } } - return !is_undef; + return true; } void model_checker::init_aux_context() { @@ -360,7 +392,7 @@ namespace smt { } bool model_checker::check(proto_model * md, obj_map const & root2value) { - SASSERT(md != 0); + SASSERT(md != nullptr); m_root2value = &root2value; @@ -391,7 +423,6 @@ namespace smt { check_quantifiers(false, found_relevant, num_failures); - if (found_relevant) m_iteration_idx++; @@ -416,35 +447,47 @@ namespace smt { return num_failures == 0; } + // + // (repeated from defined_names.cpp) + // NB. The pattern for lambdas is incomplete. + // consider store(a, i, v) == \lambda j . if i = j then v else a[j] + // the instantiation rules for store(a, i, v) are: + // sotre(a, i, v)[j] = if i = j then v else a[j] with patterns {a[j], store(a, i, v)} { store(a, i, v)[j] } + // The first pattern is not included. + // TBD use a model-based scheme for exracting instantiations instead of + // using multi-patterns. + // + void model_checker::check_quantifiers(bool strict_rec_fun, bool& found_relevant, unsigned& num_failures) { - ptr_vector::const_iterator it = m_qm->begin_quantifiers(); - ptr_vector::const_iterator end = m_qm->end_quantifiers(); - for (; it != end; ++it) { - quantifier * q = *it; - if(!m_qm->mbqi_enabled(q)) continue; + for (quantifier * q : *m_qm) { + if (!(m_qm->mbqi_enabled(q) && + m_context->is_relevant(q) && + m_context->get_assignment(q) == l_true && + !m.is_lambda_def(q))) { + continue; + } + TRACE("model_checker", tout << "Check: " << mk_pp(q, m) << "\n"; tout << m_context->get_assignment(q) << "\n";); - if (m_context->is_relevant(q) && m_context->get_assignment(q) == l_true) { - if (m_params.m_mbqi_trace && q->get_qid() != symbol::null) { - verbose_stream() << "(smt.mbqi :checking " << q->get_qid() << ")\n"; - } - found_relevant = true; - if (m.is_rec_fun_def(q)) { - if (!check_rec_fun(q, strict_rec_fun)) { - TRACE("model_checker", tout << "checking recursive function failed\n";); - num_failures++; - } - } - else if (!check(q)) { - if (m_params.m_mbqi_trace || get_verbosity_level() >= 5) { - IF_VERBOSE(0, verbose_stream() << "(smt.mbqi :failed " << q->get_qid() << ")\n"); - } - TRACE("model_checker", tout << "checking quantifier " << mk_pp(q, m) << " failed\n";); + if (m_params.m_mbqi_trace && q->get_qid() != symbol::null) { + verbose_stream() << "(smt.mbqi :checking " << q->get_qid() << ")\n"; + } + found_relevant = true; + if (m.is_rec_fun_def(q)) { + if (!check_rec_fun(q, strict_rec_fun)) { + TRACE("model_checker", tout << "checking recursive function failed\n";); num_failures++; } } + else if (!check(q)) { + if (m_params.m_mbqi_trace || get_verbosity_level() >= 5) { + IF_VERBOSE(0, verbose_stream() << "(smt.mbqi :failed " << q->get_qid() << ")\n"); + } + TRACE("model_checker", tout << "checking quantifier " << mk_pp(q, m) << " failed\n";); + num_failures++; + } } } @@ -467,7 +510,6 @@ namespace smt { void model_checker::reset_new_instances() { m_pinned_exprs.reset(); m_new_instances.reset(); - m_new_instances_region.reset(); } void model_checker::reset() { @@ -478,32 +520,30 @@ namespace smt { TRACE("model_checker_bug_detail", tout << "assert_new_instances, inconsistent: " << m_context->inconsistent() << "\n";); ptr_buffer bindings; vector> dummy; - for (instance* inst : m_new_instances) { - quantifier * q = inst->m_q; + for (instance const& inst : m_new_instances) { + quantifier * q = inst.m_q; if (m_context->b_internalized(q)) { bindings.reset(); unsigned num_decls = q->get_num_decls(); - unsigned gen = inst->m_generation; + unsigned gen = inst.m_generation; + unsigned offset = inst.m_bindings_offset; for (unsigned i = 0; i < num_decls; i++) { - expr * b = inst->m_bindings[i]; + expr * b = m_pinned_exprs.get(offset + i); if (!m_context->e_internalized(b)) { - TRACE("model_checker_bug_detail", tout << "internalizing b:\n" << mk_pp(b, m) << "\n";); + TRACE("model_checker", tout << "internalizing b:\n" << mk_pp(b, m) << "\n";); m_context->internalize(b, false, gen); } bindings.push_back(m_context->get_enode(b)); } + + if (inst.m_def) { + m_context->internalize_assertion(inst.m_def, 0, gen); + } + TRACE("model_checker_bug_detail", tout << "instantiating... q:\n" << mk_pp(q, m) << "\n"; tout << "inconsistent: " << m_context->inconsistent() << "\n"; - tout << "bindings:\n"; - for (unsigned i = 0; i < num_decls; i++) { - expr * b = inst->m_bindings[i]; - tout << mk_pp(b, m) << "\n"; - }); - TRACE("model_checker_instance", - expr_ref inst_expr(m); - instantiate(m, q, inst->m_bindings, inst_expr); - tout << "(assert " << mk_ismt2_pp(inst_expr, m) << ")\n";); - m_context->add_instance(q, nullptr, num_decls, bindings.c_ptr(), gen, gen, gen, dummy); + tout << "bindings:\n" << expr_ref_vector(m, num_decls, m_pinned_exprs.c_ptr() + offset) << "\n";); + m_context->add_instance(q, nullptr, num_decls, bindings.c_ptr(), inst.m_def, gen, gen, gen, dummy); TRACE("model_checker_bug_detail", tout << "after instantiating, inconsistent: " << m_context->inconsistent() << "\n";); } } diff --git a/src/smt/smt_model_checker.h b/src/smt/smt_model_checker.h index dd63940b7..40a58efea 100644 --- a/src/smt/smt_model_checker.h +++ b/src/smt/smt_model_checker.h @@ -21,12 +21,12 @@ Revision History: #ifndef SMT_MODEL_CHECKER_H_ #define SMT_MODEL_CHECKER_H_ +#include "util/obj_hashtable.h" #include "ast/ast.h" #include "ast/array_decl_plugin.h" -#include "util/obj_hashtable.h" +#include "ast/normal_forms/defined_names.h" #include "smt/params/qi_params.h" #include "smt/params/smt_params.h" -#include "util/region.h" class proto_model; class model; @@ -56,7 +56,9 @@ namespace smt { friend class instantiation_set; void init_aux_context(); + void init_value2expr(); expr * get_term_from_ctx(expr * val); + expr_ref replace_value_from_ctx(expr * e); void restrict_to_universe(expr * sk, obj_hashtable const & universe); void assert_neg_q_m(quantifier * q, expr_ref_vector & sks); bool add_blocking_clause(model * cex, expr_ref_vector & sks); @@ -67,15 +69,12 @@ namespace smt { struct instance { quantifier * m_q; unsigned m_generation; - expr * m_bindings[0]; - static unsigned get_obj_size(unsigned num_bindings) { return sizeof(instance) + num_bindings * sizeof(expr*); } - instance(quantifier * q, expr * const * bindings, unsigned gen):m_q(q), m_generation(gen) { - memcpy(m_bindings, bindings, q->get_num_decls() * sizeof(expr*)); - } + expr * m_def; + unsigned m_bindings_offset; + instance(quantifier * q, unsigned offset, expr* def, unsigned gen):m_q(q), m_generation(gen), m_def(def), m_bindings_offset(offset) {} }; - region m_new_instances_region; - ptr_vector m_new_instances; + svector m_new_instances; expr_ref_vector m_pinned_exprs; bool add_instance(quantifier * q, model * cex, expr_ref_vector & sks, bool use_inv); void reset_new_instances(); @@ -86,7 +85,7 @@ namespace smt { struct is_model_value {}; expr_mark m_visited; bool contains_model_value(expr * e); - void add_instance(quantifier * q, expr_ref_vector const & bindings, unsigned max_generation); + void add_instance(quantifier * q, expr_ref_vector const & bindings, unsigned max_generation, expr * def); public: model_checker(ast_manager & m, qi_params const & p, model_finder & mf); diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index 73f85faf6..b8b22b067 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -76,16 +76,16 @@ namespace smt { \brief Instantiation sets are the S_{k,j} sets in the Complete quantifier instantiation paper. */ class instantiation_set { - ast_manager & m_manager; + ast_manager & m; obj_map m_elems; // and the associated generation obj_map m_inv; expr_mark m_visited; public: - instantiation_set(ast_manager & m):m_manager(m) {} + instantiation_set(ast_manager & m):m(m) {} ~instantiation_set() { for (auto const& kv : m_elems) { - m_manager.dec_ref(kv.m_key); + m.dec_ref(kv.m_key); } m_elems.reset(); } @@ -95,10 +95,10 @@ namespace smt { void insert(expr * n, unsigned generation) { if (m_elems.contains(n) || contains_model_value(n)) return; - TRACE("model_finder", tout << mk_pp(n, m_manager) << "\n";); - m_manager.inc_ref(n); + TRACE("model_finder", tout << mk_pp(n, m) << "\n";); + m.inc_ref(n); m_elems.insert(n, generation); - SASSERT(!m_manager.is_model_value(n)); + SASSERT(!m.is_model_value(n)); } void remove(expr * n) { @@ -106,16 +106,16 @@ namespace smt { SASSERT(m_elems.contains(n)); SASSERT(m_inv.empty()); m_elems.erase(n); - m_manager.dec_ref(n); + m.dec_ref(n); } void display(std::ostream & out) const { for (auto const& kv : m_elems) { - out << mk_bounded_pp(kv.m_key, m_manager) << " [" << kv.m_value << "]\n"; + out << mk_bounded_pp(kv.m_key, m) << " [" << kv.m_value << "]\n"; } out << "inverse:\n"; for (auto const& kv : m_inv) { - out << mk_bounded_pp(kv.m_key, m_manager) << " -> " << mk_bounded_pp(kv.m_value, m_manager) << "\n"; + out << mk_bounded_pp(kv.m_key, m) << " -> " << mk_bounded_pp(kv.m_value, m) << "\n"; } } @@ -138,7 +138,7 @@ namespace smt { unsigned gen = kv.m_value; expr * t_val = ev.eval(t, true); if (!t_val) break; - TRACE("model_finder", tout << mk_pp(t, m_manager) << " " << mk_pp(t_val, m_manager) << "\n";); + TRACE("model_finder", tout << mk_pp(t, m) << " " << mk_pp(t_val, m) << "\n";); expr * old_t = nullptr; if (m_inv.find(t_val, old_t)) { @@ -161,13 +161,13 @@ namespace smt { struct is_model_value {}; void operator()(expr *n) { - if (m_manager.is_model_value(n)) { + if (m.is_model_value(n)) { throw is_model_value(); } } bool contains_model_value(expr* n) { - if (m_manager.is_model_value(n)) { + if (m.is_model_value(n)) { return true; } if (is_app(n) && to_app(n)->get_num_args() == 0) { @@ -393,6 +393,7 @@ namespace smt { ast_manager & m; arith_util m_arith; bv_util m_bv; + array_util m_array; ptr_vector m_nodes; unsigned m_next_node_id; key2node m_uvars; @@ -427,16 +428,16 @@ namespace smt { m_eval_cache_range.reset(); } - node * mk_node(key2node & m, ast * n, unsigned i, sort * s) { + node * mk_node(key2node & map, ast * n, unsigned i, sort * s) { node * r = nullptr; ast_idx_pair k(n, i); - if (m.find(k, r)) { + if (map.find(k, r)) { SASSERT(r->get_sort() == s); return r; } r = alloc(node, m_next_node_id, s); m_next_node_id++; - m.insert(k, r); + map.insert(k, r); m_nodes.push_back(r); return r; } @@ -468,6 +469,7 @@ namespace smt { m(m), m_arith(m), m_bv(m), + m_array(m), m_next_node_id(0), m_context(nullptr), m_ks(m), @@ -651,6 +653,7 @@ namespace smt { a set of values. */ app * get_k_for(sort * s) { + TRACE("model_finder", tout << sort_ref(s, m) << "\n";); SASSERT(is_infinite(s)); app * r = nullptr; if (m_sort2k.find(s, r)) @@ -709,6 +712,7 @@ namespace smt { } void set_projection_else(node * n) { + TRACE("model_finder", n->display(tout, m);); SASSERT(n->is_root()); SASSERT(!n->is_mono_proj()); instantiation_set const * s = n->get_instantiation_set(); @@ -856,7 +860,6 @@ namespace smt { bool is_signed = n->is_signed_proj(); unsigned sz = values.size(); SASSERT(sz > 0); - func_decl * p = m.mk_fresh_func_decl(1, &s, s); expr * pi = values[sz - 1]; expr_ref var(m); var = m.mk_var(0, s); @@ -872,11 +875,14 @@ namespace smt { } func_interp * rpi = alloc(func_interp, m, 1); rpi->set_else(pi); + func_decl * p = m.mk_fresh_func_decl(1, &s, s); + TRACE("model_finder", tout << expr_ref(pi, m) << "\n";); m_model->register_aux_decl(p, rpi); n->set_proj(p); } void mk_simple_proj(node * n) { + TRACE("model_finder", n->display(tout, m);); set_projection_else(n); ptr_buffer values; get_instantiation_set_values(n, values); @@ -887,7 +893,7 @@ namespace smt { if (n->get_else()) { expr * else_val = eval(n->get_else(), true); pi->set_else(else_val); - } + } for (expr * v : values) { pi->insert_new_entry(&v, v); } @@ -972,9 +978,8 @@ namespace smt { } } expr_ref_vector trail(m); - for (unsigned i = 0; i < need_fresh.size(); ++i) { + for (node * n : need_fresh) { expr * e; - node* n = need_fresh[i]; sort* s = n->get_sort(); if (!sort2elems.find(s, e)) { e = m.mk_fresh_const("elem", s); @@ -1173,10 +1178,7 @@ namespace smt { void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { node * A_f_i = s.get_A_f_i(m_f, m_arg_i); - enode_vector::const_iterator it = ctx->begin_enodes_of(m_f); - enode_vector::const_iterator end = ctx->end_enodes_of(m_f); - for (; it != end; it++) { - enode * n = *it; + for (enode * n : ctx->enodes_of(m_f)) { if (ctx->is_relevant(n)) { // Remark: it is incorrect to use // n->get_arg(m_arg_i)->get_root() @@ -1203,10 +1205,7 @@ namespace smt { instantiation_set * s = uvar_inst_sets[m_var_j]; SASSERT(s != 0); - enode_vector::const_iterator it = ctx->begin_enodes_of(m_f); - enode_vector::const_iterator end = ctx->end_enodes_of(m_f); - for (; it != end; it++) { - enode * n = *it; + for (enode * n : ctx->enodes_of(m_f)) { if (ctx->is_relevant(n)) { enode * e_arg = n->get_arg(m_arg_i); expr * arg = e_arg->get_owner(); @@ -1255,10 +1254,7 @@ namespace smt { // there is no finite fixpoint... we just copy the i-th arguments of A_f_i - m_offset // hope for the best... node * S_j = s.get_uvar(q, m_var_j); - enode_vector::const_iterator it = ctx->begin_enodes_of(m_f); - enode_vector::const_iterator end = ctx->end_enodes_of(m_f); - for (; it != end; it++) { - enode * n = *it; + for (enode * n : ctx->enodes_of(m_f)) { if (ctx->is_relevant(n)) { arith_rewriter arith_rw(ctx->get_manager()); bv_util bv(ctx->get_manager()); @@ -1375,7 +1371,7 @@ namespace smt { class select_var : public qinfo { protected: - ast_manager & m_manager; + ast_manager & m; array_util m_array; app * m_select; // It must satisfy is_auf_select... see bool is_auf_select(expr * t) const unsigned m_arg_i; @@ -1384,6 +1380,7 @@ namespace smt { app * get_array() const { return to_app(m_select->get_arg(0)); } func_decl * get_array_func_decl(app * ground_array, auf_solver & s) { + TRACE("model_evaluator", tout << expr_ref(ground_array, m) << "\n";); expr * ground_array_interp = s.eval(ground_array, false); if (ground_array_interp != nullptr && m_array.is_as_array(ground_array_interp)) return m_array.get_as_array_func_decl(ground_array_interp); @@ -1391,7 +1388,7 @@ namespace smt { } public: - select_var(ast_manager & m, app * s, unsigned i, unsigned j):m_manager(m), m_array(m), m_select(s), m_arg_i(i), m_var_j(j) {} + select_var(ast_manager & m, app * s, unsigned i, unsigned j):m(m), m_array(m), m_select(s), m_arg_i(i), m_var_j(j) {} ~select_var() override {} char const * get_kind() const override { @@ -1406,7 +1403,7 @@ namespace smt { } void display(std::ostream & out) const override { - out << "(" << mk_bounded_pp(m_select, m_manager) << ":" << m_arg_i << " -> v!" << m_var_j << ")"; + out << "(" << mk_bounded_pp(m_select, m) << ":" << m_arg_i << " -> v!" << m_var_j << ")"; } void process_auf(quantifier * q, auf_solver & s, context * ctx) override { @@ -1415,7 +1412,7 @@ namespace smt { TRACE("select_var", tout << "enodes matching: "; display(tout); tout << "\n"; for (enode* n : arrays) { - tout << "#" << n->get_owner()->get_id() << "\n" << mk_pp(n->get_owner(), m_manager) << "\n"; + tout << "#" << n->get_owner()->get_id() << "\n" << mk_pp(n->get_owner(), m) << "\n"; }); node * n1 = s.get_uvar(q, m_var_j); for (enode* n : arrays) { @@ -1574,10 +1571,7 @@ namespace smt { // For uninterpreted sorts, we add all terms in the context. // See Section 4.1 in the paper "Complete Quantifier Instantiation" node * S_q_i = slv.get_uvar(q, m_var_i); - ptr_vector::const_iterator it = ctx->begin_enodes(); - ptr_vector::const_iterator end = ctx->end_enodes(); - for (; it != end; ++it) { - enode * n = *it; + for (enode * n : ctx->enodes()) { if (ctx->is_relevant(n) && get_sort(n->get_owner()) == s) { S_q_i->insert(n->get_owner(), n->get_generation()); } @@ -1623,7 +1617,7 @@ namespace smt { class cond_macro { protected: - ast_manager & m_manager; + ast_manager & m; func_decl * m_f; expr * m_def; expr * m_cond; @@ -1633,7 +1627,7 @@ namespace smt { unsigned m_weight; public: cond_macro(ast_manager & m, func_decl * f, expr * def, expr * cond, bool ineq, bool satisfy_atom, bool hint, unsigned weight): - m_manager(m), + m(m), m_f(f), m_def(def), m_cond(cond), @@ -1641,14 +1635,14 @@ namespace smt { m_satisfy_atom(satisfy_atom), m_hint(hint), m_weight(weight) { - m_manager.inc_ref(m_def); - m_manager.inc_ref(m_cond); + m.inc_ref(m_def); + m.inc_ref(m_cond); SASSERT(!m_hint || m_cond == 0); } ~cond_macro() { - m_manager.dec_ref(m_def); - m_manager.dec_ref(m_cond); + m.dec_ref(m_def); + m.dec_ref(m_cond); } func_decl * get_f() const { return m_f; } @@ -1657,7 +1651,7 @@ namespace smt { expr * get_cond() const { return m_cond; } - bool is_unconditional() const { return m_cond == nullptr || m_manager.is_true(m_cond); } + bool is_unconditional() const { return m_cond == nullptr || m.is_true(m_cond); } bool satisfy_atom() const { return m_satisfy_atom; } @@ -1668,11 +1662,11 @@ namespace smt { } void display(std::ostream & out) const { - out << "[" << m_f->get_name() << " -> " << mk_bounded_pp(m_def, m_manager, 6); + out << "[" << m_f->get_name() << " -> " << mk_bounded_pp(m_def, m, 6); if (m_hint) out << " *hint*"; else - out << " when " << mk_bounded_pp(m_cond, m_manager, 6); + out << " when " << mk_bounded_pp(m_cond, m, 6); out << "] weight: " << m_weight; } @@ -1739,7 +1733,7 @@ namespace smt { m_has_x_eq_y(false), m_the_one(nullptr), m_uvar_inst_sets(nullptr) { - if (has_quantifiers(q->get_expr())) { + if (has_quantifiers(q->get_expr()) && !m.is_lambda_def(q)) { static bool displayed_flat_msg = false; if (!displayed_flat_msg) { // [Leo]: This warning message is not useful. @@ -1758,7 +1752,7 @@ namespace smt { } CTRACE("model_finder_bug", has_quantifiers(m_flat_q->get_expr()), tout << mk_pp(q, m) << "\n" << mk_pp(m_flat_q, m) << "\n";); - SASSERT(!has_quantifiers(m_flat_q->get_expr())); + SASSERT(m.is_lambda_def(q) || !has_quantifiers(m_flat_q->get_expr())); } ~quantifier_info() { @@ -1787,7 +1781,6 @@ namespace smt { ptr_vector const& macros() const { return m_cond_macros; } - void set_the_one(func_decl * m) { m_the_one = m; } @@ -1880,7 +1873,7 @@ namespace smt { */ class quantifier_analyzer { model_finder& m_mf; - ast_manager & m_manager; + ast_manager & m; macro_util m_mutil; array_util m_array_util; arith_util m_arith_util; @@ -1909,7 +1902,7 @@ namespace smt { bool is_var_plus_ground(expr * n, var * & v, expr_ref & t) { bool inv; - TRACE("is_var_plus_ground", tout << mk_pp(n, m_manager) << "\n"; + TRACE("is_var_plus_ground", tout << mk_pp(n, m) << "\n"; tout << "is_var_plus_ground: " << is_var_plus_ground(n, inv, v, t) << "\n"; tout << "inv: " << inv << "\n";); return is_var_plus_ground(n, inv, v, t) && !inv; @@ -1953,7 +1946,7 @@ namespace smt { bool is_var_and_ground(expr * lhs, expr * rhs, var * & v, expr_ref & t, bool & inv) { inv = false; // true if invert the sign - TRACE("is_var_and_ground", tout << "is_var_and_ground: " << mk_ismt2_pp(lhs, m_manager) << " " << mk_ismt2_pp(rhs, m_manager) << "\n";); + TRACE("is_var_and_ground", tout << "is_var_and_ground: " << mk_ismt2_pp(lhs, m) << " " << mk_ismt2_pp(rhs, m) << "\n";); if (is_var(lhs) && is_ground(rhs)) { v = to_var(lhs); t = rhs; @@ -1967,7 +1960,7 @@ namespace smt { return true; } else { - expr_ref tmp(m_manager); + expr_ref tmp(m); if (is_var_plus_ground(lhs, inv, v, tmp) && is_ground(rhs)) { if (inv) mk_sub(tmp, rhs, t); @@ -1994,7 +1987,7 @@ namespace smt { bool is_x_eq_t_atom(expr * n, var * & v, expr_ref & t) { if (!is_app(n)) return false; - if (m_manager.is_eq(n)) + if (m.is_eq(n)) return is_var_and_ground(to_app(n)->get_arg(0), to_app(n)->get_arg(1), v, t); return false; } @@ -2030,7 +2023,7 @@ namespace smt { } bool is_x_eq_y_atom(expr * n, var * & v1, var * & v2) { - return m_manager.is_eq(n) && is_var_and_var(to_app(n)->get_arg(0), to_app(n)->get_arg(1), v1, v2); + return m.is_eq(n) && is_var_and_var(to_app(n)->get_arg(0), to_app(n)->get_arg(1), v1, v2); } bool is_x_gle_y_atom(expr * n, var * & v1, var * & v2) { @@ -2042,28 +2035,28 @@ namespace smt { return false; if (sign) { bool r = is_le_ge(atom) && is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, t); - CTRACE("is_x_gle_t", r, tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m_manager) << "\n--->\n" - << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n"; + CTRACE("is_x_gle_t", r, tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m) << "\n--->\n" + << mk_ismt2_pp(v, m) << " " << mk_ismt2_pp(t, m) << "\n"; tout << "sign: " << sign << "\n";); return r; } else { if (is_le_ge(atom)) { - expr_ref tmp(m_manager); + expr_ref tmp(m); bool le = is_le(atom); bool inv = false; if (is_var_and_ground(to_app(atom)->get_arg(0), to_app(atom)->get_arg(1), v, tmp, inv)) { if (inv) le = !le; - sort * s = m_manager.get_sort(tmp); - expr_ref one(m_manager); + sort * s = m.get_sort(tmp); + expr_ref one(m); one = mk_one(s); if (le) mk_add(tmp, one, t); else mk_sub(tmp, one, t); - TRACE("is_x_gle_t", tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m_manager) << "\n--->\n" - << mk_ismt2_pp(v, m_manager) << " " << mk_ismt2_pp(t, m_manager) << "\n"; + TRACE("is_x_gle_t", tout << "is_x_gle_t: " << mk_ismt2_pp(atom, m) << "\n--->\n" + << mk_ismt2_pp(v, m) << " " << mk_ismt2_pp(t, m) << "\n"; tout << "sign: " << sign << "\n";); return true; } @@ -2113,9 +2106,9 @@ namespace smt { } var * v; - expr_ref k(m_manager); + expr_ref k(m); if (is_var_plus_ground(arg, v, k)) { - insert_qinfo(alloc(f_var_plus_offset, m_manager, t->get_decl(), i, v->get_idx(), k.get())); + insert_qinfo(alloc(f_var_plus_offset, m, t->get_decl(), i, v->get_idx(), k.get())); continue; } @@ -2157,7 +2150,7 @@ namespace smt { for (unsigned i = 1; i < num_args; i++) { expr * arg = t->get_arg(i); if (is_var(arg)) { - insert_qinfo(alloc(select_var, m_manager, t, i, to_var(arg)->get_idx())); + insert_qinfo(alloc(select_var, m, t, i, to_var(arg)->get_idx())); } else { SASSERT(is_ground(arg)); @@ -2174,7 +2167,7 @@ namespace smt { void process_app(app * t) { SASSERT(!is_ground(t)); - if (t->get_family_id() != m_manager.get_basic_family_id()) { + if (t->get_family_id() != m.get_basic_family_id()) { m_info->m_ng_decls.insert(t->get_decl()); } @@ -2191,7 +2184,7 @@ namespace smt { expr * curr = m_ttodo.back(); m_ttodo.pop_back(); - if (m_manager.is_bool(curr)) { + if (m.is_bool(curr)) { // formula nested in a term. visit_formula(curr, POS); visit_formula(curr, NEG); @@ -2212,30 +2205,30 @@ namespace smt { } void process_literal(expr * atom, bool sign) { - CTRACE("model_finder_bug", is_ground(atom), tout << mk_pp(atom, m_manager) << "\n";); + CTRACE("model_finder_bug", is_ground(atom), tout << mk_pp(atom, m) << "\n";); SASSERT(!is_ground(atom)); - SASSERT(m_manager.is_bool(atom)); + SASSERT(m.is_bool(atom)); if (is_var(atom)) { if (sign) { // atom (not X) can be viewed as X != true - insert_qinfo(alloc(x_neq_t, m_manager, to_var(atom)->get_idx(), m_manager.mk_true())); + insert_qinfo(alloc(x_neq_t, m, to_var(atom)->get_idx(), m.mk_true())); } else { // atom X can be viewed as X != false - insert_qinfo(alloc(x_neq_t, m_manager, to_var(atom)->get_idx(), m_manager.mk_false())); + insert_qinfo(alloc(x_neq_t, m, to_var(atom)->get_idx(), m.mk_false())); } return; } if (is_app(atom)) { var * v, * v1, * v2; - expr_ref t(m_manager); + expr_ref t(m); if (is_x_eq_t_atom(atom, v, t)) { if (sign) - insert_qinfo(alloc(x_neq_t, m_manager, v->get_idx(), t)); + insert_qinfo(alloc(x_neq_t, m, v->get_idx(), t)); else - insert_qinfo(alloc(x_eq_t, m_manager, v->get_idx(), t)); + insert_qinfo(alloc(x_eq_t, m, v->get_idx(), t)); } else if (is_x_eq_y_atom(atom, v1, v2)) { if (sign) @@ -2252,7 +2245,7 @@ namespace smt { insert_qinfo(alloc(x_leq_y, v1->get_idx(), v2->get_idx())); } else if (is_x_gle_t_atom(atom, sign, v, t)) { - insert_qinfo(alloc(x_gle_t, m_manager, v->get_idx(), t)); + insert_qinfo(alloc(x_gle_t, m, v->get_idx(), t)); } else { process_app(to_app(atom)); @@ -2299,7 +2292,7 @@ namespace smt { polarity pol = e.second; m_ftodo.pop_back(); if (is_app(curr)) { - if (to_app(curr)->get_family_id() == m_manager.get_basic_family_id() && m_manager.is_bool(curr)) { + if (to_app(curr)->get_family_id() == m.get_basic_family_id() && m.is_bool(curr)) { switch (static_cast(to_app(curr)->get_decl_kind())) { case OP_IMPLIES: case OP_XOR: @@ -2316,7 +2309,7 @@ namespace smt { process_ite(to_app(curr), pol); break; case OP_EQ: - if (m_manager.is_bool(to_app(curr)->get_arg(0))) { + if (m.is_bool(to_app(curr)->get_arg(0))) { process_iff(to_app(curr)); } else { @@ -2333,7 +2326,7 @@ namespace smt { } } else if (is_var(curr)) { - SASSERT(m_manager.is_bool(curr)); + SASSERT(m.is_bool(curr)); process_literal(curr, pol); } else { @@ -2344,32 +2337,32 @@ namespace smt { } void process_formula(expr * n) { - SASSERT(m_manager.is_bool(n)); + SASSERT(m.is_bool(n)); visit_formula(n, POS); } void process_clause(expr * cls) { - SASSERT(is_clause(m_manager, cls)); - unsigned num_lits = get_clause_num_literals(m_manager, cls); + SASSERT(is_clause(m, cls)); + unsigned num_lits = get_clause_num_literals(m, cls); for (unsigned i = 0; i < num_lits; i++) { - expr * lit = get_clause_literal(m_manager, cls, i); - SASSERT(is_literal(m_manager, lit)); + expr * lit = get_clause_literal(m, cls, i); + SASSERT(is_literal(m, lit)); expr * atom; bool sign; - get_literal_atom_sign(m_manager, lit, atom, sign); + get_literal_atom_sign(m, lit, atom, sign); if (!is_ground(atom)) process_literal(atom, sign); } } void collect_macro_candidates(quantifier * q) { - macro_util::macro_candidates candidates(m_manager); + macro_util::macro_candidates candidates(m); m_mutil.collect_macro_candidates(q, candidates); unsigned num_candidates = candidates.size(); for (unsigned i = 0; i < num_candidates; i++) { - cond_macro * m = alloc(cond_macro, m_manager, candidates.get_f(i), candidates.get_def(i), candidates.get_cond(i), + cond_macro * mc = alloc(cond_macro, m, candidates.get_f(i), candidates.get_def(i), candidates.get_cond(i), candidates.ineq(i), candidates.satisfy_atom(i), candidates.hint(i), q->get_weight()); - m_info->insert_macro(m); + m_info->insert_macro(mc); } } @@ -2377,7 +2370,7 @@ namespace smt { public: quantifier_analyzer(model_finder& mf, ast_manager & m): m_mf(mf), - m_manager(m), + m(m), m_mutil(m), m_array_util(m), m_arith_util(m), @@ -2389,13 +2382,14 @@ namespace smt { void operator()(quantifier_info * d) { m_info = d; quantifier * q = d->get_flat_q(); + if (m.is_lambda_def(q)) return; expr * e = q->get_expr(); SASSERT(!has_quantifiers(e)); reset_cache(); SASSERT(m_ttodo.empty()); SASSERT(m_ftodo.empty()); - if (is_clause(m_manager, e)) { + if (is_clause(m, e)) { process_clause(e); } else { @@ -2419,7 +2413,7 @@ namespace smt { */ class base_macro_solver { protected: - ast_manager & m_manager; + ast_manager & m; obj_map const & m_q2info; proto_model * m_model; @@ -2434,18 +2428,18 @@ namespace smt { SASSERT(f_else != 0); func_interp * fi = m_model->get_func_interp(f); if (fi == nullptr) { - fi = alloc(func_interp, m_manager, f->get_arity()); + fi = alloc(func_interp, m, f->get_arity()); m_model->register_decl(f, fi); } fi->set_else(f_else); - TRACE("model_finder", tout << f->get_name() << " " << mk_pp(f_else, m_manager) << "\n";); + TRACE("model_finder", tout << f->get_name() << " " << mk_pp(f_else, m) << "\n";); } virtual bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) = 0; public: base_macro_solver(ast_manager & m, obj_map const & q2i): - m_manager(m), + m(m), m_q2info(q2i), m_model(nullptr) { } @@ -2647,7 +2641,7 @@ namespace smt { bool is_candidate(quantifier * q) const { quantifier_info * qi = get_qinfo(q); - for (cond_macro * m : qi->macros()) { + for (cond_macro* m : qi->macros()) { if (m->satisfy_atom() && !m_forbidden.contains(m->get_f())) return true; } @@ -2708,7 +2702,7 @@ namespace smt { void display_qcandidates(std::ostream & out, ptr_vector const & qcandidates) const { for (quantifier * q : qcandidates) { - out << q->get_qid() << " ->\n" << mk_pp(q, m_manager) << "\n"; + out << q->get_qid() << " ->\n" << mk_pp(q, m) << "\n"; quantifier_info * qi = get_qinfo(q); qi->display(out); out << "------\n"; @@ -2724,7 +2718,7 @@ namespace smt { func_decl * f = kv.get_key1(); expr * def = kv.get_key2(); quantifier_set * s = kv.get_value(); - out << f->get_name() << " " << mk_pp(def, m_manager) << " ->\n"; display_quantifier_set(out, s); + out << f->get_name() << " " << mk_pp(def, m) << " ->\n"; display_quantifier_set(out, s); } } @@ -2846,7 +2840,7 @@ namespace smt { m_satisfied.push_scope(); m_residue.push_scope(); - TRACE("model_finder", tout << f->get_name() << " " << mk_pp(def, m_manager) << "\n";); + TRACE("model_finder", tout << f->get_name() << " " << mk_pp(def, m) << "\n";); m_fs.insert(f, def); if (update_satisfied_residue(f, def)) { @@ -3023,7 +3017,7 @@ namespace smt { qi_params const * m_qi_params; bool add_macro(func_decl * f, expr * f_else) { - TRACE("model_finder", tout << "trying to add macro for " << f->get_name() << "\n" << mk_pp(f_else, m_manager) << "\n";); + TRACE("model_finder", tout << "trying to add macro for " << f->get_name() << "\n" << mk_pp(f_else, m) << "\n";); func_decl_set * s = m_dependencies.mk_func_decl_set(); m_dependencies.collect_ng_func_decls(f_else, s); if (!m_dependencies.insert(f, s)) { @@ -3092,23 +3086,23 @@ namespace smt { } void process(func_decl * f, ptr_vector const & qs, obj_hashtable & removed) { - expr_ref fi_else(m_manager); + expr_ref fi_else(m); ptr_buffer to_remove; for (quantifier * q : qs) { if (removed.contains(q)) continue; - cond_macro * m = get_macro_for(f, q); - if (!m) + cond_macro * cm = get_macro_for(f, q); + if (!cm) continue; - SASSERT(!m->is_hint()); - if (m->is_unconditional()) + SASSERT(!cm->is_hint()); + if (cm->is_unconditional()) return; // f is part of a full macro... ignoring it. to_remove.push_back(q); if (fi_else.get() == nullptr) { - fi_else = m->get_def(); + fi_else = cm->get_def(); } else { - fi_else = m_manager.mk_ite(m->get_cond(), m->get_def(), fi_else); + fi_else = m.mk_ite(cm->get_cond(), cm->get_def(), fi_else); } } if (fi_else.get() != nullptr && add_macro(f, fi_else)) { @@ -3166,7 +3160,7 @@ namespace smt { // ----------------------------------- model_finder::model_finder(ast_manager & m): - m_manager(m), + m(m), m_context(nullptr), m_analyzer(alloc(quantifier_analyzer, *this, m)), m_auf_solver(alloc(auf_solver, m)), @@ -3206,8 +3200,8 @@ namespace smt { } void model_finder::register_quantifier(quantifier * q) { - TRACE("model_finder", tout << "registering:\n" << mk_pp(q, m_manager) << "\n";); - quantifier_info * new_info = alloc(quantifier_info, *this, m_manager, q); + TRACE("model_finder", tout << "registering:\n" << mk_pp(q, m) << "\n";); + quantifier_info * new_info = alloc(quantifier_info, *this, m, q); m_q2info.insert(q, new_info); m_quantifiers.push_back(q); m_analyzer->operator()(new_info); @@ -3261,9 +3255,9 @@ namespace smt { } } - void model_finder::process_auf(ptr_vector const & qs, proto_model * m) { + void model_finder::process_auf(ptr_vector const & qs, proto_model * mdl) { m_auf_solver->reset(); - m_auf_solver->set_model(m); + m_auf_solver->set_model(mdl); for (quantifier * q : qs) { quantifier_info * qi = get_quantifier_info(q); @@ -3280,7 +3274,7 @@ namespace smt { for (quantifier * q : qs) { quantifier_info * qi = get_quantifier_info(q); quantifier * fq = qi->get_flat_q(); - tout << "#" << fq->get_id() << " ->\n" << mk_pp(fq, m_manager) << "\n"; + tout << "#" << fq->get_id() << " ->\n" << mk_pp(fq, m) << "\n"; } m_auf_solver->display_nodes(tout);); } @@ -3310,11 +3304,8 @@ namespace smt { \brief Clean leftovers from previous invocations to fix_model. */ void model_finder::cleanup_quantifier_infos(ptr_vector const & qs) { - ptr_vector::const_iterator it = qs.begin(); - ptr_vector::const_iterator end = qs.end(); - for (; it != end; ++it) { - quantifier_info * qi = get_quantifier_info(*it); - qi->reset_the_one(); + for (quantifier* q : qs) { + get_quantifier_info(q)->reset_the_one(); } } @@ -3356,7 +3347,7 @@ namespace smt { quantifier * flat_q = get_flat_quantifier(q); SASSERT(flat_q->get_num_decls() >= q->get_num_decls()); instantiation_set const * r = m_auf_solver->get_uvar_inst_set(flat_q, flat_q->get_num_decls() - q->get_num_decls() + i); - TRACE("model_finder", tout << "q: #" << q->get_id() << "\n" << mk_pp(q,m_manager) << "\nflat_q: " << mk_pp(flat_q, m_manager) + TRACE("model_finder", tout << "q: #" << q->get_id() << "\n" << mk_pp(q,m) << "\nflat_q: " << mk_pp(flat_q, m) << "\ni: " << i << " " << flat_q->get_num_decls() - q->get_num_decls() + i << "\n";); if (r != nullptr) return r; @@ -3365,7 +3356,6 @@ namespace smt { quantifier_info * qinfo = get_quantifier_info(q); SASSERT(qinfo); SASSERT(qinfo->get_the_one() != 0); - return qinfo->get_macro_based_inst_set(i, m_context, *(m_auf_solver.get())); } @@ -3415,15 +3405,13 @@ namespace smt { if (inv.empty()) continue; // nothing to do ptr_buffer eqs; - obj_map::iterator it = inv.begin(); - obj_map::iterator end = inv.end(); - for (; it != end; ++it) { - expr * val = (*it).m_key; - eqs.push_back(m_manager.mk_eq(sk, val)); + for (auto const& kv : inv) { + expr * val = kv.m_key; + eqs.push_back(m.mk_eq(sk, val)); } - expr_ref new_cnstr(m_manager); - new_cnstr = m_manager.mk_or(eqs.size(), eqs.c_ptr()); - TRACE("model_finder", tout << "assert_restriction:\n" << mk_pp(new_cnstr, m_manager) << "\n";); + expr_ref new_cnstr(m); + new_cnstr = m.mk_or(eqs.size(), eqs.c_ptr()); + TRACE("model_finder", tout << "assert_restriction:\n" << mk_pp(new_cnstr, m) << "\n";); aux_ctx->assert_expr(new_cnstr); asserted_something = true; } @@ -3435,7 +3423,7 @@ namespace smt { if (sz > 0) { for (unsigned i = 0; i < sz; i++) { expr * c = m_new_constraints.get(i); - TRACE("model_finder_bug_detail", tout << "asserting new constraint: " << mk_pp(c, m_manager) << "\n";); + TRACE("model_finder_bug_detail", tout << "asserting new constraint: " << mk_pp(c, m) << "\n";); m_context->internalize(c, true); literal l(m_context->get_literal(c)); m_context->mark_as_relevant(l); diff --git a/src/smt/smt_model_finder.h b/src/smt/smt_model_finder.h index 2b79ab265..842a18db8 100644 --- a/src/smt/smt_model_finder.h +++ b/src/smt/smt_model_finder.h @@ -74,7 +74,7 @@ namespace smt { typedef mf::non_auf_macro_solver non_auf_macro_solver; typedef mf::instantiation_set instantiation_set; - ast_manager & m_manager; + ast_manager & m; context * m_context; scoped_ptr m_analyzer; scoped_ptr m_auf_solver; diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index 9ba2c6165..5417785d6 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -24,6 +24,7 @@ Revision History: #include "smt/smt_context.h" #include "smt/smt_model_generator.h" #include "smt/proto_model/proto_model.h" +#include "model/model_v2_pp.h" namespace smt { @@ -51,11 +52,9 @@ namespace smt { SASSERT(!m_model); // PARAM-TODO smt_params ---> params_ref m_model = alloc(proto_model, m_manager); // , m_context->get_fparams()); - ptr_vector::const_iterator it = m_context->begin_theories(); - ptr_vector::const_iterator end = m_context->end_theories(); - for (; it != end; ++it) { - TRACE("model", tout << "init_model for theory: " << (*it)->get_name() << "\n";); - (*it)->init_model(*this); + for (theory* th : m_context->theories()) { + TRACE("model_generator_bug", tout << "init_model for theory: " << th->get_name() << "\n";); + th->init_model(*this); } } @@ -82,10 +81,7 @@ namespace smt { */ void model_generator::mk_value_procs(obj_map & root2proc, ptr_vector & roots, ptr_vector & procs) { - ptr_vector::const_iterator it = m_context->begin_enodes(); - ptr_vector::const_iterator end = m_context->end_enodes(); - for (; it != end; ++it) { - enode * r = *it; + for (enode * r : m_context->enodes()) { if (r == r->get_root() && m_context->is_relevant(r)) { roots.push_back(r); sort * s = m_manager.get_sort(r->get_owner()); @@ -201,10 +197,7 @@ namespace smt { SASSERT(proc); buffer dependencies; proc->get_dependencies(dependencies); - buffer::const_iterator it = dependencies.begin(); - buffer::const_iterator end = dependencies.end(); - for (; it != end; ++it) { - model_value_dependency const & dep = *it; + for (model_value_dependency const& dep : dependencies) { visit_child(dep, colors, todo, visited); TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << " -> "; if (dep.is_fresh_value()) tout << "fresh!" << dep.get_value()->get_idx(); @@ -308,10 +301,7 @@ namespace smt { mk_value_procs(root2proc, roots, procs); top_sort_sources(roots, root2proc, sources); TRACE("sorted_sources", - svector::const_iterator it = sources.begin(); - svector::const_iterator end = sources.end(); - for (; it != end; ++it) { - source const & curr = *it; + for (source const& curr : sources) { if (curr.is_fresh_value()) { tout << "fresh!" << curr.get_value()->get_idx() << " " << mk_pp(curr.get_value()->get_sort(), m_manager) << "\n"; } @@ -326,11 +316,7 @@ namespace smt { tout << " is_fresh: " << proc->is_fresh() << "\n"; } }); - svector::const_iterator it = sources.begin(); - svector::const_iterator end = sources.end(); - for (; it != end; ++it) { - source const & curr = *it; - + for (source const& curr : sources) { if (curr.is_fresh_value()) { sort * s = curr.get_value()->get_sort(); TRACE("model_fresh_bug", tout << "mk fresh!" << curr.get_value()->get_idx() << " : " << mk_pp(s, m_manager) << "\n";); @@ -349,10 +335,7 @@ namespace smt { VERIFY(root2proc.find(n, proc)); SASSERT(proc); proc->get_dependencies(dependencies); - buffer::const_iterator it2 = dependencies.begin(); - buffer::const_iterator end2 = dependencies.end(); - for (; it2 != end2; ++it2) { - model_value_dependency const & d = *it2; + for (model_value_dependency const& d : dependencies) { if (d.is_fresh_value()) { CTRACE("mg_top_sort", !d.get_value()->get_value(), tout << "#" << n->get_owner_id() << " -> "; @@ -381,10 +364,7 @@ namespace smt { m_extra_fresh_values.reset(); // send model - ptr_vector::const_iterator it3 = m_context->begin_enodes(); - ptr_vector::const_iterator end3 = m_context->end_enodes(); - for (; it3 != end3; ++it3) { - enode * n = *it3; + for (enode * n : m_context->enodes()) { 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";); @@ -484,17 +464,12 @@ namespace smt { } void model_generator::finalize_theory_models() { - ptr_vector::const_iterator it = m_context->begin_theories(); - ptr_vector::const_iterator end = m_context->end_theories(); - for (; it != end; ++it) - (*it)->finalize_model(*this); + for (theory* th : m_context->theories()) + th->finalize_model(*this); } void model_generator::register_existing_model_values() { - ptr_vector::const_iterator it = m_context->begin_enodes(); - ptr_vector::const_iterator end = m_context->end_enodes(); - for (; it != end; ++it) { - enode * r = *it; + for (enode * r : m_context->enodes()) { if (r == r->get_root() && m_context->is_relevant(r)) { expr * n = r->get_owner(); if (m_manager.is_model_value(n)) { @@ -531,6 +506,7 @@ namespace smt { mk_func_interps(); finalize_theory_models(); register_macros(); + TRACE("model", model_v2_pp(tout, *m_model, true);); return m_model; } diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp index 0a522c072..885157ab3 100644 --- a/src/smt/smt_quantifier.cpp +++ b/src/smt/smt_quantifier.cpp @@ -191,6 +191,7 @@ namespace smt { bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, + expr* def, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, @@ -200,7 +201,7 @@ namespace smt { return false; } get_stat(q)->update_max_generation(max_generation); - fingerprint * f = m_context.add_fingerprint(q, q->get_id(), num_bindings, bindings); + fingerprint * f = m_context.add_fingerprint(q, q->get_id(), num_bindings, bindings, def); if (f) { if (has_trace_stream()) { std::ostream & out = trace_stream(); @@ -399,16 +400,17 @@ namespace smt { bool quantifier_manager::add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, + expr* def, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, vector> & used_enodes) { - return m_imp->add_instance(q, pat, num_bindings, bindings, max_generation, min_top_generation, max_generation, used_enodes); + return m_imp->add_instance(q, pat, num_bindings, bindings, def, max_generation, min_top_generation, max_generation, used_enodes); } - bool quantifier_manager::add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned generation) { + bool quantifier_manager::add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, expr* def, unsigned generation) { vector> tmp; - return add_instance(q, nullptr, num_bindings, bindings, generation, generation, generation, tmp); + return add_instance(q, nullptr, num_bindings, bindings, def, generation, generation, generation, tmp); } void quantifier_manager::init_search_eh() { diff --git a/src/smt/smt_quantifier.h b/src/smt/smt_quantifier.h index 3c95d1e23..4f14c6853 100644 --- a/src/smt/smt_quantifier.h +++ b/src/smt/smt_quantifier.h @@ -55,11 +55,12 @@ namespace smt { bool add_instance(quantifier * q, app * pat, unsigned num_bindings, enode * const * bindings, + expr* def, unsigned max_generation, unsigned min_top_generation, unsigned max_top_generation, vector> & used_enodes /*gives the equalities used for the pattern match, see mam.cpp for more info*/); - bool add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned generation = 0); + bool add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, expr* def, unsigned generation = 0); void init_search_eh(); void assign_eh(quantifier * q); @@ -92,6 +93,8 @@ namespace smt { ptr_vector::const_iterator begin_quantifiers() const; ptr_vector::const_iterator end_quantifiers() const; + ptr_vector::const_iterator begin() const { return begin_quantifiers(); } + ptr_vector::const_iterator end() const { return end_quantifiers(); } unsigned num_quantifiers() const; }; diff --git a/src/smt/smt_quick_checker.cpp b/src/smt/smt_quick_checker.cpp index d549d7c68..14719c140 100644 --- a/src/smt/smt_quick_checker.cpp +++ b/src/smt/smt_quick_checker.cpp @@ -52,10 +52,7 @@ namespace smt { bool quick_checker::collector::check_arg(enode * n, func_decl * f, unsigned i) { if (!f || !m_conservative) return true; - enode_vector::const_iterator it = m_context.begin_enodes_of(f); - enode_vector::const_iterator end = m_context.end_enodes_of(f); - for (; it != end; ++it) { - enode * curr = *it; + for (enode * curr : m_context.enodes_of(f)) { if (m_context.is_relevant(curr) && curr->is_cgr() && i < curr->get_num_args() && curr->get_arg(i)->get_root() == n->get_root()) return true; } @@ -77,10 +74,7 @@ namespace smt { if (s.empty()) continue; ns.reset(); - enode_vector::const_iterator it = m_context.begin_enodes_of(f); - enode_vector::const_iterator end = m_context.end_enodes_of(f); - for (; it != end; ++it) { - enode * curr = *it; + for (enode * curr : m_context.enodes_of(f)) { if (m_context.is_relevant(curr) && curr->is_cgr() && check_arg(curr, p, i) && j < curr->get_num_args()) { enode * arg = curr->get_arg(j)->get_root(); // intersection @@ -94,10 +88,7 @@ namespace smt { else { m_already_found[idx] = true; enode_set & s = m_candidates[idx]; - enode_vector::const_iterator it = m_context.begin_enodes_of(f); - enode_vector::const_iterator end = m_context.end_enodes_of(f); - for (; it != end; ++it) { - enode * curr = *it; + for (enode * curr : m_context.enodes_of(f)) { if (m_context.is_relevant(curr) && curr->is_cgr() && check_arg(curr, p, i) && j < curr->get_num_args()) { enode * arg = curr->get_arg(j)->get_root(); s.insert(arg); @@ -134,10 +125,7 @@ namespace smt { enode_vector & v = candidates[i]; v.reset(); enode_set & s = m_candidates[i]; - enode_set::iterator it = s.begin(); - enode_set::iterator end = s.end(); - for (; it != end; ++it) { - enode * curr = *it; + for (enode * curr : s) { v.push_back(curr); } } @@ -146,10 +134,8 @@ namespace smt { for (unsigned i = 0; i < m_num_vars; i++) { tout << "var " << i << ":"; enode_vector & v = candidates[i]; - enode_vector::iterator it = v.begin(); - enode_vector::iterator end = v.end(); - for (; it != end; ++it) - tout << " #" << (*it)->get_owner_id(); + for (enode * n : v) + tout << " #" << n->get_owner_id(); tout << "\n"; }); } @@ -227,10 +213,8 @@ namespace smt { tout << "candidates:\n"; for (unsigned i = 0; i < m_num_bindings; i++) { enode_vector & v = m_candidate_vectors[i]; - enode_vector::iterator it = v.begin(); - enode_vector::iterator end = v.end(); - for (; it != end; ++it) - tout << "#" << (*it)->get_owner_id() << " "; + for (enode * n : v) + tout << "#" << n->get_owner_id() << " "; tout << "\n"; }); bool result = false; @@ -252,7 +236,8 @@ namespace smt { TRACE("quick_checker_sizes", tout << "found new candidate\n"; for (unsigned i = 0; i < m_num_bindings; i++) tout << "#" << m_bindings[i]->get_owner_id() << " "; tout << "\n";); unsigned max_generation = get_max_generation(m_num_bindings, m_bindings.c_ptr()); - if (m_context.add_instance(q, nullptr /* no pattern was used */, m_num_bindings, m_bindings.c_ptr(), max_generation, + if (m_context.add_instance(q, nullptr /* no pattern was used */, m_num_bindings, m_bindings.c_ptr(), nullptr, + max_generation, 0, // min_top_generation is only available for instances created by the MAM 0, // max_top_generation is only available for instances created by the MAM empty_used_enodes)) diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index b90f08cd3..ca3c01189 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -222,7 +222,7 @@ namespace smt { void setup::setup_QF_BVRE() { setup_QF_BV(); setup_QF_LIA(); - m_context.register_plugin(alloc(theory_seq, m_manager)); + m_context.register_plugin(alloc(theory_seq, m_manager, m_params)); } void setup::setup_QF_UF(static_features const & st) { @@ -332,7 +332,7 @@ namespace smt { m_params.m_arith_propagate_eqs = false; m_params.m_arith_small_lemma_size = 30; m_params.m_nnf_cnf = false; - setup_i_arith(); + setup_lra_arith(); } void setup::setup_QF_IDL(static_features & st) { @@ -404,7 +404,7 @@ namespace smt { m_params.m_restart_strategy = RS_GEOMETRIC; m_params.m_restart_factor = 1.5; m_params.m_restart_adaptive = false; - setup_i_arith(); + setup_lra_arith(); } void setup::setup_QF_UFIDL(static_features & st) { @@ -454,7 +454,7 @@ namespace smt { m_params.m_arith_propagate_eqs = false; m_params.m_eliminate_term_ite = true; m_params.m_nnf_cnf = false; - setup_r_arith(); + setup_lra_arith(); } void setup::setup_QF_LRA(static_features const & st) { @@ -479,7 +479,7 @@ namespace smt { m_params.m_restart_adaptive = false; } m_params.m_arith_small_lemma_size = 32; - setup_r_arith(); + setup_lra_arith(); } void setup::setup_QF_LIRA(static_features const& st) { @@ -493,7 +493,7 @@ namespace smt { m_params.m_arith_reflect = false; m_params.m_arith_propagate_eqs = false; m_params.m_nnf_cnf = false; - setup_i_arith(); + setup_lra_arith(); } void setup::setup_QF_LIA(static_features const & st) { @@ -534,7 +534,7 @@ namespace smt { m_params.m_arith_bound_prop = BP_NONE; m_params.m_arith_stronger_lemmas = false; } - setup_i_arith(); + setup_lra_arith(); } void setup::setup_QF_UFLIA() { @@ -542,7 +542,7 @@ namespace smt { m_params.m_arith_reflect = false; m_params.m_nnf_cnf = false; m_params.m_arith_propagation_threshold = 1000; - setup_i_arith(); + setup_lra_arith(); } void setup::setup_QF_UFLIA(static_features & st) { @@ -555,7 +555,7 @@ namespace smt { m_params.m_relevancy_lvl = 0; m_params.m_arith_reflect = false; m_params.m_nnf_cnf = false; - setup_r_arith(); + setup_lra_arith(); } void setup::setup_QF_BV() { @@ -721,8 +721,18 @@ namespace smt { } void setup::setup_QF_S() { - m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); - m_context.register_plugin(alloc(smt::theory_str, m_manager, m_params)); + if (m_params.m_string_solver == "z3str3") { + setup_str(); + } + else if (m_params.m_string_solver == "seq") { + setup_unknown(); + } + else if (m_params.m_string_solver == "auto") { + setup_unknown(); + } + else { + throw default_exception("invalid parameter for smt.string_solver, valid options are 'z3str3', 'seq', 'auto'"); + } } bool is_arith(static_features const & st) { @@ -730,23 +740,34 @@ namespace smt { } void setup::setup_i_arith() { - // m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params)); - m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); + if (AS_OLD_ARITH == m_params.m_arith_mode) { + m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); + } + else { + setup_lra_arith(); + } } - void setup::setup_r_arith() { + void setup::setup_lra_arith() { m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params)); } void setup::setup_mi_arith() { - if (m_params.m_arith_mode == AS_OPTINF) { + switch (m_params.m_arith_mode) { + case AS_OPTINF: m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); - } - else { + break; + case AS_NEW_ARITH: + setup_lra_arith(); + break; + default: m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); + break; } } + + void setup::setup_arith() { static_features st(m_manager); IF_VERBOSE(100, verbose_stream() << "(smt.collecting-features)\n";); @@ -800,15 +821,18 @@ 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: + case AS_OLD_ARITH: if (m_params.m_arith_int_only && int_only) m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); else m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); break; + case AS_NEW_ARITH: + setup_lra_arith(); + break; + default: + m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); + break; } } @@ -885,7 +909,7 @@ namespace smt { } void setup::setup_seq() { - m_context.register_plugin(alloc(smt::theory_seq, m_manager)); + m_context.register_plugin(alloc(smt::theory_seq, m_manager, m_params)); } void setup::setup_unknown() { @@ -968,7 +992,7 @@ namespace smt { if (st.num_theories() == 2 && st.has_uf() && is_arith(st)) { if (!st.m_has_real) setup_QF_UFLIA(st); - else if (!st.m_has_int && st.m_num_non_linear == 0) + else if (!st.m_has_int && st.m_num_non_linear == 0) setup_QF_UFLRA(); else setup_unknown(); diff --git a/src/smt/smt_setup.h b/src/smt/smt_setup.h index 924c2caec..ed0ab066d 100644 --- a/src/smt/smt_setup.h +++ b/src/smt/smt_setup.h @@ -101,7 +101,7 @@ namespace smt { void setup_card(); void setup_i_arith(); void setup_mi_arith(); - void setup_r_arith(); + void setup_lra_arith(); void setup_fpa(); void setup_str(); diff --git a/src/smt/tactic/smt_tactic.cpp b/src/smt/tactic/smt_tactic.cpp index cc0f0f207..50fe47985 100644 --- a/src/smt/tactic/smt_tactic.cpp +++ b/src/smt/tactic/smt_tactic.cpp @@ -39,7 +39,6 @@ class smt_tactic : public tactic { smt_params m_params; params_ref m_params_ref; statistics m_stats; - std::string m_failure; smt::kernel * m_ctx; symbol m_logic; progress_callback * m_callback; @@ -259,7 +258,7 @@ public: if (m_fail_if_inconclusive && !m_candidate_models) { std::stringstream strm; strm << "smt tactic failed to show goal to be sat/unsat " << m_ctx->last_failure_as_string(); - throw tactic_exception(strm.str().c_str()); + throw tactic_exception(strm.str()); } result.push_back(in.get()); if (m_candidate_models) { @@ -281,8 +280,7 @@ public: break; } } - m_failure = m_ctx->last_failure_as_string(); - throw tactic_exception(m_failure.c_str()); + throw tactic_exception(m_ctx->last_failure_as_string()); } } catch (rewriter_exception & ex) { diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 992a87dab..4b3ee52f6 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -42,7 +42,7 @@ Revision History: namespace smt { struct theory_arith_stats { - unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests; + unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests, m_patches, m_patches_succ; unsigned m_assert_lower, m_assert_upper, m_assert_diseq, m_core2th_eqs, m_core2th_diseqs; unsigned m_th2core_eqs, m_th2core_diseqs, m_bound_props, m_offset_eqs, m_fixed_eqs, m_offline_eqs; unsigned m_max_min; diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index b5281f218..ee3bd5e2e 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -1335,7 +1335,7 @@ namespace smt { } } }); - + m_stats.m_patches++; patch_int_infeasible_vars(); fix_non_base_vars(); @@ -1368,6 +1368,7 @@ namespace smt { theory_var int_var = find_infeasible_int_base_var(); if (int_var == null_theory_var) { + m_stats.m_patches_succ++; TRACE("arith_int_incomp", tout << "FC_DONE 2...\n"; display(tout);); return m_liberal_final_check || !m_changed_assignment ? FC_DONE : FC_CONTINUE; } @@ -1385,6 +1386,7 @@ namespace smt { m_branch_cut_counter++; // TODO: add giveup code + TRACE("gomory_cut", tout << m_branch_cut_counter << ", " << m_params.m_arith_branch_cut_ratio << std::endl;); if (m_branch_cut_counter % m_params.m_arith_branch_cut_ratio == 0) { TRACE("opt_verbose", display(tout);); move_non_base_vars_to_bounds(); @@ -1399,7 +1401,7 @@ namespace smt { SASSERT(is_base(int_var)); row const & r = m_rows[get_var_row(int_var)]; if (!mk_gomory_cut(r)) { - // silent failure + TRACE("gomory_cut", tout << "silent failure\n";); } return FC_CONTINUE; } diff --git a/src/smt/theory_arith_pp.h b/src/smt/theory_arith_pp.h index a218445f5..6579856a4 100644 --- a/src/smt/theory_arith_pp.h +++ b/src/smt/theory_arith_pp.h @@ -38,6 +38,8 @@ namespace smt { st.update("arith gcd tests", m_stats.m_gcd_tests); st.update("arith ineq splits", m_stats.m_branches); st.update("arith gomory cuts", m_stats.m_gomory_cuts); + st.update("arith patches", m_stats.m_patches); + st.update("arith patches_succ", m_stats.m_patches_succ); st.update("arith max-min", m_stats.m_max_min); st.update("arith grobner", m_stats.m_gb_compute_basis); st.update("arith pseudo nonlinear", m_stats.m_nl_linear); @@ -389,8 +391,19 @@ namespace smt { void theory_arith::display_vars(std::ostream & out) const { out << "vars:\n"; int n = get_num_vars(); - for (theory_var v = 0; v < n; v++) - display_var(out, v); + int inf_vars = 0; + int int_inf_vars = 0; + for (theory_var v = 0; v < n; v++) { + if ((lower(v) && lower(v)->get_value() > get_value(v)) + || (upper(v) && upper(v)->get_value() < get_value(v))) + inf_vars++; + if (is_int(v) && !get_value(v).is_int()) + int_inf_vars++; + } + out << "infeasibles = " << inf_vars << " int_inf = " << int_inf_vars << std::endl; + for (theory_var v = 0; v < n; v++) { + display_var(out, v); + } } template diff --git a/src/smt/theory_array.cpp b/src/smt/theory_array.cpp index b8f76f9ad..ffeb1b274 100644 --- a/src/smt/theory_array.cpp +++ b/src/smt/theory_array.cpp @@ -383,7 +383,7 @@ namespace smt { } } TRACE("array", tout << "m_found_unsupported_op: " << m_found_unsupported_op << " " << r << "\n";); - if (r == FC_DONE && m_found_unsupported_op) + if (r == FC_DONE && m_found_unsupported_op && !get_context().get_fparams().m_array_fake_support) r = FC_GIVEUP; return r; } diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index a0e9fad2b..c024c0da0 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -33,8 +33,12 @@ namespace smt { } void theory_array_base::found_unsupported_op(expr * n) { - TRACE("array", tout << mk_ll_pp(n, get_manager()) << "\n";); - if (!m_found_unsupported_op) { + if (!get_context().get_fparams().m_array_fake_support && !m_found_unsupported_op) { + //array_util autil(get_manager()); + //func_decl* f = 0; + //if (autil.is_as_array(n, f) && f->is_skolem()) return; + TRACE("array", tout << mk_ll_pp(n, get_manager()) << "\n";); + get_context().push_trail(value_trail(m_found_unsupported_op)); m_found_unsupported_op = true; } diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 048e3f581..d872997c4 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -17,13 +17,13 @@ Revision History: --*/ -#include "smt/smt_context.h" -#include "smt/theory_array_full.h" +#include "util/stats.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" #include "ast/ast_util.h" #include "ast/ast_smt2_pp.h" -#include "util/stats.h" +#include "smt/smt_context.h" +#include "smt/theory_array_full.h" namespace smt { @@ -54,11 +54,9 @@ namespace smt { set_prop_upward(v,d); d_full->m_maps.push_back(s); m_trail_stack.push(push_back_trail(d_full->m_maps)); - ptr_vector::iterator it = d->m_parent_selects.begin(); - ptr_vector::iterator end = d->m_parent_selects.end(); - for (; it != end; ++it) { - SASSERT(is_select(*it)); - instantiate_select_map_axiom(*it, s); + for (enode* n : d->m_parent_selects) { + SASSERT(is_select(n)); + instantiate_select_map_axiom(n, s); } set_prop_upward(s); } @@ -67,15 +65,10 @@ namespace smt { bool result = false; var_data * d = m_var_data[v]; var_data_full * d_full = m_var_data_full[v]; - unsigned num_maps = d_full->m_parent_maps.size(); - unsigned num_selects = d->m_parent_selects.size(); - for (unsigned i = 0; i < num_maps; ++i) { - for (unsigned j = 0; j < num_selects; ++j) { - if (instantiate_select_map_axiom(d->m_parent_selects[j], d_full->m_parent_maps[i])) { - result = true; - } - } - } + for (enode* pm : d_full->m_parent_maps) + for (enode* ps : d->m_parent_selects) + if (instantiate_select_map_axiom(ps, pm)) + result = true; return result; } @@ -91,11 +84,9 @@ namespace smt { d_full->m_parent_maps.push_back(s); m_trail_stack.push(push_back_trail(d_full->m_parent_maps)); if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { - ptr_vector::iterator it = d->m_parent_selects.begin(); - ptr_vector::iterator end = d->m_parent_selects.end(); - for (; it != end; ++it) { - if (!m_params.m_array_cg || (*it)->is_cgr()) { - instantiate_select_map_axiom(*it, s); + for (enode * n : d->m_parent_selects) { + if (!m_params.m_array_cg || n->is_cgr()) { + instantiate_select_map_axiom(n, s); } } } @@ -118,20 +109,14 @@ namespace smt { instantiate_axiom_map_for(v); } var_data_full * d2 = m_var_data_full[v]; - ptr_vector::iterator it = d->m_stores.begin(); - ptr_vector::iterator end = d->m_stores.end(); - for (; it != end; ++it) { - set_prop_upward(*it); + for (enode * n : d->m_stores) { + set_prop_upward(n); } - it = d2->m_maps.begin(); - end = d2->m_maps.end(); - for (; it != end; ++it) { - set_prop_upward(*it); + for (enode * n : d2->m_maps) { + set_prop_upward(n); } - it = d2->m_consts.begin(); - end = d2->m_consts.end(); - for (; it != end; ++it) { - set_prop_upward(*it); + for (enode * n : d2->m_consts) { + set_prop_upward(n); } } } @@ -180,12 +165,9 @@ namespace smt { m_trail_stack.push(push_back_trail(consts)); consts.push_back(cnst); instantiate_default_const_axiom(cnst); - - ptr_vector::iterator it = d->m_parent_selects.begin(); - ptr_vector::iterator end = d->m_parent_selects.end(); - for (; it != end; ++it) { - SASSERT(is_select(*it)); - instantiate_select_const_axiom(*it, cnst); + for (enode * n : d->m_parent_selects) { + SASSERT(is_select(n)); + instantiate_select_const_axiom(n, cnst); } } @@ -199,12 +181,9 @@ namespace smt { m_trail_stack.push(push_back_trail(as_arrays)); as_arrays.push_back(arr); instantiate_default_as_array_axiom(arr); - - ptr_vector::iterator it = d->m_parent_selects.begin(); - ptr_vector::iterator end = d->m_parent_selects.end(); - for (; it != end; ++it) { - SASSERT(is_select(*it)); - instantiate_select_as_array_axiom(*it, arr); + for (enode * n : d->m_parent_selects) { + SASSERT(is_select(n)); + instantiate_select_as_array_axiom(n, arr); } } @@ -257,6 +236,10 @@ namespace smt { } bool theory_array_full::internalize_term(app * n) { + context & ctx = get_context(); + if (ctx.e_internalized(n)) { + return true; + } TRACE("array", tout << mk_pp(n, get_manager()) << "\n";); if (is_store(n) || is_select(n)) { @@ -272,11 +255,10 @@ namespace smt { if (!internalize_term_core(n)) { return true; } - context & ctx = get_context(); if (is_map(n) || is_array_ext(n)) { - for (unsigned i = 0; i < n->get_num_args(); ++i) { - enode* arg = ctx.get_enode(n->get_arg(i)); + for (expr* e : *n) { + enode* arg = ctx.get_enode(e); if (!is_attached_to_var(arg)) { mk_var(arg); } @@ -300,8 +282,8 @@ namespace smt { add_parent_default(v_arg); } else if (is_map(n)) { - for (unsigned i = 0; i < n->get_num_args(); ++i) { - enode* arg = ctx.get_enode(n->get_arg(i)); + for (expr* e : *n) { + enode* arg = ctx.get_enode(e); theory_var v_arg = arg->get_th_var(get_id()); add_parent_map(v_arg, node); } @@ -334,27 +316,17 @@ namespace smt { // v1 is the new root SASSERT(v1 == find(v1)); var_data_full * d2 = m_var_data_full[v2]; - ptr_vector::iterator it, end; - - it = d2->m_maps.begin(); - end = d2->m_maps.end(); - for (; it != end; ++it) { - add_map(v1, *it); + for (enode * n : d2->m_maps) { + add_map(v1, n); } - it = d2->m_parent_maps.begin(); - end = d2->m_parent_maps.end(); - for (; it != end; ++it) { - add_parent_map(v1, *it); + for (enode * n : d2->m_parent_maps) { + add_parent_map(v1, n); } - it = d2->m_consts.begin(); - end = d2->m_consts.end(); - for (; it != end; ++it) { - add_const(v1, *it); + for (enode * n : d2->m_consts) { + add_const(v1, n); } - it = d2->m_as_arrays.begin(); - end = d2->m_as_arrays.end(); - for (; it != end; ++it) { - add_as_array(v1, *it); + for (enode * n : d2->m_as_arrays) { + add_as_array(v1, n); } TRACE("array", tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << "\n"; @@ -367,21 +339,13 @@ namespace smt { SASSERT(v != null_theory_var); v = find(v); var_data* d = m_var_data[v]; - ptr_vector::iterator it, end; - - it = d->m_stores.begin(); - end = d->m_stores.end(); - for(; it != end; ++it) { - enode * store = *it; + for (enode * store : d->m_stores) { SASSERT(is_store(store)); instantiate_default_store_axiom(store); } if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { - it = d->m_parent_stores.begin(); - end = d->m_parent_stores.end(); - for (; it != end; ++it) { - enode* store = *it; + for (enode * store : d->m_parent_stores) { SASSERT(is_store(store)); if (!m_params.m_array_cg || store->is_cgr()) { instantiate_default_store_axiom(store); @@ -399,23 +363,15 @@ namespace smt { v = find(v); var_data_full* d_full = m_var_data_full[v]; var_data* d = m_var_data[v]; - ptr_vector::iterator it = d_full->m_consts.begin(); - ptr_vector::iterator end = d_full->m_consts.end(); - for (; it != end; ++it) { - instantiate_select_const_axiom(s, *it); + for (enode * n : d_full->m_consts) { + instantiate_select_const_axiom(s, n); } - it = d_full->m_maps.begin(); - end = d_full->m_maps.end(); - for (; it != end; ++it) { - enode* map = *it; + for (enode * map : d_full->m_maps) { SASSERT(is_map(map)); instantiate_select_map_axiom(s, map); } if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { - it = d_full->m_parent_maps.begin(); - end = d_full->m_parent_maps.end(); - for (; it != end; ++it) { - enode* map = *it; + for (enode * map : d_full->m_parent_maps) { SASSERT(is_map(map)); if (!m_params.m_array_cg || map->is_cgr()) { instantiate_select_map_axiom(s, map); @@ -437,7 +393,7 @@ namespace smt { enode * arg = ctx.get_enode(n->get_arg(0)); theory_var v = arg->get_th_var(get_id()); SASSERT(v != null_theory_var); - add_parent_select(find(v), node); + add_parent_select(find(v), node); } else if (is_default(n)) { enode * arg = ctx.get_enode(n->get_arg(0)); @@ -577,7 +533,14 @@ namespace smt { return try_assign_eq(val, def); } + /** + * instantiate f(ep1,ep2,..,ep_n) = default(as-array f) + * it is disabled to avoid as-array terms during search. + */ + bool theory_array_full::instantiate_default_as_array_axiom(enode* arr) { + return false; +#if 0 context& ctx = get_context(); if (!ctx.add_fingerprint(this, 0, 1, &arr)) { return false; @@ -595,6 +558,7 @@ namespace smt { ctx.internalize(def, false); ctx.internalize(val.get(), false); return try_assign_eq(val.get(), def); +#endif } bool theory_array_full::has_large_domain(app* array_term) { diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index b7aaff68a..42d90c242 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -843,14 +843,12 @@ namespace smt { context & ctx = get_context(); bool first = true; - ptr_vector::const_iterator it = ctx.begin_enodes(); - ptr_vector::const_iterator end = ctx.end_enodes(); - for (; it != end; it++) { - theory_var v = (*it)->get_th_var(get_family_id()); + for (enode* n : ctx.enodes()) { + theory_var v = n->get_th_var(get_family_id()); if (v != -1) { if (first) out << "fpa theory variables:" << std::endl; out << v << " -> " << - mk_ismt2_pp((*it)->get_owner(), m) << std::endl; + mk_ismt2_pp(n->get_owner(), m) << std::endl; first = false; } } @@ -858,30 +856,24 @@ namespace smt { if (first) return; out << "bv theory variables:" << std::endl; - it = ctx.begin_enodes(); - end = ctx.end_enodes(); - for (; it != end; it++) { - theory_var v = (*it)->get_th_var(m_bv_util.get_family_id()); + for (enode * n : ctx.enodes()) { + theory_var v = n->get_th_var(m_bv_util.get_family_id()); if (v != -1) out << v << " -> " << - mk_ismt2_pp((*it)->get_owner(), m) << std::endl; + mk_ismt2_pp(n->get_owner(), m) << std::endl; } out << "arith theory variables:" << std::endl; - it = ctx.begin_enodes(); - end = ctx.end_enodes(); - for (; it != end; it++) { - theory_var v = (*it)->get_th_var(m_arith_util.get_family_id()); + for (enode* n : ctx.enodes()) { + theory_var v = n->get_th_var(m_arith_util.get_family_id()); if (v != -1) out << v << " -> " << - mk_ismt2_pp((*it)->get_owner(), m) << std::endl; + mk_ismt2_pp(n->get_owner(), m) << std::endl; } out << "equivalence classes:\n"; - it = ctx.begin_enodes(); - end = ctx.end_enodes(); - for (; it != end; ++it) { - expr * n = (*it)->get_owner(); - expr * r = (*it)->get_root()->get_owner(); - out << r->get_id() << " --> " << mk_ismt2_pp(n, m) << std::endl; + for (enode * n : ctx.enodes()) { + expr * e = n->get_owner(); + expr * r = n->get_root()->get_owner(); + out << r->get_id() << " --> " << mk_ismt2_pp(e, m) << std::endl; } } }; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index de9b41f64..b105def2c 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1,23 +1,23 @@ /*++ -Copyright (c) 2016 Microsoft Corporation + Copyright (c) 2016 Microsoft Corporation -Module Name: + Module Name: - theory_lra.cpp + theory_lra.cpp -Abstract: + Abstract: - + -Author: + Author: - Lev Nachmanson (levnach) 2016-25-3 - Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) 2016-25-3 + Nikolaj Bjorner (nbjorner) -Revision History: + Revision History: ---*/ + --*/ #include "util/stopwatch.h" #include "util/lp/lp_solver.h" #include "util/lp/lp_primal_simplex.h" @@ -28,7 +28,6 @@ Revision History: #include "util/optional.h" #include "util/lp/lp_params.hpp" #include "util/inf_rational.h" -#include "ast/ast_pp.h" #include "smt/smt_theory.h" #include "smt/smt_context.h" #include "smt/theory_lra.h" @@ -36,2594 +35,3047 @@ Revision History: #include "smt/smt_model_generator.h" #include "smt/arith_eq_adapter.h" #include "util/nat_set.h" +#include "util/lp/nra_solver.h" #include "tactic/generic_model_converter.h" +#include "math/polynomial/algebraic_numbers.h" +#include "math/polynomial/polynomial.h" +#include "ast/ast_pp.h" +#include "util/cancel_eh.h" +#include "util/scoped_timer.h" -namespace lra_lp { - enum bound_kind { lower_t, upper_t }; +namespace lp_api { +enum bound_kind { lower_t, upper_t }; - std::ostream& operator<<(std::ostream& out, bound_kind const& k) { - switch (k) { - case lower_t: return out << "<="; - case upper_t: return out << ">="; - } - return out; +std::ostream& operator<<(std::ostream& out, bound_kind const& k) { + switch (k) { + case lower_t: return out << "<="; + case upper_t: return out << ">="; } + return out; +} - class bound { - smt::bool_var m_bv; - smt::theory_var m_var; - rational m_value; - bound_kind m_bound_kind; +class bound { + smt::bool_var m_bv; + smt::theory_var m_var; + bool m_is_int; + rational m_value; + bound_kind m_bound_kind; - public: - bound(smt::bool_var bv, smt::theory_var v, rational const & val, bound_kind k): - m_bv(bv), - m_var(v), - m_value(val), - m_bound_kind(k) { +public: + bound(smt::bool_var bv, smt::theory_var v, bool is_int, rational const & val, bound_kind k): + m_bv(bv), + m_var(v), + m_is_int(is_int), + m_value(val), + m_bound_kind(k) { + } + virtual ~bound() {} + smt::theory_var get_var() const { return m_var; } + smt::bool_var get_bv() const { return m_bv; } + bound_kind get_bound_kind() const { return m_bound_kind; } + bool is_int() const { return m_is_int; } + rational const& get_value() const { return m_value; } + inf_rational get_value(bool is_true) const { + if (is_true) return inf_rational(m_value); // v >= value or v <= value + if (m_is_int) { + SASSERT(m_value.is_int()); + if (m_bound_kind == lower_t) return inf_rational(m_value - rational::one()); // v <= value - 1 + return inf_rational(m_value + rational::one()); // v >= value + 1 } - virtual ~bound() {} - smt::theory_var get_var() const { return m_var; } - smt::bool_var get_bv() const { return m_bv; } - bound_kind get_bound_kind() const { return m_bound_kind; } - rational const& get_value() const { return m_value; } - inf_rational get_value(bool is_true) const { - if (is_true) return inf_rational(m_value); // v >= value or v <= value + else { if (m_bound_kind == lower_t) return inf_rational(m_value, false); // v <= value - epsilon return inf_rational(m_value, true); // v >= value + epsilon } - virtual std::ostream& display(std::ostream& out) const { - return out << "v" << get_var() << " " << get_bound_kind() << " " << m_value; - } - }; - - std::ostream& operator<<(std::ostream& out, bound const& b) { - return b.display(out); + } + virtual std::ostream& display(std::ostream& out) const { + return out << m_value << " " << get_bound_kind() << " v" << get_var(); } +}; - struct stats { - unsigned m_assert_lower; - unsigned m_assert_upper; - unsigned m_add_rows; - unsigned m_bounds_propagations; - unsigned m_num_iterations; - unsigned m_num_iterations_with_no_progress; - unsigned m_num_factorizations; - unsigned m_need_to_solve_inf; - unsigned m_fixed_eqs; - unsigned m_conflicts; - unsigned m_bound_propagations1; - unsigned m_bound_propagations2; - unsigned m_assert_diseq; - unsigned m_make_feasible; - unsigned m_max_cols; - unsigned m_max_rows; - stats() { reset(); } - void reset() { - memset(this, 0, sizeof(*this)); - } - }; +std::ostream& operator<<(std::ostream& out, bound const& b) { + return b.display(out); +} - typedef optional opt_inf_rational; +struct stats { + unsigned m_assert_lower; + unsigned m_assert_upper; + unsigned m_add_rows; + unsigned m_bounds_propagations; + unsigned m_num_iterations; + unsigned m_num_iterations_with_no_progress; + unsigned m_need_to_solve_inf; + unsigned m_fixed_eqs; + unsigned m_conflicts; + unsigned m_bound_propagations1; + unsigned m_bound_propagations2; + unsigned m_assert_diseq; + unsigned m_gomory_cuts; + stats() { reset(); } + void reset() { + memset(this, 0, sizeof(*this)); + } +}; + +typedef optional opt_inf_rational; } namespace smt { - typedef ptr_vector lp_bounds; +typedef ptr_vector lp_bounds; + +class theory_lra::imp { - class theory_lra::imp { - - struct scope { - unsigned m_bounds_lim; - unsigned m_asserted_qhead; - unsigned m_asserted_atoms_lim; - unsigned m_delayed_terms_lim; - unsigned m_delayed_equalities_lim; - unsigned m_delayed_defs_lim; - unsigned m_underspecified_lim; - unsigned m_var_trail_lim; - expr* m_not_handled; - }; - - struct delayed_atom { - unsigned m_bv; - bool m_is_true; - delayed_atom(unsigned b, bool t): m_bv(b), m_is_true(t) {} - }; - - class resource_limit : public lp::lp_resource_limit { - imp& m_imp; - public: - resource_limit(imp& i): m_imp(i) { } - bool get_cancel_flag() override { return m_imp.m.canceled(); } - }; - - - theory_lra& th; - ast_manager& m; - theory_arith_params& m_arith_params; - arith_util a; - - arith_eq_adapter m_arith_eq_adapter; - - vector m_columns; - // temporary values kept during internalization - struct internalize_state { - expr_ref_vector m_terms; - vector m_coeffs; - svector m_vars; - rational m_coeff; - ptr_vector m_terms_to_internalize; - internalize_state(ast_manager& m): m_terms(m) {} - void reset() { - m_terms.reset(); - m_coeffs.reset(); - m_coeff.reset(); - m_vars.reset(); - m_terms_to_internalize.reset(); - } - }; - ptr_vector m_internalize_states; - unsigned m_internalize_head; - - class scoped_internalize_state { - imp& m_imp; - internalize_state& m_st; - - internalize_state& push_internalize(imp& i) { - if (i.m_internalize_head == i.m_internalize_states.size()) { - i.m_internalize_states.push_back(alloc(internalize_state, i.m)); - } - internalize_state& st = *i.m_internalize_states[i.m_internalize_head++]; - st.reset(); - return st; - } - public: - scoped_internalize_state(imp& i): m_imp(i), m_st(push_internalize(i)) {} - ~scoped_internalize_state() { --m_imp.m_internalize_head; } - expr_ref_vector& terms() { return m_st.m_terms; } - vector& coeffs() { return m_st.m_coeffs; } - svector& vars() { return m_st.m_vars; } - rational& coeff() { return m_st.m_coeff; } - ptr_vector& terms_to_internalize() { return m_st.m_terms_to_internalize; } - void push(expr* e, rational c) { m_st.m_terms.push_back(e); m_st.m_coeffs.push_back(c); } - void set_back(unsigned i) { - if (terms().size() == i + 1) return; - terms()[i] = terms().back(); - coeffs()[i] = coeffs().back(); - terms().pop_back(); - coeffs().pop_back(); - } - }; - - typedef vector> var_coeffs; - struct delayed_def { - vector m_coeffs; - svector m_vars; - rational m_coeff; - theory_var m_var; - delayed_def(svector const& vars, vector const& coeffs, rational const& r, theory_var v): - m_coeffs(coeffs), m_vars(vars), m_coeff(r), m_var(v) {} - }; - - svector m_theory_var2var_index; // translate from theory variables to lar vars - svector m_var_index2theory_var; // reverse map from lp_solver variables to theory variables - svector m_term_index2theory_var; // reverse map from lp_solver variables to theory variables - var_coeffs m_left_side; // constraint left side - mutable std::unordered_map m_variable_values; // current model - - enum constraint_source { - inequality_source, - equality_source, - definition_source, - null_source - }; - svector m_constraint_sources; - svector m_inequalities; // asserted rows corresponding to inequality literals. - svector m_equalities; // asserted rows corresponding to equalities. - svector m_definitions; // asserted rows corresponding to definitions - - bool m_delay_constraints; // configuration - svector m_asserted_atoms; - app_ref_vector m_delayed_terms; - svector> m_delayed_equalities; - vector m_delayed_defs; - expr* m_not_handled; - ptr_vector m_underspecified; - unsigned_vector m_var_trail; - vector > m_use_list; // bounds where variables are used. - - // attributes for incremental version: - u_map m_bool_var2bound; - vector m_bounds; - unsigned_vector m_unassigned_bounds; - unsigned_vector m_bounds_trail; - unsigned m_asserted_qhead; - - svector m_to_check; // rows that should be checked for theory propagation - - svector > m_assume_eq_candidates; - unsigned m_assume_eq_head; - - unsigned m_num_conflicts; - - - struct var_value_eq { - imp & m_th; - var_value_eq(imp & th):m_th(th) {} - bool operator()(theory_var v1, theory_var v2) const { return m_th.get_ivalue(v1) == m_th.get_ivalue(v2) && m_th.is_int(v1) == m_th.is_int(v2); } - }; - struct var_value_hash { - imp & m_th; - var_value_hash(imp & th):m_th(th) {} - unsigned operator()(theory_var v) const { return (unsigned)std::hash()(m_th.get_ivalue(v)); } - }; - int_hashtable m_model_eqs; - - - svector m_scopes; - lra_lp::stats m_stats; - arith_factory* m_factory; - scoped_ptr m_solver; - resource_limit m_resource_limit; - lp_bounds m_new_bounds; - - - context& ctx() const { return th.get_context(); } - theory_id get_id() const { return th.get_id(); } - bool is_int(theory_var v) const { return is_int(get_enode(v)); } - bool is_int(enode* n) const { return a.is_int(n->get_owner()); } - enode* get_enode(theory_var v) const { return th.get_enode(v); } - enode* get_enode(expr* e) const { return ctx().get_enode(e); } - expr* get_owner(theory_var v) const { return get_enode(v)->get_owner(); } - - void init_solver() { - if (m_solver) return; - lp_params lp(ctx().get_params()); - m_solver = alloc(lp::lar_solver); - m_theory_var2var_index.reset(); - m_solver->settings().set_resource_limit(m_resource_limit); - m_solver->settings().simplex_strategy() = static_cast(lp.simplex_strategy()); - reset_variable_values(); - m_solver->settings().bound_propagation() = BP_NONE != propagation_mode(); - m_solver->set_propagate_bounds_on_pivoted_rows_mode(lp.bprop_on_pivoted_rows()); - //m_solver->settings().set_ostream(0); - } - - void found_not_handled(expr* n) { - m_not_handled = n; - if (is_app(n) && is_underspecified(to_app(n))) { - m_underspecified.push_back(to_app(n)); - } - TRACE("arith", tout << "Unhandled: " << mk_pp(n, m) << "\n";); - } - - bool is_numeral(expr* term, rational& r) { - rational mul(1); - do { - if (a.is_numeral(term, r)) { - r *= mul; - return true; - } - if (a.is_uminus(term, term)) { - mul.neg(); - continue; - } - if (a.is_to_real(term, term)) { - continue; - } - return false; - } - while (false); - return false; - } - - void linearize_term(expr* term, scoped_internalize_state& st) { - st.push(term, rational::one()); - linearize(st); - } - - void linearize_ineq(expr* lhs, expr* rhs, scoped_internalize_state& st) { - st.push(lhs, rational::one()); - st.push(rhs, rational::minus_one()); - linearize(st); - } - - void linearize(scoped_internalize_state& st) { - expr_ref_vector & terms = st.terms(); - svector& vars = st.vars(); - vector& coeffs = st.coeffs(); - rational& coeff = st.coeff(); - rational r; - expr* n1, *n2; - unsigned index = 0; - while (index < terms.size()) { - SASSERT(index >= vars.size()); - expr* n = terms[index].get(); - st.terms_to_internalize().push_back(n); - if (a.is_add(n)) { - unsigned sz = to_app(n)->get_num_args(); - for (unsigned i = 0; i < sz; ++i) { - st.push(to_app(n)->get_arg(i), coeffs[index]); - } - st.set_back(index); - } - else if (a.is_sub(n)) { - unsigned sz = to_app(n)->get_num_args(); - terms[index] = to_app(n)->get_arg(0); - for (unsigned i = 1; i < sz; ++i) { - st.push(to_app(n)->get_arg(i), -coeffs[index]); - } - } - else if (a.is_mul(n, n1, n2) && is_numeral(n1, r)) { - coeffs[index] *= r; - terms[index] = n2; - st.terms_to_internalize().push_back(n1); - } - else if (a.is_mul(n, n1, n2) && is_numeral(n2, r)) { - coeffs[index] *= r; - terms[index] = n1; - st.terms_to_internalize().push_back(n2); - } - else if (a.is_numeral(n, r)) { - coeff += coeffs[index]*r; - ++index; - } - else if (a.is_uminus(n, n1)) { - coeffs[index].neg(); - terms[index] = n1; - } - else if (is_app(n) && a.get_family_id() == to_app(n)->get_family_id()) { - app* t = to_app(n); - if (a.is_div(n, n1, n2) && is_numeral(n2, r)) { - // skip - } - else { - found_not_handled(n); - } - internalize_args(t); - mk_enode(t); - theory_var v = mk_var(n); - coeffs[vars.size()] = coeffs[index]; - vars.push_back(v); - ++index; - } - else { - if (is_app(n)) { - internalize_args(to_app(n)); - } - if (a.is_int(n)) { - found_not_handled(n); - } - theory_var v = mk_var(n); - coeffs[vars.size()] = coeffs[index]; - vars.push_back(v); - ++index; - } - } - for (unsigned i = st.terms_to_internalize().size(); i > 0; ) { - --i; - expr* n = st.terms_to_internalize()[i]; - if (is_app(n)) { - mk_enode(to_app(n)); - } - } - st.terms_to_internalize().reset(); - } - - void internalize_args(app* t) { - for (unsigned i = 0; reflect(t) && i < t->get_num_args(); ++i) { - if (!ctx().e_internalized(t->get_arg(i))) { - ctx().internalize(t->get_arg(i), false); - } - } - } - - enode * mk_enode(app * n) { - if (ctx().e_internalized(n)) { - return get_enode(n); - } - else { - return ctx().mk_enode(n, !reflect(n), false, enable_cgc_for(n)); - } - } - - bool enable_cgc_for(app * n) const { - // Congruence closure is not enabled for (+ ...) and (* ...) applications. - return !(n->get_family_id() == get_id() && (n->get_decl_kind() == OP_ADD || n->get_decl_kind() == OP_MUL)); - } - - - void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params) { - TRACE("arith", literal lits[2]; lits[0] = l1; lits[1] = l2; ctx().display_literals_verbose(tout, 2, lits); tout << "\n";); - ctx().mk_th_axiom(get_id(), l1, l2, num_params, params); - } - - void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { - TRACE("arith", literal lits[3]; lits[0] = l1; lits[1] = l2; lits[2] = l3; ctx().display_literals_verbose(tout, 3, lits); tout << "\n";); - ctx().mk_th_axiom(get_id(), l1, l2, l3, num_params, params); - } - - bool is_underspecified(app* n) const { - if (n->get_family_id() == get_id()) { - switch (n->get_decl_kind()) { - case OP_DIV: - case OP_IDIV: - case OP_REM: - case OP_MOD: - return true; - default: - break; - } - } - return false; - } - - bool reflect(app* n) const { - return m_arith_params.m_arith_reflect || is_underspecified(n); - } - - theory_var mk_var(expr* n, bool internalize = true) { - if (!ctx().e_internalized(n)) { - ctx().internalize(n, false); - } - enode* e = get_enode(n); - theory_var v; - if (!th.is_attached_to_var(e)) { - v = th.mk_var(e); - SASSERT(m_bounds.size() <= static_cast(v) || m_bounds[v].empty()); - if (m_bounds.size() <= static_cast(v)) { - m_bounds.push_back(lp_bounds()); - m_unassigned_bounds.push_back(0); - } - ctx().attach_th_var(e, &th, v); - } - else { - v = e->get_th_var(get_id()); - } - SASSERT(null_theory_var != v); - return v; - } - - lp::var_index get_var_index(theory_var v) { - lp::var_index result = UINT_MAX; - if (m_theory_var2var_index.size() > static_cast(v)) { - result = m_theory_var2var_index[v]; - } - if (result == UINT_MAX) { - result = m_solver->add_var(v); // TBD: is_int(v); - m_theory_var2var_index.setx(v, result, UINT_MAX); - m_var_index2theory_var.setx(result, v, UINT_MAX); - m_var_trail.push_back(v); - } - return result; - } - - void init_left_side(scoped_internalize_state& st) { - SASSERT(all_zeros(m_columns)); - svector const& vars = st.vars(); - vector const& coeffs = st.coeffs(); - for (unsigned i = 0; i < vars.size(); ++i) { - theory_var var = vars[i]; - rational const& coeff = coeffs[i]; - if (m_columns.size() <= static_cast(var)) { - m_columns.setx(var, coeff, rational::zero()); - } - else { - m_columns[var] += coeff; - } - } - m_left_side.clear(); - // reset the coefficients after they have been used. - for (unsigned i = 0; i < vars.size(); ++i) { - theory_var var = vars[i]; - rational const& r = m_columns[var]; - if (!r.is_zero()) { - m_left_side.push_back(std::make_pair(r, get_var_index(var))); - m_columns[var].reset(); - } - } - SASSERT(all_zeros(m_columns)); - } - - bool all_zeros(vector const& v) const { - for (unsigned i = 0; i < v.size(); ++i) { - if (!v[i].is_zero()) { - return false; - } - } - return true; - } - - void add_eq_constraint(lp::constraint_index index, enode* n1, enode* n2) { - m_constraint_sources.setx(index, equality_source, null_source); - m_equalities.setx(index, enode_pair(n1, n2), enode_pair(0, 0)); - ++m_stats.m_add_rows; - } - - void add_ineq_constraint(lp::constraint_index index, literal lit) { - m_constraint_sources.setx(index, inequality_source, null_source); - m_inequalities.setx(index, lit, null_literal); - ++m_stats.m_add_rows; - TRACE("arith", m_solver->print_constraint(index, tout); tout << "\n";); - } - - void add_def_constraint(lp::constraint_index index, theory_var v) { - m_constraint_sources.setx(index, definition_source, null_source); - m_definitions.setx(index, v, null_theory_var); - ++m_stats.m_add_rows; - } - - void internalize_eq(delayed_def const& d) { - scoped_internalize_state st(*this); - st.vars().append(d.m_vars); - st.coeffs().append(d.m_coeffs); - init_left_side(st); - add_def_constraint(m_solver->add_constraint(m_left_side, lp::EQ, -d.m_coeff), d.m_var); - } - - void internalize_eq(theory_var v1, theory_var v2) { - enode* n1 = get_enode(v1); - enode* n2 = get_enode(v2); - scoped_internalize_state st(*this); - st.vars().push_back(v1); - st.vars().push_back(v2); - st.coeffs().push_back(rational::one()); - st.coeffs().push_back(rational::minus_one()); - init_left_side(st); - add_eq_constraint(m_solver->add_constraint(m_left_side, lp::EQ, rational::zero()), n1, n2); - TRACE("arith", - tout << "v" << v1 << " = " << "v" << v2 << ": " - << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << "\n";); - } - - void del_bounds(unsigned old_size) { - for (unsigned i = m_bounds_trail.size(); i > old_size; ) { - --i; - unsigned v = m_bounds_trail[i]; - lra_lp::bound* b = m_bounds[v].back(); - // del_use_lists(b); - dealloc(b); - m_bounds[v].pop_back(); - } - m_bounds_trail.shrink(old_size); - } - - void updt_unassigned_bounds(theory_var v, int inc) { - TRACE("arith", tout << "v" << v << " " << m_unassigned_bounds[v] << " += " << inc << "\n";); - ctx().push_trail(vector_value_trail(m_unassigned_bounds, v)); - m_unassigned_bounds[v] += inc; - } - - bool is_unit_var(scoped_internalize_state& st) { - return st.coeff().is_zero() && st.vars().size() == 1 && st.coeffs()[0].is_one(); - } - - theory_var internalize_def(app* term, scoped_internalize_state& st) { - linearize_term(term, st); - if (is_unit_var(st)) { - return st.vars()[0]; - } - else { - theory_var v = mk_var(term); - SASSERT(null_theory_var != v); - st.coeffs().resize(st.vars().size() + 1); - st.coeffs()[st.vars().size()] = rational::minus_one(); - st.vars().push_back(v); - return v; - } - } - - // term - v = 0 - theory_var internalize_def(app* term) { - scoped_internalize_state st(*this); - linearize_term(term, st); - if (is_unit_var(st)) { - return st.vars()[0]; - } - else { - init_left_side(st); - theory_var v = mk_var(term); - lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX); - if (vi == UINT_MAX) { - vi = m_solver->add_term(m_left_side, st.coeff()); - m_theory_var2var_index.setx(v, vi, UINT_MAX); - if (m_solver->is_term(vi)) { - m_term_index2theory_var.setx(m_solver->adjust_term_index(vi), v, UINT_MAX); - } - else { - m_var_index2theory_var.setx(vi, v, UINT_MAX); - } - m_var_trail.push_back(v); - TRACE("arith", tout << "v" << v << " := " << mk_pp(term, m) << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; - m_solver->print_term(m_solver->get_term(vi), tout); tout << "\n";); - } - rational val; - if (a.is_numeral(term, val)) { - m_fixed_var_table.insert(value_sort_pair(val, is_int(v)), v); - } - return v; - } - } + struct scope { + unsigned m_bounds_lim; + unsigned m_asserted_qhead; + unsigned m_asserted_atoms_lim; + unsigned m_underspecified_lim; + unsigned m_var_trail_lim; + expr* m_not_handled; + }; + struct delayed_atom { + unsigned m_bv; + bool m_is_true; + delayed_atom(unsigned b, bool t): m_bv(b), m_is_true(t) {} + }; + class resource_limit : public lp::lp_resource_limit { + imp& m_imp; public: - imp(theory_lra& th, ast_manager& m, theory_arith_params& ap): - th(th), m(m), - m_arith_params(ap), - a(m), - m_arith_eq_adapter(th, ap, a), - m_internalize_head(0), - m_delay_constraints(false), - m_delayed_terms(m), - m_not_handled(nullptr), - m_asserted_qhead(0), - m_assume_eq_head(0), - m_num_conflicts(0), - m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), - m_solver(nullptr), - m_resource_limit(*this) { - } + resource_limit(imp& i): m_imp(i) { } + virtual bool get_cancel_flag() { return m_imp.m.canceled(); } + }; - ~imp() { - del_bounds(0); - std::for_each(m_internalize_states.begin(), m_internalize_states.end(), delete_proc()); - } - void init(context* ctx) { - init_solver(); - } + theory_lra& th; + ast_manager& m; + theory_arith_params& m_arith_params; + arith_util a; + arith_eq_adapter m_arith_eq_adapter; + vector m_columns; + - bool internalize_atom(app * atom, bool gate_ctx) { - if (m_delay_constraints) { - return internalize_atom_lazy(atom, gate_ctx); + // temporary values kept during internalization + struct internalize_state { + expr_ref_vector m_terms; + vector m_coeffs; + svector m_vars; + rational m_coeff; + ptr_vector m_terms_to_internalize; + internalize_state(ast_manager& m): m_terms(m) {} + void reset() { + m_terms.reset(); + m_coeffs.reset(); + m_coeff.reset(); + m_vars.reset(); + m_terms_to_internalize.reset(); + } + }; + ptr_vector m_internalize_states; + unsigned m_internalize_head; + + class scoped_internalize_state { + imp& m_imp; + internalize_state& m_st; + + internalize_state& push_internalize(imp& i) { + if (i.m_internalize_head == i.m_internalize_states.size()) { + i.m_internalize_states.push_back(alloc(internalize_state, i.m)); + } + internalize_state& st = *i.m_internalize_states[i.m_internalize_head++]; + st.reset(); + return st; + } + public: + scoped_internalize_state(imp& i): m_imp(i), m_st(push_internalize(i)) {} + ~scoped_internalize_state() { --m_imp.m_internalize_head; } + expr_ref_vector& terms() { return m_st.m_terms; } + vector& coeffs() { return m_st.m_coeffs; } + svector& vars() { return m_st.m_vars; } + rational& coeff() { return m_st.m_coeff; } + ptr_vector& terms_to_internalize() { return m_st.m_terms_to_internalize; } + void push(expr* e, rational c) { m_st.m_terms.push_back(e); m_st.m_coeffs.push_back(c); } + void set_back(unsigned i) { + if (terms().size() == i + 1) return; + terms()[i] = terms().back(); + coeffs()[i] = coeffs().back(); + terms().pop_back(); + coeffs().pop_back(); + } + }; + + typedef vector> var_coeffs; + + svector m_theory_var2var_index; // translate from theory variables to lar vars + svector m_var_index2theory_var; // reverse map from lp_solver variables to theory variables + svector m_term_index2theory_var; // reverse map from lp_solver variables to theory variables + var_coeffs m_left_side; // constraint left side + mutable std::unordered_map m_variable_values; // current model + + enum constraint_source { + inequality_source, + equality_source, + definition_source, + null_source + }; + svector m_constraint_sources; + svector m_inequalities; // asserted rows corresponding to inequality literals. + svector m_equalities; // asserted rows corresponding to equalities. + svector m_definitions; // asserted rows corresponding to definitions + + svector m_asserted_atoms; + expr* m_not_handled; + ptr_vector m_underspecified; + unsigned_vector m_var_trail; + vector > m_use_list; // bounds where variables are used. + + // attributes for incremental version: + u_map m_bool_var2bound; + vector m_bounds; + unsigned_vector m_unassigned_bounds; + unsigned_vector m_bounds_trail; + unsigned m_asserted_qhead; + + svector m_to_check; // rows that should be checked for theory propagation + + svector > m_assume_eq_candidates; + unsigned m_assume_eq_head; + + unsigned m_num_conflicts; + + // non-linear arithmetic + scoped_ptr m_nra; + bool m_use_nra_model; + scoped_ptr m_a1, m_a2; + + // integer arithmetic + scoped_ptr m_lia; + + + struct var_value_eq { + imp & m_th; + var_value_eq(imp & th):m_th(th) {} + bool operator()(theory_var v1, theory_var v2) const { + if (m_th.is_int(v1) != m_th.is_int(v2)) { + return false; + } + return m_th.is_eq(v1, v2); + } + }; + struct var_value_hash { + imp & m_th; + var_value_hash(imp & th):m_th(th) {} + unsigned operator()(theory_var v) const { + if (m_th.m_use_nra_model) { + return m_th.is_int(v); } else { - return internalize_atom_strict(atom, gate_ctx); + return (unsigned)std::hash()(m_th.get_value(v)); } } + }; + int_hashtable m_model_eqs; - bool internalize_atom_strict(app * atom, bool gate_ctx) { - SASSERT(!ctx().b_internalized(atom)); - bool_var bv = ctx().mk_bool_var(atom); - ctx().set_var_theory(bv, get_id()); - expr* n1 = nullptr, *n2 = nullptr; - rational r; - lra_lp::bound_kind k; - theory_var v = null_theory_var; - if (a.is_le(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { - v = internalize_def(to_app(n1)); - k = lra_lp::upper_t; + + svector m_scopes; + lp_api::stats m_stats; + arith_factory* m_factory; + scoped_ptr m_solver; + resource_limit m_resource_limit; + lp_bounds m_new_bounds; + + + context& ctx() const { return th.get_context(); } + theory_id get_id() const { return th.get_id(); } + bool is_int(theory_var v) const { return is_int(get_enode(v)); } + bool is_int(enode* n) const { return a.is_int(n->get_owner()); } + enode* get_enode(theory_var v) const { return th.get_enode(v); } + enode* get_enode(expr* e) const { return ctx().get_enode(e); } + expr* get_owner(theory_var v) const { return get_enode(v)->get_owner(); } + + void init_solver() { + if (m_solver) return; + lp_params lp(ctx().get_params()); + m_solver = alloc(lp::lar_solver); + m_theory_var2var_index.reset(); + m_solver->settings().set_resource_limit(m_resource_limit); + m_solver->settings().simplex_strategy() = static_cast(lp.simplex_strategy()); + reset_variable_values(); + m_solver->settings().bound_propagation() = BP_NONE != propagation_mode(); + m_solver->set_track_pivoted_rows(lp.bprop_on_pivoted_rows()); + + // todo : do not use m_arith_branch_cut_ratio for deciding on cheap cuts + unsigned branch_cut_ratio = ctx().get_fparams().m_arith_branch_cut_ratio; + m_solver->set_cut_strategy(branch_cut_ratio); + + m_solver->settings().m_int_run_gcd_test = ctx().get_fparams().m_arith_gcd_test; + m_solver->settings().set_random_seed(ctx().get_fparams().m_random_seed); + m_lia = alloc(lp::int_solver, m_solver.get()); + } + + void ensure_nra() { + if (!m_nra) { + m_nra = alloc(nra::solver, *m_solver.get(), m.limit(), ctx().get_params()); + for (auto const& _s : m_scopes) { + (void)_s; + m_nra->push(); } - else if (a.is_ge(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { - v = internalize_def(to_app(n1)); - k = lra_lp::lower_t; - } - else { - TRACE("arith", tout << "Could not internalize " << mk_pp(atom, m) << "\n";); - found_not_handled(atom); + } + } + + + void found_not_handled(expr* n) { + m_not_handled = n; + if (is_app(n) && is_underspecified(to_app(n))) { + TRACE("arith", tout << "Unhandled: " << mk_pp(n, m) << "\n";); + m_underspecified.push_back(to_app(n)); + } + } + + bool is_numeral(expr* term, rational& r) { + rational mul(1); + do { + if (a.is_numeral(term, r)) { + r *= mul; return true; } - lra_lp::bound* b = alloc(lra_lp::bound, bv, v, r, k); - m_bounds[v].push_back(b); - updt_unassigned_bounds(v, +1); - m_bounds_trail.push_back(v); - m_bool_var2bound.insert(bv, b); - TRACE("arith", tout << "Internalized " << mk_pp(atom, m) << "\n";); - mk_bound_axioms(*b); - //add_use_lists(b); - return true; - } - - bool internalize_atom_lazy(app * atom, bool gate_ctx) { - SASSERT(!ctx().b_internalized(atom)); - bool_var bv = ctx().mk_bool_var(atom); - ctx().set_var_theory(bv, get_id()); - expr* n1 = nullptr, *n2 = nullptr; - rational r; - lra_lp::bound_kind k; - theory_var v = null_theory_var; - scoped_internalize_state st(*this); - if (a.is_le(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { - v = internalize_def(to_app(n1), st); - k = lra_lp::upper_t; - } - else if (a.is_ge(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { - v = internalize_def(to_app(n1), st); - k = lra_lp::lower_t; - } - else { - TRACE("arith", tout << "Could not internalize " << mk_pp(atom, m) << "\n";); - found_not_handled(atom); - return true; - } - lra_lp::bound* b = alloc(lra_lp::bound, bv, v, r, k); - m_bounds[v].push_back(b); - updt_unassigned_bounds(v, +1); - m_bounds_trail.push_back(v); - m_bool_var2bound.insert(bv, b); - TRACE("arith", tout << "Internalized " << mk_pp(atom, m) << "\n";); - if (!is_unit_var(st) && m_bounds[v].size() == 1) { - m_delayed_defs.push_back(delayed_def(st.vars(), st.coeffs(), st.coeff(), v)); - } - return true; - } - - bool internalize_term(app * term) { - if (ctx().e_internalized(term) && th.is_attached_to_var(ctx().get_enode(term))) { - // skip - } - else if (m_delay_constraints) { - scoped_internalize_state st(*this); - linearize_term(term, st); // ensure that a theory_var was created. - SASSERT(ctx().e_internalized(term)); - if(!th.is_attached_to_var(ctx().get_enode(term))) { - mk_var(term); - } - m_delayed_terms.push_back(term); - } - else { - internalize_def(term); - } - return true; - } - - void internalize_eq_eh(app * atom, bool_var) { - expr* lhs = nullptr, *rhs = nullptr; - VERIFY(m.is_eq(atom, lhs, rhs)); - enode * n1 = get_enode(lhs); - enode * n2 = get_enode(rhs); - if (n1->get_th_var(get_id()) != null_theory_var && - n2->get_th_var(get_id()) != null_theory_var && - n1 != n2) { - TRACE("arith", tout << mk_pp(atom, m) << "\n";); - m_arith_eq_adapter.mk_axioms(n1, n2); - } - } - - void assign_eh(bool_var v, bool is_true) { - TRACE("arith", tout << mk_pp(ctx().bool_var2expr(v), m) << " " << (is_true?"true":"false") << "\n";); - m_asserted_atoms.push_back(delayed_atom(v, is_true)); - } - - void new_eq_eh(theory_var v1, theory_var v2) { - if (m_delay_constraints) { - m_delayed_equalities.push_back(std::make_pair(v1, v2)); - } - else { - // or internalize_eq(v1, v2); - m_arith_eq_adapter.new_eq_eh(v1, v2); - } - } - - bool use_diseqs() const { - return true; - } - - void new_diseq_eh(theory_var v1, theory_var v2) { - TRACE("arith", tout << "v" << v1 << " != " << "v" << v2 << "\n";); - ++m_stats.m_assert_diseq; - m_arith_eq_adapter.new_diseq_eh(v1, v2); - } - - void push_scope_eh() { - m_scopes.push_back(scope()); - scope& s = m_scopes.back(); - s.m_bounds_lim = m_bounds_trail.size(); - s.m_asserted_qhead = m_asserted_qhead; - s.m_asserted_atoms_lim = m_asserted_atoms.size(); - s.m_delayed_terms_lim = m_delayed_terms.size(); - s.m_delayed_equalities_lim = m_delayed_equalities.size(); - s.m_delayed_defs_lim = m_delayed_defs.size(); - s.m_not_handled = m_not_handled; - s.m_underspecified_lim = m_underspecified.size(); - s.m_var_trail_lim = m_var_trail.size(); - if (!m_delay_constraints) m_solver->push(); - } - - void pop_scope_eh(unsigned num_scopes) { - if (num_scopes == 0) { - return; - } - unsigned old_size = m_scopes.size() - num_scopes; - del_bounds(m_scopes[old_size].m_bounds_lim); - for (unsigned i = m_scopes[old_size].m_var_trail_lim; i < m_var_trail.size(); ++i) { - lp::var_index vi = m_theory_var2var_index[m_var_trail[i]]; - if (m_solver->is_term(vi)) { - unsigned ti = m_solver->adjust_term_index(vi); - m_term_index2theory_var[ti] = UINT_MAX; - } - else if (vi < m_var_index2theory_var.size()) { - m_var_index2theory_var[vi] = UINT_MAX; - } - m_theory_var2var_index[m_var_trail[i]] = UINT_MAX; - } - m_asserted_atoms.shrink(m_scopes[old_size].m_asserted_atoms_lim); - m_delayed_terms.shrink(m_scopes[old_size].m_delayed_terms_lim); - m_delayed_defs.shrink(m_scopes[old_size].m_delayed_defs_lim); - m_delayed_equalities.shrink(m_scopes[old_size].m_delayed_equalities_lim); - m_asserted_qhead = m_scopes[old_size].m_asserted_qhead; - m_underspecified.shrink(m_scopes[old_size].m_underspecified_lim); - m_var_trail.shrink(m_scopes[old_size].m_var_trail_lim); - m_not_handled = m_scopes[old_size].m_not_handled; - m_scopes.resize(old_size); - if (!m_delay_constraints) m_solver->pop(num_scopes); - // VERIFY(l_false != make_feasible()); - m_new_bounds.reset(); - m_to_check.reset(); - TRACE("arith", tout << "num scopes: " << num_scopes << " new scope level: " << m_scopes.size() << "\n";); - } - - void restart_eh() { - m_arith_eq_adapter.restart_eh(); - } - - void relevant_eh(app* n) { - TRACE("arith", tout << mk_pp(n, m) << "\n";); - expr* n1 = nullptr, *n2 = nullptr; - if (a.is_mod(n, n1, n2)) - mk_idiv_mod_axioms(n1, n2); - else if (a.is_rem(n, n1, n2)) - mk_rem_axiom(n1, n2); - else if (a.is_div(n, n1, n2)) - mk_div_axiom(n1, n2); - else if (a.is_to_int(n)) - mk_to_int_axiom(n); - else if (a.is_is_int(n)) - mk_is_int_axiom(n); - } - - // n < 0 || rem(a, n) = mod(a, n) - // !n < 0 || rem(a, n) = -mod(a, n) - void mk_rem_axiom(expr* dividend, expr* divisor) { - expr_ref zero(a.mk_int(0), m); - expr_ref rem(a.mk_rem(dividend, divisor), m); - expr_ref mod(a.mk_mod(dividend, divisor), m); - expr_ref mmod(a.mk_uminus(mod), m); - literal dgez = mk_literal(a.mk_ge(divisor, zero)); - mk_axiom(~dgez, th.mk_eq(rem, mod, false)); - mk_axiom( dgez, th.mk_eq(rem, mmod, false)); - } - - // q = 0 or q * (p div q) = p - void mk_div_axiom(expr* p, expr* q) { - if (a.is_zero(q)) return; - literal eqz = th.mk_eq(q, a.mk_real(0), false); - literal eq = th.mk_eq(a.mk_mul(q, a.mk_div(p, q)), p, false); - mk_axiom(eqz, eq); - } - - // to_int (to_real x) = x - // to_real(to_int(x)) <= x < to_real(to_int(x)) + 1 - void mk_to_int_axiom(app* n) { - expr* x = nullptr, *y = nullptr; - VERIFY (a.is_to_int(n, x)); - if (a.is_to_real(x, y)) { - mk_axiom(th.mk_eq(y, n, false)); - } - else { - expr_ref to_r(a.mk_to_real(n), m); - expr_ref lo(a.mk_le(a.mk_sub(to_r, x), a.mk_real(0)), m); - expr_ref hi(a.mk_ge(a.mk_sub(x, to_r), a.mk_real(1)), m); - mk_axiom(mk_literal(lo)); - mk_axiom(~mk_literal(hi)); - } - } - - // is_int(x) <=> to_real(to_int(x)) = x - void mk_is_int_axiom(app* n) { - expr* x = nullptr; - VERIFY(a.is_is_int(n, x)); - literal eq = th.mk_eq(a.mk_to_real(a.mk_to_int(x)), x, false); - literal is_int = ctx().get_literal(n); - mk_axiom(~is_int, eq); - mk_axiom(is_int, ~eq); - } - - void mk_idiv_mod_axioms(expr * p, expr * q) { - if (a.is_zero(q)) { - return; - } - // if q is zero, then idiv and mod are uninterpreted functions. - expr_ref div(a.mk_idiv(p, q), m); - expr_ref mod(a.mk_mod(p, q), m); - expr_ref zero(a.mk_int(0), m); - literal q_ge_0 = mk_literal(a.mk_ge(q, zero)); - literal q_le_0 = mk_literal(a.mk_le(q, zero)); - // literal eqz = th.mk_eq(q, zero, false); - literal eq = th.mk_eq(a.mk_add(a.mk_mul(q, div), mod), p, false); - literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero)); - // q >= 0 or p = (p mod q) + q * (p div q) - // q <= 0 or p = (p mod q) + q * (p div q) - // q >= 0 or (p mod q) >= 0 - // q <= 0 or (p mod q) >= 0 - // q <= 0 or (p mod q) < q - // q >= 0 or (p mod q) < -q - mk_axiom(q_ge_0, eq); - mk_axiom(q_le_0, eq); - mk_axiom(q_ge_0, mod_ge_0); - mk_axiom(q_le_0, mod_ge_0); - mk_axiom(q_le_0, ~mk_literal(a.mk_ge(a.mk_sub(mod, q), zero))); - mk_axiom(q_ge_0, ~mk_literal(a.mk_ge(a.mk_add(mod, q), zero))); - rational k; - if (m_arith_params.m_arith_enum_const_mod && a.is_numeral(q, k) && - k.is_pos() && k < rational(8)) { - unsigned _k = k.get_unsigned(); - literal_buffer lits; - for (unsigned j = 0; j < _k; ++j) { - literal mod_j = th.mk_eq(mod, a.mk_int(j), false); - lits.push_back(mod_j); - ctx().mark_as_relevant(mod_j); - } - ctx().mk_th_axiom(get_id(), lits.size(), lits.begin()); - } - } - - void mk_axiom(literal l) { - ctx().mk_th_axiom(get_id(), false_literal, l); - if (ctx().relevancy()) { - ctx().mark_as_relevant(l); - } - } - - void mk_axiom(literal l1, literal l2) { - if (l1 == false_literal) { - mk_axiom(l2); - return; - } - ctx().mk_th_axiom(get_id(), l1, l2); - if (ctx().relevancy()) { - ctx().mark_as_relevant(l1); - expr_ref e(m); - ctx().literal2expr(l2, e); - ctx().add_rel_watch(~l1, e); - } - } - - void mk_axiom(literal l1, literal l2, literal l3) { - ctx().mk_th_axiom(get_id(), l1, l2, l3); - if (ctx().relevancy()) { - expr_ref e(m); - ctx().mark_as_relevant(l1); - ctx().literal2expr(l2, e); - ctx().add_rel_watch(~l1, e); - ctx().literal2expr(l3, e); - ctx().add_rel_watch(~l2, e); - } - } - - literal mk_literal(expr* e) { - expr_ref pinned(e, m); - if (!ctx().e_internalized(e)) { - ctx().internalize(e, false); - } - return ctx().get_literal(e); - } - - - void init_search_eh() { - m_arith_eq_adapter.init_search_eh(); - m_num_conflicts = 0; - } - - bool can_get_value(theory_var v) const { - return - (v != null_theory_var) && - (v < static_cast(m_theory_var2var_index.size())) && - (UINT_MAX != m_theory_var2var_index[v]) && - (m_solver->is_term(m_theory_var2var_index[v]) || m_variable_values.count(m_theory_var2var_index[v]) > 0); - } - - - bool can_get_ivalue(theory_var v) const { - if (v == null_theory_var || (v >= static_cast(m_theory_var2var_index.size()))) - return false; - return m_solver->var_is_registered(m_theory_var2var_index[v]); - } - - lp::impq get_ivalue(theory_var v) const { - SASSERT(can_get_ivalue(v)); - lp::var_index vi = m_theory_var2var_index[v]; - if (!m_solver->is_term(vi)) - return m_solver->get_value(vi); - - const lp::lar_term& term = m_solver->get_term(vi); - lp::impq result(term.m_v); - for (const auto & i: term.m_coeffs) { - result += m_solver->get_value(i.first) * i.second; - } - return result; - } - - - rational get_value(theory_var v) const { - if (!can_get_value(v)) return rational::zero(); - lp::var_index vi = m_theory_var2var_index[v]; - if (m_variable_values.count(vi) > 0) { - return m_variable_values[vi]; - } - if (m_solver->is_term(vi)) { - const lp::lar_term& term = m_solver->get_term(vi); - rational result = term.m_v; - for (auto i = term.m_coeffs.begin(); i != term.m_coeffs.end(); ++i) { - result += m_variable_values[i->first] * i->second; - } - m_variable_values[vi] = result; - return result; - } - UNREACHABLE(); - return m_variable_values[vi]; - } - - void init_variable_values() { - if (m_solver.get() && th.get_num_vars() > 0) { - m_solver->get_model(m_variable_values); - } - } - - void reset_variable_values() { - m_variable_values.clear(); - } - - bool assume_eqs() { - svector vars; - theory_var sz = static_cast(th.get_num_vars()); - for (theory_var v = 0; v < sz; ++v) { - if (th.is_relevant_and_shared(get_enode(v))) { - vars.push_back(m_theory_var2var_index[v]); - } - } - if (vars.empty()) { - return false; - } - TRACE("arith", - for (theory_var v = 0; v < sz; ++v) { - if (th.is_relevant_and_shared(get_enode(v))) { - tout << "v" << v << " " << m_theory_var2var_index[v] << " "; - } - } - tout << "\n"; - ); - m_solver->random_update(vars.size(), vars.c_ptr()); - m_model_eqs.reset(); - TRACE("arith", display(tout);); - - unsigned old_sz = m_assume_eq_candidates.size(); - bool result = false; - int start = ctx().get_random_value(); - for (theory_var i = 0; i < sz; ++i) { - theory_var v = (i + start) % sz; - enode* n1 = get_enode(v); - if (!th.is_relevant_and_shared(n1)) { - continue; - } - if (!can_get_ivalue(v)) { - continue; - } - theory_var other = m_model_eqs.insert_if_not_there(v); - if (other == v) { - continue; - } - enode* n2 = get_enode(other); - if (n1->get_root() != n2->get_root()) { - TRACE("arith", tout << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << "\n"; - tout << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << "\n"; - tout << "v" << v << " = " << "v" << other << "\n";); - m_assume_eq_candidates.push_back(std::make_pair(v, other)); - result = true; - } - } - - if (result) { - ctx().push_trail(restore_size_trail, false>(m_assume_eq_candidates, old_sz)); - } - - return delayed_assume_eqs(); - } - - bool delayed_assume_eqs() { - if (m_assume_eq_head == m_assume_eq_candidates.size()) - return false; - - ctx().push_trail(value_trail(m_assume_eq_head)); - while (m_assume_eq_head < m_assume_eq_candidates.size()) { - std::pair const & p = m_assume_eq_candidates[m_assume_eq_head]; - theory_var v1 = p.first; - theory_var v2 = p.second; - enode* n1 = get_enode(v1); - enode* n2 = get_enode(v2); - m_assume_eq_head++; - CTRACE("arith", - get_ivalue(v1) == get_ivalue(v2) && n1->get_root() != n2->get_root(), - tout << "assuming eq: v" << v1 << " = v" << v2 << "\n";); - if (get_ivalue(v1) == get_ivalue(v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) { - return true; - } + if (a.is_uminus(term, term)) { + mul.neg(); + continue; } + if (a.is_to_real(term, term)) { + continue; + } return false; } + while (false); + return false; + } - bool has_delayed_constraints() const { - return !(m_asserted_atoms.empty() && m_delayed_terms.empty() && m_delayed_equalities.empty()); + void linearize_term(expr* term, scoped_internalize_state& st) { + st.push(term, rational::one()); + linearize(st); + } + + void linearize_ineq(expr* lhs, expr* rhs, scoped_internalize_state& st) { + st.push(lhs, rational::one()); + st.push(rhs, rational::minus_one()); + linearize(st); + } + + void linearize(scoped_internalize_state& st) { + expr_ref_vector & terms = st.terms(); + svector& vars = st.vars(); + vector& coeffs = st.coeffs(); + rational& coeff = st.coeff(); + rational r; + expr* n1, *n2; + unsigned index = 0; + while (index < terms.size()) { + SASSERT(index >= vars.size()); + expr* n = terms[index].get(); + st.terms_to_internalize().push_back(n); + if (a.is_add(n)) { + for (expr* arg : *to_app(n)) { + st.push(arg, coeffs[index]); + } + st.set_back(index); + } + else if (a.is_sub(n)) { + unsigned sz = to_app(n)->get_num_args(); + terms[index] = to_app(n)->get_arg(0); + for (unsigned i = 1; i < sz; ++i) { + st.push(to_app(n)->get_arg(i), -coeffs[index]); + } + } + else if (a.is_mul(n, n1, n2) && is_numeral(n1, r)) { + coeffs[index] *= r; + terms[index] = n2; + st.terms_to_internalize().push_back(n1); + } + else if (a.is_mul(n, n1, n2) && is_numeral(n2, r)) { + coeffs[index] *= r; + terms[index] = n1; + st.terms_to_internalize().push_back(n2); + } + else if (a.is_mul(n)) { + theory_var v; + internalize_mul(to_app(n), v, r); + coeffs[index] *= r; + coeffs[vars.size()] = coeffs[index]; + vars.push_back(v); + ++index; + } + else if (a.is_numeral(n, r)) { + coeff += coeffs[index]*r; + ++index; + } + else if (a.is_uminus(n, n1)) { + coeffs[index].neg(); + terms[index] = n1; + } + else if (is_app(n) && a.get_family_id() == to_app(n)->get_family_id()) { + bool is_first = !ctx().e_internalized(n); + app* t = to_app(n); + internalize_args(t); + mk_enode(t); + + theory_var v = mk_var(n); + coeffs[vars.size()] = coeffs[index]; + vars.push_back(v); + ++index; + if (!is_first) { + // skip recursive internalization + } + else if (a.is_to_int(n, n1)) { + if (!ctx().relevancy()) + mk_to_int_axiom(t); + } + else if (a.is_to_real(n, n1)) { + theory_var v1 = mk_var(n1); + internalize_eq(v, v1); + } + else if (a.is_idiv(n, n1, n2)) { + if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n); + app * mod = a.mk_mod(n1, n2); + ctx().internalize(mod, false); + if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); + } + else if (a.is_mod(n, n1, n2)) { + bool is_num = a.is_numeral(n2, r) && !r.is_zero(); + if (!is_num) { + found_not_handled(n); + } + else { + app_ref div(a.mk_idiv(n1, n2), m); + mk_enode(div); + theory_var w = mk_var(div); + theory_var u = mk_var(n1); + // add axioms: + // u = v + r*w + // abs(r) > v >= 0 + assert_idiv_mod_axioms(u, v, w, r); + } + if (!ctx().relevancy() && !is_num) mk_idiv_mod_axioms(n1, n2); + } + else if (a.is_rem(n, n1, n2)) { + if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n); + if (!ctx().relevancy()) mk_rem_axiom(n1, n2); + } + else if (a.is_div(n, n1, n2)) { + if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n); + if (!ctx().relevancy()) mk_div_axiom(n1, n2); + } + else if (a.is_power(n)) { + found_not_handled(n); + } + else { + found_not_handled(n); + } + } + else { + if (is_app(n)) { + internalize_args(to_app(n)); + } + theory_var v = mk_var(n); + coeffs[vars.size()] = coeffs[index]; + vars.push_back(v); + ++index; + } } + for (unsigned i = st.terms_to_internalize().size(); i-- > 0; ) { + expr* n = st.terms_to_internalize()[i]; + if (is_app(n)) { + mk_enode(to_app(n)); + } + } + st.terms_to_internalize().reset(); + } - final_check_status final_check_eh() { - lbool is_sat = l_true; - if (m_delay_constraints) { - init_solver(); - for (unsigned i = 0; i < m_asserted_atoms.size(); ++i) { - bool_var bv = m_asserted_atoms[i].m_bv; - assert_bound(bv, m_asserted_atoms[i].m_is_true, *m_bool_var2bound.find(bv)); - } - for (unsigned i = 0; i < m_delayed_terms.size(); ++i) { - internalize_def(m_delayed_terms[i].get()); - } - for (unsigned i = 0; i < m_delayed_defs.size(); ++i) { - internalize_eq(m_delayed_defs[i]); - } - for (unsigned i = 0; i < m_delayed_equalities.size(); ++i) { - std::pair const& eq = m_delayed_equalities[i]; - internalize_eq(eq.first, eq.second); - } - is_sat = make_feasible(); + void internalize_args(app* t) { + for (unsigned i = 0; reflect(t) && i < t->get_num_args(); ++i) { + if (!ctx().e_internalized(t->get_arg(i))) { + ctx().internalize(t->get_arg(i), false); } - else if (m_solver->get_status() != lp::lp_status::OPTIMAL) { - is_sat = make_feasible(); + } + } + + void internalize_mul(app* t, theory_var& v, rational& r) { + SASSERT(a.is_mul(t)); + bool _has_var = has_var(t); + if (!_has_var) { + internalize_args(t); + mk_enode(t); + } + r = rational::one(); + rational r1; + v = mk_var(t); + svector vars; + ptr_vector todo; + todo.push_back(t); + while (!todo.empty()) { + expr* n = todo.back(); + todo.pop_back(); + if (a.is_mul(n)) { + for (expr* arg : *to_app(n)) { + todo.push_back(arg); + } } - switch (is_sat) { - case l_true: - if (delayed_assume_eqs()) { - return FC_CONTINUE; + else if (a.is_numeral(n, r1)) { + r *= r1; + } + else { + if (!ctx().e_internalized(n)) { + ctx().internalize(n, false); } - if (assume_eqs()) { - return FC_CONTINUE; - } - if (m_not_handled != nullptr) { - return FC_GIVEUP; - } - return FC_DONE; - case l_false: - set_conflict(); - return FC_CONTINUE; - case l_undef: - return m.canceled() ? FC_CONTINUE : FC_GIVEUP; + vars.push_back(get_var_index(mk_var(n))); + } + } + TRACE("arith", tout << mk_pp(t, m) << " " << _has_var << "\n";); + if (!_has_var) { + ensure_nra(); + m_nra->add_monomial(get_var_index(v), vars.size(), vars.c_ptr()); + } + } + + enode * mk_enode(app * n) { + if (ctx().e_internalized(n)) { + return get_enode(n); + } + else { + return ctx().mk_enode(n, !reflect(n), false, enable_cgc_for(n)); + } + } + + bool enable_cgc_for(app * n) const { + // Congruence closure is not enabled for (+ ...) and (* ...) applications. + return !(n->get_family_id() == get_id() && (n->get_decl_kind() == OP_ADD || n->get_decl_kind() == OP_MUL)); + } + + + void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params) { + TRACE("arith", literal lits[2]; lits[0] = l1; lits[1] = l2; ctx().display_literals_verbose(tout, 2, lits); tout << "\n";); + ctx().mk_th_axiom(get_id(), l1, l2, num_params, params); + } + + void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params) { + TRACE("arith", literal lits[3]; lits[0] = l1; lits[1] = l2; lits[2] = l3; ctx().display_literals_verbose(tout, 3, lits); tout << "\n";); + ctx().mk_th_axiom(get_id(), l1, l2, l3, num_params, params); + } + + bool is_underspecified(app* n) const { + if (n->get_family_id() == get_id()) { + switch (n->get_decl_kind()) { + case OP_DIV: + case OP_IDIV: + case OP_REM: + case OP_MOD: + return true; default: - UNREACHABLE(); break; } - return FC_GIVEUP; } + return false; + } + bool reflect(app* n) const { + return m_arith_params.m_arith_reflect || is_underspecified(n); + } - /** - \brief We must redefine this method, because theory of arithmetic contains - underspecified operators such as division by 0. - (/ a b) is essentially an uninterpreted function when b = 0. - Thus, 'a' must be considered a shared var if it is the child of an underspecified operator. + bool has_var(expr* n) { + if (!ctx().e_internalized(n)) { + return false; + } + enode* e = get_enode(n); + return th.is_attached_to_var(e); + } - if merge(a / b, x + y) and a / b is root, then x + y become shared and all z + u in equivalence class of x + y. - - - TBD: when the set of underspecified subterms is small, compute the shared variables below it. - Recompute the set if there are merges that invalidate it. - Use the set to determine if a variable is shared. - */ - bool is_shared(theory_var v) const { - if (m_underspecified.empty()) { - return false; + theory_var mk_var(expr* n, bool internalize = true) { + if (!ctx().e_internalized(n)) { + ctx().internalize(n, false); + } + enode* e = get_enode(n); + theory_var v; + if (!th.is_attached_to_var(e)) { + v = th.mk_var(e); + SASSERT(m_bounds.size() <= static_cast(v) || m_bounds[v].empty()); + if (m_bounds.size() <= static_cast(v)) { + m_bounds.push_back(lp_bounds()); + m_unassigned_bounds.push_back(0); } - enode * n = get_enode(v); - enode * r = n->get_root(); - unsigned usz = m_underspecified.size(); - TRACE("shared", tout << ctx().get_scope_level() << " " << v << " " << r->get_num_parents() << "\n";); - if (r->get_num_parents() > 2*usz) { - for (unsigned i = 0; i < usz; ++i) { - app* u = m_underspecified[i]; - unsigned sz = u->get_num_args(); - for (unsigned j = 0; j < sz; ++j) { - if (ctx().get_enode(u->get_arg(j))->get_root() == r) { - return true; - } - } - } + ctx().attach_th_var(e, &th, v); + } + else { + v = e->get_th_var(get_id()); + } + SASSERT(null_theory_var != v); + return v; + } + + lp::var_index get_var_index(theory_var v) { + lp::var_index result = UINT_MAX; + if (m_theory_var2var_index.size() > static_cast(v)) { + result = m_theory_var2var_index[v]; + } + if (result == UINT_MAX) { + result = m_solver->add_var(v, is_int(v)); + m_theory_var2var_index.setx(v, result, UINT_MAX); + m_var_index2theory_var.setx(result, v, UINT_MAX); + m_var_trail.push_back(v); + } + return result; + } + + void init_left_side(scoped_internalize_state& st) { + SASSERT(all_zeros(m_columns)); + svector const& vars = st.vars(); + vector const& coeffs = st.coeffs(); + for (unsigned i = 0; i < vars.size(); ++i) { + theory_var var = vars[i]; + rational const& coeff = coeffs[i]; + if (m_columns.size() <= static_cast(var)) { + m_columns.setx(var, coeff, rational::zero()); } else { - enode_vector::const_iterator it = r->begin_parents(); - enode_vector::const_iterator end = r->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; - if (is_underspecified(parent->get_owner())) { + m_columns[var] += coeff; + } + } + m_left_side.clear(); + // reset the coefficients after they have been used. + for (unsigned i = 0; i < vars.size(); ++i) { + theory_var var = vars[i]; + rational const& r = m_columns[var]; + if (!r.is_zero()) { + m_left_side.push_back(std::make_pair(r, get_var_index(var))); + m_columns[var].reset(); + } + } + SASSERT(all_zeros(m_columns)); + } + + bool all_zeros(vector const& v) const { + for (rational const& r : v) { + if (!r.is_zero()) { + return false; + } + } + return true; + } + + void add_eq_constraint(lp::constraint_index index, enode* n1, enode* n2) { + m_constraint_sources.setx(index, equality_source, null_source); + m_equalities.setx(index, enode_pair(n1, n2), enode_pair(0, 0)); + ++m_stats.m_add_rows; + } + + void add_ineq_constraint(lp::constraint_index index, literal lit) { + m_constraint_sources.setx(index, inequality_source, null_source); + m_inequalities.setx(index, lit, null_literal); + ++m_stats.m_add_rows; + TRACE("arith", m_solver->print_constraint(index, tout); tout << "\n";); + } + + void add_def_constraint(lp::constraint_index index) { + m_constraint_sources.setx(index, definition_source, null_source); + m_definitions.setx(index, null_theory_var, null_theory_var); + ++m_stats.m_add_rows; + } + + void add_def_constraint(lp::constraint_index index, theory_var v) { + m_constraint_sources.setx(index, definition_source, null_source); + m_definitions.setx(index, v, null_theory_var); + ++m_stats.m_add_rows; + } + + void internalize_eq(theory_var v1, theory_var v2) { + enode* n1 = get_enode(v1); + enode* n2 = get_enode(v2); + app_ref term(m.mk_fresh_const("eq", a.mk_real()), m); + scoped_internalize_state st(*this); + st.vars().push_back(v1); + st.vars().push_back(v2); + st.coeffs().push_back(rational::one()); + st.coeffs().push_back(rational::minus_one()); + theory_var z = internalize_linearized_def(term, st); + lp::var_index vi = get_var_index(z); + add_def_constraint(m_solver->add_var_bound(vi, lp::LE, rational::zero())); + add_def_constraint(m_solver->add_var_bound(vi, lp::GE, rational::zero())); + TRACE("arith", + { + expr* o1 = n1->get_owner(); + expr* o2 = n2->get_owner(); + tout << "v" << v1 << " = " << "v" << v2 << ": " + << mk_pp(o1, m) << " = " << mk_pp(o2, m) << "\n"; + }); + } + + void del_bounds(unsigned old_size) { + for (unsigned i = m_bounds_trail.size(); i > old_size; ) { + --i; + unsigned v = m_bounds_trail[i]; + lp_api::bound* b = m_bounds[v].back(); + // del_use_lists(b); + dealloc(b); + m_bounds[v].pop_back(); + } + m_bounds_trail.shrink(old_size); + } + + void updt_unassigned_bounds(theory_var v, int inc) { + TRACE("arith", tout << "v" << v << " " << m_unassigned_bounds[v] << " += " << inc << "\n";); + ctx().push_trail(vector_value_trail(m_unassigned_bounds, v)); + m_unassigned_bounds[v] += inc; + } + + bool is_unit_var(scoped_internalize_state& st) { + return st.coeff().is_zero() && st.vars().size() == 1 && st.coeffs()[0].is_one(); + } + + theory_var internalize_def(app* term, scoped_internalize_state& st) { + if (ctx().e_internalized(term)) { + IF_VERBOSE(0, verbose_stream() << "repeated term\n";); + return mk_var(term, false); + } + linearize_term(term, st); + if (is_unit_var(st)) { + return st.vars()[0]; + } + else { + theory_var v = mk_var(term); + SASSERT(null_theory_var != v); + st.coeffs().resize(st.vars().size() + 1); + st.coeffs()[st.vars().size()] = rational::minus_one(); + st.vars().push_back(v); + return v; + } + } + + // term - v = 0 + theory_var internalize_def(app* term) { + scoped_internalize_state st(*this); + linearize_term(term, st); + return internalize_linearized_def(term, st); + } + + theory_var internalize_linearized_def(app* term, scoped_internalize_state& st) { + if (is_unit_var(st)) { + return st.vars()[0]; + } + else { + init_left_side(st); + theory_var v = mk_var(term); + lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX); + TRACE("arith", tout << mk_pp(term, m) << " " << v << " " << vi << "\n";); + if (vi == UINT_MAX) { + vi = m_solver->add_term(m_left_side, st.coeff()); + m_theory_var2var_index.setx(v, vi, UINT_MAX); + if (m_solver->is_term(vi)) { + m_term_index2theory_var.setx(m_solver->adjust_term_index(vi), v, UINT_MAX); + } + else { + m_var_index2theory_var.setx(vi, v, UINT_MAX); + } + m_var_trail.push_back(v); + TRACE("arith_verbose", tout << "v" << v << " := " << mk_pp(term, m) << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; + m_solver->print_term(m_solver->get_term(vi), tout); tout << "\n";); + } + rational val; + if (a.is_numeral(term, val)) { + m_fixed_var_table.insert(value_sort_pair(val, is_int(v)), v); + } + return v; + } + } + + +public: + imp(theory_lra& th, ast_manager& m, theory_arith_params& ap): + th(th), m(m), + m_arith_params(ap), + a(m), + m_arith_eq_adapter(th, ap, a), + m_internalize_head(0), + m_not_handled(0), + m_asserted_qhead(0), + m_assume_eq_head(0), + m_num_conflicts(0), + m_use_nra_model(false), + m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), + m_solver(nullptr), + m_resource_limit(*this) { + } + + ~imp() { + del_bounds(0); + std::for_each(m_internalize_states.begin(), m_internalize_states.end(), delete_proc()); + } + + void init(context* ctx) { + init_solver(); + } + + void internalize_is_int(app * n) { + SASSERT(a.is_is_int(n)); + (void) mk_enode(n); + if (!ctx().relevancy()) + mk_is_int_axiom(n); + } + + bool internalize_atom(app * atom, bool gate_ctx) { + SASSERT(!ctx().b_internalized(atom)); + bool_var bv = ctx().mk_bool_var(atom); + ctx().set_var_theory(bv, get_id()); + expr* n1, *n2; + rational r; + lp_api::bound_kind k; + theory_var v = null_theory_var; + if (a.is_le(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { + v = internalize_def(to_app(n1)); + k = lp_api::upper_t; + } + else if (a.is_ge(atom, n1, n2) && is_numeral(n2, r) && is_app(n1)) { + v = internalize_def(to_app(n1)); + k = lp_api::lower_t; + } + else if (a.is_is_int(atom)) { + internalize_is_int(atom); + return true; + } + else { + TRACE("arith", tout << "Could not internalize " << mk_pp(atom, m) << "\n";); + found_not_handled(atom); + return true; + } + lp_api::bound* b = alloc(lp_api::bound, bv, v, is_int(v), r, k); + m_bounds[v].push_back(b); + updt_unassigned_bounds(v, +1); + m_bounds_trail.push_back(v); + m_bool_var2bound.insert(bv, b); + TRACE("arith_verbose", tout << "Internalized " << mk_pp(atom, m) << "\n";); + mk_bound_axioms(*b); + //add_use_lists(b); + return true; + } + + bool internalize_term(app * term) { + if (ctx().e_internalized(term) && th.is_attached_to_var(ctx().get_enode(term))) { + // skip + } + else { + internalize_def(term); + } + return true; + } + + void internalize_eq_eh(app * atom, bool_var) { + expr* lhs = 0, *rhs = 0; + VERIFY(m.is_eq(atom, lhs, rhs)); + enode * n1 = get_enode(lhs); + enode * n2 = get_enode(rhs); + if (n1->get_th_var(get_id()) != null_theory_var && + n2->get_th_var(get_id()) != null_theory_var && + n1 != n2) { + TRACE("arith_verbose", tout << mk_pp(atom, m) << "\n";); + m_arith_eq_adapter.mk_axioms(n1, n2); + } + } + + void assign_eh(bool_var v, bool is_true) { + TRACE("arith", tout << mk_pp(ctx().bool_var2expr(v), m) << " " << (is_true?"true":"false") << "\n";); + m_asserted_atoms.push_back(delayed_atom(v, is_true)); + } + + void new_eq_eh(theory_var v1, theory_var v2) { + // or internalize_eq(v1, v2); + m_arith_eq_adapter.new_eq_eh(v1, v2); + } + + bool use_diseqs() const { + return true; + } + + void new_diseq_eh(theory_var v1, theory_var v2) { + TRACE("arith", tout << "v" << v1 << " != " << "v" << v2 << "\n";); + ++m_stats.m_assert_diseq; + m_arith_eq_adapter.new_diseq_eh(v1, v2); + } + + void push_scope_eh() { + m_scopes.push_back(scope()); + scope& s = m_scopes.back(); + s.m_bounds_lim = m_bounds_trail.size(); + s.m_asserted_qhead = m_asserted_qhead; + s.m_asserted_atoms_lim = m_asserted_atoms.size(); + s.m_not_handled = m_not_handled; + s.m_underspecified_lim = m_underspecified.size(); + s.m_var_trail_lim = m_var_trail.size(); + m_solver->push(); + if (m_nra) m_nra->push(); + } + + void pop_scope_eh(unsigned num_scopes) { + if (num_scopes == 0) { + return; + } + unsigned old_size = m_scopes.size() - num_scopes; + del_bounds(m_scopes[old_size].m_bounds_lim); + for (unsigned i = m_scopes[old_size].m_var_trail_lim; i < m_var_trail.size(); ++i) { + lp::var_index vi = m_theory_var2var_index[m_var_trail[i]]; + if (m_solver->is_term(vi)) { + unsigned ti = m_solver->adjust_term_index(vi); + m_term_index2theory_var[ti] = UINT_MAX; + } + else if (vi < m_var_index2theory_var.size()) { + m_var_index2theory_var[vi] = UINT_MAX; + } + m_theory_var2var_index[m_var_trail[i]] = UINT_MAX; + } + m_asserted_atoms.shrink(m_scopes[old_size].m_asserted_atoms_lim); + m_asserted_qhead = m_scopes[old_size].m_asserted_qhead; + m_underspecified.shrink(m_scopes[old_size].m_underspecified_lim); + m_var_trail.shrink(m_scopes[old_size].m_var_trail_lim); + m_not_handled = m_scopes[old_size].m_not_handled; + m_scopes.resize(old_size); + m_solver->pop(num_scopes); + // VERIFY(l_false != make_feasible()); + m_new_bounds.reset(); + m_to_check.reset(); + if (m_nra) m_nra->pop(num_scopes); + TRACE("arith", tout << "num scopes: " << num_scopes << " new scope level: " << m_scopes.size() << "\n";); + } + + void restart_eh() { + m_arith_eq_adapter.restart_eh(); + } + + void relevant_eh(app* n) { + TRACE("arith", tout << mk_pp(n, m) << "\n";); + expr* n1, *n2; + if (a.is_mod(n, n1, n2)) + mk_idiv_mod_axioms(n1, n2); + else if (a.is_rem(n, n1, n2)) + mk_rem_axiom(n1, n2); + else if (a.is_div(n, n1, n2)) + mk_div_axiom(n1, n2); + else if (a.is_to_int(n)) + mk_to_int_axiom(n); + else if (a.is_is_int(n)) + mk_is_int_axiom(n); + } + + // n < 0 || rem(a, n) = mod(a, n) + // !n < 0 || rem(a, n) = -mod(a, n) + void mk_rem_axiom(expr* dividend, expr* divisor) { + expr_ref zero(a.mk_int(0), m); + expr_ref rem(a.mk_rem(dividend, divisor), m); + expr_ref mod(a.mk_mod(dividend, divisor), m); + expr_ref mmod(a.mk_uminus(mod), m); + literal dgez = mk_literal(a.mk_ge(divisor, zero)); + mk_axiom(~dgez, th.mk_eq(rem, mod, false)); + mk_axiom( dgez, th.mk_eq(rem, mmod, false)); + } + + // q = 0 or q * (p div q) = p + void mk_div_axiom(expr* p, expr* q) { + if (a.is_zero(q)) return; + literal eqz = th.mk_eq(q, a.mk_real(0), false); + literal eq = th.mk_eq(a.mk_mul(q, a.mk_div(p, q)), p, false); + mk_axiom(eqz, eq); + } + + // to_int (to_real x) = x + // to_real(to_int(x)) <= x < to_real(to_int(x)) + 1 + void mk_to_int_axiom(app* n) { + expr* x = 0, *y = 0; + VERIFY (a.is_to_int(n, x)); + if (a.is_to_real(x, y)) { + mk_axiom(th.mk_eq(y, n, false)); + } + else { + expr_ref to_r(a.mk_to_real(n), m); + expr_ref lo(a.mk_le(a.mk_sub(to_r, x), a.mk_real(0)), m); + expr_ref hi(a.mk_ge(a.mk_sub(x, to_r), a.mk_real(1)), m); + mk_axiom(mk_literal(lo)); + mk_axiom(~mk_literal(hi)); + } + } + + // is_int(x) <=> to_real(to_int(x)) = x + void mk_is_int_axiom(app* n) { + expr* x = 0; + VERIFY(a.is_is_int(n, x)); + literal eq = th.mk_eq(a.mk_to_real(a.mk_to_int(x)), x, false); + literal is_int = ctx().get_literal(n); + mk_axiom(~is_int, eq); + mk_axiom(is_int, ~eq); + } + + // create axiom for + // u = v + r*w, + /// abs(r) > r >= 0 + void assert_idiv_mod_axioms(theory_var u, theory_var v, theory_var w, rational const& r) { + app_ref term(m); + term = a.mk_sub(get_enode(u)->get_owner(), + a.mk_add(get_enode(v)->get_owner(), + a.mk_mul(a.mk_numeral(r, true), + get_enode(w)->get_owner()))); + theory_var z = internalize_def(term); + lp::var_index vi = get_var_index(z); + add_def_constraint(m_solver->add_var_bound(vi, lp::GE, rational::zero())); + add_def_constraint(m_solver->add_var_bound(vi, lp::LE, rational::zero())); + add_def_constraint(m_solver->add_var_bound(get_var_index(v), lp::GE, rational::zero())); + add_def_constraint(m_solver->add_var_bound(get_var_index(v), lp::LT, abs(r))); + } + + void mk_idiv_mod_axioms(expr * p, expr * q) { + if (a.is_zero(q)) { + return; + } + // if q is zero, then idiv and mod are uninterpreted functions. + expr_ref div(a.mk_idiv(p, q), m); + expr_ref mod(a.mk_mod(p, q), m); + expr_ref zero(a.mk_int(0), m); + literal q_ge_0 = mk_literal(a.mk_ge(q, zero)); + literal q_le_0 = mk_literal(a.mk_le(q, zero)); + // literal eqz = th.mk_eq(q, zero, false); + literal eq = th.mk_eq(a.mk_add(a.mk_mul(q, div), mod), p, false); + literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero)); + // q >= 0 or p = (p mod q) + q * (p div q) + // q <= 0 or p = (p mod q) + q * (p div q) + // q >= 0 or (p mod q) >= 0 + // q <= 0 or (p mod q) >= 0 + // q <= 0 or (p mod q) < q + // q >= 0 or (p mod q) < -q + // enable_trace("mk_bool_var"); + mk_axiom(q_ge_0, eq); + mk_axiom(q_le_0, eq); + mk_axiom(q_ge_0, mod_ge_0); + mk_axiom(q_le_0, mod_ge_0); + mk_axiom(q_le_0, ~mk_literal(a.mk_ge(a.mk_sub(mod, q), zero))); + mk_axiom(q_ge_0, ~mk_literal(a.mk_ge(a.mk_add(mod, q), zero))); + rational k; + if (m_arith_params.m_arith_enum_const_mod && a.is_numeral(q, k) && + k.is_pos() && k < rational(8)) { + unsigned _k = k.get_unsigned(); + literal_buffer lits; + for (unsigned j = 0; j < _k; ++j) { + literal mod_j = th.mk_eq(mod, a.mk_int(j), false); + lits.push_back(mod_j); + ctx().mark_as_relevant(mod_j); + } + ctx().mk_th_axiom(get_id(), lits.size(), lits.begin()); + } + } + + void mk_axiom(literal l) { + ctx().mk_th_axiom(get_id(), false_literal, l); + if (ctx().relevancy()) { + ctx().mark_as_relevant(l); + } + } + + void mk_axiom(literal l1, literal l2) { + if (l1 == false_literal) { + mk_axiom(l2); + return; + } + ctx().mk_th_axiom(get_id(), l1, l2); + if (ctx().relevancy()) { + ctx().mark_as_relevant(l1); + ctx().add_rel_watch(~l1, ctx().bool_var2expr(l2.var())); + } + } + + void mk_axiom(literal l1, literal l2, literal l3) { + ctx().mk_th_axiom(get_id(), l1, l2, l3); + if (ctx().relevancy()) { + ctx().mark_as_relevant(l1); + ctx().add_rel_watch(~l1, ctx().bool_var2expr(l2.var())); + ctx().add_rel_watch(~l2, ctx().bool_var2expr(l3.var())); + } + } + + literal mk_literal(expr* e) { + expr_ref pinned(e, m); + TRACE("mk_bool_var", tout << pinned << " " << pinned->get_id() << "\n";); + if (!ctx().e_internalized(e)) { + ctx().internalize(e, false); + } + return ctx().get_literal(e); + } + + + void init_search_eh() { + m_arith_eq_adapter.init_search_eh(); + m_num_conflicts = 0; + } + + bool can_get_value(theory_var v) const { + return + (v != null_theory_var) && + (v < static_cast(m_theory_var2var_index.size())) && + (UINT_MAX != m_theory_var2var_index[v]) && + !m_variable_values.empty(); + } + + bool can_get_bound(theory_var v) const { + return + (v != null_theory_var) && + (v < static_cast(m_theory_var2var_index.size())) && + (UINT_MAX != m_theory_var2var_index[v]); + } + + + bool can_get_ivalue(theory_var v) const { + if (v == null_theory_var || (v >= static_cast(m_theory_var2var_index.size()))) + return false; + return m_solver->var_is_registered(m_theory_var2var_index[v]); + } + + mutable vector> m_todo_terms; + + lp::impq get_ivalue(theory_var v) const { + SASSERT(can_get_ivalue(v)); + lp::var_index vi = m_theory_var2var_index[v]; + if (!m_solver->is_term(vi)) + return m_solver->get_column_value(vi); + m_todo_terms.push_back(std::make_pair(vi, rational::one())); + lp::impq result(0); + while (!m_todo_terms.empty()) { + vi = m_todo_terms.back().first; + rational coeff = m_todo_terms.back().second; + m_todo_terms.pop_back(); + if (m_solver->is_term(vi)) { + const lp::lar_term& term = m_solver->get_term(vi); + result += term.m_v * coeff; + for (const auto & i: term) { + m_todo_terms.push_back(std::make_pair(i.var(), coeff * i.coeff())); + } + } + else { + result += m_solver->get_column_value(vi) * coeff; + } + } + return result; + } + + rational get_value(theory_var v) const { + if (!can_get_value(v)) return rational::zero(); + lp::var_index vi = m_theory_var2var_index[v]; + if (m_variable_values.count(vi) > 0) + return m_variable_values[vi]; + + if(!m_solver->is_term(vi)) + return rational::zero(); + + m_todo_terms.push_back(std::make_pair(vi, rational::one())); + rational result(0); + while (!m_todo_terms.empty()) { + lp::var_index wi = m_todo_terms.back().first; + rational coeff = m_todo_terms.back().second; + m_todo_terms.pop_back(); + if (m_solver->is_term(wi)) { + const lp::lar_term& term = m_solver->get_term(wi); + result += term.m_v * coeff; + for (const auto & i : term.m_coeffs) { + if (m_variable_values.count(i.first) > 0) { + result += m_variable_values[i.first] * coeff * i.second; + } + else { + m_todo_terms.push_back(std::make_pair(i.first, coeff * i.second)); + } + } + } + else { + result += m_variable_values[wi] * coeff; + } + } + m_variable_values[vi] = result; + return result; + } + + void init_variable_values() { + if (!m.canceled() && m_solver.get() && th.get_num_vars() > 0) { + reset_variable_values(); + m_solver->get_model(m_variable_values); + } + } + + void reset_variable_values() { + m_variable_values.clear(); + } + + bool assume_eqs() { + svector vars; + theory_var sz = static_cast(th.get_num_vars()); + for (theory_var v = 0; v < sz; ++v) { + if (th.is_relevant_and_shared(get_enode(v))) { + vars.push_back(m_theory_var2var_index[v]); + } + } + if (vars.empty()) { + return false; + } + init_variable_values(); + TRACE("arith", + for (theory_var v = 0; v < sz; ++v) { + if (th.is_relevant_and_shared(get_enode(v))) { + tout << "v" << v << " "; + } + } + tout << "\n"; ); + if (!m_use_nra_model) { + m_solver->random_update(vars.size(), vars.c_ptr()); + } + m_model_eqs.reset(); + TRACE("arith", display(tout);); + + unsigned old_sz = m_assume_eq_candidates.size(); + bool result = false; + int start = ctx().get_random_value(); + for (theory_var i = 0; i < sz; ++i) { + theory_var v = (i + start) % sz; + enode* n1 = get_enode(v); + if (!th.is_relevant_and_shared(n1)) { + continue; + } + if (!can_get_value(v)) { + continue; + } + theory_var other = m_model_eqs.insert_if_not_there(v); + TRACE("arith", tout << "insert: v" << v << " := " << get_value(v) << " found: v" << other << "\n";); + if (other == v) { + continue; + } + enode* n2 = get_enode(other); + if (n1->get_root() != n2->get_root()) { + TRACE("arith", tout << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << "\n"; + tout << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << "\n"; + tout << "v" << v << " = " << "v" << other << "\n";); + m_assume_eq_candidates.push_back(std::make_pair(v, other)); + result = true; + } + } + + if (result) { + ctx().push_trail(restore_size_trail, false>(m_assume_eq_candidates, old_sz)); + } + + return delayed_assume_eqs(); + } + + bool delayed_assume_eqs() { + if (m_assume_eq_head == m_assume_eq_candidates.size()) + return false; + + ctx().push_trail(value_trail(m_assume_eq_head)); + while (m_assume_eq_head < m_assume_eq_candidates.size()) { + std::pair const & p = m_assume_eq_candidates[m_assume_eq_head]; + theory_var v1 = p.first; + theory_var v2 = p.second; + enode* n1 = get_enode(v1); + enode* n2 = get_enode(v2); + m_assume_eq_head++; + CTRACE("arith", + is_eq(v1, v2) && n1->get_root() != n2->get_root(), + tout << "assuming eq: v" << v1 << " = v" << v2 << "\n";); + if (is_eq(v1, v2) && n1->get_root() != n2->get_root() && th.assume_eq(n1, n2)) { + return true; + } + } + return false; + } + + bool is_eq(theory_var v1, theory_var v2) { + if (m_use_nra_model) { + return m_nra->am().eq(nl_value(v1, *m_a1), nl_value(v2, *m_a2)); + } + else { + return get_value(v1) == get_value(v2); + } + } + + bool has_delayed_constraints() const { + return !m_asserted_atoms.empty(); + } + + final_check_status final_check_eh() { + m_use_nra_model = false; + lbool is_sat = l_true; + if (m_solver->get_status() != lp::lp_status::OPTIMAL) { + is_sat = make_feasible(); + } + final_check_status st = FC_DONE; + switch (is_sat) { + case l_true: + + if (delayed_assume_eqs()) { + return FC_CONTINUE; + } + if (assume_eqs()) { + return FC_CONTINUE; + } + + switch (check_lia()) { + case l_true: + break; + case l_false: + return FC_CONTINUE; + case l_undef: + TRACE("arith", tout << "check-lia giveup\n";); + st = FC_CONTINUE; + break; + } + + switch (check_nra()) { + case l_true: + break; + case l_false: + return FC_CONTINUE; + case l_undef: + TRACE("arith", tout << "check-nra giveup\n";); + st = FC_GIVEUP; + break; + } + if (m_not_handled != 0) { + TRACE("arith", tout << "unhandled operator " << mk_pp(m_not_handled, m) << "\n";); + st = FC_GIVEUP; + } + + return st; + case l_false: + set_conflict(); + return FC_CONTINUE; + case l_undef: + TRACE("arith", tout << "check feasiable is undef\n";); + return m.canceled() ? FC_CONTINUE : FC_GIVEUP; + default: + UNREACHABLE(); + break; + } + TRACE("arith", tout << "default giveup\n";); + return FC_GIVEUP; + } + + // create a bound atom representing term >= k is lower_bound is true, and term <= k if it is false + app_ref mk_bound(lp::lar_term const& term, rational const& k, bool lower_bound) { + app_ref t = mk_term(term, k.is_int()); + app_ref atom(m); + if (lower_bound) { + atom = a.mk_ge(t, a.mk_numeral(k, k.is_int())); + } + else { + atom = a.mk_le(t, a.mk_numeral(k, k.is_int())); + } + expr_ref atom1(m); + proof_ref atomp(m); + ctx().get_rewriter()(atom, atom1, atomp); + atom = to_app(atom1); + TRACE("arith", tout << atom << "\n"; + m_solver->print_term(term, tout << "bound atom: "); tout << " <= " << k << "\n";); + ctx().internalize(atom, true); + ctx().mark_as_relevant(atom.get()); + return atom; + } + + lbool check_lia() { + if (m.canceled()) { + TRACE("arith", tout << "canceled\n";); + return l_undef; + } + lp::lar_term term; + lp::mpq k; + lp::explanation ex; // TBD, this should be streamlined accross different explanations + bool upper; + switch(m_lia->check(term, k, ex, upper)) { + case lp::lia_move::sat: + return l_true; + case lp::lia_move::branch: { + app_ref b = mk_bound(term, k, !upper); + // branch on term >= k + 1 + // branch on term <= k + // TBD: ctx().force_phase(ctx().get_literal(b)); + // at this point we have a new unassigned atom that the + // SAT core assigns a value to + return l_false; + } + case lp::lia_move::cut: { + ++m_stats.m_gomory_cuts; + // m_explanation implies term <= k + app_ref b = mk_bound(term, k, !upper); + m_eqs.reset(); + m_core.reset(); + m_params.reset(); + for (auto const& ev : ex.m_explanation) { + if (!ev.first.is_zero()) { + set_evidence(ev.second); + } + } + literal lit(ctx().get_bool_var(b), false); + TRACE("arith", + ctx().display_lemma_as_smt_problem(tout << "new cut:\n", m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); + display(tout);); + assign(lit); + return l_false; + } + case lp::lia_move::conflict: + // ex contains unsat core + m_explanation = ex.m_explanation; + set_conflict1(); + return l_false; + case lp::lia_move::undef: + TRACE("arith", tout << "lia undef\n";); + return l_undef; + case lp::lia_move::continue_with_check: + return l_undef; + default: + UNREACHABLE(); + } + UNREACHABLE(); + return l_undef; + } + + lbool check_nra() { + m_use_nra_model = false; + if (m.canceled()) { + TRACE("arith", tout << "canceled\n";); + return l_undef; + } + if (!m_nra) return l_true; + if (!m_nra->need_check()) return l_true; + m_a1 = 0; m_a2 = 0; + lbool r = m_nra->check(m_explanation); + m_a1 = alloc(scoped_anum, m_nra->am()); + m_a2 = alloc(scoped_anum, m_nra->am()); + switch (r) { + case l_false: + set_conflict1(); + break; + case l_true: + m_use_nra_model = true; + if (assume_eqs()) { + return l_false; + } + break; + case l_undef: + TRACE("arith", tout << "nra-undef\n";); + default: + break; + } + return r; + } + + /** + \brief We must redefine this method, because theory of arithmetic contains + underspecified operators such as division by 0. + (/ a b) is essentially an uninterpreted function when b = 0. + Thus, 'a' must be considered a shared var if it is the child of an underspecified operator. + + if merge(a / b, x + y) and a / b is root, then x + y become shared and all z + u in equivalence class of x + y. + + + TBD: when the set of underspecified subterms is small, compute the shared variables below it. + Recompute the set if there are merges that invalidate it. + Use the set to determine if a variable is shared. + */ + bool is_shared(theory_var v) const { + if (m_underspecified.empty()) { + return false; + } + enode * n = get_enode(v); + enode * r = n->get_root(); + unsigned usz = m_underspecified.size(); + TRACE("shared", tout << ctx().get_scope_level() << " " << v << " " << r->get_num_parents() << "\n";); + if (r->get_num_parents() > 2*usz) { + for (unsigned i = 0; i < usz; ++i) { + app* u = m_underspecified[i]; + unsigned sz = u->get_num_args(); + for (unsigned j = 0; j < sz; ++j) { + if (ctx().get_enode(u->get_arg(j))->get_root() == r) { return true; } } } - return false; } + else { + enode_vector::const_iterator it = r->begin_parents(); + enode_vector::const_iterator end = r->end_parents(); + for (; it != end; ++it) { + enode * parent = *it; + if (is_underspecified(parent->get_owner())) { + return true; + } + } + } + return false; + } - bool can_propagate() { + bool can_propagate() { #if 0 - if (ctx().at_base_level() && has_delayed_constraints()) { - // we could add the delayed constraints here directly to the tableau instead of using bounds variables. - } -#endif - return m_asserted_atoms.size() > m_asserted_qhead; + if (ctx().at_base_level() && has_delayed_constraints()) { + // we could add the delayed constraints here directly to the tableau instead of using bounds variables. } +#endif + return m_asserted_atoms.size() > m_asserted_qhead; + } - void propagate() { - flush_bound_axioms(); - if (!can_propagate()) { - return; - } - while (m_asserted_qhead < m_asserted_atoms.size() && !ctx().inconsistent()) { - bool_var bv = m_asserted_atoms[m_asserted_qhead].m_bv; - bool is_true = m_asserted_atoms[m_asserted_qhead].m_is_true; - + void propagate() { + flush_bound_axioms(); + if (!can_propagate()) { + return; + } + while (m_asserted_qhead < m_asserted_atoms.size() && !ctx().inconsistent()) { + bool_var bv = m_asserted_atoms[m_asserted_qhead].m_bv; + bool is_true = m_asserted_atoms[m_asserted_qhead].m_is_true; + #if 1 - m_to_check.push_back(bv); + m_to_check.push_back(bv); #else - propagate_bound(bv, is_true, b); + propagate_bound(bv, is_true, b); #endif - if (!m_delay_constraints) { - lra_lp::bound& b = *m_bool_var2bound.find(bv); - assert_bound(bv, is_true, b); - } - - ++m_asserted_qhead; - } - if (m_delay_constraints || ctx().inconsistent()) { - m_to_check.reset(); - return; - } - /*for (; qhead < m_asserted_atoms.size() && !ctx().inconsistent(); ++qhead) { - bool_var bv = m_asserted_atoms[qhead].m_bv; - bool is_true = m_asserted_atoms[qhead].m_is_true; - lra_lp::bound& b = *m_bool_var2bound.find(bv); - propagate_bound_compound(bv, is_true, b); - }*/ - - lbool lbl = make_feasible(); - - switch(lbl) { - case l_false: - TRACE("arith", tout << "propagation conflict\n";); - set_conflict(); - break; - case l_true: - propagate_basic_bounds(); - propagate_bounds_with_lp_solver(); - break; - case l_undef: - break; - } + lp_api::bound& b = *m_bool_var2bound.find(bv); + assert_bound(bv, is_true, b); + + ++m_asserted_qhead; } + if (ctx().inconsistent()) { + m_to_check.reset(); + return; + } + /*for (; qhead < m_asserted_atoms.size() && !ctx().inconsistent(); ++qhead) { + bool_var bv = m_asserted_atoms[qhead].m_bv; + bool is_true = m_asserted_atoms[qhead].m_is_true; + lp_api::bound& b = *m_bool_var2bound.find(bv); + propagate_bound_compound(bv, is_true, b); + }*/ - void propagate_bounds_with_lp_solver() { - if (BP_NONE == propagation_mode()) { - return; - } - int num_of_p = m_solver->settings().st().m_num_of_implied_bounds; - (void)num_of_p; - local_bound_propagator bp(*this); - m_solver->propagate_bounds_for_touched_rows(bp); - if (m.canceled()) { - return; - } - int new_num_of_p = m_solver->settings().st().m_num_of_implied_bounds; - (void)new_num_of_p; - CTRACE("arith", new_num_of_p > num_of_p, tout << "found " << new_num_of_p << " implied bounds\n";); - if (m_solver->get_status() == lp::lp_status::INFEASIBLE) { - set_conflict(); - } - else { - for (unsigned i = 0; !m.canceled() && !ctx().inconsistent() && i < bp.m_ibounds.size(); ++i) { - propagate_lp_solver_bound(bp.m_ibounds[i]); - } + lbool lbl = make_feasible(); + + switch(lbl) { + case l_false: + TRACE("arith", tout << "propagation conflict\n";); + set_conflict(); + break; + case l_true: + propagate_basic_bounds(); + propagate_bounds_with_lp_solver(); + break; + case l_undef: + break; + } + + } + + void propagate_bounds_with_lp_solver() { + if (BP_NONE == propagation_mode()) { + return; + } + int num_of_p = m_solver->settings().st().m_num_of_implied_bounds; + (void)num_of_p; + local_bound_propagator bp(*this); + m_solver->propagate_bounds_for_touched_rows(bp); + if (m.canceled()) { + return; + } + int new_num_of_p = m_solver->settings().st().m_num_of_implied_bounds; + (void)new_num_of_p; + CTRACE("arith", new_num_of_p > num_of_p, tout << "found " << new_num_of_p << " implied bounds\n";); + if (m_solver->get_status() == lp::lp_status::INFEASIBLE) { + set_conflict(); + } + else { + for (unsigned i = 0; !m.canceled() && !ctx().inconsistent() && i < bp.m_ibounds.size(); ++i) { + propagate_lp_solver_bound(bp.m_ibounds[i]); } } + } - bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational & bval) const { - theory_var v; - if (m_solver->is_term(vi)) { - v = m_term_index2theory_var.get(m_solver->adjust_term_index(vi), null_theory_var); - } - else { - v = m_var_index2theory_var.get(vi, null_theory_var); - } + bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational & bval) const { + theory_var v; + if (m_solver->is_term(vi)) { + v = m_term_index2theory_var.get(m_solver->adjust_term_index(vi), null_theory_var); + } + else { + v = m_var_index2theory_var.get(vi, null_theory_var); + } - if (v == null_theory_var) return false; + if (v == null_theory_var) return false; - if (m_unassigned_bounds[v] == 0 || m_bounds.size() <= static_cast(v)) { - TRACE("arith", tout << "return\n";); - return false; - } - lp_bounds const& bounds = m_bounds[v]; - for (unsigned i = 0; i < bounds.size(); ++i) { - lra_lp::bound* b = bounds[i]; - if (ctx().get_assignment(b->get_bv()) != l_undef) { - continue; - } - literal lit = is_bound_implied(kind, bval, *b); - if (lit == null_literal) { - continue; - } - return true; - } + if (m_unassigned_bounds[v] == 0 || m_bounds.size() <= static_cast(v)) { return false; } - - struct local_bound_propagator: public lp::lp_bound_propagator { - imp & m_imp; - local_bound_propagator(imp& i) : lp_bound_propagator(*i.m_solver), m_imp(i) {} - - bool bound_is_interesting(unsigned j, lp::lconstraint_kind kind, const rational & v) override { - return m_imp.bound_is_interesting(j, kind, v); + lp_bounds const& bounds = m_bounds[v]; + for (unsigned i = 0; i < bounds.size(); ++i) { + lp_api::bound* b = bounds[i]; + if (ctx().get_assignment(b->get_bv()) != l_undef) { + continue; } - - void consume(rational const& v, unsigned j) override { - m_imp.set_evidence(j); + literal lit = is_bound_implied(kind, bval, *b); + if (lit == null_literal) { + continue; } - }; + return true; + } + return false; + } + struct local_bound_propagator: public lp::bound_propagator { + imp & m_imp; + local_bound_propagator(imp& i) : bound_propagator(*i.m_solver), m_imp(i) {} - void propagate_lp_solver_bound(lp::implied_bound& be) { + bool bound_is_interesting(unsigned j, lp::lconstraint_kind kind, const rational & v) override { + return m_imp.bound_is_interesting(j, kind, v); + } - theory_var v; - lp::var_index vi = be.m_j; - if (m_solver->is_term(vi)) { - v = m_term_index2theory_var.get(m_solver->adjust_term_index(vi), null_theory_var); + void consume(rational const& v, lp::constraint_index j) override { + m_imp.set_evidence(j); + m_imp.m_explanation.push_back(std::make_pair(v, j)); + } + }; + + + void propagate_lp_solver_bound(lp::implied_bound& be) { + + theory_var v; + lp::var_index vi = be.m_j; + if (m_solver->is_term(vi)) { + v = m_term_index2theory_var.get(m_solver->adjust_term_index(vi), null_theory_var); + } + else { + v = m_var_index2theory_var.get(vi, null_theory_var); + } + + if (v == null_theory_var) return; + TRACE("arith", tout << "v" << v << " " << be.kind() << " " << be.m_bound << "\n"; + // if (m_unassigned_bounds[v] == 0) m_solver->print_bound_evidence(be, tout); + ); + + + if (m_unassigned_bounds[v] == 0 || m_bounds.size() <= static_cast(v)) { + TRACE("arith", tout << "return\n";); + return; + } + lp_bounds const& bounds = m_bounds[v]; + bool first = true; + for (unsigned i = 0; i < bounds.size(); ++i) { + lp_api::bound* b = bounds[i]; + if (ctx().get_assignment(b->get_bv()) != l_undef) { + continue; } - else { - v = m_var_index2theory_var.get(vi, null_theory_var); + literal lit = is_bound_implied(be.kind(), be.m_bound, *b); + if (lit == null_literal) { + continue; } + TRACE("arith", tout << lit << " bound: " << *b << " first: " << first << "\n";); - if (v == null_theory_var) return; - TRACE("arith", tout << "v" << v << " " << be.kind() << " " << be.m_bound << "\n"; - // if (m_unassigned_bounds[v] == 0) m_solver->print_bound_evidence(be, tout); + m_solver->settings().st().m_num_of_implied_bounds ++; + if (first) { + first = false; + m_core.reset(); + m_eqs.reset(); + m_params.reset(); + m_explanation.clear(); + local_bound_propagator bp(*this); + m_solver->explain_implied_bound(be, bp); + } + CTRACE("arith", m_unassigned_bounds[v] == 0, tout << "missed bound\n";); + updt_unassigned_bounds(v, -1); + TRACE("arith", + ctx().display_literals_verbose(tout, m_core); + tout << "\n --> "; + ctx().display_literal_verbose(tout, lit); + tout << "\n"; + display_evidence(tout, m_explanation); + m_solver->print_implied_bound(be, tout); ); + DEBUG_CODE( + for (auto& lit : m_core) { + SASSERT(ctx().get_assignment(lit) == l_true); + }); + ++m_stats.m_bound_propagations1; + assign(lit); + } + } + literal_vector m_core2; - if (m_unassigned_bounds[v] == 0 || m_bounds.size() <= static_cast(v)) { - TRACE("arith", tout << "return\n";); - return; + void assign(literal lit) { + // SASSERT(validate_assign(lit)); + if (m_core.size() < small_lemma_size() && m_eqs.empty()) { + m_core2.reset(); + for (auto const& c : m_core) { + m_core2.push_back(~c); } - lp_bounds const& bounds = m_bounds[v]; - bool first = true; - for (unsigned i = 0; i < bounds.size(); ++i) { - lra_lp::bound* b = bounds[i]; - if (ctx().get_assignment(b->get_bv()) != l_undef) { - continue; - } - literal lit = is_bound_implied(be.kind(), be.m_bound, *b); - if (lit == null_literal) { - continue; - } - - m_solver->settings().st().m_num_of_implied_bounds ++; - if (first) { - first = false; - m_core.reset(); - m_eqs.reset(); - m_params.reset(); - local_bound_propagator bp(*this); - m_solver->explain_implied_bound(be, bp); - } - CTRACE("arith", m_unassigned_bounds[v] == 0, tout << "missed bound\n";); - updt_unassigned_bounds(v, -1); - TRACE("arith", - ctx().display_literals_verbose(tout, m_core); - tout << "\n --> "; - ctx().display_literal_verbose(tout, lit); - tout << "\n"; - // display_evidence(tout, m_explanation); - m_solver->print_implied_bound(be, tout); - ); - DEBUG_CODE( - for (auto& lit : m_core) { - SASSERT(ctx().get_assignment(lit) == l_true); - }); - ++m_stats.m_bound_propagations1; - assign(lit); + m_core2.push_back(lit); + justification * js = 0; + if (proofs_enabled()) { + js = alloc(theory_lemma_justification, get_id(), ctx(), m_core2.size(), m_core2.c_ptr(), + m_params.size(), m_params.c_ptr()); } + ctx().mk_clause(m_core2.size(), m_core2.c_ptr(), js, CLS_AUX_LEMMA, 0); + } + else { + ctx().assign( + lit, ctx().mk_justification( + ext_theory_propagation_justification( + get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), + m_eqs.size(), m_eqs.c_ptr(), lit, m_params.size(), m_params.c_ptr()))); + } + } + + literal is_bound_implied(lp::lconstraint_kind k, rational const& value, lp_api::bound const& b) const { + if ((k == lp::LE || k == lp::LT) && b.get_bound_kind() == lp_api::upper_t && value <= b.get_value()) { + TRACE("arith", tout << "v <= value <= b.get_value() => v <= b.get_value() \n";); + return literal(b.get_bv(), false); + } + if ((k == lp::GE || k == lp::GT) && b.get_bound_kind() == lp_api::lower_t && b.get_value() <= value) { + TRACE("arith", tout << "b.get_value() <= value <= v => b.get_value() <= v \n";); + return literal(b.get_bv(), false); + } + if (k == lp::LE && b.get_bound_kind() == lp_api::lower_t && value < b.get_value()) { + TRACE("arith", tout << "v <= value < b.get_value() => v < b.get_value()\n";); + return literal(b.get_bv(), true); + } + if (k == lp::LT && b.get_bound_kind() == lp_api::lower_t && value <= b.get_value()) { + TRACE("arith", tout << "v < value <= b.get_value() => v < b.get_value()\n";); + return literal(b.get_bv(), true); + } + if (k == lp::GE && b.get_bound_kind() == lp_api::upper_t && b.get_value() < value) { + TRACE("arith", tout << "b.get_value() < value <= v => b.get_value() < v\n";); + return literal(b.get_bv(), true); + } + if (k == lp::GT && b.get_bound_kind() == lp_api::upper_t && b.get_value() <= value) { + TRACE("arith", tout << "b.get_value() <= value < v => b.get_value() < v\n";); + return literal(b.get_bv(), true); } - literal_vector m_core2; + return null_literal; + } - void assign(literal lit) { - SASSERT(validate_assign(lit)); - if (m_core.size() < small_lemma_size() && m_eqs.empty()) { - m_core2.reset(); - for (unsigned i = 0; i < m_core.size(); ++i) { - m_core2.push_back(~m_core[i]); - } - m_core2.push_back(lit); - justification * js = nullptr; - if (proofs_enabled()) { - js = alloc(theory_lemma_justification, get_id(), ctx(), m_core2.size(), m_core2.c_ptr(), - m_params.size(), m_params.c_ptr()); - } - ctx().mk_clause(m_core2.size(), m_core2.c_ptr(), js, CLS_AUX_LEMMA, nullptr); - } - else { - ctx().assign( - lit, ctx().mk_justification( - ext_theory_propagation_justification( - get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), - m_eqs.size(), m_eqs.c_ptr(), lit, m_params.size(), m_params.c_ptr()))); - } + void mk_bound_axioms(lp_api::bound& b) { + if (!ctx().is_searching()) { + // + // NB. We make an assumption that user push calls propagation + // before internal scopes are pushed. This flushes all newly + // asserted atoms into the right context. + // + m_new_bounds.push_back(&b); + return; } + theory_var v = b.get_var(); + lp_api::bound_kind kind1 = b.get_bound_kind(); + rational const& k1 = b.get_value(); + lp_bounds & bounds = m_bounds[v]; - literal is_bound_implied(lp::lconstraint_kind k, rational const& value, lra_lp::bound const& b) const { - if ((k == lp::LE || k == lp::LT) && b.get_bound_kind() == lra_lp::upper_t && value <= b.get_value()) { - // v <= value <= b.get_value() => v <= b.get_value() - return literal(b.get_bv(), false); - } - if ((k == lp::GE || k == lp::GT) && b.get_bound_kind() == lra_lp::lower_t && b.get_value() <= value) { - // b.get_value() <= value <= v => b.get_value() <= v - return literal(b.get_bv(), false); - } - if (k == lp::LE && b.get_bound_kind() == lra_lp::lower_t && value < b.get_value()) { - // v <= value < b.get_value() => v < b.get_value() - return literal(b.get_bv(), true); - } - if (k == lp::LT && b.get_bound_kind() == lra_lp::lower_t && value <= b.get_value()) { - // v < value <= b.get_value() => v < b.get_value() - return literal(b.get_bv(), true); - } - if (k == lp::GE && b.get_bound_kind() == lra_lp::upper_t && b.get_value() < value) { - // b.get_value() < value <= v => b.get_value() < v - return literal(b.get_bv(), true); - } - if (k == lp::GT && b.get_bound_kind() == lra_lp::upper_t && b.get_value() <= value) { - // b.get_value() <= value < v => b.get_value() < v - return literal(b.get_bv(), true); + lp_api::bound* end = 0; + lp_api::bound* lo_inf = end, *lo_sup = end; + lp_api::bound* hi_inf = end, *hi_sup = end; + + for (unsigned i = 0; i < bounds.size(); ++i) { + lp_api::bound& other = *bounds[i]; + if (&other == &b) continue; + if (b.get_bv() == other.get_bv()) continue; + lp_api::bound_kind kind2 = other.get_bound_kind(); + rational const& k2 = other.get_value(); + if (k1 == k2 && kind1 == kind2) { + // the bounds are equivalent. + continue; } - return null_literal; - } - - void mk_bound_axioms(lra_lp::bound& b) { - if (!ctx().is_searching()) { - // - // NB. We make an assumption that user push calls propagation - // before internal scopes are pushed. This flushes all newly - // asserted atoms into the right context. - // - m_new_bounds.push_back(&b); - return; - } - theory_var v = b.get_var(); - lra_lp::bound_kind kind1 = b.get_bound_kind(); - rational const& k1 = b.get_value(); - lp_bounds & bounds = m_bounds[v]; - - lra_lp::bound* end = nullptr; - lra_lp::bound* lo_inf = end, *lo_sup = end; - lra_lp::bound* hi_inf = end, *hi_sup = end; - - for (unsigned i = 0; i < bounds.size(); ++i) { - lra_lp::bound& other = *bounds[i]; - if (&other == &b) continue; - if (b.get_bv() == other.get_bv()) continue; - lra_lp::bound_kind kind2 = other.get_bound_kind(); - rational const& k2 = other.get_value(); - if (k1 == k2 && kind1 == kind2) { - // the bounds are equivalent. - continue; - } - - SASSERT(k1 != k2 || kind1 != kind2); - if (kind2 == lra_lp::lower_t) { - if (k2 < k1) { - if (lo_inf == end || k2 > lo_inf->get_value()) { - lo_inf = &other; - } - } - else if (lo_sup == end || k2 < lo_sup->get_value()) { - lo_sup = &other; - } - } - else if (k2 < k1) { - if (hi_inf == end || k2 > hi_inf->get_value()) { - hi_inf = &other; - } - } - else if (hi_sup == end || k2 < hi_sup->get_value()) { - hi_sup = &other; - } - } - if (lo_inf != end) mk_bound_axiom(b, *lo_inf); - if (lo_sup != end) mk_bound_axiom(b, *lo_sup); - if (hi_inf != end) mk_bound_axiom(b, *hi_inf); - if (hi_sup != end) mk_bound_axiom(b, *hi_sup); - } - - - void mk_bound_axiom(lra_lp::bound& b1, lra_lp::bound& b2) { - theory_var v = b1.get_var(); - literal l1(b1.get_bv()); - literal l2(b2.get_bv()); - rational const& k1 = b1.get_value(); - rational const& k2 = b2.get_value(); - lra_lp::bound_kind kind1 = b1.get_bound_kind(); - lra_lp::bound_kind kind2 = b2.get_bound_kind(); - bool v_is_int = is_int(v); - SASSERT(v == b2.get_var()); - if (k1 == k2 && kind1 == kind2) return; SASSERT(k1 != k2 || kind1 != kind2); - parameter coeffs[3] = { parameter(symbol("farkas")), - parameter(rational(1)), parameter(rational(1)) }; - - if (kind1 == lra_lp::lower_t) { - if (kind2 == lra_lp::lower_t) { - if (k2 <= k1) { - mk_clause(~l1, l2, 3, coeffs); - } - else { - mk_clause(l1, ~l2, 3, coeffs); + if (kind2 == lp_api::lower_t) { + if (k2 < k1) { + if (lo_inf == end || k2 > lo_inf->get_value()) { + lo_inf = &other; } } - else if (k1 <= k2) { - // k1 <= k2, k1 <= x or x <= k2 - mk_clause(l1, l2, 3, coeffs); - } - else { - // k1 > hi_inf, k1 <= x => ~(x <= hi_inf) - mk_clause(~l1, ~l2, 3, coeffs); - if (v_is_int && k1 == k2 + rational(1)) { - // k1 <= x or x <= k1-1 - mk_clause(l1, l2, 3, coeffs); - } + else if (lo_sup == end || k2 < lo_sup->get_value()) { + lo_sup = &other; } } - else if (kind2 == lra_lp::lower_t) { - if (k1 >= k2) { - // k1 >= lo_inf, k1 >= x or lo_inf <= x - mk_clause(l1, l2, 3, coeffs); - } - else { - // k1 < k2, k2 <= x => ~(x <= k1) - mk_clause(~l1, ~l2, 3, coeffs); - if (v_is_int && k1 == k2 - rational(1)) { - // x <= k1 or k1+l <= x - mk_clause(l1, l2, 3, coeffs); - } - + else if (k2 < k1) { + if (hi_inf == end || k2 > hi_inf->get_value()) { + hi_inf = &other; } } - else { - // kind1 == A_UPPER, kind2 == A_UPPER - if (k1 >= k2) { - // k1 >= k2, x <= k2 => x <= k1 - mk_clause(l1, ~l2, 3, coeffs); - } - else { - // k1 <= hi_sup , x <= k1 => x <= hi_sup + else if (hi_sup == end || k2 < hi_sup->get_value()) { + hi_sup = &other; + } + } + if (lo_inf != end) mk_bound_axiom(b, *lo_inf); + if (lo_sup != end) mk_bound_axiom(b, *lo_sup); + if (hi_inf != end) mk_bound_axiom(b, *hi_inf); + if (hi_sup != end) mk_bound_axiom(b, *hi_sup); + } + + + void mk_bound_axiom(lp_api::bound& b1, lp_api::bound& b2) { + theory_var v = b1.get_var(); + literal l1(b1.get_bv()); + literal l2(b2.get_bv()); + rational const& k1 = b1.get_value(); + rational const& k2 = b2.get_value(); + lp_api::bound_kind kind1 = b1.get_bound_kind(); + lp_api::bound_kind kind2 = b2.get_bound_kind(); + bool v_is_int = is_int(v); + SASSERT(v == b2.get_var()); + if (k1 == k2 && kind1 == kind2) return; + SASSERT(k1 != k2 || kind1 != kind2); + parameter coeffs[3] = { parameter(symbol("farkas")), + parameter(rational(1)), parameter(rational(1)) }; + + if (kind1 == lp_api::lower_t) { + if (kind2 == lp_api::lower_t) { + if (k2 <= k1) { mk_clause(~l1, l2, 3, coeffs); } - } - } - - typedef lp_bounds::iterator iterator; - - void flush_bound_axioms() { - CTRACE("arith", !m_new_bounds.empty(), tout << "flush bound axioms\n";); - - while (!m_new_bounds.empty()) { - lp_bounds atoms; - atoms.push_back(m_new_bounds.back()); - m_new_bounds.pop_back(); - theory_var v = atoms.back()->get_var(); - for (unsigned i = 0; i < m_new_bounds.size(); ++i) { - if (m_new_bounds[i]->get_var() == v) { - atoms.push_back(m_new_bounds[i]); - m_new_bounds[i] = m_new_bounds.back(); - m_new_bounds.pop_back(); - --i; - } - } - CTRACE("arith_verbose", !atoms.empty(), - for (unsigned i = 0; i < atoms.size(); ++i) { - atoms[i]->display(tout); tout << "\n"; - }); - lp_bounds occs(m_bounds[v]); - - std::sort(atoms.begin(), atoms.end(), compare_bounds()); - std::sort(occs.begin(), occs.end(), compare_bounds()); - - iterator begin1 = occs.begin(); - iterator begin2 = occs.begin(); - iterator end = occs.end(); - begin1 = first(lra_lp::lower_t, begin1, end); - begin2 = first(lra_lp::upper_t, begin2, end); - - iterator lo_inf = begin1, lo_sup = begin1; - iterator hi_inf = begin2, hi_sup = begin2; - iterator lo_inf1 = begin1, lo_sup1 = begin1; - iterator hi_inf1 = begin2, hi_sup1 = begin2; - bool flo_inf, fhi_inf, flo_sup, fhi_sup; - ptr_addr_hashtable visited; - for (unsigned i = 0; i < atoms.size(); ++i) { - lra_lp::bound* a1 = atoms[i]; - lo_inf1 = next_inf(a1, lra_lp::lower_t, lo_inf, end, flo_inf); - hi_inf1 = next_inf(a1, lra_lp::upper_t, hi_inf, end, fhi_inf); - lo_sup1 = next_sup(a1, lra_lp::lower_t, lo_sup, end, flo_sup); - hi_sup1 = next_sup(a1, lra_lp::upper_t, hi_sup, end, fhi_sup); - if (lo_inf1 != end) lo_inf = lo_inf1; - if (lo_sup1 != end) lo_sup = lo_sup1; - if (hi_inf1 != end) hi_inf = hi_inf1; - if (hi_sup1 != end) hi_sup = hi_sup1; - if (!flo_inf) lo_inf = end; - if (!fhi_inf) hi_inf = end; - if (!flo_sup) lo_sup = end; - if (!fhi_sup) hi_sup = end; - visited.insert(a1); - if (lo_inf1 != end && lo_inf != end && !visited.contains(*lo_inf)) mk_bound_axiom(*a1, **lo_inf); - if (lo_sup1 != end && lo_sup != end && !visited.contains(*lo_sup)) mk_bound_axiom(*a1, **lo_sup); - if (hi_inf1 != end && hi_inf != end && !visited.contains(*hi_inf)) mk_bound_axiom(*a1, **hi_inf); - if (hi_sup1 != end && hi_sup != end && !visited.contains(*hi_sup)) mk_bound_axiom(*a1, **hi_sup); - } - } - } - - struct compare_bounds { - bool operator()(lra_lp::bound* a1, lra_lp::bound* a2) const { return a1->get_value() < a2->get_value(); } - }; - - - lp_bounds::iterator first( - lra_lp::bound_kind kind, - iterator it, - iterator end) { - for (; it != end; ++it) { - lra_lp::bound* a = *it; - if (a->get_bound_kind() == kind) return it; - } - return end; - } - - lp_bounds::iterator next_inf( - lra_lp::bound* a1, - lra_lp::bound_kind kind, - iterator it, - iterator end, - bool& found_compatible) { - rational const & k1(a1->get_value()); - iterator result = end; - found_compatible = false; - for (; it != end; ++it) { - lra_lp::bound * a2 = *it; - if (a1 == a2) continue; - if (a2->get_bound_kind() != kind) continue; - rational const & k2(a2->get_value()); - found_compatible = true; - if (k2 <= k1) { - result = it; - } else { - break; + mk_clause(l1, ~l2, 3, coeffs); } } - return result; - } - - lp_bounds::iterator next_sup( - lra_lp::bound* a1, - lra_lp::bound_kind kind, - iterator it, - iterator end, - bool& found_compatible) { - rational const & k1(a1->get_value()); - found_compatible = false; - for (; it != end; ++it) { - lra_lp::bound * a2 = *it; - if (a1 == a2) continue; - if (a2->get_bound_kind() != kind) continue; - rational const & k2(a2->get_value()); - found_compatible = true; - if (k1 < k2) { - return it; - } - } - return end; - } - - void propagate_basic_bounds() { - for (auto const& bv : m_to_check) { - lra_lp::bound& b = *m_bool_var2bound.find(bv); - propagate_bound(bv, ctx().get_assignment(bv) == l_true, b); - if (ctx().inconsistent()) break; - - } - m_to_check.reset(); - } - - // for glb lo': lo' < lo: - // lo <= x -> lo' <= x - // lo <= x -> ~(x <= lo') - // for lub hi': hi' > hi - // x <= hi -> x <= hi' - // x <= hi -> ~(x >= hi') - - void propagate_bound(bool_var bv, bool is_true, lra_lp::bound& b) { - if (BP_NONE == propagation_mode()) { - return; - } - lra_lp::bound_kind k = b.get_bound_kind(); - theory_var v = b.get_var(); - inf_rational val = b.get_value(is_true); - lp_bounds const& bounds = m_bounds[v]; - SASSERT(!bounds.empty()); - if (bounds.size() == 1) return; - if (m_unassigned_bounds[v] == 0) return; - - literal lit1(bv, !is_true); - literal lit2 = null_literal; - bool find_glb = (is_true == (k == lra_lp::lower_t)); - if (find_glb) { - rational glb; - lra_lp::bound* lb = nullptr; - for (unsigned i = 0; i < bounds.size(); ++i) { - lra_lp::bound* b2 = bounds[i]; - if (b2 == &b) continue; - rational const& val2 = b2->get_value(); - if ((is_true ? val2 < val : val2 <= val) && (!lb || glb < val2)) { - lb = b2; - glb = val2; - } - } - if (!lb) return; - bool sign = lb->get_bound_kind() != lra_lp::lower_t; - lit2 = literal(lb->get_bv(), sign); + else if (k1 <= k2) { + // k1 <= k2, k1 <= x or x <= k2 + mk_clause(l1, l2, 3, coeffs); } else { - rational lub; - lra_lp::bound* ub = nullptr; - for (unsigned i = 0; i < bounds.size(); ++i) { - lra_lp::bound* b2 = bounds[i]; - if (b2 == &b) continue; - rational const& val2 = b2->get_value(); - if ((is_true ? val < val2 : val <= val2) && (!ub || val2 < lub)) { - ub = b2; - lub = val2; - } + // k1 > hi_inf, k1 <= x => ~(x <= hi_inf) + mk_clause(~l1, ~l2, 3, coeffs); + if (v_is_int && k1 == k2 + rational(1)) { + // k1 <= x or x <= k1-1 + mk_clause(l1, l2, 3, coeffs); } - if (!ub) return; - bool sign = ub->get_bound_kind() != lra_lp::upper_t; - lit2 = literal(ub->get_bv(), sign); } - TRACE("arith", - ctx().display_literal_verbose(tout, lit1); - ctx().display_literal_verbose(tout << " => ", lit2); - tout << "\n";); - updt_unassigned_bounds(v, -1); - ++m_stats.m_bound_propagations2; - m_params.reset(); - m_core.reset(); - m_eqs.reset(); - m_core.push_back(lit2); - m_params.push_back(parameter(symbol("farkas"))); - m_params.push_back(parameter(rational(1))); - m_params.push_back(parameter(rational(1))); - assign(lit2); - ++m_stats.m_bounds_propagations; } + else if (kind2 == lp_api::lower_t) { + if (k1 >= k2) { + // k1 >= lo_inf, k1 >= x or lo_inf <= x + mk_clause(l1, l2, 3, coeffs); + } + else { + // k1 < k2, k2 <= x => ~(x <= k1) + mk_clause(~l1, ~l2, 3, coeffs); + if (v_is_int && k1 == k2 - rational(1)) { + // x <= k1 or k1+l <= x + mk_clause(l1, l2, 3, coeffs); + } + + } + } + else { + // kind1 == A_UPPER, kind2 == A_UPPER + if (k1 >= k2) { + // k1 >= k2, x <= k2 => x <= k1 + mk_clause(l1, ~l2, 3, coeffs); + } + else { + // k1 <= hi_sup , x <= k1 => x <= hi_sup + mk_clause(~l1, l2, 3, coeffs); + } + } + } - void add_use_lists(lra_lp::bound* b) { - theory_var v = b->get_var(); - lp::var_index vi = get_var_index(v); - if (m_solver->is_term(vi)) { - lp::lar_term const& term = m_solver->get_term(vi); - for (auto i = term.m_coeffs.begin(); i != term.m_coeffs.end(); ++i) { - lp::var_index wi = i->first; + typedef lp_bounds::iterator iterator; + + void flush_bound_axioms() { + CTRACE("arith", !m_new_bounds.empty(), tout << "flush bound axioms\n";); + + while (!m_new_bounds.empty()) { + lp_bounds atoms; + atoms.push_back(m_new_bounds.back()); + m_new_bounds.pop_back(); + theory_var v = atoms.back()->get_var(); + for (unsigned i = 0; i < m_new_bounds.size(); ++i) { + if (m_new_bounds[i]->get_var() == v) { + atoms.push_back(m_new_bounds[i]); + m_new_bounds[i] = m_new_bounds.back(); + m_new_bounds.pop_back(); + --i; + } + } + CTRACE("arith_verbose", !atoms.empty(), + for (unsigned i = 0; i < atoms.size(); ++i) { + atoms[i]->display(tout); tout << "\n"; + }); + lp_bounds occs(m_bounds[v]); + + std::sort(atoms.begin(), atoms.end(), compare_bounds()); + std::sort(occs.begin(), occs.end(), compare_bounds()); + + iterator begin1 = occs.begin(); + iterator begin2 = occs.begin(); + iterator end = occs.end(); + begin1 = first(lp_api::lower_t, begin1, end); + begin2 = first(lp_api::upper_t, begin2, end); + + iterator lo_inf = begin1, lo_sup = begin1; + iterator hi_inf = begin2, hi_sup = begin2; + iterator lo_inf1 = begin1, lo_sup1 = begin1; + iterator hi_inf1 = begin2, hi_sup1 = begin2; + bool flo_inf, fhi_inf, flo_sup, fhi_sup; + ptr_addr_hashtable visited; + for (unsigned i = 0; i < atoms.size(); ++i) { + lp_api::bound* a1 = atoms[i]; + lo_inf1 = next_inf(a1, lp_api::lower_t, lo_inf, end, flo_inf); + hi_inf1 = next_inf(a1, lp_api::upper_t, hi_inf, end, fhi_inf); + lo_sup1 = next_sup(a1, lp_api::lower_t, lo_sup, end, flo_sup); + hi_sup1 = next_sup(a1, lp_api::upper_t, hi_sup, end, fhi_sup); + if (lo_inf1 != end) lo_inf = lo_inf1; + if (lo_sup1 != end) lo_sup = lo_sup1; + if (hi_inf1 != end) hi_inf = hi_inf1; + if (hi_sup1 != end) hi_sup = hi_sup1; + if (!flo_inf) lo_inf = end; + if (!fhi_inf) hi_inf = end; + if (!flo_sup) lo_sup = end; + if (!fhi_sup) hi_sup = end; + visited.insert(a1); + if (lo_inf1 != end && lo_inf != end && !visited.contains(*lo_inf)) mk_bound_axiom(*a1, **lo_inf); + if (lo_sup1 != end && lo_sup != end && !visited.contains(*lo_sup)) mk_bound_axiom(*a1, **lo_sup); + if (hi_inf1 != end && hi_inf != end && !visited.contains(*hi_inf)) mk_bound_axiom(*a1, **hi_inf); + if (hi_sup1 != end && hi_sup != end && !visited.contains(*hi_sup)) mk_bound_axiom(*a1, **hi_sup); + } + } + } + + struct compare_bounds { + bool operator()(lp_api::bound* a1, lp_api::bound* a2) const { return a1->get_value() < a2->get_value(); } + }; + + + lp_bounds::iterator first( + lp_api::bound_kind kind, + iterator it, + iterator end) { + for (; it != end; ++it) { + lp_api::bound* a = *it; + if (a->get_bound_kind() == kind) return it; + } + return end; + } + + lp_bounds::iterator next_inf( + lp_api::bound* a1, + lp_api::bound_kind kind, + iterator it, + iterator end, + bool& found_compatible) { + rational const & k1(a1->get_value()); + iterator result = end; + found_compatible = false; + for (; it != end; ++it) { + lp_api::bound * a2 = *it; + if (a1 == a2) continue; + if (a2->get_bound_kind() != kind) continue; + rational const & k2(a2->get_value()); + found_compatible = true; + if (k2 <= k1) { + result = it; + } + else { + break; + } + } + return result; + } + + lp_bounds::iterator next_sup( + lp_api::bound* a1, + lp_api::bound_kind kind, + iterator it, + iterator end, + bool& found_compatible) { + rational const & k1(a1->get_value()); + found_compatible = false; + for (; it != end; ++it) { + lp_api::bound * a2 = *it; + if (a1 == a2) continue; + if (a2->get_bound_kind() != kind) continue; + rational const & k2(a2->get_value()); + found_compatible = true; + if (k1 < k2) { + return it; + } + } + return end; + } + + void propagate_basic_bounds() { + for (auto const& bv : m_to_check) { + lp_api::bound& b = *m_bool_var2bound.find(bv); + propagate_bound(bv, ctx().get_assignment(bv) == l_true, b); + if (ctx().inconsistent()) break; + + } + m_to_check.reset(); + } + + // for glb lo': lo' < lo: + // lo <= x -> lo' <= x + // lo <= x -> ~(x <= lo') + // for lub hi': hi' > hi + // x <= hi -> x <= hi' + // x <= hi -> ~(x >= hi') + + void propagate_bound(bool_var bv, bool is_true, lp_api::bound& b) { + if (BP_NONE == propagation_mode()) { + return; + } + lp_api::bound_kind k = b.get_bound_kind(); + theory_var v = b.get_var(); + inf_rational val = b.get_value(is_true); + lp_bounds const& bounds = m_bounds[v]; + SASSERT(!bounds.empty()); + if (bounds.size() == 1) return; + if (m_unassigned_bounds[v] == 0) return; + + literal lit1(bv, !is_true); + literal lit2 = null_literal; + bool find_glb = (is_true == (k == lp_api::lower_t)); + if (find_glb) { + rational glb; + lp_api::bound* lb = 0; + for (unsigned i = 0; i < bounds.size(); ++i) { + lp_api::bound* b2 = bounds[i]; + if (b2 == &b) continue; + rational const& val2 = b2->get_value(); + if ((is_true ? val2 < val : val2 <= val) && (!lb || glb < val2)) { + lb = b2; + glb = val2; + } + } + if (!lb) return; + bool sign = lb->get_bound_kind() != lp_api::lower_t; + lit2 = literal(lb->get_bv(), sign); + } + else { + rational lub; + lp_api::bound* ub = 0; + for (unsigned i = 0; i < bounds.size(); ++i) { + lp_api::bound* b2 = bounds[i]; + if (b2 == &b) continue; + rational const& val2 = b2->get_value(); + if ((is_true ? val < val2 : val <= val2) && (!ub || val2 < lub)) { + ub = b2; + lub = val2; + } + } + if (!ub) return; + bool sign = ub->get_bound_kind() != lp_api::upper_t; + lit2 = literal(ub->get_bv(), sign); + } + TRACE("arith", + ctx().display_literal_verbose(tout, lit1); + ctx().display_literal_verbose(tout << " => ", lit2); + tout << "\n";); + updt_unassigned_bounds(v, -1); + ++m_stats.m_bound_propagations2; + m_params.reset(); + m_core.reset(); + m_eqs.reset(); + m_core.push_back(lit2); + m_params.push_back(parameter(symbol("farkas"))); + m_params.push_back(parameter(rational(1))); + m_params.push_back(parameter(rational(1))); + assign(lit2); + ++m_stats.m_bounds_propagations; + } + + svector m_todo_vars; + + void add_use_lists(lp_api::bound* b) { + theory_var v = b->get_var(); + lp::var_index vi = get_var_index(v); + if (!m_solver->is_term(vi)) { + return; + } + m_todo_vars.push_back(vi); + while (!m_todo_vars.empty()) { + vi = m_todo_vars.back(); + m_todo_vars.pop_back(); + lp::lar_term const& term = m_solver->get_term(vi); + for (auto const& p : term) { + lp::var_index wi = p.var(); + if (m_solver->is_term(wi)) { + m_todo_vars.push_back(wi); + } + else { unsigned w = m_var_index2theory_var[wi]; - m_use_list.reserve(w + 1, ptr_vector()); + m_use_list.reserve(w + 1, ptr_vector()); m_use_list[w].push_back(b); } } } + } - void del_use_lists(lra_lp::bound* b) { - theory_var v = b->get_var(); - lp::var_index vi = m_theory_var2var_index[v]; - if (m_solver->is_term(vi)) { - lp::lar_term const& term = m_solver->get_term(vi); - for (auto i = term.m_coeffs.begin(); i != term.m_coeffs.end(); ++i) { - lp::var_index wi = i->first; + void del_use_lists(lp_api::bound* b) { + theory_var v = b->get_var(); + lp::var_index vi = m_theory_var2var_index[v]; + if (!m_solver->is_term(vi)) { + return; + } + m_todo_vars.push_back(vi); + while (!m_todo_vars.empty()) { + vi = m_todo_vars.back(); + m_todo_vars.pop_back(); + lp::lar_term const& term = m_solver->get_term(vi); + for (auto const& coeff : term.m_coeffs) { + lp::var_index wi = coeff.first; + if (m_solver->is_term(wi)) { + m_todo_vars.push_back(wi); + } + else { unsigned w = m_var_index2theory_var[wi]; SASSERT(m_use_list[w].back() == b); m_use_list[w].pop_back(); } } } + } - // - // propagate bounds to compound terms - // The idea is that if bounds on all variables in an inequality ax + by + cz >= k - // have been assigned we may know the truth value of the inequality by using simple - // bounds propagation. - // - void propagate_bound_compound(bool_var bv, bool is_true, lra_lp::bound& b) { - theory_var v = b.get_var(); - TRACE("arith", tout << mk_pp(get_owner(v), m) << "\n";); - if (static_cast(v) >= m_use_list.size()) { - return; + // + // propagate bounds to compound terms + // The idea is that if bounds on all variables in an inequality ax + by + cz >= k + // have been assigned we may know the truth value of the inequality by using simple + // bounds propagation. + // + void propagate_bound_compound(bool_var bv, bool is_true, lp_api::bound& b) { + theory_var v = b.get_var(); + TRACE("arith", tout << mk_pp(get_owner(v), m) << "\n";); + if (static_cast(v) >= m_use_list.size()) { + return; + } + for (auto const& vb : m_use_list[v]) { + if (ctx().get_assignment(vb->get_bv()) != l_undef) { + TRACE("arith_verbose", display_bound(tout << "assigned ", *vb) << "\n";); + continue; } - for (auto const& vb : m_use_list[v]) { - if (ctx().get_assignment(vb->get_bv()) != l_undef) { - TRACE("arith_verbose", display_bound(tout << "assigned ", *vb) << "\n";); - continue; + inf_rational r; + // x + y + // x >= 0, y >= 1 -> x + y >= 1 + // x <= 0, y <= 2 -> x + y <= 2 + literal lit = null_literal; + if (lp_api::lower_t == vb->get_bound_kind()) { + if (get_glb(*vb, r) && r >= vb->get_value()) { // vb is assigned true + lit = literal(vb->get_bv(), false); } - inf_rational r; - // x + y - // x >= 0, y >= 1 -> x + y >= 1 - // x <= 0, y <= 2 -> x + y <= 2 - literal lit = null_literal; - if (lra_lp::lower_t == vb->get_bound_kind()) { - if (get_glb(*vb, r) && r >= vb->get_value()) { // vb is assigned true - lit = literal(vb->get_bv(), false); - } - else if (get_lub(*vb, r) && r < vb->get_value()) { // vb is assigned false - lit = literal(vb->get_bv(), true); - } - } - else { - if (get_glb(*vb, r) && r > vb->get_value()) { // VB <= value < val(VB) - lit = literal(vb->get_bv(), true); - } - else if (get_lub(*vb, r) && r <= vb->get_value()) { // val(VB) <= value - lit = literal(vb->get_bv(), false); - } - } - - if (lit != null_literal) { - TRACE("arith", - ctx().display_literals_verbose(tout, m_core); - tout << "\n --> "; - ctx().display_literal_verbose(tout, lit); - tout << "\n"; - ); - - - assign(lit); - } - else { - TRACE("arith_verbose", display_bound(tout << "skip ", *vb) << "\n";); + else if (get_lub(*vb, r) && r < vb->get_value()) { // vb is assigned false + lit = literal(vb->get_bv(), true); } } - } - - bool get_lub(lra_lp::bound const& b, inf_rational& lub) { - return get_bound(b, lub, true); - } - - bool get_glb(lra_lp::bound const& b, inf_rational& glb) { - return get_bound(b, glb, false); - } - - std::ostream& display_bound(std::ostream& out, lra_lp::bound const& b) { - return out << mk_pp(ctx().bool_var2expr(b.get_bv()), m); - } - - bool get_bound(lra_lp::bound const& b, inf_rational& r, bool is_lub) { - m_core.reset(); - m_eqs.reset(); - m_params.reset(); - r.reset(); - theory_var v = b.get_var(); - lp::var_index vi = m_theory_var2var_index[v]; - SASSERT(m_solver->is_term(vi)); - lp::lar_term const& term = m_solver->get_term(vi); - for (auto const& coeff : term.m_coeffs) { - lp::var_index wi = coeff.first; - lp::constraint_index ci; - rational value; - bool is_strict; - if (coeff.second.is_neg() == is_lub) { - // -3*x ... <= lub based on lower bound for x. - if (!m_solver->has_lower_bound(wi, ci, value, is_strict)) { - return false; - } - if (is_strict) { - r += inf_rational(rational::zero(), coeff.second.is_pos()); - } + else { + if (get_glb(*vb, r) && r > vb->get_value()) { // VB <= value < val(VB) + lit = literal(vb->get_bv(), true); } - else { - if (!m_solver->has_upper_bound(wi, ci, value, is_strict)) { - return false; - } - if (is_strict) { - r += inf_rational(rational::zero(), coeff.second.is_pos()); - } + else if (get_lub(*vb, r) && r <= vb->get_value()) { // val(VB) <= value + lit = literal(vb->get_bv(), false); } - r += value * coeff.second; - set_evidence(ci); - } - TRACE("arith_verbose", tout << (is_lub?"lub":"glb") << " is " << r << "\n";); - return true; - } + } + + if (lit != null_literal) { + TRACE("arith", + ctx().display_literals_verbose(tout, m_core); + tout << "\n --> "; + ctx().display_literal_verbose(tout, lit); + tout << "\n"; + ); + - void assert_bound(bool_var bv, bool is_true, lra_lp::bound& b) { - if (m_solver->get_status() == lp::lp_status::INFEASIBLE) { - return; - } - scoped_internalize_state st(*this); - st.vars().push_back(b.get_var()); - st.coeffs().push_back(rational::one()); - init_left_side(st); - lp::lconstraint_kind k = lp::EQ; - switch (b.get_bound_kind()) { - case lra_lp::lower_t: - k = is_true ? lp::GE : lp::LT; - break; - case lra_lp::upper_t: - k = is_true ? lp::LE : lp::GT; - break; - } - if (k == lp::LT || k == lp::LE) { - ++m_stats.m_assert_lower; + assign(lit); } else { - ++m_stats.m_assert_upper; - } - auto vi = get_var_index(b.get_var()); - auto ci = m_solver->add_var_bound(vi, k, b.get_value()); - TRACE("arith", tout << "v" << b.get_var() << "\n";); - add_ineq_constraint(ci, literal(bv, !is_true)); - - propagate_eqs(vi, ci, k, b); - } - - // - // fixed equalities. - // A fixed equality is inferred if there are two variables v1, v2 whose - // upper and lower bounds coincide. - // Then the equality v1 == v2 is propagated to the core. - // - - typedef std::pair constraint_bound; - vector m_lower_terms; - vector m_upper_terms; - typedef std::pair value_sort_pair; - typedef pair_hash, bool_hash> value_sort_pair_hash; - typedef map > value2var; - value2var m_fixed_var_table; - - void propagate_eqs(lp::var_index vi, lp::constraint_index ci, lp::lconstraint_kind k, lra_lp::bound& b) { - if (propagate_eqs()) { - rational const& value = b.get_value(); - if (k == lp::GE) { - set_lower_bound(vi, ci, value); - if (has_upper_bound(vi, ci, value)) { - fixed_var_eh(b.get_var(), value); - } - } - else if (k == lp::LE) { - set_upper_bound(vi, ci, value); - if (has_lower_bound(vi, ci, value)) { - fixed_var_eh(b.get_var(), value); - } - } + TRACE("arith_verbose", display_bound(tout << "skip ", *vb) << "\n";); } } + } - bool dump_lemmas() const { return m_arith_params.m_arith_dump_lemmas; } + bool get_lub(lp_api::bound const& b, inf_rational& lub) { + return get_bound(b, lub, true); + } - bool propagate_eqs() const { return m_arith_params.m_arith_propagate_eqs && m_num_conflicts < m_arith_params.m_arith_propagation_threshold; } + bool get_glb(lp_api::bound const& b, inf_rational& glb) { + return get_bound(b, glb, false); + } - bound_prop_mode propagation_mode() const { return m_num_conflicts < m_arith_params.m_arith_propagation_threshold ? m_arith_params.m_arith_bound_prop : BP_NONE; } + std::ostream& display_bound(std::ostream& out, lp_api::bound const& b) { + return out << mk_pp(ctx().bool_var2expr(b.get_bv()), m); + } - unsigned small_lemma_size() const { return m_arith_params.m_arith_small_lemma_size; } - - bool proofs_enabled() const { return m.proofs_enabled(); } - - bool use_tableau() const { return lp_params(ctx().get_params()).simplex_strategy() < 2; } - - void set_upper_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { set_bound(vi, ci, v, false); } - - void set_lower_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { set_bound(vi, ci, v, true); } - - void set_bound(lp::var_index vi, lp::constraint_index ci, rational const& v, bool is_lower) { - if (!m_solver->is_term(vi)) { - // m_solver already tracks bounds on proper variables, but not on terms. - return; + bool get_bound(lp_api::bound const& b, inf_rational& r, bool is_lub) { + m_core.reset(); + m_eqs.reset(); + m_params.reset(); + r.reset(); + theory_var v = b.get_var(); + lp::var_index vi = m_theory_var2var_index[v]; + SASSERT(m_solver->is_term(vi)); + lp::lar_term const& term = m_solver->get_term(vi); + for (auto const & coeff : term.m_coeffs) { + lp::var_index wi = coeff.first; + lp::constraint_index ci; + rational value; + bool is_strict; + if (m_solver->is_term(wi)) { + return false; } - lp::var_index ti = m_solver->adjust_term_index(vi); - auto& vec = is_lower ? m_lower_terms : m_upper_terms; - if (vec.size() <= ti) { - vec.resize(ti + 1, constraint_bound(UINT_MAX, rational())); - } - constraint_bound& b = vec[ti]; - if (b.first == UINT_MAX || (is_lower? b.second < v : b.second > v)) { - ctx().push_trail(vector_value_trail(vec, ti)); - b.first = ci; - b.second = v; - } - } - - bool has_upper_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, false); } - - bool has_lower_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } - - bool has_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound, bool is_lower) { - - if (m_solver->is_term(vi)) { - - lp::var_index ti = m_solver->adjust_term_index(vi); - theory_var v = m_term_index2theory_var.get(ti, null_theory_var); - rational val; - TRACE("arith", tout << vi << " " << v << "\n";); - if (v != null_theory_var && a.is_numeral(get_owner(v), val) && bound == val) { - ci = UINT_MAX; - return bound == val; - } - - auto& vec = is_lower ? m_lower_terms : m_upper_terms; - if (vec.size() > ti) { - constraint_bound& b = vec[ti]; - ci = b.first; - return ci != UINT_MAX && bound == b.second; - } - else { + if (coeff.second.is_neg() == is_lub) { + // -3*x ... <= lub based on lower bound for x. + if (!m_solver->has_lower_bound(wi, ci, value, is_strict)) { return false; - + } + if (is_strict) { + r += inf_rational(rational::zero(), coeff.second.is_pos()); } } else { - bool is_strict = false; - rational b; - if (is_lower) { - return m_solver->has_lower_bound(vi, ci, b, is_strict) && b == bound && !is_strict; + if (!m_solver->has_upper_bound(wi, ci, value, is_strict)) { + return false; } - else { - return m_solver->has_upper_bound(vi, ci, b, is_strict) && b == bound && !is_strict; + if (is_strict) { + r += inf_rational(rational::zero(), coeff.second.is_pos()); + } + } + r += value * coeff.second; + set_evidence(ci); + } + TRACE("arith_verbose", tout << (is_lub?"lub":"glb") << " is " << r << "\n";); + return true; + } + + void assert_bound(bool_var bv, bool is_true, lp_api::bound& b) { + if (m_solver->get_status() == lp::lp_status::INFEASIBLE) { + return; + } + scoped_internalize_state st(*this); + st.vars().push_back(b.get_var()); + st.coeffs().push_back(rational::one()); + init_left_side(st); + lp::lconstraint_kind k = lp::EQ; + bool is_int = b.is_int(); + switch (b.get_bound_kind()) { + case lp_api::lower_t: + k = is_true ? lp::GE : (is_int ? lp::LE : lp::LT); + break; + case lp_api::upper_t: + k = is_true ? lp::LE : (is_int ? lp::GE : lp::GT); + break; + } + if (k == lp::LT || k == lp::LE) { + ++m_stats.m_assert_lower; + } + else { + ++m_stats.m_assert_upper; + } + auto vi = get_var_index(b.get_var()); + rational bound = b.get_value(); + lp::constraint_index ci; + if (is_int && !is_true) { + rational bound = b.get_value(false).get_rational(); + ci = m_solver->add_var_bound(vi, k, bound); + } + else { + ci = m_solver->add_var_bound(vi, k, b.get_value()); + } + TRACE("arith", tout << "v" << b.get_var() << "\n";); + add_ineq_constraint(ci, literal(bv, !is_true)); + + propagate_eqs(vi, ci, k, b); + } + + // + // fixed equalities. + // A fixed equality is inferred if there are two variables v1, v2 whose + // upper and lower bounds coincide. + // Then the equality v1 == v2 is propagated to the core. + // + + typedef std::pair constraint_bound; + vector m_lower_terms; + vector m_upper_terms; + typedef std::pair value_sort_pair; + typedef pair_hash, bool_hash> value_sort_pair_hash; + typedef map > value2var; + value2var m_fixed_var_table; + + void propagate_eqs(lp::var_index vi, lp::constraint_index ci, lp::lconstraint_kind k, lp_api::bound& b) { + if (propagate_eqs()) { + rational const& value = b.get_value(); + if (k == lp::GE) { + set_lower_bound(vi, ci, value); + if (has_upper_bound(vi, ci, value)) { + fixed_var_eh(b.get_var(), value); + } + } + else if (k == lp::LE) { + set_upper_bound(vi, ci, value); + if (has_lower_bound(vi, ci, value)) { + fixed_var_eh(b.get_var(), value); } } } + } + bool dump_lemmas() const { return m_arith_params.m_arith_dump_lemmas; } - bool is_equal(theory_var x, theory_var y) const { return get_enode(x)->get_root() == get_enode(y)->get_root(); } + bool propagate_eqs() const { return m_arith_params.m_arith_propagate_eqs && m_num_conflicts < m_arith_params.m_arith_propagation_threshold; } + bound_prop_mode propagation_mode() const { return m_num_conflicts < m_arith_params.m_arith_propagation_threshold ? m_arith_params.m_arith_bound_prop : BP_NONE; } - void fixed_var_eh(theory_var v1, rational const& bound) { - theory_var v2; - value_sort_pair key(bound, is_int(v1)); - if (m_fixed_var_table.find(key, v2)) { - if (static_cast(v2) < th.get_num_vars() && !is_equal(v1, v2)) { - auto vi1 = get_var_index(v1); - auto vi2 = get_var_index(v2); - lp::constraint_index ci1, ci2, ci3, ci4; - TRACE("arith", tout << "fixed: " << mk_pp(get_owner(v1), m) << " " << mk_pp(get_owner(v2), m) << " " << bound << " " << has_lower_bound(vi2, ci3, bound) << "\n";); - if (has_lower_bound(vi2, ci3, bound) && has_upper_bound(vi2, ci4, bound)) { - VERIFY (has_lower_bound(vi1, ci1, bound)); - VERIFY (has_upper_bound(vi1, ci2, bound)); - ++m_stats.m_fixed_eqs; - m_core.reset(); - m_eqs.reset(); - set_evidence(ci1); - set_evidence(ci2); - set_evidence(ci3); - set_evidence(ci4); - enode* x = get_enode(v1); - enode* y = get_enode(v2); - justification* js = - ctx().mk_justification( - ext_theory_eq_propagation_justification( - get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), x, y, 0, nullptr)); + unsigned small_lemma_size() const { return m_arith_params.m_arith_small_lemma_size; } - TRACE("arith", - for (unsigned i = 0; i < m_core.size(); ++i) { - ctx().display_detailed_literal(tout, m_core[i]); - tout << "\n"; - } - for (unsigned i = 0; i < m_eqs.size(); ++i) { - tout << mk_pp(m_eqs[i].first->get_owner(), m) << " = " << mk_pp(m_eqs[i].second->get_owner(), m) << "\n"; - } - tout << " ==> "; - tout << mk_pp(x->get_owner(), m) << " = " << mk_pp(y->get_owner(), m) << "\n"; - ); + bool proofs_enabled() const { return m.proofs_enabled(); } - // parameters are TBD. - SASSERT(validate_eq(x, y)); - ctx().assign_eq(x, y, eq_justification(js)); - } - } - else { - // bounds on v2 were changed. - m_fixed_var_table.insert(key, v1); - } - } - else { - m_fixed_var_table.insert(key, v1); - } + bool use_tableau() const { return lp_params(ctx().get_params()).simplex_strategy() < 2; } + + void set_upper_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { set_bound(vi, ci, v, false); } + + void set_lower_bound(lp::var_index vi, lp::constraint_index ci, rational const& v) { set_bound(vi, ci, v, true); } + + void set_bound(lp::var_index vi, lp::constraint_index ci, rational const& v, bool is_lower) { + if (!m_solver->is_term(vi)) { + // m_solver already tracks bounds on proper variables, but not on terms. + return; } - - lbool make_feasible() { - reset_variable_values(); - ++m_stats.m_make_feasible; - if (m_solver->A_r().column_count() > m_stats.m_max_cols) - m_stats.m_max_cols = m_solver->A_r().column_count(); - if (m_solver->A_r().row_count() > m_stats.m_max_rows) - m_stats.m_max_rows = m_solver->A_r().row_count(); - TRACE("arith_verbose", display(tout);); - lp::lp_status status = m_solver->find_feasible_solution(); - m_stats.m_num_iterations = m_solver->settings().st().m_total_iterations; - m_stats.m_num_factorizations = m_solver->settings().st().m_num_factorizations; - m_stats.m_need_to_solve_inf = m_solver->settings().st().m_need_to_solve_inf; - - switch (status) { - case lp::lp_status::INFEASIBLE: - return l_false; - case lp::lp_status::FEASIBLE: - case lp::lp_status::OPTIMAL: - // SASSERT(m_solver->all_constraints_hold()); - return l_true; - case lp::lp_status::TIME_EXHAUSTED: - - default: - TRACE("arith", tout << "status treated as inconclusive: " << status << "\n";); - // TENTATIVE_UNBOUNDED, UNBOUNDED, TENTATIVE_DUAL_UNBOUNDED, DUAL_UNBOUNDED, - // FLOATING_POINT_ERROR, TIME_EXAUSTED, ITERATIONS_EXHAUSTED, EMPTY, UNSTABLE - return l_undef; - } + lp::var_index ti = m_solver->adjust_term_index(vi); + auto& vec = is_lower ? m_lower_terms : m_upper_terms; + if (vec.size() <= ti) { + vec.resize(ti + 1, constraint_bound(UINT_MAX, rational())); } - - vector> m_explanation; - literal_vector m_core; - svector m_eqs; - vector m_params; - - // lp::constraint_index const null_constraint_index = UINT_MAX; // not sure what a correct fix is - - void set_evidence(lp::constraint_index idx) { - if (idx == UINT_MAX) { - return; - } - switch (m_constraint_sources[idx]) { - case inequality_source: { - literal lit = m_inequalities[idx]; - SASSERT(lit != null_literal); - m_core.push_back(lit); - break; - } - case equality_source: { - SASSERT(m_equalities[idx].first != nullptr); - SASSERT(m_equalities[idx].second != nullptr); - m_eqs.push_back(m_equalities[idx]); - break; - } - case definition_source: { - // skip definitions (these are treated as hard constraints) - break; - } - default: - UNREACHABLE(); - break; - } + constraint_bound& b = vec[ti]; + if (b.first == UINT_MAX || (is_lower? b.second < v : b.second > v)) { + ctx().push_trail(vector_value_trail(vec, ti)); + b.first = ci; + b.second = v; } + } - void set_conflict() { - m_eqs.reset(); - m_core.reset(); - m_params.reset(); - m_explanation.clear(); - m_solver->get_infeasibility_explanation(m_explanation); - // m_solver->shrink_explanation_to_minimum(m_explanation); // todo, enable when perf is fixed - /* - static unsigned cn = 0; - static unsigned num_l = 0; - num_l+=m_explanation.size(); - std::cout << num_l / (++cn) << "\n"; - */ - ++m_num_conflicts; - ++m_stats.m_conflicts; - TRACE("arith", tout << "scope: " << ctx().get_scope_level() << "\n"; display_evidence(tout, m_explanation); ); - TRACE("arith", display(tout);); - for (auto const& ev : m_explanation) { - if (!ev.first.is_zero()) { - set_evidence(ev.second); - } + bool has_upper_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, false); } + + bool has_lower_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } + + bool has_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound, bool is_lower) { + + if (m_solver->is_term(vi)) { + + lp::var_index ti = m_solver->adjust_term_index(vi); + theory_var v = m_term_index2theory_var.get(ti, null_theory_var); + rational val; + TRACE("arith", tout << vi << " " << v << "\n";); + if (v != null_theory_var && a.is_numeral(get_owner(v), val) && bound == val) { + ci = UINT_MAX; + return bound == val; } - SASSERT(validate_conflict()); - ctx().set_conflict( - ctx().mk_justification( - ext_theory_conflict_justification( - get_id(), ctx().get_region(), - m_core.size(), m_core.c_ptr(), - m_eqs.size(), m_eqs.c_ptr(), m_params.size(), m_params.c_ptr()))); - } - justification * why_is_diseq(theory_var v1, theory_var v2) { - return nullptr; - } - - void reset_eh() { - m_arith_eq_adapter.reset_eh(); - m_solver = nullptr; - m_not_handled = nullptr; - del_bounds(0); - m_unassigned_bounds.reset(); - m_asserted_qhead = 0; - m_scopes.reset(); - m_stats.reset(); - m_to_check.reset(); - } - - void init_model(model_generator & mg) { - init_variable_values(); - m_factory = alloc(arith_factory, m); - mg.register_factory(m_factory); - TRACE("arith", display(tout);); - } - - model_value_proc * mk_value(enode * n, model_generator & mg) { - theory_var v = n->get_th_var(get_id()); - return alloc(expr_wrapper_proc, m_factory->mk_value(get_value(v), m.get_sort(n->get_owner()))); - } - - bool get_value(enode* n, expr_ref& r) { - theory_var v = n->get_th_var(get_id()); - if (can_get_value(v)) { - r = a.mk_numeral(get_value(v), is_int(n)); - return true; + auto& vec = is_lower ? m_lower_terms : m_upper_terms; + if (vec.size() > ti) { + constraint_bound& b = vec[ti]; + ci = b.first; + return ci != UINT_MAX && bound == b.second; } else { return false; + } } - - bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { - SASSERT(v1 != null_theory_var); - SASSERT(v2 != null_theory_var); - return (get_value(v1) == get_value(v2)) == is_true; - } - - // Auxiliary verification utilities. - - bool validate_conflict() { - if (dump_lemmas()) { - ctx().display_lemma_as_smt_problem(m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), false_literal); - } - context nctx(m, ctx().get_fparams(), ctx().get_params()); - add_background(nctx); - bool result = l_true != nctx.check(); - CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), false_literal); - display(tout);); - return result; - } - - bool validate_assign(literal lit) { - if (dump_lemmas()) { - ctx().display_lemma_as_smt_problem(m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); - } - context nctx(m, ctx().get_fparams(), ctx().get_params()); - m_core.push_back(~lit); - add_background(nctx); - m_core.pop_back(); - bool result = l_true != nctx.check(); - CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); - display(tout);); - return result; - } - - bool validate_eq(enode* x, enode* y) { - context nctx(m, ctx().get_fparams(), ctx().get_params()); - add_background(nctx); - nctx.assert_expr(m.mk_not(m.mk_eq(x->get_owner(), y->get_owner()))); - return l_true != nctx.check(); - } - - void add_background(context& nctx) { - for (unsigned i = 0; i < m_core.size(); ++i) { - expr_ref tmp(m); - ctx().literal2expr(m_core[i], tmp); - nctx.assert_expr(tmp); - } - for (unsigned i = 0; i < m_eqs.size(); ++i) { - nctx.assert_expr(m.mk_eq(m_eqs[i].first->get_owner(), m_eqs[i].second->get_owner())); - } - } - - theory_lra::inf_eps value(theory_var v) { - lp::impq ival = get_ivalue(v); - return inf_eps(0, inf_rational(ival.x, ival.y)); - } - - theory_lra::inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) { - lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX); - vector > coeffs; - rational coeff; - if (vi == UINT_MAX) { - has_shared = false; - blocker = m.mk_false(); - return inf_eps(rational::one(), inf_rational()); - } - else if (m_solver->is_term(vi)) { - const lp::lar_term& term = m_solver->get_term(vi); - for (auto & ti : term.m_coeffs) { - coeffs.push_back(std::make_pair(ti.second, ti.first)); - } - coeff = term.m_v; + else { + bool is_strict = false; + rational b; + if (is_lower) { + return m_solver->has_lower_bound(vi, ci, b, is_strict) && b == bound && !is_strict; } else { - coeffs.push_back(std::make_pair(rational::one(), vi)); - coeff = rational::zero(); + return m_solver->has_upper_bound(vi, ci, b, is_strict) && b == bound && !is_strict; } - lp::impq term_max; - if (m_solver->maximize_term(coeffs, term_max)) { - blocker = mk_gt(v); - inf_rational val(term_max.x + coeff, term_max.y); - return inf_eps(rational::zero(), val); + } + } + + + bool is_equal(theory_var x, theory_var y) const { return get_enode(x)->get_root() == get_enode(y)->get_root(); } + + + void fixed_var_eh(theory_var v1, rational const& bound) { + theory_var v2; + value_sort_pair key(bound, is_int(v1)); + if (m_fixed_var_table.find(key, v2)) { + if (static_cast(v2) < th.get_num_vars() && !is_equal(v1, v2)) { + auto vi1 = get_var_index(v1); + auto vi2 = get_var_index(v2); + lp::constraint_index ci1, ci2, ci3, ci4; + TRACE("arith", tout << "fixed: " << mk_pp(get_owner(v1), m) << " " << mk_pp(get_owner(v2), m) << " " << bound << " " << has_lower_bound(vi2, ci3, bound) << "\n";); + if (has_lower_bound(vi2, ci3, bound) && has_upper_bound(vi2, ci4, bound)) { + VERIFY (has_lower_bound(vi1, ci1, bound)); + VERIFY (has_upper_bound(vi1, ci2, bound)); + ++m_stats.m_fixed_eqs; + m_core.reset(); + m_eqs.reset(); + set_evidence(ci1); + set_evidence(ci2); + set_evidence(ci3); + set_evidence(ci4); + enode* x = get_enode(v1); + enode* y = get_enode(v2); + justification* js = + ctx().mk_justification( + ext_theory_eq_propagation_justification( + get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), x, y, 0, 0)); + + TRACE("arith", + for (unsigned i = 0; i < m_core.size(); ++i) { + ctx().display_detailed_literal(tout, m_core[i]); + tout << "\n"; + } + for (unsigned i = 0; i < m_eqs.size(); ++i) { + tout << mk_pp(m_eqs[i].first->get_owner(), m) << " = " << mk_pp(m_eqs[i].second->get_owner(), m) << "\n"; + } + tout << " ==> "; + tout << mk_pp(x->get_owner(), m) << " = " << mk_pp(y->get_owner(), m) << "\n"; + ); + + // parameters are TBD. + // SASSERT(validate_eq(x, y)); + ctx().assign_eq(x, y, eq_justification(js)); + } } else { - TRACE("arith", tout << "Unbounded " << v << "\n";); - has_shared = false; - blocker = m.mk_false(); - return inf_eps(rational::one(), inf_rational()); + // bounds on v2 were changed. + m_fixed_var_table.insert(key, v1); } } - - expr_ref mk_gt(theory_var v) { - lp::impq val = get_ivalue(v); - expr* obj = get_enode(v)->get_owner(); - rational r = val.x; - expr_ref e(m); - if (a.is_int(m.get_sort(obj))) { - if (r.is_int()) { - r += rational::one(); - } - else { - r = ceil(r); - } - e = a.mk_numeral(r, m.get_sort(obj)); - e = a.mk_ge(obj, e); - } - else { - e = a.mk_numeral(r, m.get_sort(obj)); - if (val.y.is_neg()) { - e = a.mk_ge(obj, e); - } - else { - e = a.mk_gt(obj, e); - } - } - TRACE("opt", tout << "v" << v << " " << val << " " << r << " " << e << "\n";); - return e; + else { + m_fixed_var_table.insert(key, v1); } + } - theory_var add_objective(app* term) { - return internalize_def(term); + lbool make_feasible() { + auto status = m_solver->find_feasible_solution(); + TRACE("arith_verbose", display(tout);); + switch (status) { + case lp::lp_status::INFEASIBLE: + return l_false; + case lp::lp_status::FEASIBLE: + case lp::lp_status::OPTIMAL: + // SASSERT(m_solver->all_constraints_hold()); + return l_true; + case lp::lp_status::TIME_EXHAUSTED: + + default: + TRACE("arith", tout << "status treated as inconclusive: " << status << "\n";); + // TENTATIVE_UNBOUNDED, UNBOUNDED, TENTATIVE_DUAL_UNBOUNDED, DUAL_UNBOUNDED, + // FLOATING_POINT_ERROR, TIME_EXAUSTED, ITERATIONS_EXHAUSTED, EMPTY, UNSTABLE + return l_undef; } + } + + vector> m_explanation; + literal_vector m_core; + svector m_eqs; + vector m_params; - app_ref mk_obj(theory_var v) { - lp::var_index vi = m_theory_var2var_index[v]; - bool is_int = a.is_int(get_enode(v)->get_owner()); - if (m_solver->is_term(vi)) { - expr_ref_vector args(m); - const lp::lar_term& term = m_solver->get_term(vi); - for (auto & ti : term.m_coeffs) { - theory_var w = m_var_index2theory_var[ti.first]; - expr* o = get_enode(w)->get_owner(); - args.push_back(a.mk_mul(a.mk_numeral(ti.second, is_int), o)); - } - args.push_back(a.mk_numeral(term.m_v, is_int)); - return app_ref(a.mk_add(args.size(), args.c_ptr()), m); - } - else { - theory_var w = m_var_index2theory_var[vi]; - return app_ref(get_enode(w)->get_owner(), m); + // lp::constraint_index const null_constraint_index = UINT_MAX; // not sure what a correct fix is + + void set_evidence(lp::constraint_index idx) { + if (idx == UINT_MAX) { + return; + } + switch (m_constraint_sources[idx]) { + case inequality_source: { + literal lit = m_inequalities[idx]; + SASSERT(lit != null_literal); + m_core.push_back(lit); + break; + } + case equality_source: { + SASSERT(m_equalities[idx].first != nullptr); + SASSERT(m_equalities[idx].second != nullptr); + m_eqs.push_back(m_equalities[idx]); + break; + } + case definition_source: { + // skip definitions (these are treated as hard constraints) + break; + } + default: + UNREACHABLE(); + break; + } + } + + void set_conflict() { + m_explanation.clear(); + m_solver->get_infeasibility_explanation(m_explanation); + set_conflict1(); + } + + void set_conflict1() { + m_eqs.reset(); + m_core.reset(); + m_params.reset(); + // m_solver->shrink_explanation_to_minimum(m_explanation); // todo, enable when perf is fixed + /* + static unsigned cn = 0; + static unsigned num_l = 0; + num_l+=m_explanation.size(); + std::cout << num_l / (++cn) << "\n"; + */ + ++m_num_conflicts; + ++m_stats.m_conflicts; + TRACE("arith", tout << "scope: " << ctx().get_scope_level() << "\n"; display_evidence(tout, m_explanation); ); + TRACE("arith", display(tout);); + for (auto const& ev : m_explanation) { + if (!ev.first.is_zero()) { + set_evidence(ev.second); } } + // SASSERT(validate_conflict()); + ctx().set_conflict( + ctx().mk_justification( + ext_theory_conflict_justification( + get_id(), ctx().get_region(), + m_core.size(), m_core.c_ptr(), + m_eqs.size(), m_eqs.c_ptr(), m_params.size(), m_params.c_ptr()))); + } - 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); - bool is_int = a.is_int(get_enode(v)->get_owner()); - if (is_strict) { - b = a.mk_le(mk_obj(v), a.mk_numeral(r, is_int)); - } - else { - b = a.mk_ge(mk_obj(v), a.mk_numeral(r, is_int)); - } - if (!ctx().b_internalized(b)) { - fm.hide(b); - bool_var bv = ctx().mk_bool_var(b); - ctx().set_var_theory(bv, get_id()); - // ctx().set_enode_flag(bv, true); - lra_lp::bound_kind bkind = lra_lp::bound_kind::lower_t; - if (is_strict) bkind = lra_lp::bound_kind::upper_t; - lra_lp::bound* a = alloc(lra_lp::bound, bv, v, r, bkind); - mk_bound_axioms(*a); - updt_unassigned_bounds(v, +1); - m_bounds[v].push_back(a); - m_bounds_trail.push_back(v); - m_bool_var2bound.insert(bv, a); - TRACE("arith", tout << mk_pp(b, m) << "\n";); - } - if (is_strict) { - b = m.mk_not(b); - } - TRACE("arith", tout << b << "\n";); - return expr_ref(b, m); + justification * why_is_diseq(theory_var v1, theory_var v2) { + return nullptr; + } + void reset_eh() { + m_use_nra_model = false; + m_arith_eq_adapter.reset_eh(); + m_solver = nullptr; + m_internalize_head = 0; + m_not_handled = nullptr; + del_bounds(0); + m_unassigned_bounds.reset(); + m_asserted_qhead = 0; + m_assume_eq_head = 0; + m_scopes.reset(); + m_stats.reset(); + m_to_check.reset(); + reset_variable_values(); + } + + void init_model(model_generator & mg) { + init_variable_values(); + m_factory = alloc(arith_factory, m); + mg.register_factory(m_factory); + TRACE("arith", display(tout);); + } + + nlsat::anum const& nl_value(theory_var v, scoped_anum& r) { + SASSERT(m_nra); + SASSERT(m_use_nra_model); + lp::var_index vi = m_theory_var2var_index[v]; + if (m_solver->is_term(vi)) { + + m_todo_terms.push_back(std::make_pair(vi, rational::one())); + + m_nra->am().set(r, 0); + while (!m_todo_terms.empty()) { + rational wcoeff = m_todo_terms.back().second; + // lp::var_index wi = m_todo_terms.back().first; // todo : got a warning "wi is not used" + m_todo_terms.pop_back(); + lp::lar_term const& term = m_solver->get_term(vi); + scoped_anum r1(m_nra->am()); + rational c1 = term.m_v * wcoeff; + m_nra->am().set(r1, c1.to_mpq()); + m_nra->am().add(r, r1, r); + for (auto const coeff : term.m_coeffs) { + lp::var_index wi = coeff.first; + c1 = coeff.second * wcoeff; + if (m_solver->is_term(wi)) { + m_todo_terms.push_back(std::make_pair(wi, c1)); + } + else { + m_nra->am().set(r1, c1.to_mpq()); + m_nra->am().mul(m_nra->value(wi), r1, r1); + m_nra->am().add(r1, r, r); + } + } + } + return r; } - - - void display(std::ostream & out) const { - if (m_solver) { - m_solver->print_constraints(out); - m_solver->print_terms(out); - } - unsigned nv = th.get_num_vars(); - for (unsigned v = 0; v < nv; ++v) { - out << "v" << v; - if (can_get_value(v)) out << ", value: " << get_value(v); - out << ", shared: " << ctx().is_shared(get_enode(v)) - << ", rel: " << ctx().is_relevant(get_enode(v)) - << ", def: "; th.display_var_flat_def(out, v) << "\n"; - } + else { + return m_nra->value(vi); } + } - void display_evidence(std::ostream& out, vector> const& evidence) { - for (auto const& ev : evidence) { - expr_ref e(m); - SASSERT(!ev.first.is_zero()); - if (ev.first.is_zero()) { - continue; - } - unsigned idx = ev.second; - switch (m_constraint_sources.get(idx, null_source)) { - case inequality_source: { - literal lit = m_inequalities[idx]; - ctx().literal2expr(lit, e); - out << e << " " << ctx().get_assignment(lit) << "\n"; - break; - } - case equality_source: - out << mk_pp(m_equalities[idx].first->get_owner(), m) << " = " - << mk_pp(m_equalities[idx].second->get_owner(), m) << "\n"; - break; - case definition_source: { - theory_var v = m_definitions[idx]; - out << "def: v" << v << " := " << mk_pp(th.get_enode(v)->get_owner(), m) << "\n"; - break; - } - case null_source: - default: - UNREACHABLE(); - break; - } - } - for (auto const& ev : evidence) { - m_solver->print_constraint(ev.second, out << ev.first << ": "); + model_value_proc * mk_value(enode * n, model_generator & mg) { + theory_var v = n->get_th_var(get_id()); + expr* o = n->get_owner(); + if (m_use_nra_model) { + anum const& an = nl_value(v, *m_a1); + if (a.is_int(o) && !m_nra->am().is_int(an)) { + return alloc(expr_wrapper_proc, a.mk_numeral(rational::zero(), a.is_int(o))); } + return alloc(expr_wrapper_proc, a.mk_numeral(nl_value(v, *m_a1), a.is_int(o))); } + else { + rational r = get_value(v); + if (a.is_int(o) && !r.is_int()) r = floor(r); + return alloc(expr_wrapper_proc, m_factory->mk_value(r, m.get_sort(o))); + } + } - void collect_statistics(::statistics & st) const { - m_arith_eq_adapter.collect_statistics(st); - st.update("arith-lower", m_stats.m_assert_lower); - st.update("arith-upper", m_stats.m_assert_upper); - st.update("arith-rows", m_stats.m_add_rows); - st.update("arith-propagations", m_stats.m_bounds_propagations); - st.update("arith-iterations", m_stats.m_num_iterations); - st.update("arith-factorizations", m_stats.m_num_factorizations); - st.update("arith-pivots", m_stats.m_need_to_solve_inf); - st.update("arith-plateau-iterations", m_stats.m_num_iterations_with_no_progress); - st.update("arith-fixed-eqs", m_stats.m_fixed_eqs); - st.update("arith-conflicts", m_stats.m_conflicts); - st.update("arith-bound-propagations-lp", m_stats.m_bound_propagations1); - st.update("arith-bound-propagations-cheap", m_stats.m_bound_propagations2); - st.update("arith-diseq", m_stats.m_assert_diseq); - st.update("arith-make-feasible", m_stats.m_make_feasible); - st.update("arith-max-columns", m_stats.m_max_cols); - st.update("arith-max-rows", m_stats.m_max_rows); + bool get_value(enode* n, expr_ref& r) { + theory_var v = n->get_th_var(get_id()); + if (!can_get_bound(v)) return false; + lp::var_index vi = m_theory_var2var_index[v]; + rational val; + if (m_solver->has_value(vi, val)) { + if (is_int(n) && !val.is_int()) return false; + r = a.mk_numeral(val, is_int(n)); + return true; + } + else { + return false; + } + } + + bool get_lower(enode* n, expr_ref& r) { + theory_var v = n->get_th_var(get_id()); + if (!can_get_bound(v)) { + TRACE("arith", tout << "cannot get lower for " << v << "\n";); + return false; + } + lp::var_index vi = m_theory_var2var_index[v]; + lp::constraint_index ci; + rational val; + bool is_strict; + if (m_solver->has_lower_bound(vi, ci, val, is_strict)) { + r = a.mk_numeral(val, is_int(n)); + return true; + } + TRACE("arith", m_solver->print_constraints(tout << "does not have lower bound " << vi << "\n");); + return false; + } + + bool get_upper(enode* n, expr_ref& r) { + theory_var v = n->get_th_var(get_id()); + if (!can_get_bound(v)) + return false; + lp::var_index vi = m_theory_var2var_index[v]; + lp::constraint_index ci; + rational val; + bool is_strict; + if (m_solver->has_upper_bound(vi, ci, val, is_strict)) { + r = a.mk_numeral(val, is_int(n)); + return true; + } + TRACE("arith", m_solver->print_constraints(tout << "does not have upper bound " << vi << "\n");); + return false; + } + + bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { + SASSERT(v1 != null_theory_var); + SASSERT(v2 != null_theory_var); + return (get_value(v1) == get_value(v2)) == is_true; + } + + // Auxiliary verification utilities. + + struct scoped_arith_mode { + smt_params& p; + scoped_arith_mode(smt_params& p) : p(p) { + p.m_arith_mode = AS_OLD_ARITH; + } + ~scoped_arith_mode() { + p.m_arith_mode = AS_NEW_ARITH; } }; - theory_lra::theory_lra(ast_manager& m, theory_arith_params& ap): - theory(m.get_family_id("arith")) { - m_imp = alloc(imp, *this, m, ap); + bool validate_conflict() { + if (dump_lemmas()) { + ctx().display_lemma_as_smt_problem(m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), false_literal); + } + if (m_arith_params.m_arith_mode != AS_NEW_ARITH) return true; + scoped_arith_mode _sa(ctx().get_fparams()); + context nctx(m, ctx().get_fparams(), ctx().get_params()); + add_background(nctx); + cancel_eh eh(m.limit()); + scoped_timer timer(1000, &eh); + bool result = l_true != nctx.check(); + CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), false_literal);); + return result; } - theory_lra::~theory_lra() { - dealloc(m_imp); + + bool validate_assign(literal lit) { + if (dump_lemmas()) { + ctx().display_lemma_as_smt_problem(m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); + } + if (m_arith_params.m_arith_mode != AS_NEW_ARITH) return true; + scoped_arith_mode _sa(ctx().get_fparams()); + context nctx(m, ctx().get_fparams(), ctx().get_params()); + m_core.push_back(~lit); + add_background(nctx); + m_core.pop_back(); + cancel_eh eh(m.limit()); + scoped_timer timer(1000, &eh); + bool result = l_true != nctx.check(); + CTRACE("arith", !result, ctx().display_lemma_as_smt_problem(tout, m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), lit); + display(tout);); + return result; } - theory* theory_lra::mk_fresh(context* new_ctx) { - return alloc(theory_lra, new_ctx->get_manager(), new_ctx->get_fparams()); + + bool validate_eq(enode* x, enode* y) { + if (m_arith_params.m_arith_mode == AS_NEW_ARITH) return true; + context nctx(m, ctx().get_fparams(), ctx().get_params()); + add_background(nctx); + nctx.assert_expr(m.mk_not(m.mk_eq(x->get_owner(), y->get_owner()))); + cancel_eh eh(m.limit()); + scoped_timer timer(1000, &eh); + return l_true != nctx.check(); } - void theory_lra::init(context * ctx) { - theory::init(ctx); - m_imp->init(ctx); + + void add_background(context& nctx) { + for (unsigned i = 0; i < m_core.size(); ++i) { + expr_ref tmp(m); + ctx().literal2expr(m_core[i], tmp); + nctx.assert_expr(tmp); + } + for (unsigned i = 0; i < m_eqs.size(); ++i) { + nctx.assert_expr(m.mk_eq(m_eqs[i].first->get_owner(), m_eqs[i].second->get_owner())); + } + } + + theory_lra::inf_eps value(theory_var v) { + lp::impq ival = get_ivalue(v); + return inf_eps(0, inf_rational(ival.x, ival.y)); } - bool theory_lra::internalize_atom(app * atom, bool gate_ctx) { - return m_imp->internalize_atom(atom, gate_ctx); + + theory_lra::inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) { + lp::impq term_max; + lp::lp_status st; + if (!can_get_bound(v)) { + st = lp::lp_status::UNBOUNDED; + } + else { + lp::var_index vi = m_theory_var2var_index[v]; + st = m_solver->maximize_term(vi, term_max); + } + switch (st) { + case lp::lp_status::OPTIMAL: { + inf_rational val(term_max.x, term_max.y); + blocker = mk_gt(v); + return inf_eps(rational::zero(), val); + } + case lp::lp_status::FEASIBLE: { + inf_rational val(term_max.x, term_max.y); + blocker = mk_gt(v); + return inf_eps(rational::zero(), val); + } + default: + SASSERT(st == lp::lp_status::UNBOUNDED); + TRACE("arith", tout << "Unbounded v" << v << "\n";); + has_shared = false; + blocker = m.mk_false(); + return inf_eps(rational::one(), inf_rational()); + } } - bool theory_lra::internalize_term(app * term) { - return m_imp->internalize_term(term); + + expr_ref mk_gt(theory_var v) { + lp::impq val = get_ivalue(v); + expr* obj = get_enode(v)->get_owner(); + rational r = val.x; + expr_ref e(m); + if (a.is_int(m.get_sort(obj))) { + if (r.is_int()) { + r += rational::one(); + } + else { + r = ceil(r); + } + e = a.mk_numeral(r, m.get_sort(obj)); + e = a.mk_ge(obj, e); + } + else { + e = a.mk_numeral(r, m.get_sort(obj)); + if (val.y.is_neg()) { + e = a.mk_ge(obj, e); + } + else { + e = a.mk_gt(obj, e); + } + } + TRACE("opt", tout << "v" << v << " " << val << " " << r << " " << e << "\n";); + return e; } - void theory_lra::internalize_eq_eh(app * atom, bool_var v) { - m_imp->internalize_eq_eh(atom, v); + + theory_var add_objective(app* term) { + return internalize_def(term); } - void theory_lra::assign_eh(bool_var v, bool is_true) { - m_imp->assign_eh(v, is_true); + + app_ref mk_term(lp::lar_term const& term, bool is_int) { + expr_ref_vector args(m); + for (const auto & ti : term) { + theory_var w; + if (m_solver->is_term(ti.var())) { + w = m_term_index2theory_var[m_solver->adjust_term_index(ti.var())]; + } + else { + w = m_var_index2theory_var[ti.var()]; + } + expr* o = get_enode(w)->get_owner(); + if (ti.coeff().is_one()) { + args.push_back(o); + } + else { + args.push_back(a.mk_mul(a.mk_numeral(ti.coeff(), is_int), o)); + } + } + if (!term.m_v.is_zero()) { + args.push_back(a.mk_numeral(term.m_v, is_int)); + } + switch (args.size()) { + case 0: + return app_ref(a.mk_numeral(rational::zero(), is_int), m); + case 1: + return app_ref(to_app(args[0].get()), m); + default: + return app_ref(a.mk_add(args.size(), args.c_ptr()), m); + } } - void theory_lra::new_eq_eh(theory_var v1, theory_var v2) { - m_imp->new_eq_eh(v1, v2); + + app_ref mk_obj(theory_var v) { + lp::var_index vi = m_theory_var2var_index[v]; + bool is_int = a.is_int(get_enode(v)->get_owner()); + if (m_solver->is_term(vi)) { + return mk_term(m_solver->get_term(vi), is_int); + } + else { + theory_var w = m_var_index2theory_var[vi]; + return app_ref(get_enode(w)->get_owner(), m); + } } - bool theory_lra::use_diseqs() const { - return m_imp->use_diseqs(); + + 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); + bool is_int = a.is_int(get_enode(v)->get_owner()); + if (is_strict) { + b = a.mk_le(mk_obj(v), a.mk_numeral(r, is_int)); + } + else { + b = a.mk_ge(mk_obj(v), a.mk_numeral(r, is_int)); + } + if (!ctx().b_internalized(b)) { + 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); + lp_api::bound_kind bkind = lp_api::bound_kind::lower_t; + if (is_strict) bkind = lp_api::bound_kind::upper_t; + lp_api::bound* a = alloc(lp_api::bound, bv, v, is_int, r, bkind); + mk_bound_axioms(*a); + updt_unassigned_bounds(v, +1); + m_bounds[v].push_back(a); + m_bounds_trail.push_back(v); + m_bool_var2bound.insert(bv, a); + TRACE("arith", tout << mk_pp(b, m) << "\n";); + } + if (is_strict) { + b = m.mk_not(b); + } + TRACE("arith", tout << b << "\n";); + return expr_ref(b, m); + } - void theory_lra::new_diseq_eh(theory_var v1, theory_var v2) { - m_imp->new_diseq_eh(v1, v2); + + + void display(std::ostream & out) const { + if (m_solver) { + m_solver->print_constraints(out); + m_solver->print_terms(out); + // auto pp = lp ::core_solver_pretty_printer(m_solver->m_mpq_lar_core_solver.m_r_solver, out); + // pp.print(); + } + unsigned nv = th.get_num_vars(); + for (unsigned v = 0; v < nv; ++v) { + if (!ctx().is_relevant(get_enode(v))) out << "irr: "; + out << "v" << v; + if (can_get_value(v)) out << " = " << get_value(v); + if (is_int(v)) out << ", int"; + if (ctx().is_shared(get_enode(v))) out << ", shared"; + out << " := "; th.display_var_flat_def(out, v) << "\n"; + } } - void theory_lra::push_scope_eh() { - theory::push_scope_eh(); - m_imp->push_scope_eh(); - } - void theory_lra::pop_scope_eh(unsigned num_scopes) { - m_imp->pop_scope_eh(num_scopes); - theory::pop_scope_eh(num_scopes); - } - void theory_lra::restart_eh() { - m_imp->restart_eh(); - } - void theory_lra::relevant_eh(app* e) { - m_imp->relevant_eh(e); - } - void theory_lra::init_search_eh() { - m_imp->init_search_eh(); - } - final_check_status theory_lra::final_check_eh() { - return m_imp->final_check_eh(); - } - bool theory_lra::is_shared(theory_var v) const { - return m_imp->is_shared(v); - } - bool theory_lra::can_propagate() { - return m_imp->can_propagate(); - } - void theory_lra::propagate() { - m_imp->propagate(); - } - justification * theory_lra::why_is_diseq(theory_var v1, theory_var v2) { - return m_imp->why_is_diseq(v1, v2); - } - void theory_lra::reset_eh() { - m_imp->reset_eh(); - } - void theory_lra::init_model(model_generator & m) { - m_imp->init_model(m); - } - model_value_proc * theory_lra::mk_value(enode * n, model_generator & mg) { - return m_imp->mk_value(n, mg); - } - bool theory_lra::get_value(enode* n, expr_ref& r) { - return m_imp->get_value(n, r); - } - bool theory_lra::validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { - return m_imp->validate_eq_in_model(v1, v2, is_true); - } - void theory_lra::display(std::ostream & out) const { - m_imp->display(out); - } - void theory_lra::collect_statistics(::statistics & st) const { - m_imp->collect_statistics(st); - } - theory_lra::inf_eps theory_lra::value(theory_var v) { - return m_imp->value(v); - } - theory_lra::inf_eps theory_lra::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { - return m_imp->maximize(v, blocker, has_shared); - } - theory_var theory_lra::add_objective(app* term) { - return m_imp->add_objective(term); - } - 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); + + void display_evidence(std::ostream& out, vector> const& evidence) { + for (auto const& ev : evidence) { + expr_ref e(m); + SASSERT(!ev.first.is_zero()); + if (ev.first.is_zero()) { + continue; + } + unsigned idx = ev.second; + switch (m_constraint_sources.get(idx, null_source)) { + case inequality_source: { + literal lit = m_inequalities[idx]; + ctx().literal2expr(lit, e); + out << e << " " << ctx().get_assignment(lit) << "\n"; + break; + } + case equality_source: + out << mk_pp(m_equalities[idx].first->get_owner(), m) << " = " + << mk_pp(m_equalities[idx].second->get_owner(), m) << "\n"; + break; + case definition_source: { + theory_var v = m_definitions[idx]; + if (v != null_theory_var) + out << "def: v" << v << " := " << mk_pp(th.get_enode(v)->get_owner(), m) << "\n"; + break; + } + case null_source: + default: + UNREACHABLE(); + break; + } + } + for (auto const& ev : evidence) { + m_solver->print_constraint(ev.second, out << ev.first << ": "); + } } + void collect_statistics(::statistics & st) const { + m_arith_eq_adapter.collect_statistics(st); + st.update("arith-lower", m_stats.m_assert_lower); + st.update("arith-upper", m_stats.m_assert_upper); + st.update("arith-rows", m_stats.m_add_rows); + st.update("arith-propagations", m_stats.m_bounds_propagations); + st.update("arith-iterations", m_stats.m_num_iterations); + st.update("arith-factorizations", m_solver->settings().st().m_num_factorizations); + st.update("arith-pivots", m_stats.m_need_to_solve_inf); + st.update("arith-plateau-iterations", m_stats.m_num_iterations_with_no_progress); + st.update("arith-fixed-eqs", m_stats.m_fixed_eqs); + st.update("arith-conflicts", m_stats.m_conflicts); + st.update("arith-bound-propagations-lp", m_stats.m_bound_propagations1); + st.update("arith-bound-propagations-cheap", m_stats.m_bound_propagations2); + st.update("arith-diseq", m_stats.m_assert_diseq); + st.update("arith-make-feasible", m_solver->settings().st().m_make_feasible); + st.update("arith-max-columns", m_solver->settings().st().m_max_cols); + st.update("arith-max-rows", m_solver->settings().st().m_max_rows); + st.update("gcd-calls", m_solver->settings().st().m_gcd_calls); + st.update("gcd-conflict", m_solver->settings().st().m_gcd_conflicts); + st.update("cube-calls", m_solver->settings().st().m_cube_calls); + st.update("cube-success", m_solver->settings().st().m_cube_success); + st.update("arith-patches", m_solver->settings().st().m_patches); + st.update("arith-patches-success", m_solver->settings().st().m_patches_success); + st.update("arith-hnf-calls", m_solver->settings().st().m_hnf_cutter_calls); + st.update("arith-hnf-cuts", m_solver->settings().st().m_hnf_cuts); + } +}; + +theory_lra::theory_lra(ast_manager& m, theory_arith_params& ap): + theory(m.get_family_id("arith")) { + m_imp = alloc(imp, *this, m, ap); +} +theory_lra::~theory_lra() { + dealloc(m_imp); +} +theory* theory_lra::mk_fresh(context* new_ctx) { + return alloc(theory_lra, new_ctx->get_manager(), new_ctx->get_fparams()); +} +void theory_lra::init(context * ctx) { + theory::init(ctx); + m_imp->init(ctx); +} +bool theory_lra::internalize_atom(app * atom, bool gate_ctx) { + return m_imp->internalize_atom(atom, gate_ctx); +} +bool theory_lra::internalize_term(app * term) { + return m_imp->internalize_term(term); +} +void theory_lra::internalize_eq_eh(app * atom, bool_var v) { + m_imp->internalize_eq_eh(atom, v); +} +void theory_lra::assign_eh(bool_var v, bool is_true) { + m_imp->assign_eh(v, is_true); +} +void theory_lra::new_eq_eh(theory_var v1, theory_var v2) { + m_imp->new_eq_eh(v1, v2); +} +bool theory_lra::use_diseqs() const { + return m_imp->use_diseqs(); +} +void theory_lra::new_diseq_eh(theory_var v1, theory_var v2) { + m_imp->new_diseq_eh(v1, v2); +} +void theory_lra::push_scope_eh() { + theory::push_scope_eh(); + m_imp->push_scope_eh(); +} +void theory_lra::pop_scope_eh(unsigned num_scopes) { + m_imp->pop_scope_eh(num_scopes); + theory::pop_scope_eh(num_scopes); +} +void theory_lra::restart_eh() { + m_imp->restart_eh(); +} +void theory_lra::relevant_eh(app* e) { + m_imp->relevant_eh(e); +} +void theory_lra::init_search_eh() { + m_imp->init_search_eh(); +} +final_check_status theory_lra::final_check_eh() { + return m_imp->final_check_eh(); +} +bool theory_lra::is_shared(theory_var v) const { + return m_imp->is_shared(v); +} +bool theory_lra::can_propagate() { + return m_imp->can_propagate(); +} +void theory_lra::propagate() { + m_imp->propagate(); +} +justification * theory_lra::why_is_diseq(theory_var v1, theory_var v2) { + return m_imp->why_is_diseq(v1, v2); +} +void theory_lra::reset_eh() { + m_imp->reset_eh(); +} +void theory_lra::init_model(model_generator & m) { + m_imp->init_model(m); +} +model_value_proc * theory_lra::mk_value(enode * n, model_generator & mg) { + return m_imp->mk_value(n, mg); +} +bool theory_lra::get_value(enode* n, expr_ref& r) { + return m_imp->get_value(n, r); +} +bool theory_lra::get_lower(enode* n, expr_ref& r) { + return m_imp->get_lower(n, r); +} +bool theory_lra::get_upper(enode* n, expr_ref& r) { + return m_imp->get_upper(n, r); +} + +bool theory_lra::validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { + return m_imp->validate_eq_in_model(v1, v2, is_true); +} +void theory_lra::display(std::ostream & out) const { + m_imp->display(out); +} +void theory_lra::collect_statistics(::statistics & st) const { + m_imp->collect_statistics(st); +} +theory_lra::inf_eps theory_lra::value(theory_var v) { + return m_imp->value(v); +} +theory_lra::inf_eps theory_lra::maximize(theory_var v, expr_ref& blocker, bool& has_shared) { + return m_imp->maximize(v, blocker, has_shared); +} +theory_var theory_lra::add_objective(app* term) { + return m_imp->add_objective(term); +} +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 4b1f67b79..074b11ba7 100644 --- a/src/smt/theory_lra.h +++ b/src/smt/theory_lra.h @@ -31,7 +31,7 @@ namespace smt { theory_lra(ast_manager& m, theory_arith_params& ap); ~theory_lra() override; theory* mk_fresh(context* new_ctx) override; - char const* get_name() const override { return "lra"; } + char const* get_name() const override { return "arithmetic"; } void init(context * ctx) override; @@ -78,6 +78,8 @@ namespace smt { model_value_proc * mk_value(enode * n, model_generator & mg) override; bool get_value(enode* n, expr_ref& r) override; + bool get_lower(enode* n, expr_ref& r); + bool get_upper(enode* n, expr_ref& r); bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const override; diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 953ecea2c..ed0481938 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -752,9 +752,10 @@ namespace smt { class theory_pb::card_justification : public justification { card& m_card; family_id m_fid; + literal m_lit; public: - card_justification(card& c, family_id fid) - : justification(true), m_card(c), m_fid(fid) {} + card_justification(card& c, literal lit, family_id fid) + : justification(true), m_card(c), m_fid(fid), m_lit(lit) {} card& get_card() { return m_card; } @@ -769,7 +770,28 @@ namespace smt { return m_fid; } - virtual proof* mk_proof(smt::conflict_resolution& cr) { return 0; } + virtual proof* mk_proof(smt::conflict_resolution& cr) { + ptr_buffer prs; + ast_manager& m = cr.get_context().get_manager(); + expr_ref fact(m); + cr.get_context().literal2expr(m_lit, fact); + bool all_valid = true; + proof* pr = nullptr; + pr = cr.get_proof(m_card.lit()); + all_valid &= pr != nullptr; + prs.push_back(pr); + for (unsigned i = m_card.k(); i < m_card.size(); ++i) { + pr = cr.get_proof(~m_card.lit(i)); + all_valid &= pr != nullptr; + prs.push_back(pr); + } + if (!all_valid) { + return nullptr; + } + else { + return m.mk_th_lemma(m_fid, fact, prs.size(), prs.c_ptr()); + } + } }; @@ -940,11 +962,11 @@ namespace smt { 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)) { + if (proofs_enabled()) { + js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); + } ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); } SASSERT(ctx.inconsistent()); @@ -959,7 +981,7 @@ namespace smt { 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()))); + ctx.assign(l, ctx.mk_justification(card_justification(c, l, get_id()))); } void theory_pb::clear_watch(card& c) { diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index d5fe54fae..48d4ddcc0 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -26,6 +26,7 @@ Revision History: #include "smt/smt_model_generator.h" #include "smt/theory_seq.h" #include "smt/theory_arith.h" +#include "smt/theory_lra.h" #include "smt/smt_kernel.h" using namespace smt; @@ -191,13 +192,17 @@ void theory_seq::exclusion_table::display(std::ostream& out) const { } -theory_seq::theory_seq(ast_manager& m): +theory_seq::theory_seq(ast_manager& m, theory_seq_params const & params): theory(m.mk_family_id("seq")), m(m), + m_params(params), m_rep(m, m_dm), m_reset_cache(false), m_eq_id(0), m_find(*this), + m_overlap(m), + m_overlap2(m), + m_len_prop_lvl(-1), m_factory(nullptr), m_exclude(m), m_axioms(m), @@ -230,6 +235,7 @@ theory_seq::theory_seq(ast_manager& m): m_pre = "seq.pre"; // (seq.pre s l): prefix of string s of length l m_post = "seq.post"; // (seq.post s l): suffix of string s of length l m_eq = "seq.eq"; + m_seq_align = "seq.align"; } @@ -264,6 +270,16 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>solve_nqs\n";); return FC_CONTINUE; } + if (fixed_length(true)) { + ++m_stats.m_fixed_length; + TRACE("seq", tout << ">>zero_length\n";); + return FC_CONTINUE; + } + if (m_params.m_split_w_len && len_based_split()) { + ++m_stats.m_branch_variable; + TRACE("seq", tout << ">>split_based_on_length\n";); + return FC_CONTINUE; + } if (fixed_length()) { ++m_stats.m_fixed_length; TRACE("seq", tout << ">>fixed_length\n";); @@ -284,12 +300,17 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>branch_unit_variable\n";); return FC_CONTINUE; } - if (branch_binary_variable() || branch_variable_mb()) { + if (branch_binary_variable()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>branch_variable\n";); + TRACE("seq", tout << ">>branch_binary_variable\n";); return FC_CONTINUE; } - if (branch_variable()) { + if (branch_ternary_variable1() || branch_ternary_variable2() || branch_quat_variable()) { + ++m_stats.m_branch_variable; + TRACE("seq", tout << ">>split_based_on_alignment\n";); + return FC_CONTINUE; + } + if (branch_variable_mb() || branch_variable()) { ++m_stats.m_branch_variable; TRACE("seq", tout << ">>branch_variable\n";); return FC_CONTINUE; @@ -332,7 +353,7 @@ bool theory_seq::reduce_length_eq() { } bool theory_seq::branch_binary_variable() { - for (eq const& e : m_eqs) { + for (auto const& e : m_eqs) { if (branch_binary_variable(e)) { TRACE("seq", display_equation(tout, e);); return true; @@ -395,9 +416,9 @@ bool theory_seq::branch_binary_variable(eq const& e) { expr_ref Y1(mk_skolem(symbol("seq.left"), x, y), m); expr_ref Y2(mk_skolem(symbol("seq.right"), x, y), m); ys.push_back(Y1); - expr_ref ysY1(mk_concat(ys)); - expr_ref xsE(mk_concat(xs)); - expr_ref Y1Y2(mk_concat(Y1, Y2)); + expr_ref ysY1 = mk_concat(ys); + expr_ref xsE = mk_concat(xs); + expr_ref Y1Y2 = mk_concat(Y1, Y2); dependency* dep = e.dep(); propagate_eq(dep, ~lit, x, ysY1); propagate_eq(dep, ~lit, y, Y1Y2); @@ -411,7 +432,7 @@ bool theory_seq::branch_binary_variable(eq const& e) { bool theory_seq::branch_unit_variable() { bool result = false; - for (eq const& e : m_eqs) { + for (auto const& e : m_eqs) { if (is_unit_eq(e.ls(), e.rs())) { branch_unit_variable(e.dep(), e.ls()[0], e.rs()); result = true; @@ -434,8 +455,8 @@ bool theory_seq::is_unit_eq(expr_ref_vector const& ls, expr_ref_vector const& rs if (ls.empty() || !is_var(ls[0])) { return false; } - for (expr* r : rs) { - if (!m_util.str.is_unit(r)) { + for (auto const& elem : rs) { + if (!m_util.str.is_unit(elem)) { return false; } } @@ -478,9 +499,869 @@ void theory_seq::branch_unit_variable(dependency* dep, expr* X, expr_ref_vector } } +bool theory_seq::branch_ternary_variable1() { + for (auto const& e : m_eqs) { + if (branch_ternary_variable(e) || branch_ternary_variable2(e)) { + return true; + } + } + return false; +} + +bool theory_seq::branch_ternary_variable2() { + for (auto const& e : m_eqs) { + if (branch_ternary_variable(e, true)) { + return true; + } + } + return false; +} + +bool theory_seq::eq_unit(expr* const& l, expr* const &r) const { + return l == r || is_unit_nth(l) || is_unit_nth(r); +} + +// exists x, y, rs' != empty s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = rs' ++ x && rs = y ++ rs') +// TBD: spec comment above doesn't seem to match what this function does. +unsigned_vector theory_seq::overlap(expr_ref_vector const& ls, expr_ref_vector const& rs) { + SASSERT(!ls.empty() && !rs.empty()); + unsigned_vector result; + expr_ref l = mk_concat(ls); + expr_ref r = mk_concat(rs); + expr_ref pair(m.mk_eq(l,r), m); + if (m_overlap.find(pair, result)) { + return result; + } + result.reset(); + for (unsigned i = 0; i < ls.size(); ++i) { + if (eq_unit(ls[i], rs.back())) { + bool same = rs.size() > i; + for (unsigned j = 0; same && j < i; ++j) { + same = eq_unit(ls[j], rs[rs.size() - 1 - i + j]); + } + if (same) + result.push_back(i+1); + } + } + m_overlap.insert(pair, result); + return result; +} + +// exists x, y, rs' != empty s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = x ++ rs' && rs = rs' ++ y) +unsigned_vector theory_seq::overlap2(expr_ref_vector const& ls, expr_ref_vector const& rs) { + SASSERT(!ls.empty() && !rs.empty()); + unsigned_vector res; + expr_ref l = mk_concat(ls); + expr_ref r = mk_concat(rs); + expr_ref pair(m.mk_eq(l,r), m); + if (m_overlap2.find(pair, res)) { + return res; + } + unsigned_vector result; + for (unsigned i = 0; i < ls.size(); ++i) { + if (eq_unit(ls[i],rs[0])) { + bool same = true; + unsigned j = i+1; + while (j < ls.size() && j-i < rs.size()) { + if (!eq_unit(ls[j], rs[j-i])) { + same = false; + break; + } + ++j; + } + if (same) + result.push_back(i); + } + } + m_overlap2.insert(pair, result); + return result; +} + +bool theory_seq::branch_ternary_variable_base( + dependency* dep, unsigned_vector indexes, + expr* const& x, expr_ref_vector const& xs, expr* const& y1, expr_ref_vector const& ys, expr* const& y2) { + context& ctx = get_context(); + bool change = false; + for (auto ind : indexes) { + TRACE("seq", tout << "ind = " << ind << "\n";); + expr_ref xs2E(m); + if (xs.size() > ind) { + xs2E = m_util.str.mk_concat(xs.size()-ind, xs.c_ptr()+ind); + } + else { + xs2E = m_util.str.mk_empty(m.get_sort(x)); + } + literal lit1 = mk_literal(m_autil.mk_le(m_util.str.mk_length(y2), m_autil.mk_int(xs.size()-ind))); + if (ctx.get_assignment(lit1) == l_undef) { + TRACE("seq", tout << "base case init\n";); + ctx.mark_as_relevant(lit1); + ctx.force_phase(lit1); + change = true; + continue; + } + else if (ctx.get_assignment(lit1) == l_true) { + TRACE("seq", tout << "base case: true branch\n";); + literal_vector lits; + lits.push_back(lit1); + propagate_eq(dep, lits, y2, xs2E, true); + if (ind > ys.size()) { + expr_ref xs1E(m_util.str.mk_concat(ind-ys.size(), xs.c_ptr()), m); + expr_ref xxs1E = mk_concat(x, xs1E); + propagate_eq(dep, lits, xxs1E, y1, true); + } + else if (ind == ys.size()) { + propagate_eq(dep, lits, x, y1, true); + } + else { + expr_ref ys1E(m_util.str.mk_concat(ys.size()-ind, ys.c_ptr()), m); + expr_ref y1ys1E = mk_concat(y1, ys1E); + propagate_eq(dep, lits, x, y1ys1E, true); + } + return true; + } + else { + TRACE("seq", tout << "base case: false branch\n";); + continue; + } + } + return change; +} + +// Equation is of the form x ++ xs = y1 ++ ys ++ y2 where xs, ys are units. +bool theory_seq::branch_ternary_variable(eq const& e, bool flag1) { + expr_ref_vector xs(m), ys(m); + expr* x = nullptr, *y1 = nullptr, *y2 = nullptr; + bool is_ternary = is_ternary_eq(e.ls(), e.rs(), x, xs, y1, ys, y2, flag1); + if (!is_ternary) { + is_ternary = is_ternary_eq(e.rs(), e.ls(), x, xs, y1, ys, y2, flag1); + } + if (!is_ternary) { + return false; + } + + rational lenX, lenY1, lenY2; + context& ctx = get_context(); + if (!get_length(x, lenX)) { + enforce_length(ensure_enode(x)); + } + if (!get_length(y1, lenY1)) { + enforce_length(ensure_enode(y1)); + } + if (!get_length(y2, lenY2)) { + enforce_length(ensure_enode(y2)); + } + + SASSERT(!xs.empty() && !ys.empty()); + unsigned_vector indexes = overlap(xs, ys); + if (branch_ternary_variable_base(e.dep(), indexes, x, xs, y1, ys, y2)) + return true; + + // x ++ xs = y1 ++ ys ++ y2 => x = y1 ++ ys ++ z, z ++ xs = y2 + expr_ref xsE = mk_concat(xs); + expr_ref ysE = mk_concat(ys); + expr_ref y1ys = mk_concat(y1, ysE); + expr_ref Z(mk_skolem(m_seq_align, y2, xsE, x, y1ys), m); + expr_ref ZxsE = mk_concat(Z, xsE); + expr_ref y1ysZ = mk_concat(y1ys, Z); + if (indexes.empty()) { + TRACE("seq", tout << "one case\n";); + TRACE("seq", display_equation(tout, e);); + literal_vector lits; + dependency* dep = e.dep(); + propagate_eq(dep, lits, x, y1ysZ, true); + propagate_eq(dep, lits, y2, ZxsE, true); + } + else { + expr_ref ge(m_autil.mk_ge(m_util.str.mk_length(y2), m_autil.mk_int(xs.size())), m); + literal lit2 = mk_literal(ge); + if (ctx.get_assignment(lit2) == l_undef) { + TRACE("seq", tout << "rec case init\n";); + ctx.mark_as_relevant(lit2); + ctx.force_phase(lit2); + } + else if (ctx.get_assignment(lit2) == l_true) { + TRACE("seq", tout << "true branch\n";); + TRACE("seq", display_equation(tout, e);); + literal_vector lits; + lits.push_back(lit2); + dependency* dep = e.dep(); + propagate_eq(dep, lits, x, y1ysZ, true); + propagate_eq(dep, lits, y2, ZxsE, true); + } + else { + return branch_ternary_variable_base(e.dep(), indexes, x, xs, y1, ys, y2); + } + } + return true; +} + +bool theory_seq::branch_ternary_variable_base2(dependency* dep, unsigned_vector indexes, + expr_ref_vector const& xs, expr* const& x, expr* const& y1, expr_ref_vector const& ys, expr* const& y2) { + context& ctx = get_context(); + bool change = false; + for (auto ind : indexes) { + expr_ref xs1E(m); + if (ind > 0) { + xs1E = m_util.str.mk_concat(ind, xs.c_ptr()); + } + else { + xs1E = m_util.str.mk_empty(m.get_sort(x)); + } + literal lit1 = mk_literal(m_autil.mk_le(m_util.str.mk_length(y1), m_autil.mk_int(ind))); + if (ctx.get_assignment(lit1) == l_undef) { + TRACE("seq", tout << "base case init\n";); + ctx.mark_as_relevant(lit1); + ctx.force_phase(lit1); + change = true; + continue; + } + else if (ctx.get_assignment(lit1) == l_true) { + TRACE("seq", tout << "base case: true branch\n";); + literal_vector lits; + lits.push_back(lit1); + propagate_eq(dep, lits, y1, xs1E, true); + if (xs.size() - ind > ys.size()) { + expr_ref xs2E(m_util.str.mk_concat(xs.size()-ind-ys.size(), xs.c_ptr()+ind+ys.size()), m); + expr_ref xs2x = mk_concat(xs2E, x); + propagate_eq(dep, lits, xs2x, y2, true); + } + else if (xs.size() - ind == ys.size()) { + propagate_eq(dep, lits, x, y2, true); + } + else { + expr_ref ys1E(m_util.str.mk_concat(ys.size()-xs.size()+ind, ys.c_ptr()+xs.size()-ind), m); + expr_ref ys1y2 = mk_concat(ys1E, y2); + propagate_eq(dep, lits, x, ys1y2, true); + } + return true; + } + else { + TRACE("seq", tout << "base case: false branch\n";); + continue; + } + } + return change; +} + +// Equation is of the form xs ++ x = y1 ++ ys ++ y2 where xs, ys are units. +bool theory_seq::branch_ternary_variable2(eq const& e, bool flag1) { + expr_ref_vector xs(m), ys(m); + expr* x = nullptr, *y1 = nullptr, *y2 = nullptr; + bool is_ternary = is_ternary_eq2(e.ls(), e.rs(), xs, x, y1, ys, y2, flag1); + if (!is_ternary) { + is_ternary = is_ternary_eq2(e.rs(), e.ls(), xs, x, y1, ys, y2, flag1); + } + if (!is_ternary) { + return false; + } + + rational lenX, lenY1, lenY2; + context& ctx = get_context(); + if (!get_length(x, lenX)) { + enforce_length(ensure_enode(x)); + } + if (!get_length(y1, lenY1)) { + enforce_length(ensure_enode(y1)); + } + if (!get_length(y2, lenY2)) { + enforce_length(ensure_enode(y2)); + } + SASSERT(!xs.empty() && !ys.empty()); + unsigned_vector indexes = overlap2(xs, ys); + if (branch_ternary_variable_base2(e.dep(), indexes, xs, x, y1, ys, y2)) + return true; + + // xs ++ x = y1 ++ ys ++ y2 => xs ++ z = y1, x = z ++ ys ++ y2 + expr_ref xsE = mk_concat(xs); + expr_ref ysE = mk_concat(ys); + expr_ref ysy2 = mk_concat(ysE, y2); + expr_ref Z(mk_skolem(m_seq_align, x, ysy2, y1, xsE), m); + expr_ref xsZ = mk_concat(xsE, Z); + expr_ref Zysy2 = mk_concat(Z, ysy2); + if (indexes.empty()) { + TRACE("seq", tout << "one case\n";); + TRACE("seq", display_equation(tout, e);); + literal_vector lits; + dependency* dep = e.dep(); + propagate_eq(dep, lits, x, Zysy2, true); + propagate_eq(dep, lits, y1, xsZ, true); + } + else { + expr_ref ge(m_autil.mk_ge(m_util.str.mk_length(y1), m_autil.mk_int(xs.size())), m); + literal lit2 = mk_literal(ge); + if (ctx.get_assignment(lit2) == l_undef) { + TRACE("seq", tout << "rec case init\n";); + ctx.mark_as_relevant(lit2); + ctx.force_phase(lit2); + } + else if (ctx.get_assignment(lit2) == l_true) { + TRACE("seq", tout << "true branch\n";); + TRACE("seq", display_equation(tout, e);); + literal_vector lits; + lits.push_back(lit2); + dependency* dep = e.dep(); + propagate_eq(dep, lits, x, Zysy2, true); + propagate_eq(dep, lits, y1, xsZ, true); + } + else { + return branch_ternary_variable_base2(e.dep(), indexes, xs, x, y1, ys, y2); + } + } + + return true; +} + +bool theory_seq::branch_quat_variable() { + for (auto const& e : m_eqs) { + if (branch_quat_variable(e)) { + return true; + } + } + return false; +} + +// Equation is of the form x1 ++ xs ++ x2 = y1 ++ ys ++ y2 where xs, ys are units. +bool theory_seq::branch_quat_variable(eq const& e) { + expr_ref_vector xs(m), ys(m); + expr* x1_l = nullptr, *x2 = nullptr, *y1_l = nullptr, *y2 = nullptr; + bool is_quat = is_quat_eq(e.ls(), e.rs(), x1_l, xs, x2, y1_l, ys, y2); + if (!is_quat) { + return false; + } + + rational lenX1, lenX2, lenY1, lenY2; + context& ctx = get_context(); + if (!get_length(x1_l, lenX1)) { + enforce_length(ensure_enode(x1_l)); + } + if (!get_length(y1_l, lenY1)) { + enforce_length(ensure_enode(y1_l)); + } + if (!get_length(x2, lenX2)) { + enforce_length(ensure_enode(x2)); + } + if (!get_length(y2, lenY2)) { + enforce_length(ensure_enode(y2)); + } + SASSERT(!xs.empty() && !ys.empty()); + + xs.push_back(x2); + expr_ref xsx2 = mk_concat(xs); + ys.push_back(y2); + expr_ref ysy2 = mk_concat(ys); + expr_ref x1(x1_l, m); + expr_ref y1(y1_l, m); + expr_ref sub(mk_sub(m_util.str.mk_length(x1_l), m_util.str.mk_length(y1_l)), m); + expr_ref le(m_autil.mk_le(sub, m_autil.mk_int(0)), m); + literal lit2 = mk_literal(le); + if (ctx.get_assignment(lit2) == l_undef) { + TRACE("seq", tout << "init branch\n";); + TRACE("seq", display_equation(tout, e);); + ctx.mark_as_relevant(lit2); + ctx.force_phase(lit2); + } + else if (ctx.get_assignment(lit2) == l_false) { + // |x1| > |y1| => x1 = y1 ++ z1, z1 ++ xs ++ x2 = ys ++ y2 + TRACE("seq", tout << "false branch\n";); + TRACE("seq", display_equation(tout, e);); + expr_ref Z1(mk_skolem(m_seq_align, ysy2, xsx2, x1, y1), m); + expr_ref y1Z1 = mk_concat(y1, Z1); + expr_ref Z1xsx2 = mk_concat(Z1, xsx2); + literal_vector lits; + lits.push_back(~lit2); + dependency* dep = e.dep(); + propagate_eq(dep, lits, x1, y1Z1, true); + propagate_eq(dep, lits, Z1xsx2, ysy2, true); + } + else if (ctx.get_assignment(lit2) == l_true) { + // |x1| <= |y1| => x1 ++ z2 = y1, xs ++ x2 = z2 ++ ys ++ y2 + TRACE("seq", tout << "true branch\n";); + TRACE("seq", display_equation(tout, e);); + expr_ref Z2(mk_skolem(m_seq_align, xsx2, ysy2, y1, x1), m); + expr_ref x1Z2 = mk_concat(x1, Z2); + expr_ref Z2ysy2 = mk_concat(Z2, ysy2); + literal_vector lits; + lits.push_back(lit2); + dependency* dep = e.dep(); + propagate_eq(dep, lits, x1Z2, y1, true); + propagate_eq(dep, lits, xsx2, Z2ysy2, true); + } + return true; +} + +void theory_seq::len_offset(expr* const& e, rational val) { + context & ctx = get_context(); + expr *l1 = nullptr, *l2 = nullptr, *l21 = nullptr, *l22 = nullptr; + rational fact; + if (m_autil.is_add(e, l1, l2) && m_autil.is_mul(l2, l21, l22) && + m_autil.is_numeral(l21, fact) && fact.is_minus_one()) { + if (ctx.e_internalized(l1) && ctx.e_internalized(l22)) { + enode* r1 = ctx.get_enode(l1)->get_root(), *n1 = r1; + enode* r2 = ctx.get_enode(l22)->get_root(), *n2 = r2; + expr *e1 = nullptr, *e2 = nullptr; + do { + if (!m_util.str.is_length(n1->get_owner(), e1)) + n1 = n1->get_next(); + else + break; + } + while (n1 != r1); + do { + if (!m_util.str.is_length(n2->get_owner(), e2)) + n2 = n2->get_next(); + else + break; + } + while (n2 != r2); + if (m_util.str.is_length(n1->get_owner(), e1) + && m_util.str.is_length(n2->get_owner(), e2)) { + obj_map tmp; + m_len_offset.find(r1, tmp); + tmp.insert(r2, val.get_int32()); + m_len_offset.insert(r1, tmp); + TRACE("seq", tout << "a length pair: " << mk_pp(e1, m) + << ", " << mk_pp(e2, m) << "\n";); + return; + } + } + } +} + +// Find the length offset from the congruence closure core +void theory_seq::prop_arith_to_len_offset() { + context & ctx = get_context(); + obj_hashtable const_set; + ptr_vector::const_iterator it = ctx.begin_enodes(); + ptr_vector::const_iterator end = ctx.end_enodes(); + for (; it != end; ++it) { + enode * root = (*it)->get_root(); + rational val; + if (m_autil.is_numeral(root->get_owner(), val) && val.is_neg()) { + if (const_set.contains(root)) continue; + const_set.insert(root); + TRACE("seq", tout << "offset: " << mk_pp(root->get_owner(), m) << "\n";); + enode *next = root->get_next(); + while (next != root) { + TRACE("seq", tout << "eqc: " << mk_pp(next->get_owner(), m) << "\n";); + len_offset(next->get_owner(), val); + next = next->get_next(); + } + } + } +} + +int theory_seq::find_fst_non_empty_idx(expr_ref_vector const& xs) const { + context & ctx = get_context(); + for (unsigned i = 0; i < xs.size(); ++i) { + expr* x = xs[i]; + if (!is_var(x)) return -1; + expr_ref e(m_util.str.mk_length(x), m); + if (ctx.e_internalized(e)) { + enode* root = ctx.get_enode(e)->get_root(); + rational val; + if (m_autil.is_numeral(root->get_owner(), val) && val.is_zero()) { + continue; + } + } + return i; + } + return -1; +} + +expr* theory_seq::find_fst_non_empty_var(expr_ref_vector const& x) const { + int i = find_fst_non_empty_idx(x); + if (i >= 0) + return x[i]; + return nullptr; +} + +void theory_seq::find_max_eq_len(expr_ref_vector const& ls, expr_ref_vector const& rs) { + context& ctx = get_context(); + if (ls.size() >= 2 && rs.size() >= 2) { + expr_ref len1(m_autil.mk_int(0), m), len2(m_autil.mk_int(0), m); + int l_fst = find_fst_non_empty_idx(ls); + int r_fst = find_fst_non_empty_idx(rs); + if (l_fst<0 || r_fst<0) + return; + unsigned j = 2 + l_fst; + rational lo1(-1), hi1(-1), lo2(-1), hi2(-1); + if (j >= ls.size()) { + lo1 = 0; + hi1 = 0; + } + while (j < ls.size()) { + rational lo(-1), hi(-1); + if (m_util.str.is_unit(ls.get(j))) { + lo = 1; + hi = 1; + } + else { + lower_bound(ls.get(j), lo); + upper_bound(ls.get(j), hi); + } + if (!lo.is_minus_one()) { + if (lo1.is_minus_one()) + lo1 = lo; + else + lo1 += lo; + } + if (!hi.is_minus_one()) { + if (hi1.is_minus_one()) + hi1 = hi; + else if (hi1.is_nonneg()) + hi1 += hi; + } + else { + hi1 = rational(-2); + } + len1 = mk_add(len1, m_util.str.mk_length(ls.get(j))); + j++; + } + j = 2 + r_fst; + if (j >= rs.size()) { + lo2 = 0; + hi2 = 0; + } + while (j < rs.size()) { + rational lo(-1), hi(-1); + if (m_util.str.is_unit(rs.get(j))) { + lo = 1; + hi = 1; + } + else { + lower_bound(rs.get(j), lo); + upper_bound(rs.get(j), hi); + } + if (!lo.is_minus_one()) { + if (lo2.is_minus_one()) + lo2 = lo; + else + lo2 += lo; + } + if (!hi.is_minus_one()) { + if (hi2.is_minus_one()) + hi2 = hi; + else if (hi1.is_nonneg()) + hi2 += hi; + } + else { + hi2 = rational(-2); + } + len2 = mk_add(len2, m_util.str.mk_length(rs.get(j))); + j++; + } + if (m_autil.is_numeral(len1) && m_autil.is_numeral(len2)) + return; + TRACE("seq", tout << lo1 << ", " << hi1 << ", " << lo2 << ", " << hi2 << "\n";); + if (!lo1.is_neg() && !hi2.is_neg() && lo1 > hi2) + return; + if (!lo2.is_neg() && !hi1.is_neg() && lo2 > hi1) + return; + + literal lit1 = null_literal; + if (hi1.is_zero()) { + if (!is_var(rs[1 + r_fst])) + return; + lit1 = mk_literal(m_autil.mk_le(len2, len1)); + TRACE("seq", tout << mk_pp(len1, m) << " >= " << mk_pp(len2, m) << "\n";); + } + else if (hi2.is_zero()) { + if (!is_var(ls[1 + l_fst])) + return; + lit1 = mk_literal(m_autil.mk_le(len1, len2)); + TRACE("seq", tout << mk_pp(len1, m) << " <= " << mk_pp(len2, m) << "\n";); + } + else { + lit1 = mk_eq(len1, len2, false); + TRACE("seq", tout << mk_pp(len1, m) << " = " << mk_pp(len2, m) << "\n";); + } + if (ctx.get_assignment(lit1) == l_undef) { + ctx.mark_as_relevant(lit1); + ctx.force_phase(lit1); + } + } +} + +// TODO: propagate length offsets for last vars +bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector const& rs, unsigned const& idx, + dependency*& deps, expr_ref_vector & res) { + context& ctx = get_context(); + + if (ls.empty() || rs.empty()) + return false; + expr* l_fst = find_fst_non_empty_var(ls); + expr* r_fst = find_fst_non_empty_var(rs); + if (!r_fst) return false; + expr_ref len_r_fst(m_util.str.mk_length(r_fst), m); + enode * root2; + if (!ctx.e_internalized(len_r_fst)) + return false; + else + root2 = ctx.get_enode(len_r_fst)->get_root(); + + // Offset = 0, No change + if (l_fst) { + expr_ref len_l_fst(m_util.str.mk_length(l_fst), m); + if (ctx.e_internalized(len_l_fst)) { + enode * root1 = ctx.get_enode(len_l_fst)->get_root(); + if (root1 == root2) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + return false; + } + } + } + + // Offset = 0, Changed + { + for (unsigned i = 0; i < idx; ++i) { + eq const& e = m_eqs[i]; + if (e.ls().size() == ls.size()) { + bool flag = true; + for (unsigned j = 0; j < ls.size(); ++j) + if (e.ls().get(j) != ls.get(j)) { + flag = false; + break; + } + if (flag) { + expr* nl_fst = 0; + if (e.rs().size()>1 && is_var(e.rs().get(0))) + nl_fst = e.rs().get(0); + if (nl_fst && nl_fst != r_fst) { + expr_ref len_nl_fst(m_util.str.mk_length(nl_fst), m); + if (ctx.e_internalized(len_nl_fst)) { + enode * root1 = ctx.get_enode(len_nl_fst)->get_root(); + if (root1 == root2) { + res.reset(); + res.append(e.rs().size(), e.rs().c_ptr()); + deps = m_dm.mk_join(e.dep(), deps); + return true; + } + } + } + } + } + } + } + // Offset != 0, No change + if (l_fst) { + expr_ref len_l_fst(m_util.str.mk_length(l_fst), m); + if (ctx.e_internalized(len_l_fst)) { + enode * root1 = ctx.get_enode(len_l_fst)->get_root(); + obj_map tmp; + int offset; + if (!m_autil.is_numeral(root1->get_owner()) && !m_autil.is_numeral(root2->get_owner())) { + if (m_len_offset.find(root1, tmp) && tmp.find(root2, offset)) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + find_max_eq_len(ls, rs); + return false; + } + else if (m_len_offset.find(root2, tmp) && tmp.find(root1, offset)) { + TRACE("seq", tout << "(" << mk_pp(r_fst, m) << ", " << mk_pp(l_fst,m) << ")\n";); + find_max_eq_len(ls ,rs); + return false; + } + } + } + } + // Offset != 0, Changed + obj_map tmp; + if (!m_autil.is_numeral(root2->get_owner()) && m_len_offset.find(root2, tmp)) { + for (unsigned i = 0; i < idx; ++i) { + eq const& e = m_eqs[i]; + if (e.ls().size() == ls.size()) { + bool flag = true; + for (unsigned j = 0; j < ls.size(); ++j) + if (e.ls().get(j) != ls.get(j)) { + flag = false; + break; + } + if (flag) { + expr* nl_fst = 0; + if (e.rs().size()>1 && is_var(e.rs().get(0))) + nl_fst = e.rs().get(0); + if (nl_fst && nl_fst != r_fst) { + int offset; + expr_ref len_nl_fst(m_util.str.mk_length(nl_fst), m); + if (ctx.e_internalized(len_nl_fst)) { + enode * root1 = ctx.get_enode(len_nl_fst)->get_root(); + if (!m_autil.is_numeral(root1->get_owner()) && tmp.find(root1, offset)) { + res.reset(); + res.append(e.rs().size(), e.rs().c_ptr()); + deps = m_dm.mk_join(e.dep(), deps); + find_max_eq_len(res, rs); + return true; + } + } + } + } + } + } + } + return false; +} + +bool theory_seq::has_len_offset(expr_ref_vector const& ls, expr_ref_vector const& rs, int & offset) { + context& ctx = get_context(); + + if (ls.size() == 0 || rs.size() == 0) + return false; + expr* l_fst = ls[0]; + expr* r_fst = rs[0]; + if (!is_var(l_fst) || !is_var(r_fst)) + return false; + + expr_ref len_r_fst(m_util.str.mk_length(r_fst), m); + enode * root2; + if (!ctx.e_internalized(len_r_fst)) + return false; + else + root2 = ctx.get_enode(len_r_fst)->get_root(); + + expr_ref len_l_fst(m_util.str.mk_length(l_fst), m); + if (ctx.e_internalized(len_l_fst)) { + enode * root1 = ctx.get_enode(len_l_fst)->get_root(); + if (root1 == root2) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + offset = 0; + return true; + } + obj_map tmp; + if (!m_autil.is_numeral(root1->get_owner()) && !m_autil.is_numeral(root2->get_owner())) { + if (m_len_offset.find(root1, tmp) && tmp.find(root2, offset)) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + return true; + } + else if (m_len_offset.find(root2, tmp) && tmp.find(root1, offset)) { + offset = -offset; + TRACE("seq", tout << "(" << mk_pp(r_fst, m) << ", " << mk_pp(l_fst,m) << ")\n";); + return true; + } + } + } + return false; +} + +bool theory_seq::len_based_split() { + unsigned sz = m_eqs.size(); + if (sz == 0) + return false; + + if ((int) get_context().get_scope_level() > m_len_prop_lvl) { + m_len_prop_lvl = get_context().get_scope_level(); + prop_arith_to_len_offset(); + if (!m_len_offset.empty()) { + for (unsigned i = sz-1; i > 0; --i) { + eq const& e = m_eqs[i]; + expr_ref_vector new_ls(m); + dependency *deps = e.dep(); + if (find_better_rep(e.ls(), e.rs(), i, deps, new_ls)) { + expr_ref_vector rs(m); + rs.append(e.rs().size(), e.rs().c_ptr()); + m_eqs.set(i, eq(m_eq_id++, new_ls, rs, deps)); + TRACE("seq", display_equation(tout, m_eqs[i]);); + } + } + } + } + + for (auto const& e : m_eqs) { + if (len_based_split(e)) { + return true; + } + } + return false; +} + +bool theory_seq::len_based_split(eq const& e) { + context& ctx = get_context(); + expr_ref_vector const& ls = e.ls(); + expr_ref_vector const& rs = e.rs(); + + int offset_orig = 0; + if (!has_len_offset(ls, rs, offset_orig)) + return false; + + TRACE("seq", tout << "split based on length\n";); + TRACE("seq", display_equation(tout, e);); + expr_ref x11(m_util.str.mk_concat(1, ls.c_ptr()), m); + expr_ref x12(m_util.str.mk_concat(ls.size()-1, ls.c_ptr()+1), m); + expr_ref y11(m_util.str.mk_concat(1, rs.c_ptr()), m); + expr_ref y12(m_util.str.mk_concat(rs.size()-1, rs.c_ptr()+1), m); + + expr_ref lenX11(m_util.str.mk_length(x11),m); + expr_ref lenY11(m); + expr_ref Z(m); + int offset = 0; + if (offset_orig != 0) { + lenY11 = m_autil.mk_add(m_util.str.mk_length(y11), m_autil.mk_int(offset_orig)); + if (offset_orig > 0) { + offset = offset_orig; + Z = mk_skolem(m_seq_align, y12, x12, x11, y11); + y11 = mk_concat(y11, Z); + x12 = mk_concat(Z, x12); + } + else { + offset = -offset_orig; + Z = mk_skolem(m_seq_align, x12, y12, y11, x11); + x11 = mk_concat(x11, Z); + y12 = mk_concat(Z, y12); + } + } + else + lenY11 = m_util.str.mk_length(y11); + + dependency* dep = e.dep(); + literal_vector lits; + literal lit1 = mk_eq(lenX11, lenY11, false); + lits.push_back(lit1); + + if (ls.size()>=2 && rs.size()>=2 && (ls.size()>2 || rs.size()>2)) { + expr_ref len1(m_autil.mk_int(0),m), len2(m_autil.mk_int(0),m); + for (unsigned i = 2; i < ls.size(); ++i) + len1 = mk_add(len1, m_util.str.mk_length(ls[i])); + for (unsigned i = 2; i < rs.size(); ++i) + len2 = mk_add(len2, m_util.str.mk_length(rs[i])); + bool flag = false; + if (!m_autil.is_numeral(len1) && !m_autil.is_numeral(len2)) { + literal lit2 = mk_eq(len1, len2, false); + flag = ctx.get_assignment(lit2) == l_true; + } + else { + expr_ref eq_len(m.mk_eq(len1, len2), m); + flag = ctx.find_assignment(eq_len) == l_true; + } + + if (flag) { + literal lit2 = mk_eq(len1, len2, false); + lits.push_back(lit2); + TRACE("seq", tout << mk_pp(len1, m) << " = " << mk_pp(len2, m) << "\n";); + expr_ref lhs(m), rhs(m); + if (ls.size() > 2) + lhs = m_util.str.mk_concat(ls.size()-2, ls.c_ptr()+2); + else + lhs = m_util.str.mk_empty(m.get_sort(x11)); + if (rs.size() > 2) + rhs = m_util.str.mk_concat(rs.size()-2, rs.c_ptr()+2); + else + rhs = m_util.str.mk_empty(m.get_sort(x11)); + propagate_eq(dep, lits, lhs, rhs, true); + lits.pop_back(); + } + } + + if (offset != 0) { + expr_ref lenZ(m_util.str.mk_length(Z), m); + propagate_eq(dep, lits, lenZ, m_autil.mk_int(offset), false); + } + propagate_eq(dep, lits, y11, x11, true); + propagate_eq(dep, lits, x12, y12, false); + + return true; +} + bool theory_seq::branch_variable_mb() { bool change = false; - for (eq const& e : m_eqs) { + for (auto const& e : m_eqs) { vector len1, len2; if (!is_complex(e)) { continue; @@ -495,8 +1376,8 @@ bool theory_seq::branch_variable_mb() { continue; } rational l1, l2; - for (unsigned j = 0; j < len1.size(); ++j) l1 += len1[j]; - for (unsigned j = 0; j < len2.size(); ++j) l2 += len2[j]; + for (auto elem : len1) l1 += elem; + for (auto elem : len2) l2 += elem; if (l1 != l2) { TRACE("seq", tout << "lengths are not compatible\n";); expr_ref l = mk_concat(e.ls()); @@ -519,11 +1400,11 @@ bool theory_seq::branch_variable_mb() { bool theory_seq::is_complex(eq const& e) { unsigned num_vars1 = 0, num_vars2 = 0; - for (unsigned i = 0; i < e.ls().size(); ++i) { - if (is_var(e.ls()[i])) ++num_vars1; + for (auto const& elem : e.ls()) { + if (is_var(elem)) ++num_vars1; } - for (unsigned i = 0; i < e.rs().size(); ++i) { - if (is_var(e.rs()[i])) ++num_vars2; + for (auto const& elem : e.rs()) { + if (is_var(elem)) ++num_vars2; } return num_vars1 > 0 && num_vars2 > 0 && num_vars1 + num_vars2 > 2; } @@ -875,11 +1756,11 @@ bool theory_seq::propagate_length_coherence(expr* e) { if (!is_var(e) || !m_rep.is_root(e)) { return false; } - if (!lower_bound(e, lo) || !lo.is_pos() || lo >= rational(2048)) { + if (!lower_bound2(e, lo) || !lo.is_pos() || lo >= rational(2048)) { return false; } TRACE("seq", tout << "Unsolved " << mk_pp(e, m); - if (!lower_bound(e, lo)) lo = -rational::one(); + if (!lower_bound2(e, lo)) lo = -rational::one(); if (!upper_bound(e, hi)) hi = -rational::one(); tout << " lo: " << lo << " hi: " << hi << "\n"; ); @@ -950,13 +1831,13 @@ bool theory_seq::check_length_coherence0(expr* e) { bool theory_seq::check_length_coherence() { #if 1 - for (expr* e : m_length) { + for (auto e : m_length) { if (check_length_coherence0(e)) { return true; } } #endif - for (expr* e : m_length) { + for (auto e : m_length) { if (check_length_coherence(e)) { return true; } @@ -964,19 +1845,20 @@ bool theory_seq::check_length_coherence() { return false; } -bool theory_seq::fixed_length() { +bool theory_seq::fixed_length(bool is_zero) { bool found = false; - for (expr* e : m_length) { - if (fixed_length(e)) { + for (auto e : m_length) { + if (fixed_length(e, is_zero)) { found = true; } } return found; } -bool theory_seq::fixed_length(expr* e) { +bool theory_seq::fixed_length(expr* e, bool is_zero) { rational lo, hi; - if (!(is_var(e) && lower_bound(e, lo) && upper_bound(e, hi) && lo == hi && lo.is_unsigned())) { + if (!(is_var(e) && lower_bound(e, lo) && upper_bound(e, hi) && lo == hi + && ((is_zero && lo.is_zero()) || (!is_zero && lo.is_unsigned())))) { return false; } if (is_skolem(m_tail, e) || is_skolem(m_seq_first, e) || @@ -996,7 +1878,7 @@ bool theory_seq::fixed_length(expr* e) { if (lo.is_zero()) { seq = m_util.str.mk_empty(m.get_sort(e)); } - else { + else if (!is_zero) { unsigned _lo = lo.get_unsigned(); expr_ref_vector elems(m); @@ -1039,6 +1921,11 @@ bool theory_seq::propagate_is_conc(expr* e, expr* conc) { } } +bool theory_seq::is_unit_nth(expr* e) const { + expr *s = nullptr; + return m_util.str.is_unit(e, s) && is_nth(s); +} + bool theory_seq::is_nth(expr* e) const { return is_skolem(m_nth, e); } @@ -1080,11 +1967,11 @@ bool theory_seq::is_post(expr* e, expr*& s, expr*& i) { expr_ref theory_seq::mk_nth(expr* s, expr* idx) { sort* char_sort = nullptr; VERIFY(m_util.is_seq(m.get_sort(s), char_sort)); - return mk_skolem(m_nth, s, idx, nullptr, char_sort); + return mk_skolem(m_nth, s, idx, nullptr, nullptr, char_sort); } expr_ref theory_seq::mk_sk_ite(expr* c, expr* t, expr* e) { - return mk_skolem(symbol("seq.if"), c, t, e, m.get_sort(t)); + return mk_skolem(symbol("seq.if"), c, t, e, nullptr, m.get_sort(t)); } expr_ref theory_seq::mk_last(expr* s) { @@ -1094,7 +1981,7 @@ expr_ref theory_seq::mk_last(expr* s) { } sort* char_sort = nullptr; VERIFY(m_util.is_seq(m.get_sort(s), char_sort)); - return mk_skolem(m_seq_last, s, nullptr, nullptr, char_sort); + return mk_skolem(m_seq_last, s, nullptr, nullptr, nullptr, char_sort); } expr_ref theory_seq::mk_first(expr* s) { @@ -1412,6 +2299,10 @@ bool theory_seq::solve_unit_eq(expr_ref_vector const& l, expr_ref_vector const& if (r.size() == 1 && is_var(r[0]) && !occurs(r[0], l) && add_solution(r[0], mk_concat(l, m.get_sort(r[0])), deps)) { return true; } +// if (l.size() == 1 && r.size() == 1 && l[0] != r[0] && is_nth(l[0]) && add_solution(l[0], r[0], deps)) +// return true; +// if (l.size() == 1 && r.size() == 1 && l[0] != r[0] && is_nth(r[0]) && add_solution(r[0], l[0], deps)) +// return true; return false; } @@ -1440,14 +2331,18 @@ bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps) { if (is_var(r) && !occurs(r, l) && add_solution(r, l, deps)) { return true; } +// if (is_nth(l) && !occurs(l, r) && add_solution(l, r, deps)) +// return true; +// if (is_nth(r) && !occurs(r, l) && add_solution(r, l, deps)) +// return true; return false; } bool theory_seq::occurs(expr* a, expr_ref_vector const& b) { - for (unsigned i = 0; i < b.size(); ++i) { - if (a == b[i] || m.is_ite(b[i])) return true; + for (auto const& elem : b) { + if (a == elem || m.is_ite(elem)) return true; } return false; } @@ -1474,7 +2369,7 @@ bool theory_seq::occurs(expr* a, expr* b) { } -bool theory_seq::is_var(expr* a) { +bool theory_seq::is_var(expr* a) const { return m_util.is_seq(a) && !m_util.str.is_concat(a) && @@ -1507,7 +2402,7 @@ bool theory_seq::solve_eqs(unsigned i) { bool change = false; for (; !ctx.inconsistent() && i < m_eqs.size(); ++i) { eq const& e = m_eqs[i]; - if (solve_eq(e.ls(), e.rs(), e.dep())) { + if (solve_eq(e.ls(), e.rs(), e.dep(), i)) { if (i + 1 != m_eqs.size()) { eq e1 = m_eqs[m_eqs.size()-1]; m_eqs.set(i, e1); @@ -1521,7 +2416,7 @@ bool theory_seq::solve_eqs(unsigned i) { return change || m_new_propagation || ctx.inconsistent(); } -bool theory_seq::solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* deps) { +bool theory_seq::solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* deps, unsigned idx) { context& ctx = get_context(); expr_ref_vector& ls = m_ls; expr_ref_vector& rs = m_rs; @@ -1550,8 +2445,26 @@ bool theory_seq::solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, de return true; } if (!ctx.inconsistent() && change) { + // The propagation step from arithmetic state (e.g. length offset) to length constraints + if (get_context().get_scope_level() == 0) { + prop_arith_to_len_offset(); + } TRACE("seq", tout << "inserting equality\n";); - m_eqs.push_back(eq(m_eq_id++, ls, rs, deps)); + bool updated = false; + if (!m_len_offset.empty()) { + // Find a better equivalent term for lhs of an equation based on length constraints + expr_ref_vector new_ls(m); + if (find_better_rep(ls, rs, idx, deps, new_ls)) { + eq const & new_eq = eq(m_eq_id++, new_ls, rs, deps); + m_eqs.push_back(new_eq); + TRACE("seq", tout << "find_better_rep\n";); + TRACE("seq", display_equation(tout, new_eq);); + updated = true; + } + } + if (!updated) { + m_eqs.push_back(eq(m_eq_id++, ls, rs, deps)); + } return true; } return false; @@ -1591,6 +2504,124 @@ bool theory_seq::is_binary_eq(expr_ref_vector const& ls, expr_ref_vector const& return false; } +bool theory_seq::is_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, +expr*& x1, expr_ref_vector& xs, expr*& x2, expr*& y1, expr_ref_vector& ys, expr*& y2) { + if (ls.size() > 1 && is_var(ls[0]) && is_var(ls.back()) && + rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { + unsigned l_start = 1; + for (; l_start < ls.size()-1; ++l_start) { + if (m_util.str.is_unit(ls[l_start])) break; + } + if (l_start == ls.size()-1) return false; + unsigned l_end = l_start; + for (; l_end < ls.size()-1; ++l_end) { + if (!m_util.str.is_unit(ls[l_end])) break; + } + --l_end; + unsigned r_start = 1; + for (; r_start < rs.size()-1; ++r_start) { + if (m_util.str.is_unit(rs[r_start])) break; + } + if (r_start == rs.size()-1) return false; + unsigned r_end = r_start; + for (; r_end < rs.size()-1; ++r_end) { + if (!m_util.str.is_unit(rs[r_end])) break; + } + --r_end; + for (unsigned i = l_start; i < l_end+1; ++i) { + if (!m_util.str.is_unit(ls[i])) return false; + } + for (unsigned i = r_start; i < r_end+1; ++i) { + if (!m_util.str.is_unit(rs[i])) return false; + } + xs.reset(); + xs.append(l_end-l_start+1, ls.c_ptr()+l_start); + x1 = m_util.str.mk_concat(l_start, ls.c_ptr()); + x2 = m_util.str.mk_concat(ls.size()-l_end-1, ls.c_ptr()+l_end+1); + ys.reset(); + ys.append(r_end-r_start+1, rs.c_ptr()+r_start); + y1 = m_util.str.mk_concat(r_start, rs.c_ptr()); + y2 = m_util.str.mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1); + return true; + } + return false; +} + +bool theory_seq::is_ternary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, +expr*& x, expr_ref_vector& xs, expr*& y1, expr_ref_vector& ys, expr*& y2, bool flag1) { + if (ls.size() > 1 && (is_var(ls[0]) || flag1) && + rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { + unsigned l_start = ls.size()-1; + for (; l_start > 0; --l_start) { + if (!m_util.str.is_unit(ls[l_start])) break; + } + if (l_start == ls.size()-1) return false; + ++l_start; + unsigned r_end = rs.size()-2; + for (; r_end > 0; --r_end) { + if (m_util.str.is_unit(rs[r_end])) break; + } + if (r_end == 0) return false; + unsigned r_start = r_end; + for (; r_start > 0; --r_start) { + if (!m_util.str.is_unit(rs[r_start])) break; + } + ++r_start; + for (unsigned i = l_start; i < ls.size(); ++i) { + if (!m_util.str.is_unit(ls[i])) return false; + } + for (unsigned i = r_start; i < r_end+1; ++i) { + if (!m_util.str.is_unit(rs[i])) return false; + } + xs.reset(); + xs.append(ls.size()-l_start, ls.c_ptr()+l_start); + x = m_util.str.mk_concat(l_start, ls.c_ptr()); + ys.reset(); + ys.append(r_end-r_start+1, rs.c_ptr()+r_start); + y1 = m_util.str.mk_concat(r_start, rs.c_ptr()); + y2 = m_util.str.mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1); + return true; + } + return false; +} + +bool theory_seq::is_ternary_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, + expr_ref_vector& xs, expr*& x, expr*& y1, expr_ref_vector& ys, expr*& y2, bool flag1) { + if (ls.size() > 1 && (is_var(ls.back()) || flag1) && + rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { + unsigned l_start = 0; + for (; l_start < ls.size()-1; ++l_start) { + if (!m_util.str.is_unit(ls[l_start])) break; + } + if (l_start == 0) return false; + unsigned r_start = 1; + for (; r_start < rs.size()-1; ++r_start) { + if (m_util.str.is_unit(rs[r_start])) break; + } + if (r_start == rs.size()-1) return false; + unsigned r_end = r_start; + for (; r_end < rs.size()-1; ++r_end) { + if (!m_util.str.is_unit(rs[r_end])) break; + } + --r_end; + for (unsigned i = 0; i < l_start; ++i) { + if (!m_util.str.is_unit(ls[i])) return false; + } + for (unsigned i = r_start; i < r_end+1; ++i) { + if (!m_util.str.is_unit(rs[i])) return false; + } + xs.reset(); + xs.append(l_start, ls.c_ptr()); + x = m_util.str.mk_concat(ls.size()-l_start, ls.c_ptr()+l_start); + ys.reset(); + ys.append(r_end-r_start+1, rs.c_ptr()+r_start); + y1 = m_util.str.mk_concat(r_start, rs.c_ptr()); + y2 = m_util.str.mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1); + return true; + } + return false; +} + bool theory_seq::reduce_length_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* deps) { if (ls.empty() || rs.empty()) { return false; @@ -1695,6 +2726,10 @@ bool theory_seq::reduce_length(unsigned i, unsigned j, bool front, expr_ref_vect expr_ref lenr(m_util.str.mk_length(r), m); literal lit = mk_eq(lenl, lenr, false); if (ctx.get_assignment(lit) == l_true) { +// expr_ref len_eq(m.mk_eq(lenl, lenr), m); +// if (ctx.find_assignment(len_eq) == l_true) { +// literal lit = mk_eq(lenl, lenr, false); +// literal_vector lits; expr_ref_vector lhs(m), rhs(m); lhs.append(l2, ls2); rhs.append(r2, rs2); @@ -2255,7 +3290,7 @@ bool theory_seq::internalize_term(app* term) { return true; } TRACE("seq_verbose", tout << mk_pp(term, m) << "\n";); - for (expr* arg : *term) { + for (auto arg : *term) { mk_var(ensure_enode(arg)); } if (m.is_bool(term)) { @@ -2283,7 +3318,7 @@ void theory_seq::add_length(expr* e) { /* - ensure that all elements in equivalence class occur under an applicatin of 'length' + ensure that all elements in equivalence class occur under an application of 'length' */ void theory_seq::enforce_length(enode* n) { enode* n1 = n; @@ -2405,7 +3440,7 @@ bool theory_seq::add_stoi_val_axiom(expr* e) { literal theory_seq::is_digit(expr* ch) { bv_util bv(m); - literal isd = mk_literal(mk_skolem(symbol("seq.is_digit"), ch, nullptr, nullptr, m.mk_bool_sort())); + literal isd = mk_literal(mk_skolem(symbol("seq.is_digit"), ch, nullptr, nullptr, nullptr, m.mk_bool_sort())); expr_ref d2i = digit2int(ch); expr_ref _lo(bv.mk_ule(bv.mk_numeral(rational('0'), bv.mk_sort(8)), ch), m); expr_ref _hi(bv.mk_ule(ch, bv.mk_numeral(rational('9'), bv.mk_sort(8))), m); @@ -2421,7 +3456,7 @@ literal theory_seq::is_digit(expr* ch) { } expr_ref theory_seq::digit2int(expr* ch) { - return expr_ref(mk_skolem(symbol("seq.digit2int"), ch, nullptr, nullptr, m_autil.mk_int()), m); + return expr_ref(mk_skolem(symbol("seq.digit2int"), ch, nullptr, nullptr, nullptr, m_autil.mk_int()), m); } void theory_seq::add_itos_axiom(expr* e) { @@ -2519,7 +3554,7 @@ void theory_seq::display(std::ostream & out) const { } if (!m_length.empty()) { - for (expr* e : m_length) { + for (auto e : m_length) { rational lo(-1), hi(-1); lower_bound(e, lo); upper_bound(e, hi); @@ -2544,7 +3579,7 @@ void theory_seq::display_nc(std::ostream& out, nc const& nc) const { } void theory_seq::display_equations(std::ostream& out) const { - for (eq const& e : m_eqs) { + for (auto const& e : m_eqs) { display_equation(out, e); } } @@ -2640,7 +3675,7 @@ void theory_seq::init_search_eh() { void theory_seq::init_model(expr_ref_vector const& es) { expr_ref new_s(m); - for (expr* e : es) { + for (auto e : es) { dependency* eqs = nullptr; expr_ref s = canonize(e, eqs); if (is_var(s)) { @@ -3480,6 +4515,12 @@ expr_ref theory_seq::mk_sub(expr* a, expr* b) { return result; } +expr_ref theory_seq::mk_add(expr* a, expr* b) { + expr_ref result(m_autil.mk_add(a, b), m); + m_rewrite(result); + return result; +} + enode* theory_seq::ensure_enode(expr* e) { context& ctx = get_context(); if (!ctx.e_internalized(e)) { @@ -3506,6 +4547,8 @@ static bool get_arith_value(context& ctx, theory_id afid, expr* e, expr_ref& v) if (tha) return tha->get_value(ctx.get_enode(e), v); theory_i_arith* thi = get_th_arith(ctx, afid, e); if (thi) return thi->get_value(ctx.get_enode(e), v); + theory_lra* thr = get_th_arith(ctx, afid, e); + if (thr) return thr->get_value(ctx.get_enode(e), v); TRACE("seq", tout << "no arithmetic theory\n";); return false; } @@ -3528,28 +4571,79 @@ bool theory_seq::get_num_value(expr* e, rational& val) const { } bool theory_seq::lower_bound(expr* _e, rational& lo) const { + context& ctx = get_context(); + expr_ref e(m_util.str.mk_length(_e), m); + expr_ref _lo(m); + family_id afid = m_autil.get_family_id(); + do { + theory_mi_arith* tha = get_th_arith(ctx, afid, e); + if (tha && tha->get_lower(ctx.get_enode(e), _lo)) break; + theory_i_arith* thi = get_th_arith(ctx, afid, e); + if (thi && thi->get_lower(ctx.get_enode(e), _lo)) break; + theory_lra* thr = get_th_arith(ctx, afid, e); + if (thr && thr->get_lower(ctx.get_enode(e), _lo)) break; + TRACE("seq", tout << "no lower bound " << mk_pp(_e, m) << "\n";); + return false; + } + while (false); + return m_autil.is_numeral(_lo, lo) && lo.is_int(); +} + +// The difference with lower_bound function is that since in some cases, +// the lower bound is not updated for all the enodes in the same eqc, +// we have to traverse the eqc to query for the better lower bound. +bool theory_seq::lower_bound2(expr* _e, rational& lo) { context& ctx = get_context(); expr_ref e(m_util.str.mk_length(_e), m); expr_ref _lo(m); theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); - if (tha && !tha->get_lower(ctx.get_enode(e), _lo)) return false; if (!tha) { theory_i_arith* thi = get_th_arith(ctx, m_autil.get_family_id(), e); if (!thi || !thi->get_lower(ctx.get_enode(e), _lo)) return false; } - return m_autil.is_numeral(_lo, lo) && lo.is_int(); + enode *ee = ctx.get_enode(e); + if (!tha->get_lower(ee, _lo) || m_autil.is_numeral(_lo, lo)) { + enode *next = ee->get_next(); + bool flag = false; + while (next != ee) { + if (!m_autil.is_numeral(next->get_owner()) && !m_util.str.is_length(next->get_owner())) { + expr *var = next->get_owner(); + TRACE("seq", tout << mk_pp(var, m) << "\n";); + expr_ref _lo2(m); + rational lo2; + if (tha->get_lower(next, _lo2) && m_autil.is_numeral(_lo2, lo2) && lo2>lo) { + flag = true; + lo = lo2; + literal low(mk_literal(m_autil.mk_ge(var, _lo2))); + add_axiom(~low, mk_literal(m_autil.mk_ge(e, _lo2))); + } + } + next = next->get_next(); + } + if (flag) + return true; + if (!tha->get_lower(ee, _lo)) + return false; + } + return true; } bool theory_seq::upper_bound(expr* _e, rational& hi) const { context& ctx = get_context(); expr_ref e(m_util.str.mk_length(_e), m); - theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); + family_id afid = m_autil.get_family_id(); expr_ref _hi(m); - if (tha && !tha->get_upper(ctx.get_enode(e), _hi)) return false; - if (!tha) { - theory_i_arith* thi = get_th_arith(ctx, m_autil.get_family_id(), e); - if (!thi || !thi->get_upper(ctx.get_enode(e), _hi)) return false; + do { + theory_mi_arith* tha = get_th_arith(ctx, afid, e); + if (tha && tha->get_upper(ctx.get_enode(e), _hi)) break; + theory_i_arith* thi = get_th_arith(ctx, afid, e); + if (thi && thi->get_upper(ctx.get_enode(e), _hi)) break; + theory_lra* thr = get_th_arith(ctx, afid, e); + if (thr && thr->get_upper(ctx.get_enode(e), _hi)) break; + TRACE("seq", tout << "no upper bound " << mk_pp(_e, m) << "\n";); + return false; } + while (false); return m_autil.is_numeral(_hi, hi) && hi.is_int(); } @@ -3847,7 +4941,7 @@ literal theory_seq::mk_literal(expr* _e) { literal theory_seq::mk_seq_eq(expr* a, expr* b) { SASSERT(m_util.is_seq(a)); - return mk_literal(mk_skolem(m_eq, a, b, nullptr, m.mk_bool_sort())); + return mk_literal(mk_skolem(m_eq, a, b, nullptr, nullptr, m.mk_bool_sort())); } literal theory_seq::mk_eq_empty(expr* _e, bool phase) { @@ -3861,7 +4955,7 @@ literal theory_seq::mk_eq_empty(expr* _e, bool phase) { } expr_ref_vector concats(m); m_util.str.get_concat(e, concats); - for (expr* c : concats) { + for (auto c : concats) { if (m_util.str.is_unit(c)) { return false_literal; } @@ -3892,13 +4986,73 @@ void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4, liter ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } +expr* theory_seq::coalesce_chars(expr* const& e) { + context& ctx = get_context(); + expr* s; + if (m_util.str.is_concat(e)) { + expr_ref_vector concats(m); + m_util.str.get_concat(e, concats); + expr_ref_vector result(m); + for (unsigned i = 0; i < concats.size(); ++i) { + expr_ref tmp(coalesce_chars(concats[i].get()), m); + if (m_util.str.is_empty(tmp)) continue; + zstring zs, a; + bool flag = false; + while (m_util.str.is_string(tmp, a)) { + if (flag) + zs = zs + a; + else + zs = a; + flag = true; + if (i < concats.size()-1) + tmp = coalesce_chars(concats[++i].get()); + else { + ++i; + break; + } + } + if (flag) { + result.push_back(m_util.str.mk_string(zs)); + if (i < concats.size()) + result.push_back(tmp); + } + else + result.push_back(tmp); + } + SASSERT(result.size() > 0); + if (result.size() > 1) + return m_util.str.mk_concat(result.size(), result.c_ptr()); + else + return e; + } + else if (m_util.str.is_unit(e, s)) { + bv_util bvu(m); + if (bvu.is_bv(s)) { + expr_ref result(m); + expr * args[1] = {s}; + if (BR_FAILED != m_seq_rewrite.mk_app_core(to_app(e)->get_decl(), 1, args, result)) { + if (!ctx.e_internalized(result)) + ctx.internalize(result, false); + return result; + } + } + } + return e; +} + +expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, expr* e2, expr* e3, expr*e4, sort* range) { + expr* es[4] = { e1, e2, e3, e4 }; + unsigned len = e4?4:(e3?3:(e2?2:1)); -expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, expr* e2, expr* e3, sort* range) { - expr* es[3] = { e1, e2, e3 }; - unsigned len = e3?3:(e2?2:1); if (!range) { range = m.get_sort(e1); } + if (name == m_seq_align) { + for (unsigned i = 0; i < len; ++i) { + es[i] = coalesce_chars(es[i]); + TRACE("seq", tout << mk_pp(es[i], m) << "\n";); + } + } return expr_ref(m_util.mk_skolem(name, len, es, range), m); } @@ -4158,6 +5312,10 @@ void theory_seq::pop_scope_eh(unsigned num_scopes) { if (ctx.get_base_level() > ctx.get_scope_level() - num_scopes) { m_replay.reset(); } + if (m_len_prop_lvl > (int) ctx.get_scope_level()) { + m_len_prop_lvl = ctx.get_scope_level(); + m_len_offset.reset(); + } } void theory_seq::restart_eh() { @@ -4509,8 +5667,8 @@ void theory_seq::propagate_not_prefix(expr* e) { expr_ref x = mk_skolem(symbol("seq.prefix.x"), e1, e2); expr_ref y = mk_skolem(symbol("seq.prefix.y"), e1, e2); expr_ref z = mk_skolem(symbol("seq.prefix.z"), e1, e2); - expr_ref c = mk_skolem(symbol("seq.prefix.c"), e1, e2, nullptr, char_sort); - expr_ref d = mk_skolem(symbol("seq.prefix.d"), e1, e2, nullptr, char_sort); + expr_ref c = mk_skolem(symbol("seq.prefix.c"), e1, e2, nullptr, nullptr, char_sort); + expr_ref d = mk_skolem(symbol("seq.prefix.d"), e1, e2, nullptr, nullptr, char_sort); add_axiom(lit, e2_is_emp, mk_seq_eq(e1, mk_concat(x, m_util.str.mk_unit(c), y))); add_axiom(lit, e2_is_emp, mk_seq_eq(e2, mk_concat(x, m_util.str.mk_unit(d), z)), mk_seq_eq(e2, x)); add_axiom(lit, e2_is_emp, ~mk_eq(c, d, false), mk_seq_eq(e2, x)); @@ -4566,8 +5724,8 @@ void theory_seq::propagate_not_suffix(expr* e) { expr_ref x = mk_skolem(symbol("seq.suffix.x"), e1, e2); expr_ref y = mk_skolem(symbol("seq.suffix.y"), e1, e2); expr_ref z = mk_skolem(symbol("seq.suffix.z"), e1, e2); - expr_ref c = mk_skolem(symbol("seq.suffix.c"), e1, e2, nullptr, char_sort); - expr_ref d = mk_skolem(symbol("seq.suffix.d"), e1, e2, nullptr, char_sort); + expr_ref c = mk_skolem(symbol("seq.suffix.c"), e1, e2, nullptr, nullptr, char_sort); + expr_ref d = mk_skolem(symbol("seq.suffix.d"), e1, e2, nullptr, nullptr, char_sort); add_axiom(lit, e2_is_emp, mk_seq_eq(e1, mk_concat(y, m_util.str.mk_unit(c), x))); add_axiom(lit, e2_is_emp, mk_seq_eq(e2, mk_concat(z, m_util.str.mk_unit(d), x)), mk_seq_eq(e2, x)); add_axiom(lit, e2_is_emp, ~mk_eq(c, d, false), mk_seq_eq(e2, x)); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 2f7cfd2b3..abb202884 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -29,6 +29,7 @@ Revision History: #include "math/automata/automaton.h" #include "ast/rewriter/seq_rewriter.h" #include "util/union_find.h" +#include "util/obj_ref_hashtable.h" namespace smt { @@ -48,7 +49,7 @@ namespace smt { typedef union_find th_union_find; class seq_value_proc; - + // cache to track evaluations under equalities class eval_cache { eqdep_map_t m_map; @@ -292,6 +293,7 @@ namespace smt { typedef hashtable rational_set; ast_manager& m; + theory_seq_params const& m_params; dependency_manager m_dm; solution_map m_rep; // unification representative. bool m_reset_cache; // invalidate cache. @@ -301,6 +303,12 @@ namespace smt { unsigned m_eq_id; th_union_find m_find; + obj_ref_map m_overlap; + obj_ref_map m_overlap2; + obj_map> m_len_offset; + int m_len_prop_lvl; + + seq_factory* m_factory; // value factory exclusion_table m_exclude; // set of asserted disequalities. expr_ref_vector m_axioms; // list of axioms to add. @@ -321,7 +329,7 @@ namespace smt { stats m_stats; symbol m_prefix, m_suffix, m_accept, m_reject; symbol m_tail, m_nth, m_seq_first, m_seq_last, m_indexof_left, m_indexof_right, m_aut_step; - symbol m_pre, m_post, m_eq; + symbol m_pre, m_post, m_eq, m_seq_align; ptr_vector m_todo; expr_ref_vector m_ls, m_rs, m_lhs, m_rhs; @@ -354,7 +362,7 @@ namespace smt { void pop_scope_eh(unsigned num_scopes) override; void restart_eh() override; void relevant_eh(app* n) override; - theory* mk_fresh(context* new_ctx) override { return alloc(theory_seq, new_ctx->get_manager()); } + theory* mk_fresh(context* new_ctx) override { return alloc(theory_seq, new_ctx->get_manager(), m_params); } char const * get_name() const override { return "seq"; } theory_var mk_var(enode* n) override; void apply_sort_cnstr(enode* n, sort* s) override; @@ -366,23 +374,44 @@ namespace smt { void init_search_eh() override; void init_model(expr_ref_vector const& es); + + void len_offset(expr* const& e, rational val); + void prop_arith_to_len_offset(); + int find_fst_non_empty_idx(expr_ref_vector const& x) const; + expr* find_fst_non_empty_var(expr_ref_vector const& x) const; + void find_max_eq_len(expr_ref_vector const& ls, expr_ref_vector const& rs); + bool has_len_offset(expr_ref_vector const& ls, expr_ref_vector const& rs, int & diff); + bool find_better_rep(expr_ref_vector const& ls, expr_ref_vector const& rs, unsigned const& idx, dependency*& deps, expr_ref_vector & res); + // final check bool simplify_and_solve_eqs(); // solve unitary equalities bool reduce_length_eq(); bool branch_unit_variable(); // branch on XYZ = abcdef bool branch_binary_variable(); // branch on abcX = Ydefg + bool branch_ternary_variable1(); // branch on XabcY = Zdefg or XabcY = defgZ + bool branch_ternary_variable2(); // branch on XabcY = defgZmnpq + bool branch_quat_variable(); // branch on XabcY = ZdefgT + bool len_based_split(); // split based on len offset bool branch_variable_mb(); // branch on a variable, model based on length bool branch_variable(); // branch on a variable - bool split_variable(); // split a variable bool is_solved(); bool check_length_coherence(); bool check_length_coherence0(expr* e); bool check_length_coherence(expr* e); - bool fixed_length(); - bool fixed_length(expr* e); + bool fixed_length(bool is_zero = false); + bool fixed_length(expr* e, bool is_zero); void branch_unit_variable(dependency* dep, expr* X, expr_ref_vector const& units); bool branch_variable(eq const& e); bool branch_binary_variable(eq const& e); + bool eq_unit(expr* const& l, expr* const &r) const; + unsigned_vector overlap(expr_ref_vector const& ls, expr_ref_vector const& rs); + unsigned_vector overlap2(expr_ref_vector const& ls, expr_ref_vector const& rs); + bool branch_ternary_variable_base(dependency* dep, unsigned_vector indexes, expr* const& x, expr_ref_vector const& xs, expr* const& y1, expr_ref_vector const& ys, expr* const& y2); + bool branch_ternary_variable_base2(dependency* dep, unsigned_vector indexes, expr_ref_vector const& xs, expr* const& x, expr* const& y1, expr_ref_vector const& ys, expr* const& y2); + bool branch_ternary_variable(eq const& e, bool flag1 = false); + bool branch_ternary_variable2(eq const& e, bool flag1 = false); + bool branch_quat_variable(eq const& e); + bool len_based_split(eq const& e); bool is_unit_eq(expr_ref_vector const& ls, expr_ref_vector const& rs); bool propagate_length_coherence(expr* e); bool split_lengths(dependency* dep, @@ -394,11 +423,14 @@ namespace smt { bool check_extensionality(); bool check_contains(); bool solve_eqs(unsigned start); - bool solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep); + bool solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep, unsigned idx); bool simplify_eq(expr_ref_vector& l, expr_ref_vector& r, dependency* dep); bool solve_unit_eq(expr* l, expr* r, dependency* dep); bool solve_unit_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep); bool is_binary_eq(expr_ref_vector const& l, expr_ref_vector const& r, expr*& x, ptr_vector& xunits, ptr_vector& yunits, expr*& y); + bool is_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr*& x1, expr_ref_vector& xs, expr*& x2, expr*& y1, expr_ref_vector& ys, expr*& y2); + bool is_ternary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr*& x, expr_ref_vector& xs, expr*& y1, expr_ref_vector& ys, expr*& y2, bool flag1); + bool is_ternary_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_vector& xs, expr*& x, expr*& y1, expr_ref_vector& ys, expr*& y2, bool flag1); bool solve_binary_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep); bool propagate_max_length(expr* l, expr* r, dependency* dep); @@ -410,7 +442,7 @@ namespace smt { expr_ref mk_empty(sort* s) { return expr_ref(m_util.str.mk_empty(s), m); } expr_ref mk_concat(unsigned n, expr*const* es) { return expr_ref(m_util.str.mk_concat(n, es), m); } expr_ref mk_concat(expr_ref_vector const& es, sort* s) { if (es.empty()) return mk_empty(s); return mk_concat(es.size(), es.c_ptr()); } - expr_ref mk_concat(expr_ref_vector const& es) { SASSERT(!es.empty()); return mk_concat(es.size(), es.c_ptr()); } + expr_ref mk_concat(expr_ref_vector const& es) { SASSERT(!es.empty()); return expr_ref(m_util.str.mk_concat(es.size(), es.c_ptr()), m); } expr_ref mk_concat(ptr_vector const& es) { SASSERT(!es.empty()); return mk_concat(es.size(), es.c_ptr()); } expr_ref mk_concat(expr* e1, expr* e2) { return expr_ref(m_util.str.mk_concat(e1, e2), m); } expr_ref mk_concat(expr* e1, expr* e2, expr* e3) { return expr_ref(m_util.str.mk_concat(e1, e2, e3), m); } @@ -454,8 +486,9 @@ namespace smt { // variable solving utilities bool occurs(expr* a, expr* b); bool occurs(expr* a, expr_ref_vector const& b); - bool is_var(expr* b); + bool is_var(expr* b) const; bool add_solution(expr* l, expr* r, dependency* dep); + bool is_unit_nth(expr* a) const; bool is_nth(expr* a) const; bool is_nth(expr* a, expr*& e1, expr*& e2) const; bool is_tail(expr* a, expr*& s, unsigned& idx) const; @@ -521,6 +554,7 @@ namespace smt { literal mk_seq_eq(expr* a, expr* b); void tightest_prefix(expr* s, expr* x); expr_ref mk_sub(expr* a, expr* b); + expr_ref mk_add(expr* a, expr* b); enode* ensure_enode(expr* a); dependency* mk_join(dependency* deps, literal lit); @@ -530,11 +564,13 @@ namespace smt { // arithmetic integration bool get_num_value(expr* s, rational& val) const; bool lower_bound(expr* s, rational& lo) const; + bool lower_bound2(expr* s, rational& lo); bool upper_bound(expr* s, rational& hi) const; bool get_length(expr* s, rational& val) const; void mk_decompose(expr* e, expr_ref& head, expr_ref& tail); - expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = nullptr, expr* e3 = nullptr, sort* range = nullptr); + expr* coalesce_chars(expr* const& str); + expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = nullptr, expr* e3 = nullptr, expr* e4 = nullptr, sort* range = nullptr); bool is_skolem(symbol const& s, expr* e) const; void set_incomplete(app* term); @@ -586,7 +622,7 @@ namespace smt { void display_deps(std::ostream& out, literal_vector const& lits, enode_pair_vector const& eqs) const; void display_nc(std::ostream& out, nc const& nc) const; public: - theory_seq(ast_manager& m); + theory_seq(ast_manager& m, theory_seq_params const & params); ~theory_seq() override; // model building diff --git a/src/solver/parallel_tactic.cpp b/src/solver/parallel_tactic.cpp index 451a62064..142ba4bb8 100644 --- a/src/solver/parallel_tactic.cpp +++ b/src/solver/parallel_tactic.cpp @@ -635,7 +635,7 @@ private: t.join(); m_manager.limit().reset_cancel(); if (m_exn_code == -1) - throw default_exception(m_exn_msg); + throw default_exception(std::move(m_exn_msg)); if (m_exn_code != 0) throw z3_error(m_exn_code); if (!m_models.empty()) { diff --git a/src/solver/smt_logics.cpp b/src/solver/smt_logics.cpp index 7ed2b0445..59a9a1562 100644 --- a/src/solver/smt_logics.cpp +++ b/src/solver/smt_logics.cpp @@ -150,7 +150,7 @@ bool smt_logics::logic_has_horn(symbol const& s) { } bool smt_logics::logic_has_pb(symbol const& s) { - return s == "QF_FD" || s == "ALL"; + return s == "QF_FD" || s == "ALL" || logic_has_horn(s); } bool smt_logics::logic_has_datatype(symbol const& s) { diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index 9dacfb5ce..598bb6c02 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -101,9 +101,11 @@ public: proof * get_proof() override { scoped_watch _t_(m_pool.m_proof_watch); if (!m_proof.get()) { - elim_aux_assertions pc(m_pred); m_proof = m_base->get_proof(); - pc(m, m_proof, m_proof); + if (m_proof) { + elim_aux_assertions pc(m_pred); + pc(m, m_proof, m_proof); + } } return m_proof; } diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index 94509e3f6..135d402e6 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -19,6 +19,7 @@ Author: Notes: --*/ +#include "solver/tactic2solver.h" #include "solver/solver_na2as.h" #include "tactic/tactic.h" #include "ast/ast_translation.h" @@ -31,6 +32,8 @@ Notes: option for applications trying to solve many easy queries that a similar to each other. */ + +namespace { class tactic2solver : public solver_na2as { expr_ref_vector m_assertions; unsigned_vector m_scopes; @@ -258,6 +261,7 @@ unsigned tactic2solver::get_num_assertions() const { expr * tactic2solver::get_assertion(unsigned idx) const { return m_assertions.get(idx); } +} solver * mk_tactic2solver(ast_manager & m, @@ -270,6 +274,7 @@ solver * mk_tactic2solver(ast_manager & m, return alloc(tactic2solver, m, t, p, produce_proofs, produce_models, produce_unsat_cores, logic); } +namespace { class tactic2solver_factory : public solver_factory { ref m_tactic; public: @@ -284,24 +289,23 @@ public: }; class tactic_factory2solver_factory : public solver_factory { - scoped_ptr m_factory; + tactic_factory m_factory; public: - tactic_factory2solver_factory(tactic_factory * f):m_factory(f) { + tactic_factory2solver_factory(tactic_factory f):m_factory(f) { } - ~tactic_factory2solver_factory() override {} - solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) override { tactic * t = (*m_factory)(m, p); return mk_tactic2solver(m, t, p, proofs_enabled, models_enabled, unsat_core_enabled, logic); } }; +} solver_factory * mk_tactic2solver_factory(tactic * t) { return alloc(tactic2solver_factory, t); } -solver_factory * mk_tactic_factory2solver_factory(tactic_factory * f) { +solver_factory * mk_tactic_factory2solver_factory(tactic_factory f) { return alloc(tactic_factory2solver_factory, f); } diff --git a/src/solver/tactic2solver.h b/src/solver/tactic2solver.h index ddab66dc5..d9fffba24 100644 --- a/src/solver/tactic2solver.h +++ b/src/solver/tactic2solver.h @@ -23,12 +23,14 @@ Notes: #define TACTIC2SOLVER_H_ #include "util/params.h" + class ast_manager; class tactic; -class tactic_factory; class solver; class solver_factory; +typedef tactic* (*tactic_factory)(ast_manager&, const params_ref&); + solver * mk_tactic2solver(ast_manager & m, tactic * t = nullptr, params_ref const & p = params_ref(), @@ -39,6 +41,6 @@ solver * mk_tactic2solver(ast_manager & m, solver_factory * mk_tactic2solver_factory(tactic * t); -solver_factory * mk_tactic_factory2solver_factory(tactic_factory * f); +solver_factory * mk_tactic_factory2solver_factory(tactic_factory f); #endif diff --git a/src/tactic/arith/pb2bv_tactic.cpp b/src/tactic/arith/pb2bv_tactic.cpp index 836808de1..f36e3a6db 100644 --- a/src/tactic/arith/pb2bv_tactic.cpp +++ b/src/tactic/arith/pb2bv_tactic.cpp @@ -968,7 +968,7 @@ private: void throw_tactic(expr* e) { std::stringstream strm; strm << "goal is in a fragment unsupported by pb2bv. Offending expression: " << mk_pp(e, m); - throw tactic_exception(strm.str().c_str()); + throw tactic_exception(strm.str()); } }; diff --git a/src/tactic/bv/elim_small_bv_tactic.cpp b/src/tactic/bv/elim_small_bv_tactic.cpp index 9959780b7..0b5b98308 100644 --- a/src/tactic/bv/elim_small_bv_tactic.cpp +++ b/src/tactic/bv/elim_small_bv_tactic.cpp @@ -101,7 +101,7 @@ class elim_small_bv_tactic : public tactic { }); var_subst vsbst(m); - vsbst(e, substitution.size(), substitution.c_ptr(), res); + res = vsbst(e, substitution.size(), substitution.c_ptr()); SASSERT(is_well_sorted(m, res)); proof_ref pr(m); @@ -123,6 +123,9 @@ class elim_small_bv_tactic : public tactic { expr * const * new_no_patterns, expr_ref & result, proof_ref & result_pr) { + if (is_lambda(q)) { + return false; + } TRACE("elim_small_bv", tout << "reduce_quantifier " << mk_ismt2_pp(q, m) << std::endl; ); unsigned long long num_steps = 0; unsigned curr_sz = m_bindings.size(); @@ -158,8 +161,8 @@ class elim_small_bv_tactic : public tactic { for (unsigned k = 0; k < new_bodies.size(); k++) tout << mk_ismt2_pp(new_bodies[k].get(), m) << std::endl; ); - body = q->is_forall() ? m.mk_and(new_bodies.size(), new_bodies.c_ptr()) : - m.mk_or(new_bodies.size(), new_bodies.c_ptr()); + body = is_forall(q) ? m.mk_and(new_bodies.size(), new_bodies.c_ptr()) : + m.mk_or(new_bodies.size(), new_bodies.c_ptr()); SASSERT(is_well_sorted(m, body)); proof_ref pr(m); @@ -171,7 +174,7 @@ class elim_small_bv_tactic : public tactic { quantifier_ref new_q(m); new_q = m.update_quantifier(q, body); unused_vars_eliminator el(m, m_params); - el(new_q, result); + result = el(new_q); TRACE("elim_small_bv", tout << "elimination result: " << mk_ismt2_pp(result, m) << std::endl; ); diff --git a/src/tactic/core/CMakeLists.txt b/src/tactic/core/CMakeLists.txt index 16778d8dd..44284b625 100644 --- a/src/tactic/core/CMakeLists.txt +++ b/src/tactic/core/CMakeLists.txt @@ -16,6 +16,7 @@ z3_add_component(core_tactics pb_preprocess_tactic.cpp propagate_values_tactic.cpp reduce_args_tactic.cpp + reduce_invertible_tactic.cpp simplify_tactic.cpp solve_eqs_tactic.cpp split_clause_tactic.cpp @@ -42,6 +43,7 @@ z3_add_component(core_tactics pb_preprocess_tactic.h propagate_values_tactic.h reduce_args_tactic.h + reduce_invertible_tactic.h simplify_tactic.h solve_eqs_tactic.h split_clause_tactic.h diff --git a/src/tactic/core/collect_occs.cpp b/src/tactic/core/collect_occs.cpp index 27de476f7..97bd969e2 100644 --- a/src/tactic/core/collect_occs.cpp +++ b/src/tactic/core/collect_occs.cpp @@ -85,12 +85,9 @@ void collect_occs::operator()(goal const & g, obj_hashtable & r) { process(t); } - ptr_vector::const_iterator it = m_vars.begin(); - ptr_vector::const_iterator end = m_vars.end(); - for (; it != end; ++it) { - if (m_more_than_once.is_marked(*it)) - continue; - r.insert(*it); + for (expr* e : m_vars) { + if (!m_more_than_once.is_marked(e)) + r.insert(e); } m_visited.reset(); m_more_than_once.reset(); diff --git a/src/tactic/core/collect_statistics_tactic.cpp b/src/tactic/core/collect_statistics_tactic.cpp index 939541957..b3c6d0e24 100644 --- a/src/tactic/core/collect_statistics_tactic.cpp +++ b/src/tactic/core/collect_statistics_tactic.cpp @@ -109,10 +109,17 @@ protected: m_stats["quantifiers"]++; SASSERT(is_app(q->get_expr())); app * body = to_app(q->get_expr()); - if (q->is_forall()) + switch (q->get_kind()) { + case forall_k: m_stats["forall-variables"] += q->get_num_decls(); - else + break; + case exists_k: m_stats["exists-variables"] += q->get_num_decls(); + break; + case lambda_k: + m_stats["lambda-variables"] += q->get_num_decls(); + break; + } m_stats["patterns"] += q->get_num_patterns(); m_stats["no-patterns"] += q->get_num_no_patterns(); m_qdepth++; diff --git a/src/tactic/core/distribute_forall_tactic.cpp b/src/tactic/core/distribute_forall_tactic.cpp index 98c942432..c2688f741 100644 --- a/src/tactic/core/distribute_forall_tactic.cpp +++ b/src/tactic/core/distribute_forall_tactic.cpp @@ -31,7 +31,7 @@ class distribute_forall_tactic : public tactic { expr_ref & result, proof_ref & result_pr) { - if (!old_q->is_forall()) { + if (!is_forall(old_q)) { return false; } @@ -49,8 +49,7 @@ class distribute_forall_tactic : public tactic { expr * not_arg = m.mk_not(arg); quantifier_ref tmp_q(m); tmp_q = m.update_quantifier(old_q, not_arg); - expr_ref new_q(m); - elim_unused_vars(m, tmp_q, params_ref(), new_q); + expr_ref new_q = elim_unused_vars(m, tmp_q, params_ref()); new_args.push_back(new_q); } result = m.mk_and(new_args.size(), new_args.c_ptr()); @@ -69,8 +68,7 @@ class distribute_forall_tactic : public tactic { expr * arg = to_app(new_body)->get_arg(i); quantifier_ref tmp_q(m); tmp_q = m.update_quantifier(old_q, arg); - expr_ref new_q(m); - elim_unused_vars(m, tmp_q, params_ref(), new_q); + expr_ref new_q = elim_unused_vars(m, tmp_q, params_ref()); new_args.push_back(new_q); } result = m.mk_and(new_args.size(), new_args.c_ptr()); diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 27a5bdc94..87a480bdb 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -337,12 +337,14 @@ expr_ref dom_simplify_tactic::simplify_and_or(bool is_and, app * e) { } args.reverse(); } + pop(scope_level() - old_lvl); r = is_and ? mk_and(args) : mk_or(args); return r; } + bool dom_simplify_tactic::init(goal& g) { expr_ref_vector args(m); unsigned sz = g.size(); diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index eb3500729..caca89afc 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -5,6 +5,7 @@ Module Name: dom_simplify_tactic.cpp + Abstract: Dominator-based context simplifer. diff --git a/src/tactic/core/injectivity_tactic.cpp b/src/tactic/core/injectivity_tactic.cpp index 64947eca0..69e5b4cee 100644 --- a/src/tactic/core/injectivity_tactic.cpp +++ b/src/tactic/core/injectivity_tactic.cpp @@ -79,11 +79,11 @@ class injectivity_tactic : public tactic { ast_manager & m() const { return m_manager; } bool is_axiom(expr* n, func_decl* &f, func_decl* &g) { - if (!is_quantifier(n)) + if (!is_forall(n)) return false; quantifier* const q = to_quantifier(n); - if (!q->is_forall() || q->get_num_decls() != 1) + if (q->get_num_decls() != 1) return false; const expr * const body = q->get_expr(); diff --git a/src/tactic/core/reduce_invertible_tactic.cpp b/src/tactic/core/reduce_invertible_tactic.cpp new file mode 100644 index 000000000..3420a3f99 --- /dev/null +++ b/src/tactic/core/reduce_invertible_tactic.cpp @@ -0,0 +1,504 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + reduce_invertible_tactic.cpp + +Abstract: + + Reduce invertible variables. + +Author: + + Nuno Lopes (nlopes) 2018-6-30 + Nikolaj Bjorner (nbjorner) + +Notes: + + 1. Walk through top-level uninterpreted constants. + +--*/ + +#include "util/cooperate.h" +#include "ast/bv_decl_plugin.h" +#include "ast/arith_decl_plugin.h" +#include "ast/ast_pp.h" +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/rewriter/rewriter_def.h" +#include "tactic/tactic.h" +#include "tactic/core/reduce_invertible_tactic.h" +#include "tactic/core/collect_occs.h" +#include "tactic/generic_model_converter.h" +#include + +namespace { +class reduce_invertible_tactic : public tactic { + ast_manager& m; + bv_util m_bv; + arith_util m_arith; + +public: + reduce_invertible_tactic(ast_manager & m): + m(m), + m_bv(m), + m_arith(m) + {} + + ~reduce_invertible_tactic() override { } + + tactic * translate(ast_manager & m) override { + return alloc(reduce_invertible_tactic, m); + } + + void operator()(goal_ref const & g, goal_ref_buffer & result) override { + TRACE("reduce_invertible", g->display(tout);); + tactic_report report("reduce-invertible", *g); + m_inverted.reset(); + m_parents.reset(); + collect_parents(g); + collect_occs occs; + obj_hashtable vars; + generic_model_converter_ref mc; + occs(*g, vars); + expr_safe_replace sub(m); + expr_ref new_v(m); + expr * p; + for (expr* v : vars) { + if (is_invertible(v, p, new_v, &mc)) { + mark_inverted(p); + sub.insert(p, new_v); + } + } + reduce_q_rw rw(*this); + unsigned sz = g->size(); + for (unsigned idx = 0; idx < sz; idx++) { + checkpoint(); + expr* f = g->form(idx); + expr_ref f_new(m); + sub(f, f_new); + rw(f_new, f_new); + if (f == f_new) continue; + proof_ref new_pr(m); + if (g->proofs_enabled()) { + proof * pr = g->pr(idx); + new_pr = m.mk_modus_ponens(pr, new_pr); + } + g->update(idx, f_new, new_pr, g->dep(idx)); + } + if (mc) g->add(mc.get()); + result.push_back(g.get()); + g->inc_depth(); + } + + void cleanup() override {} + +private: + void checkpoint() { + if (m.canceled()) + throw tactic_exception(m.limit().get_cancel_msg()); + cooperate("reduce-invertible"); + } + + expr_mark m_inverted; + void mark_inverted(expr *p) { + ptr_buffer todo; + todo.push_back(p); + while (!todo.empty()) { + p = todo.back(); + todo.pop_back(); + if (!m_inverted.is_marked(p)) { + m_inverted.mark(p, true); + if (is_app(p)) { + for (expr* arg : *to_app(p)) { + todo.push_back(arg); + } + } + else if (is_quantifier(p)) { + todo.push_back(to_quantifier(p)->get_expr()); + } + } + } + } + + // store one parent of expression, or null if multiple + struct parents { + parents(): m_p(0) {} + uintptr_t m_p; + + void set(expr * e) { + SASSERT((uintptr_t)e != 1); + if (!m_p) m_p = (uintptr_t)e; + else m_p = 1; + } + + expr * get() const { + return m_p == 1 ? nullptr : (expr*)m_p; + } + }; + svector m_parents; + struct parent_collector { + reduce_invertible_tactic& c; + parent_collector(reduce_invertible_tactic& c):c(c) {} + void operator()(app* n) { + for (expr* arg : *n) { + c.m_parents.reserve(arg->get_id() + 1); + c.m_parents[arg->get_id()].set(n); + } + } + + void operator()(var* v) { + c.m_parents.reserve(v->get_id() + 1); + } + + void operator()(quantifier* q) {} + }; + + void collect_parents(goal_ref const& g) { + parent_collector proc(*this); + expr_fast_mark1 visited; + unsigned sz = g->size(); + for (unsigned i = 0; i < sz; i++) { + checkpoint(); + quick_for_each_expr(proc, visited, g->form(i)); + } + } + + void ensure_mc(generic_model_converter_ref* mc) { + if (mc && !(*mc)) *mc = alloc(generic_model_converter, m, "reduce-invertible"); + } + + bool is_full_domain_var(expr* v, rational& model) { + auto f = is_app(v) ? to_app(v)->get_decl() : nullptr; + if (!f || f->get_family_id() != m_bv.get_family_id() || f->get_arity() == 0) + return false; + + switch (f->get_decl_kind()) { + case OP_BADD: + case OP_BSUB: + case OP_BSHL: + case OP_BASHR: + case OP_BLSHR: + case OP_BOR: + model = rational::zero(); + return true; + + case OP_BAND: + model = rational::power_of_two(m_bv.get_bv_size(v)) - rational::one(); + return true; + + case OP_BMUL: + case OP_BSDIV: + case OP_BSDIV0: + case OP_BSDIV_I: + case OP_BUDIV: + case OP_BUDIV0: + case OP_BUDIV_I: + model = rational::one(); + return true; + + default: + return false; + } + } + + bool rewrite_unconstr(expr* v, expr_ref& new_v, generic_model_converter_ref* mc, unsigned max_var) { + rational mdl; + if (!is_full_domain_var(v, mdl)) + return false; + + rational r; + app* a = to_app(v); + expr* fst_arg = a->get_arg(0); + bool fst_is_var = is_var(fst_arg); + + for (unsigned i = 0, n = a->get_num_args(); i != n; ++i) { + auto arg = a->get_arg(i); + if (!m_parents[arg->get_id()].get() || is_var(arg) != fst_is_var) + return false; + + if (fst_is_var && to_var(arg)->get_idx() >= max_var) + return false; + + if (m_bv.is_numeral(arg, r) && r != mdl) + return false; + } + + if (mc) { + ensure_mc(mc); + expr_ref num(m_bv.mk_numeral(mdl, get_sort(fst_arg)), m); + for (unsigned i = 1, n = a->get_num_args(); i != n; ++i) { + (*mc)->add(a->get_arg(i), num); + } + } + new_v = fst_arg; + return true; + } + + // TBD: could be made to be recursive, by walking multiple layers of parents. + + bool is_invertible(expr* v, expr*& p, expr_ref& new_v, generic_model_converter_ref* mc, unsigned max_var = 0) { + p = m_parents[v->get_id()].get(); + if (!p) return false; + if (m_inverted.is_marked(p)) return false; + if (mc && !is_ground(p)) return false; + + if (m_bv.is_bv_xor(p) || + m_bv.is_bv_not(p) || + m_bv.is_bv_neg(p)) { + if (mc) { + ensure_mc(mc); + (*mc)->add(v, p); + } + new_v = v; + return true; + } + + if (rewrite_unconstr(p, new_v, mc, max_var)) + return true; + + if (m_bv.is_bv_add(p)) { + if (mc) { + ensure_mc(mc); + // if we solve for v' := v + t + // then the value for v is v' - t + expr_ref def(v, m); + for (expr* arg : *to_app(p)) { + if (arg != v) def = m_bv.mk_bv_sub(def, arg); + } + (*mc)->add(v, def); + } + new_v = v; + return true; + } + + if (m_bv.is_bv_mul(p)) { + expr_ref rest(m); + for (expr* arg : *to_app(p)) { + if (arg != v) { + if (rest) + rest = m_bv.mk_bv_mul(rest, arg); + else + rest = arg; + } + } + if (!rest) return false; + + // create case split on + // divisbility of 2 + // v * t -> + // if t = 0, set v' := 0 and the solution for v is 0. + // otherwise, + // let i be the position of the least bit of t set to 1 + // then extract[sz-1:i](v) ++ zero[i-1:0] is the invertible of v * t + // thus + // extract[i+1:0](t) = 1 ++ zero[i-1:0] -> extract[sz-1:i](v) ++ zero[i-1:0] + // to reproduce the original v from t + // solve for v*t = extract[sz-1:i](v') ++ zero[i-1:0] + // using values for t and v' + // thus + // + // udiv(extract[sz-1:i](v') ++ zero[i-1:0], t) + // + // TBD: this argument is flawed. Unit test breaks with either + // the above or udiv(v, t) + + unsigned sz = m_bv.get_bv_size(p); + expr_ref bit1(m_bv.mk_numeral(1, 1), m); + new_v = m_bv.mk_numeral(0, sz); + for (unsigned i = sz; i-- > 0; ) { + expr_ref extr_i(m_bv.mk_extract(i, i, rest), m); + expr_ref cond(m.mk_eq(extr_i, bit1), m); + expr_ref part(v, m); + if (i > 0) { + part = m_bv.mk_concat(m_bv.mk_extract(sz-1, i, v), m_bv.mk_numeral(0, i)); + } + new_v = m.mk_ite(cond, part, new_v); + } + if (mc) { + ensure_mc(mc); + expr_ref div(m), def(m); + div = m.mk_app(m_bv.get_fid(), OP_BUDIV_I, v, rest); + def = m_bv.mk_numeral(0, sz); + def = m.mk_ite(m.mk_eq(def, rest), def, div); + (*mc)->add(v, def); + TRACE("invertible_tactic", tout << def << "\n";); + } + return true; + } + if (m_bv.is_bv_sub(p)) { + // TBD + } + if (m_bv.is_bv_udivi(p)) { + // TBD + } + // sdivi, sremi, uremi, smodi + // TBD + + if (m_arith.is_mul(p) && m_arith.is_real(p)) { + expr_ref rest(m); + for (expr* arg : *to_app(p)) { + if (arg != v) { + if (rest) + rest = m_arith.mk_mul(rest, arg); + else + rest = arg; + } + } + if (!rest) return false; + expr_ref zero(m_arith.mk_real(0), m); + new_v = m.mk_ite(m.mk_eq(zero, rest), zero, v); + if (mc) { + ensure_mc(mc); + expr_ref def(m_arith.mk_div(v, rest), m); + (*mc)->add(v, def); + } + return true; + } + + + expr* e1 = nullptr, *e2 = nullptr; + + // t / v -> if t = 0 then 0 else v + // inverse: t = 0 then 1 else v / t + if (m_arith.is_div(p, e1, e2) && e2 == v) { + expr_ref zero(m_arith.mk_real(0), m); + new_v = m.mk_ite(m.mk_eq(zero, e1), zero, v); + if (mc) { + ensure_mc(mc); + expr_ref def(m.mk_ite(m.mk_eq(zero, e1), m_arith.mk_real(1), m_arith.mk_div(e1, v)), m); + (*mc)->add(v, def); + } + return true; + } + + // v / t unless t != 0 + if (m_arith.is_div(p, e1, e2) && e1 == v) { + return false; + } + + if (m.is_eq(p, e1, e2)) { + if (mc && has_diagonal(e1)) { + ensure_mc(mc); + new_v = m.mk_fresh_const("eq", m.mk_bool_sort()); + SASSERT(v == e1 || v == e2); + expr* other = (v == e1) ? e2 : e1; + (*mc)->hide(new_v); + (*mc)->add(v, m.mk_ite(new_v, other, mk_diagonal(other))); + return true; + } + else if (mc) { + // diagonal functions for other types depend on theory. + return false; + } + else if (is_var(v) && is_non_singleton_sort(m.get_sort(v))) { + new_v = m.mk_var(to_var(v)->get_idx(), m.mk_bool_sort()); + return true; + } + } + return false; + } + + bool has_diagonal(expr* e) { + return + m_bv.is_bv(e) || + m.is_bool(e) || + m_arith.is_int_real(e); + } + + expr * mk_diagonal(expr* e) { + if (m_bv.is_bv(e)) return m_bv.mk_bv_neg(e); + if (m.is_bool(e)) return m.mk_not(e); + if (m_arith.is_int(e)) return m_arith.mk_add(m_arith.mk_int(1), e); + if (m_arith.is_real(e)) return m_arith.mk_add(m_arith.mk_real(1), e); + UNREACHABLE(); + return e; + } + + bool is_non_singleton_sort(sort* s) { + if (m.is_uninterp(s)) return false; + sort_size sz = s->get_num_elements(); + if (sz.is_finite() && sz.size() == 1) return false; + return true; + } + + struct reduce_q_rw_cfg : public default_rewriter_cfg { + ast_manager& m; + reduce_invertible_tactic& t; + + reduce_q_rw_cfg(reduce_invertible_tactic& t): m(t.m), t(t) {} + + bool reduce_quantifier(quantifier * old_q, + expr * new_body, + expr * const * new_patterns, + expr * const * new_no_patterns, + expr_ref & result, + proof_ref & result_pr) { + if (is_lambda(old_q)) return false; + if (has_quantifiers(new_body)) return false; + ref_buffer vars(m); + ptr_buffer new_sorts; + unsigned n = old_q->get_num_decls(); + for (unsigned i = 0; i < n; ++i) { + sort* srt = old_q->get_decl_sort(i); + vars.push_back(m.mk_var(n - i - 1, srt)); + new_sorts.push_back(srt); + } + // for each variable, collect parents, + // ensure they are in unique location and not under other quantifiers. + // if they are invertible, then produce inverting expression. + // + expr_safe_replace sub(m); + t.m_parents.reset(); + t.m_inverted.reset(); + expr_ref new_v(m); + expr * p; + + { + parent_collector proc(t); + expr_fast_mark1 visited; + quick_for_each_expr(proc, visited, new_body); + } + bool has_new_var = false; + for (unsigned i = 0; i < vars.size(); ++i) { + var* v = vars[i]; + if (!occurs_under_nested_q(v, new_body) && t.is_invertible(v, p, new_v, nullptr, vars.size())) { + t.mark_inverted(p); + sub.insert(p, new_v); + new_sorts[i] = m.get_sort(new_v); + has_new_var |= new_v != v; + } + } + if (has_new_var) { + sub(new_body, result); + result = m.mk_quantifier(old_q->get_kind(), new_sorts.size(), new_sorts.c_ptr(), old_q->get_decl_names(), result); + result_pr = nullptr; + return true; + } + if (!sub.empty()) { + sub(new_body, result); + result = m.update_quantifier(old_q, old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns, result); + result_pr = nullptr; + return true; + } + return false; + } + + bool occurs_under_nested_q(var* v, expr* body) { + return has_quantifiers(body); + } + }; + + struct reduce_q_rw : rewriter_tpl { + reduce_q_rw_cfg m_cfg; + public: + reduce_q_rw(reduce_invertible_tactic& t): + rewriter_tpl(t.m, false, m_cfg), + m_cfg(t) {} + }; +}; +} + +tactic * mk_reduce_invertible_tactic(ast_manager & m, params_ref const &) { + return alloc(reduce_invertible_tactic, m); +} diff --git a/src/tactic/core/reduce_invertible_tactic.h b/src/tactic/core/reduce_invertible_tactic.h new file mode 100644 index 000000000..d40bf8a59 --- /dev/null +++ b/src/tactic/core/reduce_invertible_tactic.h @@ -0,0 +1,32 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + reduce_invertible_tactic.h + +Abstract: + + Reduce invertible variables. + +Author: + + Nuno Lopes (nlopes) 2018-6-30 + Nikolaj Bjorner (nbjorner) + +Notes: + +--*/ + +#pragma once +#include "util/params.h" + +class tactic; +class ast_manager; + +tactic * mk_reduce_invertible_tactic(ast_manager & m, params_ref const & p = params_ref()); + +/* + ADD_TACTIC("reduce-invertible", "reduce invertible variable occurrences.", "mk_reduce_invertible_tactic(m, p)") +*/ + diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index b29a80927..929168ed0 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -474,7 +474,6 @@ class tseitin_cnf_tactic : public tactic { bool sign = m.is_not(_b, _b); if (!m.is_or(_b)) return NO; app* b = to_app(_b); - unsigned num = b->get_num_args(); if (first) { bool visited = true; visit(a, visited); diff --git a/src/tactic/extension_model_converter.h b/src/tactic/extension_model_converter.h deleted file mode 100644 index 0816fdd8b..000000000 --- a/src/tactic/extension_model_converter.h +++ /dev/null @@ -1,49 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - extension_model_converter.h - -Abstract: - - Model converter that introduces new interpretations into a model. - It used to be called elim_var_model_converter - -Author: - - Leonardo (leonardo) 2011-10-21 - -Notes: - ---*/ -#ifndef EXTENSION_MODEL_CONVERTER_H_ -#define EXTENSION_MODEL_CONVERTER_H_ - -#include "ast/ast.h" -#include "tactic/model_converter.h" - - -class extension_model_converter : public model_converter { - func_decl_ref_vector m_vars; - expr_ref_vector m_defs; -public: - extension_model_converter(ast_manager & m):m_vars(m), m_defs(m) { - } - - ~extension_model_converter() override; - - ast_manager & m() const { return m_vars.get_manager(); } - - void operator()(model_ref & md) override; - - void display(std::ostream & out) override; - - // register a variable that was eliminated - void insert(func_decl * v, expr * def); - - model_converter * translate(ast_translation & translator) override; -}; - - -#endif diff --git a/src/tactic/generic_model_converter.cpp b/src/tactic/generic_model_converter.cpp index 0790c962a..a64a3a79d 100644 --- a/src/tactic/generic_model_converter.cpp +++ b/src/tactic/generic_model_converter.cpp @@ -33,10 +33,9 @@ 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); + m_first_idx.insert_if_not_there(d, m_entries.size()); + m_entries.push_back(entry(d, e, m, ADD)); } void generic_model_converter::operator()(model_ref & md) { @@ -250,7 +249,6 @@ void generic_model_converter::get_units(obj_map& units) { */ 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); diff --git a/src/tactic/generic_model_converter.h b/src/tactic/generic_model_converter.h index a190b8525..c67dd5eff 100644 --- a/src/tactic/generic_model_converter.h +++ b/src/tactic/generic_model_converter.h @@ -30,13 +30,6 @@ class generic_model_converter : public model_converter { 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; diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index d5d2ee558..f00f0e77e 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -716,3 +716,26 @@ bool is_equal(goal const & s1, goal const & s2) { SASSERT(num1 >= num2); return num1 == num2; } + +bool goal::is_cnf() const { + for (unsigned i = 0; i < size(); i++) { + expr * f = form(i); + if (m_manager.is_or(f)) { + for (expr* l : *to_app(f)) { + if (!is_literal(f)) return false; + } + } + if (!is_literal(f)) return false; + } + return true; +} + +bool goal::is_literal(expr* f) const { + m_manager.is_not(f, f); + if (!is_app(f)) return false; + if (to_app(f)->get_family_id() == m_manager.get_basic_family_id()) { + for (expr* arg : *to_app(f)) + if (m_manager.is_bool(arg)) return false; + } + return true; +} diff --git a/src/tactic/goal.h b/src/tactic/goal.h index 2d91bc67f..4125fab99 100644 --- a/src/tactic/goal.h +++ b/src/tactic/goal.h @@ -77,7 +77,7 @@ protected: unsigned get_not_idx(expr * f) const; void shrink(unsigned j); void reset_core(); - + bool is_literal(expr* f) const; public: goal(ast_manager & m, bool models_enabled = true, bool core_enabled = false); @@ -159,6 +159,8 @@ public: void set(model_converter* m) { m_mc = m; } void set(proof_converter* p) { m_pc = p; } + bool is_cnf() const; + 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 eeb2967f2..fa3e82e64 100644 --- a/src/tactic/horn_subsume_model_converter.cpp +++ b/src/tactic/horn_subsume_model_converter.cpp @@ -96,8 +96,7 @@ bool horn_subsume_model_converter::mk_horn( // substitute variables directly. if (!subst.empty()) { - expr_ref tmp(body_expr); - vs(tmp, subst.size(), subst.c_ptr(), body_expr); + body_expr = vs(body_expr, subst.size(), subst.c_ptr()); } if (fv.empty()) { @@ -123,7 +122,7 @@ bool horn_subsume_model_converter::mk_horn( // formula is closed. DEBUG_CODE(expr_free_vars fv; fv(clause); SASSERT(fv.empty());); - while (is_quantifier(clause) && to_quantifier(clause)->is_forall()) { + while (is_quantifier(clause) && to_quantifier(clause)->get_kind() == forall_k) { quantifier* q = to_quantifier(clause); clause = q->get_expr(); } diff --git a/src/tactic/sine_filter.cpp b/src/tactic/sine_filter.cpp index 647791784..b1db12964 100644 --- a/src/tactic/sine_filter.cpp +++ b/src/tactic/sine_filter.cpp @@ -177,7 +177,7 @@ private: } else if (is_quantifier(curr.first)) { quantifier *q = to_quantifier(curr.first); - if (q->is_forall()) { + if (is_forall(q)) { if (q->has_patterns()) { ptr_vector next_consts; if (quantifier_matches(q, consts, next_consts)) { @@ -199,7 +199,7 @@ private: stack.push_back(work_item(q->get_expr(), curr.second)); } } - else if (q->is_exists()) { + else if (is_exists(q)) { stack.push_back(work_item(q->get_expr(), curr.second)); } } diff --git a/src/tactic/smtlogics/qflia_tactic.cpp b/src/tactic/smtlogics/qflia_tactic.cpp index 541a81682..46b766bd4 100644 --- a/src/tactic/smtlogics/qflia_tactic.cpp +++ b/src/tactic/smtlogics/qflia_tactic.cpp @@ -211,17 +211,23 @@ tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p) { params_ref no_cut_p; no_cut_p.set_uint("arith.branch_cut_ratio", 10000000); - - + + + tactic * st = using_params(and_then(preamble_st, +#if 0 + mk_smt_tactic()), +#else or_else(mk_ilp_model_finder_tactic(m), mk_pb_tactic(m), and_then(fail_if_not(mk_is_quasi_pb_probe()), using_params(mk_lia2sat_tactic(m), quasi_pb_p), mk_fail_if_undecided_tactic()), mk_bounded_tactic(m), - mk_psmt_tactic(m, p))), + mk_smt_tactic())), +#endif main_p); + st->updt_params(p); return st; diff --git a/src/tactic/smtlogics/qfufbv_tactic.cpp b/src/tactic/smtlogics/qfufbv_tactic.cpp index 3bd28ad6d..a4e169856 100644 --- a/src/tactic/smtlogics/qfufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfufbv_tactic.cpp @@ -62,7 +62,7 @@ public: TRACE("qfufbv_ackr_tactic", g->display(tout << "goal:\n");); // running implementation - expr_ref_vector flas(m); + ptr_vector flas; const unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) flas.push_back(g->form(i)); scoped_ptr uffree_solver = setup_sat(); diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index 4fa9ca43f..3b3c2444f 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -206,7 +206,7 @@ void fail_if_proof_generation(char const * tactic_name, goal_ref const & in) { if (in->proofs_enabled()) { std::string msg = tactic_name; msg += " does not support proof production"; - throw tactic_exception(msg.c_str()); + throw tactic_exception(std::move(msg)); } } @@ -214,7 +214,7 @@ void fail_if_unsat_core_generation(char const * tactic_name, goal_ref const & in if (in->unsat_core_enabled()) { std::string msg = tactic_name; msg += " does not support unsat core production"; - throw tactic_exception(msg.c_str()); + throw tactic_exception(std::move(msg)); } } @@ -222,6 +222,6 @@ void fail_if_model_generation(char const * tactic_name, goal_ref const & in) { if (in->models_enabled()) { std::string msg = tactic_name; msg += " does not generate models"; - throw tactic_exception(msg.c_str()); + throw tactic_exception(std::move(msg)); } } diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index c9b5a23fd..92cc7315f 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -119,21 +119,6 @@ tactic * mk_fail_if_undecided_tactic(); tactic * mk_report_verbose_tactic(char const * msg, unsigned lvl); tactic * mk_trace_tactic(char const * tag); -class tactic_factory { -public: - virtual ~tactic_factory() {} - virtual tactic * operator()(ast_manager & m, params_ref const & p) = 0; -}; - -#define MK_TACTIC_FACTORY(NAME, CODE) \ -class NAME : public tactic_factory { \ -public: \ - virtual ~NAME() {} \ - virtual tactic * operator()(ast_manager & m, params_ref const & p) { CODE } \ -}; - -#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); 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); diff --git a/src/tactic/tactic_exception.h b/src/tactic/tactic_exception.h index bdf2636a9..0af6d66b1 100644 --- a/src/tactic/tactic_exception.h +++ b/src/tactic/tactic_exception.h @@ -26,7 +26,7 @@ class tactic_exception : public z3_exception { protected: std::string m_msg; public: - tactic_exception(char const * msg):m_msg(msg) {} + tactic_exception(std::string && msg) : m_msg(std::move(msg)) {} ~tactic_exception() override {} char const * msg() const override { return m_msg.c_str(); } }; diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index bb04a2be7..43087f50f 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -463,9 +463,9 @@ public: if (finished_id == UINT_MAX) { switch (ex_kind) { case ERROR_EX: throw z3_error(error_code); - case TACTIC_EX: throw tactic_exception(ex_msg.c_str()); + case TACTIC_EX: throw tactic_exception(std::move(ex_msg)); default: - throw default_exception(ex_msg.c_str()); + throw default_exception(std::move(ex_msg)); } } } @@ -660,9 +660,9 @@ public: if (failed) { switch (ex_kind) { case ERROR_EX: throw z3_error(error_code); - case TACTIC_EX: throw tactic_exception(ex_msg.c_str()); + case TACTIC_EX: throw tactic_exception(std::move(ex_msg)); default: - throw default_exception(ex_msg.c_str()); + throw default_exception(std::move(ex_msg)); } } diff --git a/src/tactic/ufbv/ufbv_rewriter.cpp b/src/tactic/ufbv/ufbv_rewriter.cpp index 875ef2edb..ee8a4605b 100644 --- a/src/tactic/ufbv/ufbv_rewriter.cpp +++ b/src/tactic/ufbv/ufbv_rewriter.cpp @@ -52,7 +52,7 @@ ufbv_rewriter::~ufbv_rewriter() { bool ufbv_rewriter::is_demodulator(expr * e, expr_ref & large, expr_ref & small) const { if (e->get_kind() == AST_QUANTIFIER) { quantifier * q = to_quantifier(e); - if (q->is_forall()) { + if (is_forall(q)) { expr * qe = q->get_expr(); if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) { app * eq = to_app(q->get_expr()); @@ -439,8 +439,7 @@ expr * ufbv_rewriter::rewrite(expr * n) { quantifier_ref q(m_manager); q = m_manager.update_quantifier(to_quantifier(actual), new_body); m_new_exprs.push_back(q); - expr_ref new_q(m_manager); - elim_unused_vars(m_manager, q, params_ref(), new_q); + expr_ref new_q = elim_unused_vars(m_manager, q, params_ref()); m_new_exprs.push_back(new_q); rewrite_cache(e, new_q, true); m_rewrite_todo.pop_back(); diff --git a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp index e9db976f9..9e41cee7c 100644 --- a/src/tactic/ufbv/ufbv_rewriter_tactic.cpp +++ b/src/tactic/ufbv/ufbv_rewriter_tactic.cpp @@ -21,74 +21,19 @@ Notes: #include "tactic/ufbv/ufbv_rewriter_tactic.h" class ufbv_rewriter_tactic : public tactic { - - struct imp { - ast_manager & m_manager; - - imp(ast_manager & m, params_ref const & p) : m_manager(m) { - updt_params(p); - } - - ast_manager & m() const { return m_manager; } - - void operator()(goal_ref const & g, - goal_ref_buffer & result) { - SASSERT(g->is_well_sorted()); - tactic_report report("ufbv-rewriter", *g); - fail_if_unsat_core_generation("ufbv-rewriter", g); - - bool produce_proofs = g->proofs_enabled(); - - ufbv_rewriter dem(m_manager); - - expr_ref_vector forms(m_manager), new_forms(m_manager); - proof_ref_vector proofs(m_manager), new_proofs(m_manager); - - unsigned size = g->size(); - for (unsigned i = 0; i < size; i++) { - forms.push_back(g->form(i)); - proofs.push_back(g->pr(i)); - } - - dem(forms.size(), forms.c_ptr(), proofs.c_ptr(), new_forms, new_proofs); - - g->reset(); - for (unsigned i = 0; i < new_forms.size(); i++) - g->assert_expr(new_forms.get(i), produce_proofs ? new_proofs.get(i) : nullptr, nullptr); - - // CMW: Remark: The demodulator could potentially - // remove all references to a variable. - - g->inc_depth(); - result.push_back(g.get()); - TRACE("ufbv-rewriter", g->display(tout);); - SASSERT(g->is_well_sorted()); - } - - void updt_params(params_ref const & p) { - } - }; - - imp * m_imp; - params_ref m_params; + ast_manager & m_manager; + params_ref m_params; public: ufbv_rewriter_tactic(ast_manager & m, params_ref const & p): - m_params(p) { - m_imp = alloc(imp, m, p); - } + m_manager(m), m_params(p) {} tactic * translate(ast_manager & m) override { return alloc(ufbv_rewriter_tactic, m, m_params); } - ~ufbv_rewriter_tactic() override { - dealloc(m_imp); - } - void updt_params(params_ref const & p) override { m_params = p; - m_imp->updt_params(p); } void collect_param_descrs(param_descrs & r) override { @@ -97,16 +42,40 @@ public: insert_produce_proofs(r); } - void operator()(goal_ref const & in, goal_ref_buffer & result) override { - (*m_imp)(in, result); + void operator()(goal_ref const & g, goal_ref_buffer & result) override { + SASSERT(g->is_well_sorted()); + tactic_report report("ufbv-rewriter", *g); + fail_if_unsat_core_generation("ufbv-rewriter", g); + + bool produce_proofs = g->proofs_enabled(); + + ufbv_rewriter dem(m_manager); + + expr_ref_vector forms(m_manager), new_forms(m_manager); + proof_ref_vector proofs(m_manager), new_proofs(m_manager); + + unsigned size = g->size(); + for (unsigned i = 0; i < size; i++) { + forms.push_back(g->form(i)); + proofs.push_back(g->pr(i)); + } + + dem(forms.size(), forms.c_ptr(), proofs.c_ptr(), new_forms, new_proofs); + + g->reset(); + for (unsigned i = 0; i < new_forms.size(); i++) + g->assert_expr(new_forms.get(i), produce_proofs ? new_proofs.get(i) : nullptr, nullptr); + + // CMW: Remark: The demodulator could potentially + // remove all references to a variable. + + g->inc_depth(); + result.push_back(g.get()); + TRACE("ufbv-rewriter", g->display(tout);); + SASSERT(g->is_well_sorted()); } - void cleanup() override { - ast_manager & m = m_imp->m(); - imp * d = alloc(imp, m, m_params); - std::swap(d, m_imp); - dealloc(d); - } + void cleanup() override {} }; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 7367a52ff..c2da596fa 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(fuzzing) +add_subdirectory(lp) ################################################################################ # z3-test executable ################################################################################ @@ -120,6 +121,7 @@ add_executable(test-z3 upolynomial.cpp var_subst.cpp vector.cpp + lp/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 index ea5a0bc34..423142557 100644 --- a/src/test/bdd.cpp +++ b/src/test/bdd.cpp @@ -30,8 +30,8 @@ namespace sat { 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)); + SASSERT((!(v0 && v1)) == (!v0 || !v1)); + SASSERT((!(v0 || v1)) == (!v0 && !v1)); } static void test3() { diff --git a/src/test/lp/CMakeLists.txt b/src/test/lp/CMakeLists.txt new file mode 100644 index 000000000..32d55bca8 --- /dev/null +++ b/src/test/lp/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(lp_tst +EXCLUDE_FROM_ALL +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/src/test/argument_parser.h b/src/test/lp/argument_parser.h similarity index 100% rename from src/test/argument_parser.h rename to src/test/lp/argument_parser.h diff --git a/src/test/lp/gomory_test.h b/src/test/lp/gomory_test.h new file mode 100644 index 000000000..03ad5b187 --- /dev/null +++ b/src/test/lp/gomory_test.h @@ -0,0 +1,236 @@ +namespace lp { +struct gomory_test { + gomory_test( + std::function name_function_p, + std::function get_value_p, + std::function at_low_p, + std::function at_upper_p, + std::function lower_bound_p, + std::function upper_bound_p, + std::function column_lower_bound_constraint_p, + std::function column_upper_bound_constraint_p + ) : + m_name_function(name_function_p), + get_value(get_value_p), + at_low(at_low_p), + at_upper(at_upper_p), + lower_bound(lower_bound_p), + upper_bound(upper_bound_p), + column_lower_bound_constraint(column_lower_bound_constraint_p), + column_upper_bound_constraint(column_upper_bound_constraint_p) + {} + + std::function m_name_function; + std::function get_value; + std::function at_low; + std::function at_upper; + std::function lower_bound; + std::function upper_bound; + std::function column_lower_bound_constraint; + std::function column_upper_bound_constraint; + + bool is_real(unsigned) { return false; } // todo: test real case + void print_row(std::ostream& out, vector> & row ) { + bool first = true; + for (const auto & it : row) { + auto val = it.first; + if (first) { + first = false; + } else { + if (numeric_traits::is_pos(val)) { + out << " + "; + } else { + out << " - "; + val = -val; + } + } + if (val == -numeric_traits::one()) + out << " - "; + else if (val != numeric_traits::one()) + out << T_to_string(val); + + out << m_name_function(it.second); + } + } + + void real_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, lar_term& pol, explanation & expl, const mpq& f_0, const mpq& one_minus_f_0) { + TRACE("gomory_cut_detail_real", tout << "real\n";); + mpq new_a; + if (at_low(x_j)) { + if (a.is_pos()) { + new_a = a / (1 - f_0); + } + else { + new_a = a / f_0; + new_a.neg(); + } + k.addmul(new_a, lower_bound(x_j).x); // is it a faster operation than + // k += lower_bound(x_j).x * new_a; + expl.push_justification(column_lower_bound_constraint(x_j), new_a); + } + else { + lp_assert(at_upper(x_j)); + if (a.is_pos()) { + new_a = a / f_0; + new_a.neg(); // the upper terms are inverted. + } + else { + new_a = a / (mpq(1) - f_0); + } + k.addmul(new_a, upper_bound(x_j).x); // k += upper_bound(x_j).x * new_a; + expl.push_justification(column_upper_bound_constraint(x_j), new_a); + } + TRACE("gomory_cut_detail_real", tout << a << "*v" << x_j << " k: " << k << "\n";); + pol.add_monomial(new_a, x_j); + } + + void int_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & k, lar_term & t, explanation& expl, mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0) { + lp_assert(is_int(x_j)); + lp_assert(!a.is_int()); + lp_assert(f_0 > zero_of_type() && f_0 < one_of_type()); + mpq f_j = int_solver::fractional_part(a); + TRACE("gomory_cut_detail", + tout << a << " x_j = " << x_j << ", k = " << k << "\n"; + tout << "f_j: " << f_j << "\n"; + tout << "f_0: " << f_0 << "\n"; + tout << "1 - f_0: " << one_minus_f_0 << "\n"; + tout << "at_low(" << x_j << ") = " << at_low(x_j) << std::endl; + ); + lp_assert (!f_j.is_zero()); + mpq new_a; + if (at_low(x_j)) { + if (f_j <= one_minus_f_0) { + new_a = f_j / one_minus_f_0; + } + else { + new_a = (1 - f_j) / f_0; + } + k.addmul(new_a, lower_bound(x_j).x); + expl.push_justification(column_lower_bound_constraint(x_j), new_a); + } + else { + lp_assert(at_upper(x_j)); + if (f_j <= f_0) { + new_a = f_j / f_0; + } + else { + new_a = (mpq(1) - f_j) / (one_minus_f_0); + } + new_a.neg(); // the upper terms are inverted + k.addmul(new_a, upper_bound(x_j).x); + expl.push_justification(column_upper_bound_constraint(x_j), new_a); + } + TRACE("gomory_cut_detail", tout << "new_a: " << new_a << " k: " << k << "\n";); + t.add_monomial(new_a, x_j); + lcm_den = lcm(lcm_den, denominator(new_a)); + } + + + void report_conflict_from_gomory_cut(mpq &k) { + lp_assert(false); + } + + void adjust_term_and_k_for_some_ints_case_gomory(lar_term& t, mpq& k, mpq &lcm_den) { + lp_assert(!t.is_empty()); + auto pol = t.coeffs_as_vector(); + t.clear(); + if (pol.size() == 1) { + TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); + unsigned v = pol[0].second; + lp_assert(is_int(v)); + const mpq& a = pol[0].first; + k /= a; + if (a.is_pos()) { // we have av >= k + if (!k.is_int()) + k = ceil(k); + // switch size + t.add_monomial(- mpq(1), v); + k.neg(); + } else { + if (!k.is_int()) + k = floor(k); + t.add_monomial(mpq(1), v); + } + } else { + TRACE("gomory_cut_detail", tout << "pol.size() > 1" << std::endl;); + lcm_den = lcm(lcm_den, denominator(k)); + TRACE("gomory_cut_detail", tout << "k: " << k << " lcm_den: " << lcm_den << "\n"; + for (unsigned i = 0; i < pol.size(); i++) { + tout << pol[i].first << " " << pol[i].second << "\n"; + } + tout << "k: " << k << "\n";); + lp_assert(lcm_den.is_pos()); + if (!lcm_den.is_one()) { + // normalize coefficients of integer parameters to be integers. + for (auto & pi: pol) { + pi.first *= lcm_den; + SASSERT(!is_int(pi.second) || pi.first.is_int()); + } + k *= lcm_den; + } + TRACE("gomory_cut_detail", tout << "after *lcm\n"; + for (unsigned i = 0; i < pol.size(); i++) { + tout << pol[i].first << " * v" << pol[i].second << "\n"; + } + tout << "k: " << k << "\n";); + + // negate everything to return -pol <= -k + for (const auto & pi: pol) + t.add_monomial(-pi.first, pi.second); + k.neg(); + } + TRACE("gomory_cut_detail", tout << "k = " << k << std::endl;); + lp_assert(k.is_int()); + } + + void print_term(lar_term & t, std::ostream & out) { + lp_assert(is_zero(t.m_v)); + vector> row; + for (auto p : t.m_coeffs) + row.push_back(std::make_pair(p.second, p.first)); + print_row(out, row); + } + + void mk_gomory_cut(lar_term& t, mpq& k, explanation & expl, unsigned inf_col, vector> & row) { + enable_trace("gomory_cut"); + enable_trace("gomory_cut_detail"); + + TRACE("gomory_cut", + tout << "applying cut at:\n"; print_row(tout, row); + tout << std::endl << "inf_col = " << inf_col << std::endl; + ); + + // gomory will be t >= k + k = 1; + mpq lcm_den(1); + unsigned x_j; + mpq a; + bool some_int_columns = false; + mpq f_0 = int_solver::fractional_part(get_value(inf_col)); + mpq one_min_f_0 = 1 - f_0; + for ( auto pp : row) { + a = pp.first; + x_j = pp.second; + if (x_j == inf_col) + continue; + // make the format compatible with the format used in: Integrating Simplex with DPLL(T) + a.neg(); + if (is_real(x_j)) + real_case_in_gomory_cut(a, x_j, k, t, expl, f_0, one_min_f_0); + else { + if (a.is_int()) continue; // f_j will be zero and no monomial will be added + some_int_columns = true; + int_case_in_gomory_cut(a, x_j, k, t, expl, lcm_den, f_0, one_min_f_0); + } + } + + if (t.is_empty()) + return report_conflict_from_gomory_cut(k); + if (some_int_columns) + adjust_term_and_k_for_some_ints_case_gomory(t, k, lcm_den); + + TRACE("gomory_cut", tout<<"new cut :"; print_term(t, tout); tout << " >= " << k << std::endl;); + + } +}; +} diff --git a/src/test/lp.cpp b/src/test/lp/lp.cpp similarity index 67% rename from src/test/lp.cpp rename to src/test/lp/lp.cpp index 0f4186252..e5375a0df 100644 --- a/src/test/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1,22 +1,22 @@ /*++ -Copyright (c) 2017 Microsoft Corporation + Copyright (c) 2017 Microsoft Corporation -Module Name: + Module Name: - + -Abstract: + Abstract: - + -Author: + Author: - Lev Nachmanson (levnach) + Lev Nachmanson (levnach) -Revision History: + Revision History: ---*/ + --*/ #include #if _LINUX_ @@ -35,26 +35,38 @@ Revision History: #include "util/lp/lp_utils.h" #include "util/lp/lp_primal_simplex.h" #include "util/lp/mps_reader.h" -#include "test/smt_reader.h" +#include "test/lp/smt_reader.h" #include "util/lp/binary_heap_priority_queue.h" -#include "test/argument_parser.h" -#include "test/test_file_reader.h" +#include "test/lp/argument_parser.h" +#include "test/lp/test_file_reader.h" #include "util/lp/indexed_value.h" #include "util/lp/lar_solver.h" #include "util/lp/numeric_pair.h" #include "util/lp/binary_heap_upair_queue.h" #include "util/lp/stacked_value.h" -#include "util/lp/stacked_unordered_set.h" #include "util/lp/int_set.h" #include "util/stopwatch.h" - +#include +#include "test/lp/gomory_test.h" +#include "util/lp/matrix.h" +#include "util/lp/hnf.h" +#include "util/lp/square_sparse_matrix_def.h" +#include "util/lp/lu_def.h" +#include "util/lp/general_matrix.h" +#include "util/lp/bound_propagator.h" namespace lp { - unsigned seed = 1; +unsigned seed = 1; - random_gen g_rand; - static unsigned my_random() { - return g_rand(); - } + class my_bound_propagator : public bound_propagator { + public: + my_bound_propagator(lar_solver & ls): bound_propagator(ls) {} + void consume(mpq const& v, lp::constraint_index j) override {} + }; + +random_gen g_rand; +static unsigned my_random() { + return g_rand(); +} struct simple_column_namer:public column_namer { std::string get_column_name(unsigned j) const override { @@ -64,11 +76,11 @@ struct simple_column_namer:public column_namer template -void test_matrix(sparse_matrix & a) { +void test_matrix(square_sparse_matrix & a) { auto m = a.dimension(); -// copy a to b in the reversed order - sparse_matrix b(m); + // copy a to b in the reversed order + square_sparse_matrix b(m, m); std::cout << "copy b to a"<< std::endl; for (int row = m - 1; row >= 0; row--) for (int col = m - 1; col >= 0; col --) { @@ -95,7 +107,7 @@ void test_matrix(sparse_matrix & a) { a.set(i, j, t); - SASSERT(a.get(i, j) == t); + lp_assert(a.get(i, j) == t); unsigned j1; if (j < m - 1) { @@ -106,7 +118,7 @@ void test_matrix(sparse_matrix & a) { void tst1() { std::cout << "testing the minimial matrix with 1 row and 1 column" << std::endl; - sparse_matrix m0(1); + square_sparse_matrix m0(1, 1); m0.set(0, 0, 1); // print_matrix(m0); m0.set(0, 0, 0); @@ -114,7 +126,7 @@ void tst1() { test_matrix(m0); unsigned rows = 2; - sparse_matrix m(rows); + square_sparse_matrix m(rows, rows); std::cout << "setting m(0,1)=" << std::endl; m.set(0, 1, 11); @@ -124,7 +136,7 @@ void tst1() { test_matrix(m); - sparse_matrix m1(2); + square_sparse_matrix m1(2, 2); m1.set(0, 0, 2); m1.set(1, 0, 3); // print_matrix(m1); @@ -137,7 +149,7 @@ void tst1() { std::cout << "printing zero matrix 3 by 1" << std::endl; - sparse_matrix m2(3); + square_sparse_matrix m2(3, 3); // print_matrix(m2); m2.set(0, 0, 1); @@ -147,7 +159,7 @@ void tst1() { test_matrix(m2); - sparse_matrix m10by9(10); + square_sparse_matrix m10by9(10, 10); m10by9.set(0, 1, 1); m10by9(0, 1) = 4; @@ -187,7 +199,7 @@ vector allocate_basis_heading(unsigned count) { // the rest of initilizatio void init_basic_part_of_basis_heading(vector & basis, vector & basis_heading) { - SASSERT(basis_heading.size() >= basis.size()); + lp_assert(basis_heading.size() >= basis.size()); unsigned m = basis.size(); for (unsigned i = 0; i < m; i++) { unsigned column = basis[i]; @@ -199,9 +211,9 @@ void init_non_basic_part_of_basis_heading(vector & basis_heading, vector(non_basic_columns.size()); + non_basic_columns.push_back(j); + // the index of column j in m_nbasis is (- basis_heading[j] - 1) + basis_heading[j] = - static_cast(non_basic_columns.size()); } } } @@ -222,6 +234,7 @@ void change_basis(unsigned entering, unsigned leaving, vector& basis, } + #ifdef Z3DEBUG void test_small_lu(lp_settings & settings) { std::cout << " test_small_lu" << std::endl; @@ -241,8 +254,8 @@ void test_small_lu(lp_settings & settings) { vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu l(m, basis, settings); - SASSERT(l.is_correct(basis)); + lu> l(m, basis, settings); + lp_assert(l.is_correct(basis)); indexed_vector w(m.row_count()); std::cout << "entering 2, leaving 0" << std::endl; l.prepare_entering(2, w); // to init vector w @@ -252,7 +265,7 @@ void test_small_lu(lp_settings & settings) { // std::cout << "we were factoring " << std::endl; // print_matrix(get_B(l)); // #endif - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); std::cout << "entering 4, leaving 3" << std::endl; l.prepare_entering(4, w); // to init vector w l.replace_column(0, w, heading[3]); @@ -264,7 +277,7 @@ void test_small_lu(lp_settings & settings) { print_matrix(&bl, std::cout); } #endif - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); std::cout << "entering 5, leaving 1" << std::endl; l.prepare_entering(5, w); // to init vector w @@ -277,7 +290,7 @@ void test_small_lu(lp_settings & settings) { print_matrix(&bl, std::cout); } #endif - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); std::cout << "entering 3, leaving 2" << std::endl; l.prepare_entering(3, w); // to init vector w l.replace_column(0, w, heading[2]); @@ -289,7 +302,7 @@ void test_small_lu(lp_settings & settings) { print_matrix(&bl, std::cout); } #endif - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); m.add_row(); m.add_column(); @@ -308,12 +321,12 @@ void test_small_lu(lp_settings & settings) { auto columns_to_replace = l.get_set_of_columns_to_replace_for_add_last_rows(heading); l.add_last_rows_to_B(heading, columns_to_replace); std::cout << "here" << std::endl; - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); } #endif -void fill_long_row(sparse_matrix &m, int i) { +void fill_long_row(square_sparse_matrix &m, int i) { int n = m.dimension(); for (int j = 0; j < n; j ++) { m (i, (j + i) % n) = j * j; @@ -328,7 +341,7 @@ void fill_long_row(static_matrix &m, int i) { } -void fill_long_row_exp(sparse_matrix &m, int i) { +void fill_long_row_exp(square_sparse_matrix &m, int i) { int n = m.dimension(); for (int j = 0; j < n; j ++) { @@ -344,23 +357,23 @@ void fill_long_row_exp(static_matrix &m, int i) { } } -void fill_larger_sparse_matrix_exp(sparse_matrix & m){ +void fill_larger_square_sparse_matrix_exp(square_sparse_matrix & m){ for ( unsigned i = 0; i < m.dimension(); i++ ) fill_long_row_exp(m, i); } -void fill_larger_sparse_matrix_exp(static_matrix & m){ +void fill_larger_square_sparse_matrix_exp(static_matrix & m){ for ( unsigned i = 0; i < m.row_count(); i++ ) fill_long_row_exp(m, i); } -void fill_larger_sparse_matrix(sparse_matrix & m){ +void fill_larger_square_sparse_matrix(square_sparse_matrix & m){ for ( unsigned i = 0; i < m.dimension(); i++ ) fill_long_row(m, i); } -void fill_larger_sparse_matrix(static_matrix & m){ +void fill_larger_square_sparse_matrix(static_matrix & m){ for ( unsigned i = 0; i < m.row_count(); i++ ) fill_long_row(m, i); } @@ -381,16 +394,16 @@ void test_larger_lu_exp(lp_settings & settings) { basis[5] = 6; - fill_larger_sparse_matrix_exp(m); + fill_larger_square_sparse_matrix_exp(m); // print_matrix(m); vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu l(m, basis, settings); + lu> l(m, basis, settings); dense_matrix left_side = l.get_left_side(basis); dense_matrix right_side = l.get_right_side(); - SASSERT(left_side == right_side); + lp_assert(left_side == right_side); int leaving = 3; int entering = 8; for (unsigned i = 0; i < m.row_count(); i++) { @@ -402,12 +415,12 @@ void test_larger_lu_exp(lp_settings & settings) { l.prepare_entering(entering, w); l.replace_column(0, w, heading[leaving]); change_basis(entering, leaving, basis, non_basic_columns, heading); - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); l.prepare_entering(11, w); // to init vector w l.replace_column(0, w, heading[0]); change_basis(11, 0, basis, non_basic_columns, heading); - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); } void test_larger_lu_with_holes(lp_settings & settings) { @@ -430,13 +443,13 @@ void test_larger_lu_with_holes(lp_settings & settings) { vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - lu l(m, basis, settings); + lu> l(m, basis, settings); std::cout << "printing factorization" << std::endl; for (int i = l.tail_size() - 1; i >=0; i--) { auto lp = l.get_lp_matrix(i); lp->set_number_of_columns(m.row_count()); lp->set_number_of_rows(m.row_count()); - print_matrix( lp, std::cout); + print_matrix( *lp, std::cout); } dense_matrix left_side = l.get_left_side(basis); @@ -449,7 +462,7 @@ void test_larger_lu_with_holes(lp_settings & settings) { l.prepare_entering(8, w); // to init vector w l.replace_column(0, w, heading[0]); change_basis(8, 0, basis, non_basic_columns, heading); - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); } @@ -465,13 +478,13 @@ void test_larger_lu(lp_settings& settings) { basis[5] = 6; - fill_larger_sparse_matrix(m); + fill_larger_square_sparse_matrix(m); print_matrix(m, std::cout); vector heading = allocate_basis_heading(m.column_count()); vector non_basic_columns; init_basis_heading_and_non_basic_columns_vector(basis, heading, non_basic_columns); - auto l = lu (m, basis, settings); + auto l = lu> (m, basis, settings); // std::cout << "printing factorization" << std::endl; // for (int i = lu.tail_size() - 1; i >=0; i--) { // auto lp = lu.get_lp_matrix(i); @@ -496,7 +509,7 @@ void test_larger_lu(lp_settings& settings) { l.prepare_entering(9, w); // to init vector w l.replace_column(0, w, heading[0]); change_basis(9, 0, basis, non_basic_columns, heading); - SASSERT(l.is_correct(basis)); + lp_assert(l.is_correct(basis)); } @@ -513,7 +526,7 @@ void test_lu(lp_settings & settings) { -void init_b(vector & b, sparse_matrix & m, vector& x) { +void init_b(vector & b, square_sparse_matrix & m, vector& x) { for (unsigned i = 0; i < m.dimension(); i++) { b.push_back(m.dot_product_with_row(i, x)); } @@ -548,14 +561,14 @@ void test_lp_0() { costs[5] = 0; costs[6] = 0; - vector column_types(7, column_type::low_bound); + vector column_types(7, column_type::lower_bound); vector upper_bound_values; lp_settings settings; simple_column_namer cn; vector nbasis; vector heading; - lp_primal_core_solver lpsolver(m_, b, x_star, basis, nbasis, heading, costs, column_types, upper_bound_values, settings, cn); + lp_primal_core_solver lpsolver(m_, b, x_star, basis, nbasis, heading, costs, column_types, upper_bound_values, settings, cn); lpsolver.solve(); } @@ -594,7 +607,7 @@ void test_lp_1() { - vector column_types(7, column_type::low_bound); + vector column_types(7, column_type::lower_bound); vector upper_bound_values; std::cout << "calling lp\n"; @@ -623,13 +636,13 @@ void test_lp_primal_core_solver() { #ifdef Z3DEBUG template -void test_swap_rows_with_permutation(sparse_matrix& m){ +void test_swap_rows_with_permutation(square_sparse_matrix& m){ std::cout << "testing swaps" << std::endl; unsigned dim = m.row_count(); dense_matrix original(&m); permutation_matrix q(dim); print_matrix(m, std::cout); - SASSERT(original == q * m); + lp_assert(original == q * m); for (int i = 0; i < 100; i++) { unsigned row1 = my_random() % dim; unsigned row2 = my_random() % dim; @@ -637,23 +650,23 @@ void test_swap_rows_with_permutation(sparse_matrix& m){ std::cout << "swap " << row1 << " " << row2 << std::endl; m.swap_rows(row1, row2); q.transpose_from_left(row1, row2); - SASSERT(original == q * m); + lp_assert(original == q * m); print_matrix(m, std::cout); std::cout << std::endl; } } #endif template -void fill_matrix(sparse_matrix& m); // forward definition +void fill_matrix(square_sparse_matrix& m); // forward definition #ifdef Z3DEBUG template -void test_swap_cols_with_permutation(sparse_matrix& m){ +void test_swap_cols_with_permutation(square_sparse_matrix& m){ std::cout << "testing swaps" << std::endl; unsigned dim = m.row_count(); dense_matrix original(&m); permutation_matrix q(dim); print_matrix(m, std::cout); - SASSERT(original == q * m); + lp_assert(original == q * m); for (int i = 0; i < 100; i++) { unsigned row1 = my_random() % dim; unsigned row2 = my_random() % dim; @@ -661,7 +674,7 @@ void test_swap_cols_with_permutation(sparse_matrix& m){ std::cout << "swap " << row1 << " " << row2 << std::endl; m.swap_rows(row1, row2); q.transpose_from_right(row1, row2); - SASSERT(original == q * m); + lp_assert(original == q * m); print_matrix(m, std::cout); std::cout << std::endl; } @@ -669,48 +682,48 @@ void test_swap_cols_with_permutation(sparse_matrix& m){ template -void test_swap_rows(sparse_matrix& m, unsigned i0, unsigned i1){ +void test_swap_rows(square_sparse_matrix& m, unsigned i0, unsigned i1){ std::cout << "test_swap_rows(" << i0 << "," << i1 << ")" << std::endl; - sparse_matrix mcopy(m.dimension()); + square_sparse_matrix mcopy(m.dimension(), 0); for (unsigned i = 0; i < m.dimension(); i++) for (unsigned j = 0; j < m.dimension(); j++) { mcopy(i, j)= m(i, j); - } + } std::cout << "swapping rows "<< i0 << "," << i1 << std::endl; m.swap_rows(i0, i1); for (unsigned j = 0; j < m.dimension(); j++) { - SASSERT(mcopy(i0, j) == m(i1, j)); - SASSERT(mcopy(i1, j) == m(i0, j)); + lp_assert(mcopy(i0, j) == m(i1, j)); + lp_assert(mcopy(i1, j) == m(i0, j)); } } template -void test_swap_columns(sparse_matrix& m, unsigned i0, unsigned i1){ +void test_swap_columns(square_sparse_matrix& m, unsigned i0, unsigned i1){ std::cout << "test_swap_columns(" << i0 << "," << i1 << ")" << std::endl; - sparse_matrix mcopy(m.dimension()); + square_sparse_matrix mcopy(m.dimension(), 0); // the second argument does not matter for (unsigned i = 0; i < m.dimension(); i++) for (unsigned j = 0; j < m.dimension(); j++) { mcopy(i, j)= m(i, j); - } + } m.swap_columns(i0, i1); for (unsigned j = 0; j < m.dimension(); j++) { - SASSERT(mcopy(j, i0) == m(j, i1)); - SASSERT(mcopy(j, i1) == m(j, i0)); + lp_assert(mcopy(j, i0) == m(j, i1)); + lp_assert(mcopy(j, i1) == m(j, i0)); } for (unsigned i = 0; i < m.dimension(); i++) { if (i == i0 || i == i1) continue; for (unsigned j = 0; j < m.dimension(); j++) { - SASSERT(mcopy(j, i)== m(j, i)); + lp_assert(mcopy(j, i)== m(j, i)); } } } #endif template -void fill_matrix(sparse_matrix& m){ +void fill_matrix(square_sparse_matrix& m){ int v = 0; for (int i = m.dimension() - 1; i >= 0; i--) { for (int j = m.dimension() - 1; j >=0; j--){ @@ -720,10 +733,10 @@ void fill_matrix(sparse_matrix& m){ } void test_pivot_like_swaps_and_pivot(){ - sparse_matrix m(10); + square_sparse_matrix m(10, 10); fill_matrix(m); // print_matrix(m); -// pivot at 2,7 + // pivot at 2,7 m.swap_columns(0, 7); // print_matrix(m); m.swap_rows(2, 0); @@ -733,7 +746,7 @@ void test_pivot_like_swaps_and_pivot(){ } // print_matrix(m); -// say pivot at 3,4 + // say pivot at 3,4 m.swap_columns(1, 4); // print_matrix(m); m.swap_rows(1, 3); @@ -765,13 +778,13 @@ void test_pivot_like_swaps_and_pivot(){ m.pivot_row_to_row(pivot_row_0, beta, target_row, settings); // print_matrix(m); for (unsigned j = 0; j < m.dimension(); j++) { - SASSERT(abs(row[j] - m(target_row, j)) < 0.00000001); + lp_assert(abs(row[j] - m(target_row, j)) < 0.00000001); } } #ifdef Z3DEBUG void test_swap_rows() { - sparse_matrix m(10); + square_sparse_matrix m(10, 10); fill_matrix(m); // print_matrix(m); test_swap_rows(m, 3, 5); @@ -791,8 +804,8 @@ void test_swap_rows() { // print_matrix(m); test_swap_rows(m, 0, 7); -// go over some corner cases - sparse_matrix m0(2); + // go over some corner cases + square_sparse_matrix m0(2, 2); test_swap_rows(m0, 0, 1); m0(0, 0) = 3; test_swap_rows(m0, 0, 1); @@ -800,7 +813,7 @@ void test_swap_rows() { test_swap_rows(m0, 0, 1); - sparse_matrix m1(10); + square_sparse_matrix m1(10, 10); test_swap_rows(m1, 0, 1); m1(0, 0) = 3; test_swap_rows(m1, 0, 1); @@ -812,7 +825,7 @@ void test_swap_rows() { test_swap_rows(m1, 0, 1); - sparse_matrix m2(3); + square_sparse_matrix m2(3, 3); test_swap_rows(m2, 0, 1); m2(0, 0) = 3; test_swap_rows(m2, 0, 1); @@ -820,7 +833,7 @@ void test_swap_rows() { test_swap_rows(m2, 0, 2); } -void fill_uniformly(sparse_matrix & m, unsigned dim) { +void fill_uniformly(square_sparse_matrix & m, unsigned dim) { int v = 0; for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { @@ -838,9 +851,9 @@ void fill_uniformly(dense_matrix & m, unsigned dim) { } } -void sparse_matrix_with_permutaions_test() { +void square_sparse_matrix_with_permutaions_test() { unsigned dim = 4; - sparse_matrix m(dim); + square_sparse_matrix m(dim, dim); fill_uniformly(m, dim); dense_matrix dm(dim, dim); fill_uniformly(dm, dim); @@ -870,61 +883,61 @@ void sparse_matrix_with_permutaions_test() { m.multiply_from_left(q0); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { - SASSERT(m(i, j) == dm0.get_elem(q0[i], j)); + lp_assert(m(i, j) == dm0.get_elem(q0[i], j)); } } auto q0_dm = q0 * dm; - SASSERT(m == q0_dm); + lp_assert(m == q0_dm); m.multiply_from_left(q1); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { - SASSERT(m(i, j) == dm0.get_elem(q0[q1[i]], j)); + lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], j)); } } auto q1_q0_dm = q1 * q0_dm; - SASSERT(m == q1_q0_dm); + lp_assert(m == q1_q0_dm); m.multiply_from_right(p0); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { - SASSERT(m(i, j) == dm0.get_elem(q0[q1[i]], p0[j])); + lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p0[j])); } } auto q1_q0_dm_p0 = q1_q0_dm * p0; - SASSERT(m == q1_q0_dm_p0); + lp_assert(m == q1_q0_dm_p0); m.multiply_from_right(p1); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { - SASSERT(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p0[j]])); + lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p0[j]])); } } auto q1_q0_dm_p0_p1 = q1_q0_dm_p0 * p1; - SASSERT(m == q1_q0_dm_p0_p1); + lp_assert(m == q1_q0_dm_p0_p1); m.multiply_from_right(p1); for (unsigned i = 0; i < dim; i++) { for (unsigned j = 0; j < dim; j++) { - SASSERT(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p1[p0[j]]])); + lp_assert(m(i, j) == dm0.get_elem(q0[q1[i]], p1[p1[p0[j]]])); } } auto q1_q0_dm_p0_p1_p1 = q1_q0_dm_p0_p1 * p1; - SASSERT(m == q1_q0_dm_p0_p1_p1); + lp_assert(m == q1_q0_dm_p0_p1_p1); } void test_swap_columns() { - sparse_matrix m(10); + square_sparse_matrix m(10, 10); fill_matrix(m); // print_matrix(m); @@ -943,8 +956,8 @@ void test_swap_columns() { test_swap_columns(m, 0, 7); -// go over some corner cases - sparse_matrix m0(2); + // go over some corner cases + square_sparse_matrix m0(2, 2); test_swap_columns(m0, 0, 1); m0(0, 0) = 3; test_swap_columns(m0, 0, 1); @@ -952,7 +965,7 @@ void test_swap_columns() { test_swap_columns(m0, 0, 1); - sparse_matrix m1(10); + square_sparse_matrix m1(10, 10); test_swap_columns(m1, 0, 1); m1(0, 0) = 3; test_swap_columns(m1, 0, 1); @@ -964,7 +977,7 @@ void test_swap_columns() { test_swap_columns(m1, 0, 1); - sparse_matrix m2(3); + square_sparse_matrix m2(3, 3); test_swap_columns(m2, 0, 1); m2(0, 0) = 3; test_swap_columns(m2, 0, 1); @@ -1041,9 +1054,9 @@ void test_apply_reverse_from_right_to_perm(permutation_matrix & #ifdef Z3DEBUG auto rev = l.get_inverse(); auto rs = pclone * rev; - SASSERT(p == rs); + lp_assert(p == rs) #endif -} + } void test_apply_reverse_from_right() { auto vec = vector_of_permutaions(); @@ -1068,8 +1081,8 @@ void test_permutations() { p.apply_reverse_from_right_to_T(v); p.apply_reverse_from_right_to_T(vi); - SASSERT(vectors_are_equal(v, vi.m_data)); - SASSERT(vi.is_OK()); + lp_assert(vectors_are_equal(v, vi.m_data)); + lp_assert(vi.is_OK()); } void lp_solver_test() { @@ -1125,7 +1138,7 @@ void update_settings(argument_parser & args_parser, lp_settings& settings) { settings.harris_feasibility_tolerance = d; } if (get_int_from_args_parser("--random_seed", args_parser, n)) { - settings.random_seed(n); + settings.set_random_seed(n); } if (get_int_from_args_parser("--simplex_strategy", args_parser, n)) { settings.simplex_strategy() = static_cast(n); @@ -1217,7 +1230,7 @@ void solve_mps_double(std::string file_name, bool look_for_min, unsigned max_ite compare_solutions(reader, primal_solver, solver); print_x(reader, primal_solver); std::cout << "dual cost is " << cost << ", but primal cost is " << primal_cost << std::endl; - SASSERT(false); + lp_assert(false); } } } @@ -1335,7 +1348,7 @@ void test_binary_priority_queue() { for (unsigned i = 0; i < 10; i++) { unsigned de = q.dequeue(); - SASSERT(i == de); + lp_assert(i == de); std::cout << de << std::endl; } q.enqueue(2, 2); @@ -1358,7 +1371,7 @@ void test_binary_priority_queue() { unsigned t = 0; while (q.size() > 0) { unsigned d =q.dequeue(); - SASSERT(t++ == d); + lp_assert(t++ == d); std::cout << d << std::endl; } #endif @@ -1387,7 +1400,7 @@ void solve_mps_with_known_solution(std::string file_name, std::unordered_mapget_status()) << std::endl; if (status != solver->get_status()){ std::cout << "status should be " << lp_status_to_string(status) << std::endl; - SASSERT(status == solver->get_status()); + lp_assert(status == solver->get_status()); throw "status is wrong"; } if (solver->get_status() == lp_status::OPTIMAL) { @@ -1398,7 +1411,7 @@ void solve_mps_with_known_solution(std::string file_name, std::unordered_mapget_column_value_by_name(it.first) << std::endl; } - SASSERT(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); + lp_assert(fabs(it.second - solver->get_column_value_by_name(it.first)) < 0.000001); } } if (reader.column_names().size() < 20) { @@ -1483,127 +1496,127 @@ void fill_file_names(vector &file_names, std::set & m return; } std::string home_dir_str(home_dir); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l0redund.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l1.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l2.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l3.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l4.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l4fix.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/plan.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/samp2.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/murtagh.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/l0.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/AFIRO.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SC50B.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SC50A.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/KB2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SC105.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STOCFOR1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/ADLITTLE.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BLEND.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCAGR7.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SC205.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHARE2B.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/RECIPELP.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/LOTFI.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/VTP-BASE.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHARE1B.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BOEING2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BORE3D.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCORPION.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/CAPRI.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BRANDY.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCAGR25.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCTAP1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/ISRAEL.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCFXM1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BANDM.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/E226.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/AGG.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GROW7.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/ETAMACRO.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FINNIS.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCSD1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STANDATA.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STANDGUB.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BEACONFD.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STAIR.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STANDMPS.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GFRD-PNC.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCRS8.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BOEING1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/MODSZK1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/DEGEN2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FORPLAN.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/AGG2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/AGG3.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCFXM2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHELL.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOT4.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCSD6.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP04S.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SEBA.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GROW15.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FFFFF800.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BNL1.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PEROLD.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/QAP8.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCFXM3.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP04L.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GANGES.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCTAP2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GROW22.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP08S.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOT-WE.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/MAROS.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STOCFOR2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/25FV47.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP12S.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCSD8.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FIT1P.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SCTAP3.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SIERRA.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOTNOV.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/CZPROB.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FIT1D.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOT-JA.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP08L.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/BNL2.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/NESM.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/CYCLE.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/acc-tight5.mps"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/SHIP12L.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/DEGEN3.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GREENBEA.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/GREENBEB.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/80BAU3B.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/TRUSS.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/D2Q06C.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/WOODW.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/QAP12.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/D6CUBE.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOT.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/DFL001.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/WOOD1P.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FIT2P.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/PILOT87.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/STOCFOR3.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/QAP15.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/FIT2D.SIF"); - file_names.push_back(home_dir_str + "/projects/lean/src/tests/util/lp/test_files/netlib/MAROS-R7.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/FIT2P.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/DFL001.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/D2Q06C.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/80BAU3B.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/GREENBEB.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/GREENBEA.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/BNL2.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/SHIP08L.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/FIT1D.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/SCTAP3.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/SCSD8.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/SCSD6.SIF"); - minimums.insert("/projects/lean/src/tests/util/lp/test_files/netlib/MAROS-R7.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l0redund.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l1.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l2.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l3.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l4.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l4fix.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/plan.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/samp2.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/murtagh.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/l0.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AFIRO.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC50B.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC50A.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/KB2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC105.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STOCFOR1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/ADLITTLE.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BLEND.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCAGR7.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SC205.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHARE2B.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/RECIPELP.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/LOTFI.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/VTP-BASE.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHARE1B.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BOEING2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BORE3D.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCORPION.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/CAPRI.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BRANDY.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCAGR25.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/ISRAEL.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCFXM1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BANDM.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/E226.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AGG.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GROW7.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/ETAMACRO.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FINNIS.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCSD1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STANDATA.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STANDGUB.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BEACONFD.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STAIR.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STANDMPS.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GFRD-PNC.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCRS8.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BOEING1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/MODSZK1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/DEGEN2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FORPLAN.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AGG2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/AGG3.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCFXM2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHELL.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT4.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCSD6.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP04S.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SEBA.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GROW15.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FFFFF800.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BNL1.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PEROLD.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/QAP8.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCFXM3.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP04L.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GANGES.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GROW22.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP08S.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT-WE.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/MAROS.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STOCFOR2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/25FV47.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP12S.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCSD8.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT1P.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP3.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SIERRA.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOTNOV.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/CZPROB.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT1D.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT-JA.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP08L.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/BNL2.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/NESM.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/CYCLE.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/acc-tight5.mps"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/SHIP12L.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/DEGEN3.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEA.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEB.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/80BAU3B.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/TRUSS.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/D2Q06C.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/WOODW.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/QAP12.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/D6CUBE.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/DFL001.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/WOOD1P.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT2P.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/PILOT87.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/STOCFOR3.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/QAP15.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/FIT2D.SIF"); + file_names.push_back(home_dir_str + "/projects/lp/src/tests/util/lp/test_files/netlib/MAROS-R7.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/FIT2P.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/DFL001.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/D2Q06C.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/80BAU3B.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEB.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/GREENBEA.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/BNL2.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SHIP08L.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/FIT1D.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SCTAP3.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SCSD8.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/SCSD6.SIF"); + minimums.insert("/projects/lp/src/tests/util/lp/test_files/netlib/MAROS-R7.SIF"); } void test_out_dir(std::string out_dir) { @@ -1673,7 +1686,7 @@ void solve_some_mps(argument_parser & args_parser) { } if (!solve_for_rational) { solve_mps(file_names[6], false, 0, time_limit, false, dual, compare_with_primal, args_parser); - solve_mps_with_known_solution(file_names[3], nullptr, INFEASIBLE, dual); // chvatal: 135(d) + solve_mps_with_known_solution(file_names[3], nullptr, lp_status::INFEASIBLE, dual); // chvatal: 135(d) std::unordered_map sol; sol["X1"] = 0; sol["X2"] = 6; @@ -1683,8 +1696,8 @@ void solve_some_mps(argument_parser & args_parser) { sol["X6"] = 1; sol["X7"] = 1; sol["X8"] = 0; - solve_mps_with_known_solution(file_names[9], &sol, OPTIMAL, dual); - solve_mps_with_known_solution(file_names[0], &sol, OPTIMAL, dual); + solve_mps_with_known_solution(file_names[9], &sol, lp_status::OPTIMAL, dual); + solve_mps_with_known_solution(file_names[0], &sol, lp_status::OPTIMAL, dual); sol.clear(); sol["X1"] = 25.0/14.0; // sol["X2"] = 0; @@ -1693,10 +1706,10 @@ void solve_some_mps(argument_parser & args_parser) { // sol["X5"] = 0; // sol["X6"] = 0; // sol["X7"] = 9.0/14.0; - solve_mps_with_known_solution(file_names[5], &sol, OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[4], &sol, OPTIMAL, dual); // chvatal: 135(e) - solve_mps_with_known_solution(file_names[2], nullptr, UNBOUNDED, dual); // chvatal: 135(c) - solve_mps_with_known_solution(file_names[1], nullptr, UNBOUNDED, dual); // chvatal: 135(b) + solve_mps_with_known_solution(file_names[5], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) + solve_mps_with_known_solution(file_names[4], &sol, lp_status::OPTIMAL, dual); // chvatal: 135(e) + solve_mps_with_known_solution(file_names[2], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(c) + solve_mps_with_known_solution(file_names[1], nullptr, lp_status::UNBOUNDED, dual); // chvatal: 135(b) solve_mps(file_names[8], false, 0, time_limit, false, dual, compare_with_primal, args_parser); // return; for (auto& s : file_names) { @@ -1748,7 +1761,7 @@ void solve_rational() { int bounds[] = {8, 6, 4, 15, 2, 10, 10, 3}; for (unsigned i = 0; i < 8; i++) { - solver.set_low_bound(i, lp::mpq(0)); + solver.set_lower_bound(i, lp::mpq(0)); solver.set_upper_bound(i, lp::mpq(bounds[i])); } @@ -1762,9 +1775,9 @@ void solve_rational() { expected_sol["x7"] = lp::mpq(1); expected_sol["x8"] = lp::mpq(0); solver.find_maximal_solution(); - SASSERT(solver.get_status() == OPTIMAL); + lp_assert(solver.get_status() == lp_status::OPTIMAL); for (auto it : expected_sol) { - SASSERT(it.second == solver.get_column_value_by_name(it.first)); + lp_assert(it.second == solver.get_column_value_by_name(it.first)); } } @@ -1819,10 +1832,10 @@ std::unordered_map * get_solution_from_glpsol_output(std::s } auto split = string_split(s, " \t", false); if (split.size() == 0) { - return ret; + return ret; } - SASSERT(split.size() > 3); + lp_assert(split.size() > 3); (*ret)[split[1]] = atof(split[3].c_str()); } while (true); } @@ -1842,11 +1855,11 @@ void test_init_U() { basis[1] = 2; basis[2] = 4; - sparse_matrix u(m, basis); + square_sparse_matrix u(m, basis); for (unsigned i = 0; i < 3; i++) { for (unsigned j = 0; j < 3; j ++) { - SASSERT(m(i, basis[j]) == u(i, j)); + lp_assert(m(i, basis[j]) == u(i, j)); } } @@ -1855,7 +1868,7 @@ void test_init_U() { } void test_replace_column() { - sparse_matrix m(10); + square_sparse_matrix m(10, 10); fill_matrix(m); m.swap_columns(0, 7); m.swap_columns(6, 3); @@ -1874,13 +1887,17 @@ void test_replace_column() { for (unsigned column_to_replace = 0; column_to_replace < m.dimension(); column_to_replace ++) { m.replace_column(column_to_replace, w, settings); for (unsigned i = 0; i < m.dimension(); i++) { - SASSERT(abs(w[i] - m(i, column_to_replace)) < 0.00000001); + lp_assert(abs(w[i] - m(i, column_to_replace)) < 0.00000001); } } } + void setup_args_parser(argument_parser & parser) { + parser.add_option_with_help_string("-hnf", "test hermite normal form"); + parser.add_option_with_help_string("-gomory", "gomory"); + parser.add_option_with_help_string("-intd", "test integer_domain"); parser.add_option_with_help_string("-xyz_sample", "run a small interactive scenario"); parser.add_option_with_after_string_with_help("--density", "the percentage of non-zeroes in the matrix below which it is not dense"); parser.add_option_with_after_string_with_help("--harris_toler", "harris tolerance"); @@ -1926,48 +1943,11 @@ void setup_args_parser(argument_parser & parser) { parser.add_option_with_help_string("--test_mpq", "test rationals"); parser.add_option_with_help_string("--test_mpq_np", "test rationals"); parser.add_option_with_help_string("--test_mpq_np_plus", "test rationals using plus instead of +="); + parser.add_option_with_help_string("--maximize_term", "test maximize_term()"); } struct fff { int a; int b;}; -void test_stacked_map_itself() { - vector v(3,0); - for(auto u : v) - std::cout << u << std::endl; - - std::unordered_map foo; - fff l; - l.a = 0; - l.b =1; - foo[1] = l; - int r = 1; - int k = foo[r].a; - std::cout << k << std::endl; - - stacked_map m; - m[0] = 3; - m[1] = 4; - m.push(); - m[1] = 5; - m[2] = 2; - m.pop(); - m.erase(2); - m[2] = 3; - m.erase(1); - m.push(); - m[3] = 100; - m[4] = 200; - m.erase(1); - m.push(); - m[5] = 300; - m[6] = 400; - m[5] = 301; - m.erase(5); - m[3] = 122; - - m.pop(2); - m.pop(); -} void test_stacked_unsigned() { std::cout << "test stacked unsigned" << std::endl; @@ -1978,7 +1958,7 @@ void test_stacked_unsigned() { v = 3; v = 4; v.pop(); - SASSERT(v == 2); + lp_assert(v == 2); v ++; v++; std::cout << "before push v=" << v << std::endl; @@ -1988,7 +1968,7 @@ void test_stacked_unsigned() { v+=1; std::cout << "v = " << v << std::endl; v.pop(2); - SASSERT(v == 4); + lp_assert(v == 4); const unsigned & rr = v; std::cout << rr << std:: endl; @@ -2026,48 +2006,22 @@ void test_stacked_vector() { } -void test_stacked_set() { -#ifdef Z3DEBUG - std::cout << "test_stacked_set" << std::endl; - stacked_unordered_set s; - s.insert(1); - s.insert(2); - s.insert(3); - std::unordered_set scopy = s(); - s.push(); - s.insert(4); - s.pop(); - SASSERT(s() == scopy); - s.push(); - s.push(); - s.insert(4); - s.insert(5); - s.push(); - s.insert(4); - s.pop(3); - SASSERT(s() == scopy); -#endif -} void test_stacked() { - std::cout << "test_stacked_map()" << std::endl; - test_stacked_map_itself(); test_stacked_value(); test_stacked_vector(); - test_stacked_set(); - } char * find_home_dir() { - #ifdef _WINDOWS - #else +#ifdef _WINDOWS +#else char * home_dir = getenv("HOME"); - if (home_dir == nullptr) { + if (home_dir == nullptr) { std::cout << "cannot find home directory" << std::endl; return nullptr; } - #endif - return nullptr; +#endif + return nullptr; } @@ -2089,8 +2043,8 @@ struct mem_cpy_place_holder { void finalize(unsigned ret) { /* - finalize_util_module(); - finalize_numerics_module(); + finalize_util_module(); + finalize_numerics_module(); */ // return ret; } @@ -2228,7 +2182,7 @@ bool values_are_one_percent_close(double a, double b) { // returns true if both are optimal void compare_costs(std::string glpk_out_file_name, - std::string lp_out_file_name, + std::string lp_out_file_name, unsigned & successes, unsigned & failures) { double a = get_glpk_cost(glpk_out_file_name); @@ -2307,78 +2261,78 @@ void process_test_file(std::string test_dir, std::string test_file_name, argumen } /* int my_readdir(DIR *dirp, struct dirent * -#ifndef LEAN_WINDOWS - entry -#endif - , struct dirent **result) { -#ifdef LEAN_WINDOWS - *result = readdir(dirp); // NOLINT - return *result != nullptr? 0 : 1; -#else - return readdir_r(dirp, entry, result); -#endif -} + #ifndef LEAN_WINDOWS + entry + #endif + , struct dirent **result) { + #ifdef LEAN_WINDOWS + *result = readdir(dirp); // NOLINT + return *result != nullptr? 0 : 1; + #else + return readdir_r(dirp, entry, result); + #endif + } */ /* -vector> get_file_list_of_dir(std::string test_file_dir) { - DIR *dir; - if ((dir = opendir(test_file_dir.c_str())) == nullptr) { - std::cout << "Cannot open directory " << test_file_dir << std::endl; - throw 0; - } - vector> ret; - struct dirent entry; - struct dirent* result; - int return_code; - for (return_code = my_readdir(dir, &entry, &result); -#ifndef LEAN_WINDOWS - result != nullptr && -#endif - return_code == 0; - return_code = my_readdir(dir, &entry, &result)) { - DIR *tmp_dp = opendir(result->d_name); - struct stat file_record; - if (tmp_dp == nullptr) { - std::string s = test_file_dir+ "/" + result->d_name; - int stat_ret = stat(s.c_str(), & file_record); - if (stat_ret!= -1) { - ret.push_back(make_pair(result->d_name, file_record.st_size)); - } else { - perror("stat"); - exit(1); - } - } else { - closedir(tmp_dp); - } - } - closedir(dir); - return ret; -} + vector> get_file_list_of_dir(std::string test_file_dir) { + DIR *dir; + if ((dir = opendir(test_file_dir.c_str())) == nullptr) { + std::cout << "Cannot open directory " << test_file_dir << std::endl; + throw 0; + } + vector> ret; + struct dirent entry; + struct dirent* result; + int return_code; + for (return_code = my_readdir(dir, &entry, &result); + #ifndef LEAN_WINDOWS + result != nullptr && + #endif + return_code == 0; + return_code = my_readdir(dir, &entry, &result)) { + DIR *tmp_dp = opendir(result->d_name); + struct stat file_record; + if (tmp_dp == nullptr) { + std::string s = test_file_dir+ "/" + result->d_name; + int stat_ret = stat(s.c_str(), & file_record); + if (stat_ret!= -1) { + ret.push_back(make_pair(result->d_name, file_record.st_size)); + } else { + perror("stat"); + exit(1); + } + } else { + closedir(tmp_dp); + } + } + closedir(dir); + return ret; + } */ /* -struct file_size_comp { - unordered_map& m_file_sizes; - file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} - int operator()(std::string a, std::string b) { - std::cout << m_file_sizes.size() << std::endl; - std::cout << a << std::endl; - std::cout << b << std::endl; + struct file_size_comp { + unordered_map& m_file_sizes; + file_size_comp(unordered_map& fs) :m_file_sizes(fs) {} + int operator()(std::string a, std::string b) { + std::cout << m_file_sizes.size() << std::endl; + std::cout << a << std::endl; + std::cout << b << std::endl; - auto ls = m_file_sizes.find(a); - std::cout << "fa" << std::endl; - auto rs = m_file_sizes.find(b); - std::cout << "fb" << std::endl; - if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { - std::cout << "fc " << std::endl; - int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); - std::cout << "calc r " << std::endl; - return r; - } else { - std::cout << "sc " << std::endl; - return 0; - } - } -}; + auto ls = m_file_sizes.find(a); + std::cout << "fa" << std::endl; + auto rs = m_file_sizes.find(b); + std::cout << "fb" << std::endl; + if (ls != m_file_sizes.end() && rs != m_file_sizes.end()) { + std::cout << "fc " << std::endl; + int r = (*ls < *rs? -1: (*ls > *rs)? 1 : 0); + std::cout << "calc r " << std::endl; + return r; + } else { + std::cout << "sc " << std::endl; + return 0; + } + } + }; */ struct sort_pred { @@ -2390,26 +2344,26 @@ struct sort_pred { void test_files_from_directory(std::string test_file_dir, argument_parser & args_parser) { /* - std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; - std::string out_dir = args_parser.get_option_value("--out_dir"); - if (out_dir.size() == 0) { - out_dir = "/tmp/test"; - } - DIR *out_dir_p = opendir(out_dir.c_str()); - if (out_dir_p == nullptr) { - std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; - return; - } - closedir(out_dir_p); - vector> files = get_file_list_of_dir(test_file_dir); - std::sort(files.begin(), files.end(), sort_pred()); - unsigned max_iters, time_limit; - get_time_limit_and_max_iters_from_parser(args_parser, time_limit, max_iters); - unsigned successes = 0, failures = 0, inconclusives = 0; - for (auto & t : files) { - process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); - } - std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; + std::cout << "loading files from directory \"" << test_file_dir << "\"" << std::endl; + std::string out_dir = args_parser.get_option_value("--out_dir"); + if (out_dir.size() == 0) { + out_dir = "/tmp/test"; + } + DIR *out_dir_p = opendir(out_dir.c_str()); + if (out_dir_p == nullptr) { + std::cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl; + return; + } + closedir(out_dir_p); + vector> files = get_file_list_of_dir(test_file_dir); + std::sort(files.begin(), files.end(), sort_pred()); + unsigned max_iters, time_limit; + get_time_limit_and_max_iters_from_parser(args_parser, time_limit, max_iters); + unsigned successes = 0, failures = 0, inconclusives = 0; + for (auto & t : files) { + process_test_file(test_file_dir, t.first, args_parser, out_dir, max_iters, time_limit, successes, failures, inconclusives); + } + std::cout << "comparing with glpk: successes " << successes << ", failures " << failures << ", inconclusives " << inconclusives << std::endl; */ } @@ -2450,12 +2404,12 @@ void run_lar_solver(argument_parser & args_parser, lar_solver * solver, mps_read sw.start(); lp_status status = solver->solve(); std::cout << "status is " << lp_status_to_string(status) << ", processed for " << sw.get_current_seconds() <<" seconds, and " << solver->get_total_iterations() << " iterations" << std::endl; - if (solver->get_status() == INFEASIBLE) { + if (solver->get_status() == lp_status::INFEASIBLE) { vector> evidence; solver->get_infeasibility_explanation(evidence); } if (args_parser.option_is_used("--randomize_lar")) { - if (solver->get_status() != OPTIMAL) { + if (solver->get_status() != lp_status::OPTIMAL) { std::cout << "cannot check randomize on an infeazible problem" << std::endl; return; } @@ -2538,6 +2492,8 @@ void test_lar_solver(argument_parser & args_parser) { test_lar_on_file(fn, args_parser); return; } + + std::cout << "give option --file or --filelist to test_lar_solver\n"; } void test_numeric_pair() { @@ -2547,22 +2503,22 @@ void test_numeric_pair() { numeric_pair c(0.1, 0.5); a += 2*c; a -= c; - SASSERT (a == b + c); + lp_assert (a == b + c); numeric_pair d = a * 2; std::cout << a << std::endl; - SASSERT(b == b); - SASSERT(b < a); - SASSERT(b <= a); - SASSERT(a > b); - SASSERT(a != b); - SASSERT(a >= b); - SASSERT(-a < b); - SASSERT(a < 2 * b); - SASSERT(b + b > a); - SASSERT(lp::mpq(2.1) * b + b > a); - SASSERT(-b * lp::mpq(2.1) - b < lp::mpq(0.99) * a); + lp_assert(b == b); + lp_assert(b < a); + lp_assert(b <= a); + lp_assert(a > b); + lp_assert(a != b); + lp_assert(a >= b); + lp_assert(-a < b); + lp_assert(a < 2 * b); + lp_assert(b + b > a); + lp_assert(lp::mpq(2.1) * b + b > a); + lp_assert(-b * lp::mpq(2.1) - b < lp::mpq(0.99) * a); std::cout << - b * lp::mpq(2.1) - b << std::endl; - SASSERT(-b *(lp::mpq(2.1) + 1) == - b * lp::mpq(2.1) - b); + lp_assert(-b *(lp::mpq(2.1) + 1) == - b * lp::mpq(2.1) - b); } void get_matrix_dimensions(std::ifstream & f, unsigned & m, unsigned & n) { @@ -2583,7 +2539,7 @@ void read_row_cols(unsigned i, static_matrix& A, std::ifstream & if (line== "row_end") break; auto r = split_and_trim(line); - SASSERT(r.size() == 4); + lp_assert(r.size() == 4); unsigned j = atoi(r[1].c_str()); double v = atof(r[3].c_str()); A.set(i, j, v); @@ -2611,7 +2567,7 @@ void read_basis(vector & basis, std::ifstream & f) { std::cout << "reading basis" << std::endl; std::string line; getline(f, line); - SASSERT(line == "basis_start"); + lp_assert(line == "basis_start"); do { getline(f, line); if (line == "basis_end") @@ -2624,7 +2580,7 @@ void read_basis(vector & basis, std::ifstream & f) { void read_indexed_vector(indexed_vector & v, std::ifstream & f) { std::string line; getline(f, line); - SASSERT(line == "vector_start"); + lp_assert(line == "vector_start"); do { getline(f, line); if (line == "vector_end") break; @@ -2654,8 +2610,8 @@ void check_lu_from_file(std::string lufile_name) { vector basis_heading; lp_settings settings; vector non_basic_columns; - lu lsuhl(A, basis, settings); - indexed_vector d(A.row_count()); + lu> lsuhl(A, basis, settings); + indexed_vector d(A.row_count()); unsigned entering = 26; lsuhl.solve_Bd(entering, d, v); #ifdef Z3DEBUG @@ -2664,14 +2620,14 @@ void check_lu_from_file(std::string lufile_name) { A.copy_column_to_vector(entering, a); indexed_vector cd(d); B.apply_from_left(cd.m_data, settings); - SASSERT(vectors_are_equal(cd.m_data , a)); + lp_assert(vectors_are_equal(cd.m_data , a)); #endif } void test_square_dense_submatrix() { std::cout << "testing square_dense_submatrix" << std::endl; unsigned parent_dim = 7; - sparse_matrix parent(parent_dim); + square_sparse_matrix parent(parent_dim, 0); fill_matrix(parent); unsigned index_start = 3; square_dense_submatrix d; @@ -2712,8 +2668,8 @@ void test_term() { lar_solver solver; unsigned _x = 0; unsigned _y = 1; - var_index x = solver.add_var(_x); - var_index y = solver.add_var(_y); + var_index x = solver.add_var(_x, false); + var_index y = solver.add_var(_y, false); vector> term_ls; term_ls.push_back(std::pair((int)1, x)); @@ -2726,9 +2682,16 @@ void test_term() { ls.push_back(std::pair((int)1, z)); solver.add_constraint(ls, lconstraint_kind::EQ, mpq(0)); + ls.clear(); + ls.push_back(std::pair((int)1, x)); + solver.add_constraint(ls, lconstraint_kind::LT, mpq(0)); + ls.push_back(std::pair((int)2, y)); + solver.add_constraint(ls, lconstraint_kind::GT, mpq(0)); auto status = solver.solve(); std::cout << lp_status_to_string(status) << std::endl; std::unordered_map model; + if (status != lp_status::OPTIMAL) + return; solver.get_model(model); for (auto & t : model) { @@ -2740,8 +2703,8 @@ void test_term() { void test_evidence_for_total_inf_simple(argument_parser & args_parser) { lar_solver solver; - var_index x = solver.add_var(0); - var_index y = solver.add_var(1); + var_index x = solver.add_var(0, false); + var_index y = solver.add_var(1, false); solver.add_var_bound(x, LE, -mpq(1)); solver.add_var_bound(y, GE, mpq(0)); vector> ls; @@ -2755,29 +2718,29 @@ void test_evidence_for_total_inf_simple(argument_parser & args_parser) { auto status = solver.solve(); std::cout << lp_status_to_string(status) << std::endl; std::unordered_map model; - SASSERT(solver.get_status() == INFEASIBLE); + lp_assert(solver.get_status() == lp_status::INFEASIBLE); } void test_bound_propagation_one_small_sample1() { /* -(<= (+ a (* (- 1.0) b)) 0.0) -(<= (+ b (* (- 1.0) x_13)) 0.0) ---> (<= (+ a (* (- 1.0) c)) 0.0) + (<= (+ a (* (- 1.0) b)) 0.0) + (<= (+ b (* (- 1.0) x_13)) 0.0) + --> (<= (+ a (* (- 1.0) c)) 0.0) -the inequality on (<= a c) is obtained from a triangle inequality (<= a b) (<= b c). -If b becomes basic variable, then it is likely the old solver ends up with a row that implies (<= a c). - a - b <= 0.0 - b - c <= 0.0 + the inequality on (<= a c) is obtained from a triangle inequality (<= a b) (<= b c). + If b becomes basic variable, then it is likely the old solver ends up with a row that implies (<= a c). + a - b <= 0.0 + b - c <= 0.0 - got to get a <= c + got to get a <= c */ std::function bound_is_relevant = - [&](unsigned j, bool is_low_bound, bool strict, const rational& bound_val) { - return true; - }; + [&](unsigned j, bool is_lower_bound, bool strict, const rational& bound_val) { + return true; + }; lar_solver ls; - unsigned a = ls.add_var(0); - unsigned b = ls.add_var(1); - unsigned c = ls.add_var(2); + unsigned a = ls.add_var(0, false); + unsigned b = ls.add_var(1, false); + unsigned c = ls.add_var(2, false); vector> coeffs; coeffs.push_back(std::pair(1, a)); coeffs.push_back(std::pair(-1, c)); @@ -2796,7 +2759,7 @@ If b becomes basic variable, then it is likely the old solver ends up with a row vector ev; ls.add_var_bound(a, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); std::cout << " bound ev from test_bound_propagation_one_small_sample1" << std::endl; for (auto & be : bp.m_ibounds) { @@ -2809,39 +2772,39 @@ void test_bound_propagation_one_small_samples() { test_bound_propagation_one_small_sample1(); /* (>= x_46 0.0) -(<= x_29 0.0) -(not (<= x_68 0.0)) -(<= (+ (* (/ 1001.0 1998.0) x_10) (* (- 1.0) x_151) x_68) (- (/ 1001.0 999.0))) -(<= (+ (* (/ 1001.0 999.0) x_9) - (* (- 1.0) x_152) - (* (/ 1001.0 999.0) x_151) - (* (/ 1001.0 999.0) x_68)) - (- (/ 1502501.0 999000.0))) -(not (<= (+ (* (/ 999.0 2.0) x_10) (* (- 1.0) x_152) (* (- (/ 999.0 2.0)) x_151)) - (/ 1001.0 2.0))) -(not (<= x_153 0.0))z -(>= (+ x_9 (* (- (/ 1001.0 999.0)) x_10) (* (- 1.0) x_153) (* (- 1.0) x_68)) - (/ 5003.0 1998.0)) ---> (not (<= (+ x_10 x_46 (* (- 1.0) x_29)) 0.0)) + (<= x_29 0.0) + (not (<= x_68 0.0)) + (<= (+ (* (/ 1001.0 1998.0) x_10) (* (- 1.0) x_151) x_68) (- (/ 1001.0 999.0))) + (<= (+ (* (/ 1001.0 999.0) x_9) + (* (- 1.0) x_152) + (* (/ 1001.0 999.0) x_151) + (* (/ 1001.0 999.0) x_68)) + (- (/ 1502501.0 999000.0))) + (not (<= (+ (* (/ 999.0 2.0) x_10) (* (- 1.0) x_152) (* (- (/ 999.0 2.0)) x_151)) + (/ 1001.0 2.0))) + (not (<= x_153 0.0))z + (>= (+ x_9 (* (- (/ 1001.0 999.0)) x_10) (* (- 1.0) x_153) (* (- 1.0) x_68)) + (/ 5003.0 1998.0)) + --> (not (<= (+ x_10 x_46 (* (- 1.0) x_29)) 0.0)) -and + and -(<= (+ a (* (- 1.0) b)) 0.0) -(<= (+ b (* (- 1.0) x_13)) 0.0) ---> (<= (+ a (* (- 1.0) x_13)) 0.0) + (<= (+ a (* (- 1.0) b)) 0.0) + (<= (+ b (* (- 1.0) x_13)) 0.0) + --> (<= (+ a (* (- 1.0) x_13)) 0.0) -In the first case, there typically are no atomic formulas for bounding x_10. So there is never some -basic lemma of the form (>= x46 0), (<= x29 0), (>= x10 0) -> (not (<= (+ x10 x46 (- x29)) 0)). -Instead the bound on x_10 falls out from a bigger blob of constraints. + In the first case, there typically are no atomic formulas for bounding x_10. So there is never some + basic lemma of the form (>= x46 0), (<= x29 0), (>= x10 0) -> (not (<= (+ x10 x46 (- x29)) 0)). + Instead the bound on x_10 falls out from a bigger blob of constraints. -In the second case, the inequality on (<= x19 x13) is obtained from a triangle inequality (<= x19 x9) (<= x9 x13). -If x9 becomes basic variable, then it is likely the old solver ends up with a row that implies (<= x19 x13). - */ + In the second case, the inequality on (<= x19 x13) is obtained from a triangle inequality (<= x19 x9) (<= x9 x13). + If x9 becomes basic variable, then it is likely the old solver ends up with a row that implies (<= x19 x13). + */ } void test_bound_propagation_one_row() { lar_solver ls; - unsigned x0 = ls.add_var(0); - unsigned x1 = ls.add_var(1); + unsigned x0 = ls.add_var(0, false); + unsigned x1 = ls.add_var(1, false); vector> c; c.push_back(std::pair(1, x0)); c.push_back(std::pair(-1, x1)); @@ -2849,13 +2812,13 @@ void test_bound_propagation_one_row() { vector ev; ls.add_var_bound(x0, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_bound_propagation_one_row_with_bounded_vars() { lar_solver ls; - unsigned x0 = ls.add_var(0); - unsigned x1 = ls.add_var(1); + unsigned x0 = ls.add_var(0, false); + unsigned x1 = ls.add_var(1, false); vector> c; c.push_back(std::pair(1, x0)); c.push_back(std::pair(-1, x1)); @@ -2865,13 +2828,13 @@ void test_bound_propagation_one_row_with_bounded_vars() { ls.add_var_bound(x0, LE, mpq(3)); ls.add_var_bound(x0, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_bound_propagation_one_row_mixed() { lar_solver ls; - unsigned x0 = ls.add_var(0); - unsigned x1 = ls.add_var(1); + unsigned x0 = ls.add_var(0, false); + unsigned x1 = ls.add_var(1, false); vector> c; c.push_back(std::pair(1, x0)); c.push_back(std::pair(-1, x1)); @@ -2879,15 +2842,15 @@ void test_bound_propagation_one_row_mixed() { vector ev; ls.add_var_bound(x1, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_bound_propagation_two_rows() { lar_solver ls; - unsigned x = ls.add_var(0); - unsigned y = ls.add_var(1); - unsigned z = ls.add_var(2); + unsigned x = ls.add_var(0, false); + unsigned y = ls.add_var(1, false); + unsigned z = ls.add_var(2, false); vector> c; c.push_back(std::pair(1, x)); c.push_back(std::pair(2, y)); @@ -2902,16 +2865,16 @@ void test_bound_propagation_two_rows() { vector ev; ls.add_var_bound(y, LE, mpq(1)); ls.solve(); - lp_bound_propagator bp(ls); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } void test_total_case_u() { std::cout << "test_total_case_u\n"; lar_solver ls; - unsigned x = ls.add_var(0); - unsigned y = ls.add_var(1); - unsigned z = ls.add_var(2); + unsigned x = ls.add_var(0, false); + unsigned y = ls.add_var(1, false); + unsigned z = ls.add_var(2, false); vector> c; c.push_back(std::pair(1, x)); c.push_back(std::pair(2, y)); @@ -2922,7 +2885,7 @@ void test_total_case_u() { vector ev; ls.add_var_bound(z, GE, zero_of_type()); ls.solve(); - lp_bound_propagator bp(ls); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); } bool contains_j_kind(unsigned j, lconstraint_kind kind, const mpq & rs, const vector & ev) { @@ -2935,9 +2898,9 @@ bool contains_j_kind(unsigned j, lconstraint_kind kind, const mpq & rs, const ve void test_total_case_l(){ std::cout << "test_total_case_l\n"; lar_solver ls; - unsigned x = ls.add_var(0); - unsigned y = ls.add_var(1); - unsigned z = ls.add_var(2); + unsigned x = ls.add_var(0, false); + unsigned y = ls.add_var(1, false); + unsigned z = ls.add_var(2, false); vector> c; c.push_back(std::pair(1, x)); c.push_back(std::pair(2, y)); @@ -2949,10 +2912,10 @@ void test_total_case_l(){ vector ev; ls.add_var_bound(z, LE, zero_of_type()); ls.solve(); - lp_bound_propagator bp(ls); + my_bound_propagator bp(ls); ls.propagate_bounds_for_touched_rows(bp); - SASSERT(ev.size() == 4); - SASSERT(contains_j_kind(x, GE, - one_of_type(), ev)); + lp_assert(ev.size() == 4); + lp_assert(contains_j_kind(x, GE, - one_of_type(), ev)); } void test_bound_propagation() { test_total_case_u(); @@ -2972,17 +2935,17 @@ void test_int_set() { s.insert(1); s.insert(2); s.print(std::cout); - SASSERT(s.contains(2)); - SASSERT(s.size() == 2); + lp_assert(s.contains(2)); + lp_assert(s.size() == 2); s.erase(2); - SASSERT(s.size() == 1); + lp_assert(s.size() == 1); s.erase(2); - SASSERT(s.size() == 1); + lp_assert(s.size() == 1); s.print(std::cout); s.insert(3); s.insert(2); s.clear(); - SASSERT(s.size() == 0); + lp_assert(s.size() == 0); } @@ -3077,11 +3040,507 @@ void test_rationals() { std::cout << T_to_string(r) << std::endl; } -void test_lp_local(int argn, char**argv) { - std::cout << "resize\n"; - vector r; - r.resize(1); +void get_random_interval(bool& neg_inf, bool& pos_inf, int& x, int &y) { + int i = my_random() % 10; + if (i == 0) { + neg_inf = true; + } else { + neg_inf = false; + x = my_random() % 100; + } + i = my_random() % 10; + if (i == 0) { + pos_inf = true; + } else { + pos_inf = false; + if (!neg_inf) { + y = x + my_random() % (101 - x); + lp_assert(y >= x); + } + else { + y = my_random() % 100; + } + } + lp_assert((neg_inf || (0 <= x && x <= 100)) && (pos_inf || (0 <= y && y <= 100))); +} + +void test_gomory_cut_0() { + gomory_test g( + [](unsigned j) { return "v" + T_to_string(j);} // name_function_p + , + [](unsigned j) { //get_value_p + if (j == 1) + return mpq(2730, 1727); + if (j == 2) + return zero_of_type(); + if (j == 3) return mpq(3); + lp_assert(false); + return zero_of_type(); + }, + [](unsigned j) { // at_low_p + if (j == 1) + return false; + if (j == 2) + return true; + if (j == 3) + return true; + lp_assert(false); + return false; + }, + [](unsigned j) { // at_upper + if (j == 1) + return false; + if (j == 2) + return true; + if (j == 3) + return false; + lp_assert(false); + return false; + }, + [](unsigned j) { // lower_bound + if (j == 1) { + lp_assert(false); //unlimited from below + return 0; + } + if (j == 2) + return 0; + if (j == 3) + return 3; + lp_assert(false); + return 0; + }, + [](unsigned j) { // upper + if (j == 1) { + lp_assert(false); //unlimited from above + return 0; + } + if (j == 2) + return 0; + if (j == 3) + return 10; + lp_assert(false); + return 0; + }, + [] (unsigned) { return 0; }, + [] (unsigned) { return 0; } + ); + lar_term t; + mpq k; + explanation expl; + unsigned inf_col = 1; + vector> row; + row.push_back(std::make_pair(mpq(1), 1)); + row.push_back(std::make_pair(mpq(2731, 1727), 2)); + row.push_back(std::make_pair(mpq(-910, 1727), 3)); + g.mk_gomory_cut(t, k, expl, inf_col, row); +} + +void test_gomory_cut_1() { + gomory_test g( + [](unsigned j) { return "v" + T_to_string(j);} // name_function_p + , + [](unsigned j) { //get_value_p + if (j == 1) + return mpq(-2); + if (j == 2) + return mpq(4363334, 2730001); + if (j == 3) + return mpq(1); + lp_assert(false); + return zero_of_type(); + }, + [](unsigned j) { // at_low_p + if (j == 1) + return false; + if (j == 2) + return false; + if (j == 3) + return true; + lp_assert(false); + return false; + }, + [](unsigned j) { // at_upper + if (j == 1) + return true; + if (j == 2) + return false; + if (j == 3) + return true; + lp_assert(false); + return false; + }, + [](unsigned j) { // lower_bound + if (j == 1) { + lp_assert(false); //unlimited from below + return 0; + } + if (j == 2) + return 1; + if (j == 3) + return 1; + lp_assert(false); + return 0; + }, + [](unsigned j) { // upper + if (j == 1) { + return -2; + } + if (j == 2) + return 3333; + if (j == 3) + return 10000; + lp_assert(false); + return 0; + }, + [] (unsigned) { return 0; }, + [] (unsigned) { return 0; } + ); + lar_term t; + mpq k; + explanation expl; + unsigned inf_col = 2; + vector> row; + row.push_back(std::make_pair(mpq(1726667, 2730001), 1)); + row.push_back(std::make_pair(mpq(-910000, 2730001), 3)); + row.push_back(std::make_pair(mpq(1), 2)); + g.mk_gomory_cut(t, k, expl, inf_col, row); +} + +void call_hnf(general_matrix & A); + +void test_hnf_m_less_than_n() { +#ifdef Z3DEBUG + general_matrix A; + vector v; + // example 4.3 from Nemhauser, Wolsey + v.push_back(mpq(2)); + v.push_back(mpq(6)); + v.push_back(mpq(1)); + v.push_back(mpq(3)); + A.push_row(v); + v.clear(); + v.push_back(mpq(4)); + v.push_back(mpq(7)); + v.push_back(mpq(7)); + v.push_back(mpq(3)); + A.push_row(v); + v.clear(); + v.push_back(mpq(0)); + v.push_back(mpq(0)); + v.push_back(mpq(1)); + v.push_back(mpq(5)); + A.push_row(v); + call_hnf(A); +#endif +} +void test_hnf_m_greater_than_n() { +#ifdef Z3DEBUG + general_matrix A; + vector v; + v.push_back(mpq(2)); + v.push_back(mpq(6)); + A.push_row(v); + v.clear(); + v.push_back(mpq(4)); + v.push_back(mpq(7)); + A.push_row(v); + v.clear(); + v.push_back(mpq(0)); + v.push_back(mpq(0)); + A.push_row(v); + v.clear(); + v.push_back(mpq(12)); + v.push_back(mpq(55)); + A.push_row(v); + call_hnf(A); +#endif +} + + +void cutting_the_mix_example_1() { + mpq sev(7); + mpq nine(9); + mpq d, u, vv; + hnf_calc::extended_gcd_minimal_uv(sev, nine, d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + hnf_calc::extended_gcd_minimal_uv(sev, -nine, d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + + hnf_calc::extended_gcd_minimal_uv(-nine, -nine, d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + + hnf_calc::extended_gcd_minimal_uv(-sev*2, sev, d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + + hnf_calc::extended_gcd_minimal_uv(mpq(24), mpq(-7), d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + hnf_calc::extended_gcd_minimal_uv(-mpq(24), mpq(7), d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + hnf_calc::extended_gcd_minimal_uv(mpq(24), mpq(7), d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + hnf_calc::extended_gcd_minimal_uv(-mpq(21), mpq(7), d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; + + hnf_calc::extended_gcd_minimal_uv(mpq(21), -mpq(7), d, u, vv); + std::cout << "d = " << d << ", u = " << u << ", vv = " << vv << std::endl; +} + +#ifdef Z3DEBUG + +void fill_general_matrix(general_matrix & M) { + unsigned m = M.row_count(); + unsigned n = M.column_count(); + for (unsigned i = 0; i < m; i++) + for (unsigned j = 0; j < n; j++) + M[i][j] = mpq(static_cast(my_random() % 13) - 6); +} + +void call_hnf(general_matrix& A) { + svector r; + mpq d = hnf_calc::determinant_of_rectangular_matrix(A, r, mpq((int)1000000000)); + A.shrink_to_rank(r); + hnf h(A, d); +} + + +void test_hnf_for_dim(int m) { + general_matrix M(m, m + my_random() % m); + fill_general_matrix(M); + call_hnf(M); +} +void test_hnf_1_2() { + std::cout << "test_hnf_1_2" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(5)); + v.push_back(mpq(26)); + A.push_row(v); + call_hnf(A); + std::cout << "test_hnf_1_2 passed" << std::endl; +} +void test_hnf_2_2() { + std::cout << "test_hnf_2_2" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(5)); + v.push_back(mpq(26)); + A.push_row(v); + v.clear(); + v.push_back(mpq(2)); + v.push_back(mpq(11)); + A.push_row(v); + call_hnf(A); + + std::cout << "test_hnf_2_2 passed" << std::endl; +} + +void test_hnf_3_3() { + std::cout << "test_hnf_3_3" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(-3)); + v.push_back(mpq(0)); + v.push_back(mpq(-1)); + A.push_row(v); + v.clear(); + v.push_back(mpq(-1)); + v.push_back(mpq(0)); + v.push_back(mpq(-6)); + A.push_row(v); + v.clear(); + v.push_back(mpq(-2)); + v.push_back(mpq(-4)); + v.push_back(mpq(-3)); + A.push_row(v); + + call_hnf(A); + std::cout << "test_hnf_3_3 passed" << std::endl; +} +void test_hnf_4_4() { + std::cout << "test_hnf_4_4" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(4)); + v.push_back(mpq(3)); + v.push_back(mpq(-5)); + v.push_back(mpq(6)); + A.push_row(v); + v.clear(); + v.push_back(mpq(1)); + v.push_back(mpq(-3)); + v.push_back(mpq(1)); + v.push_back(mpq(-4)); + A.push_row(v); + v.clear(); + v.push_back(mpq(4)); + v.push_back(mpq(4)); + v.push_back(mpq(4)); + v.push_back(mpq(4)); + A.push_row(v); + v.clear(); + v.push_back(mpq(2)); + v.push_back(mpq(-2)); + v.push_back(mpq(-5)); + v.push_back(mpq(6)); + A.push_row(v); + call_hnf(A); + std::cout << "test_hnf_4_4 passed" << std::endl; +} +void test_hnf_5_5() { + std::cout << "test_hnf_5_5" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(-4)); + v.push_back(mpq(5)); + v.push_back(mpq(-5)); + v.push_back(mpq(1)); + v.push_back(mpq(-3)); + A.push_row(v); + v.clear(); + v.push_back(mpq(3)); + v.push_back(mpq(-1)); + v.push_back(mpq(2)); + v.push_back(mpq(3)); + v.push_back(mpq(-5)); + A.push_row(v); + v.clear(); + v.push_back(mpq(0)); + v.push_back(mpq(6)); + v.push_back(mpq(-5)); + v.push_back(mpq(-6)); + v.push_back(mpq(-2)); + A.push_row(v); + v.clear(); + v.push_back(mpq(1)); + v.push_back(mpq(0)); + v.push_back(mpq(-4)); + v.push_back(mpq(-4)); + v.push_back(mpq(4)); + A.push_row(v); + v.clear(); + v.push_back(mpq(-2)); + v.push_back(mpq(3)); + v.push_back(mpq(6)); + v.push_back(mpq(-5)); + v.push_back(mpq(-1)); + A.push_row(v); + call_hnf(A); + std::cout << "test_hnf_5_5 passed" << std::endl; +} + +void test_small_generated_hnf() { + std::cout << "test_small_rank_hnf" << std::endl; + general_matrix A; + vector v; + v.push_back(mpq(5)); + v.push_back(mpq(26)); + A.push_row(v); + v.clear(); + v.push_back(zero_of_type()); + v.push_back(zero_of_type()); + A.push_row(v); + call_hnf(A); + std::cout << "test_small_rank_hnf passed" << std::endl; +} +void test_larger_generated_hnf() { + std::cout << "test_larger_generated_rank_hnf" << std::endl; + general_matrix A; + vector v; + v.clear(); + v.push_back(mpq(5)); + v.push_back(mpq(6)); + v.push_back(mpq(3)); + v.push_back(mpq(1)); + A.push_row(v); + v.clear(); + v.push_back(mpq(5)); + v.push_back(mpq(2)); + v.push_back(mpq(3)); + v.push_back(mpq(7)); + A.push_row(v); + v.clear(); + v.push_back(mpq(5)); + v.push_back(mpq(6)); + v.push_back(mpq(3)); + v.push_back(mpq(1)); + A.push_row(v); + v.clear(); + v.push_back(mpq(5)); + v.push_back(mpq(2)); + v.push_back(mpq(3)); + v.push_back(mpq(7)); + A.push_row(v); + call_hnf(A); + std::cout << "test_larger_generated_rank_hnf passed" << std::endl; +} +#endif +void test_maximize_term() { + std::cout << "test_maximize_term\n"; + lar_solver solver; + int_solver i_solver(&solver); // have to create it too + unsigned _x = 0; + unsigned _y = 1; + var_index x = solver.add_var(_x, false); + var_index y = solver.add_var(_y, true); + vector> term_ls; + term_ls.push_back(std::pair((int)1, x)); + term_ls.push_back(std::pair((int)-1, y)); + unsigned term_x_min_y = solver.add_term(term_ls, mpq(0)); + term_ls.clear(); + term_ls.push_back(std::pair((int)2, x)); + term_ls.push_back(std::pair((int)2, y)); + + unsigned term_2x_pl_2y = solver.add_term(term_ls, mpq(0)); + solver.add_var_bound(term_x_min_y, LE, zero_of_type()); + solver.add_var_bound(term_2x_pl_2y, LE, mpq((int)5)); + solver.find_feasible_solution(); + lp_assert(solver.get_status() == lp_status::OPTIMAL); + solver.print_constraints(std::cout); + std::unordered_map model; + solver.get_model(model); + for (auto p : model) { + std::cout<< "v[" << p.first << "] = " << p.second << std::endl; + } + std::cout << "calling int_solver\n"; + lar_term t; mpq k; explanation ex; bool upper; + lia_move lm = i_solver.check(t, k, ex, upper); + VERIFY(lm == lia_move::sat); + impq term_max; + lp_status st = solver.maximize_term(term_2x_pl_2y, term_max); + + std::cout << "status = " << lp_status_to_string(st) << std::endl; + std::cout << "term_max = " << term_max << std::endl; + solver.get_model(model); + for (auto p : model) { + std::cout<< "v[" << p.first << "] = " << p.second << std::endl; + } + +} +#ifdef Z3DEBUG +void test_hnf() { + test_larger_generated_hnf(); + test_small_generated_hnf(); + test_hnf_1_2(); + test_hnf_3_3(); + test_hnf_4_4(); + test_hnf_5_5(); + test_hnf_2_2(); + for (unsigned k=1000; k>0; k--) + for (int i = 1; i < 8; i++) + test_hnf_for_dim(i); + cutting_the_mix_example_1(); + // test_hnf_m_less_than_n(); + // test_hnf_m_greater_than_n(); +} +#endif +void test_gomory_cut() { + test_gomory_cut_0(); + test_gomory_cut_1(); +} + +void test_lp_local(int argn, char**argv) { + // initialize_util_module(); // initialize_numerics_module(); int ret; @@ -3096,6 +3555,18 @@ void test_lp_local(int argn, char**argv) { args_parser.print(); + if (args_parser.option_is_used("-hnf")) { +#ifdef Z3DEBUG + test_hnf(); +#endif + return finalize(0); + } + + if (args_parser.option_is_used("-gomory")) { + test_gomory_cut(); + return finalize(0); + } + if (args_parser.option_is_used("--test_mpq")) { test_rationals(); return finalize(0); @@ -3106,7 +3577,7 @@ void test_lp_local(int argn, char**argv) { return finalize(0); } - if (args_parser.option_is_used("--test_mpq_np_plus")) { + if (args_parser.option_is_used("--test_mpq_np_plus")) { test_rationals_no_numeric_pairs_plus(); return finalize(0); } @@ -3131,7 +3602,7 @@ void test_lp_local(int argn, char**argv) { #ifdef Z3DEBUG if (args_parser.option_is_used("--test_swaps")) { - sparse_matrix m(10); + square_sparse_matrix m(10, 0); fill_matrix(m); test_swap_rows_with_permutation(m); test_swap_cols_with_permutation(m); @@ -3199,7 +3670,12 @@ void test_lp_local(int argn, char**argv) { ret = 0; return finalize(ret); } - + + if (args_parser.option_is_used("--maximize_term")) { + test_maximize_term(); + ret = 0; + return finalize(ret); + } if (args_parser.option_is_used("--test_lp_0")) { test_lp_0(); @@ -3241,7 +3717,7 @@ void test_lp_local(int argn, char**argv) { test_init_U(); test_replace_column(); #ifdef Z3DEBUG - sparse_matrix_with_permutaions_test(); + square_sparse_matrix_with_permutaions_test(); test_dense_matrix(); test_swap_operations(); test_permutations(); @@ -3255,3 +3731,8 @@ void test_lp_local(int argn, char**argv) { void tst_lp(char ** argv, int argc, int& i) { lp::test_lp_local(argc - 2, argv + 2); } +#ifdef Z3DEBUG +namespace lp { +template void print_matrix(general_matrix&, std::ostream&); +} +#endif diff --git a/src/test/lp/lp_main.cpp b/src/test/lp/lp_main.cpp new file mode 100644 index 000000000..450664fd0 --- /dev/null +++ b/src/test/lp/lp_main.cpp @@ -0,0 +1,14 @@ +void gparams_register_modules(){} +void mem_initialize() {} +void mem_finalize() {} +#include "util/rational.h" +namespace lp { +void test_lp_local(int argc, char**argv); +} +int main(int argn, char**argv){ + rational::initialize(); + lp::test_lp_local(argn, argv); + rational::finalize(); + return 0; +} + diff --git a/src/test/smt_reader.h b/src/test/lp/smt_reader.h similarity index 94% rename from src/test/smt_reader.h rename to src/test/lp/smt_reader.h index 437cb7a6b..16f44e3b3 100644 --- a/src/test/smt_reader.h +++ b/src/test/lp/smt_reader.h @@ -121,13 +121,13 @@ namespace lp { void fill_simple_elem(lisp_elem & lm) { int separator = first_separator(); - SASSERT(-1 != separator && separator != 0); + lp_assert(-1 != separator && separator != 0); lm.m_head = m_line.substr(0, separator); m_line = m_line.substr(separator); } void fill_nested_elem(lisp_elem & lm) { - SASSERT(m_line[0] == '('); + lp_assert(m_line[0] == '('); m_line = m_line.substr(1); int separator = first_separator(); lm.m_head = m_line.substr(0, separator); @@ -194,11 +194,11 @@ namespace lp { } void adjust_rigth_side(formula_constraint & /* c*/, lisp_elem & /*el*/) { - // SASSERT(el.m_head == "0"); // do nothing for the time being + // lp_assert(el.m_head == "0"); // do nothing for the time being } void set_constraint_coeffs(formula_constraint & c, lisp_elem & el) { - SASSERT(el.m_elems.size() == 2); + lp_assert(el.m_elems.size() == 2); set_constraint_coeffs_on_coeff_element(c, el.m_elems[0]); adjust_rigth_side(c, el.m_elems[1]); } @@ -214,7 +214,7 @@ namespace lp { add_mult_elem(c, el.m_elems); } else if (el.m_head == "~") { lisp_elem & minel = el.m_elems[0]; - SASSERT(minel.is_simple()); + lp_assert(minel.is_simple()); c.m_right_side += mpq(str_to_int(minel.m_head)); } else { std::cout << "unexpected input " << el.m_head << std::endl; @@ -224,14 +224,14 @@ namespace lp { } std::string get_name(lisp_elem & name) { - SASSERT(name.is_simple()); - SASSERT(!is_integer(name.m_head)); + lp_assert(name.is_simple()); + lp_assert(!is_integer(name.m_head)); return name.m_head; } void add_mult_elem(formula_constraint & c, std::vector & els) { - SASSERT(els.size() == 2); + lp_assert(els.size() == 2); mpq coeff = get_coeff(els[0]); std::string col_name = get_name(els[1]); c.add_pair(coeff, col_name); @@ -241,16 +241,16 @@ namespace lp { if (le.is_simple()) { return mpq(str_to_int(le.m_head)); } else { - SASSERT(le.m_head == "~"); - SASSERT(le.size() == 1); + lp_assert(le.m_head == "~"); + lp_assert(le.size() == 1); lisp_elem & el = le.m_elems[0]; - SASSERT(el.is_simple()); + lp_assert(el.is_simple()); return -mpq(str_to_int(el.m_head)); } } int str_to_int(std::string & s) { - SASSERT(is_integer(s)); + lp_assert(is_integer(s)); return atoi(s.c_str()); } @@ -258,7 +258,7 @@ namespace lp { if (el.size()) { add_complex_sum_elem(c, el); } else { - SASSERT(is_integer(el.m_head)); + lp_assert(is_integer(el.m_head)); int v = atoi(el.m_head.c_str()); mpq vr(v); c.m_right_side -= vr; @@ -276,7 +276,7 @@ namespace lp { } else if (el.m_head == "+") { add_sum(c, el.m_elems); } else { - SASSERT(false); // unexpected input + lp_assert(false); // unexpected input } } @@ -389,7 +389,7 @@ namespace lp { void add_constraint_to_solver(lar_solver * solver, formula_constraint & fc) { vector> ls; for (auto & it : fc.m_coeffs) { - ls.push_back(std::make_pair(it.first, solver->add_var(register_name(it.second)))); + ls.push_back(std::make_pair(it.first, solver->add_var(register_name(it.second), false))); } solver->add_constraint(ls, fc.m_kind, fc.m_right_side); } diff --git a/src/test/test_file_reader.h b/src/test/lp/test_file_reader.h similarity index 100% rename from src/test/test_file_reader.h rename to src/test/lp/test_file_reader.h diff --git a/src/test/quant_solve.cpp b/src/test/quant_solve.cpp index ac334c718..04a75c42e 100644 --- a/src/test/quant_solve.cpp +++ b/src/test/quant_solve.cpp @@ -147,7 +147,7 @@ static void parse_fml(char const* str, app_ref_vector& vars, expr_ref& fml) { } fml = q->get_expr(); var_subst vs(m, true); - vs(fml, vars.size(), (expr*const*)vars.c_ptr(), fml); + fml = vs(fml, vars.size(), (expr*const*)vars.c_ptr()); } } diff --git a/src/test/simplex.cpp b/src/test/simplex.cpp index 6620dc83b..782be7ea5 100644 --- a/src/test/simplex.cpp +++ b/src/test/simplex.cpp @@ -4,7 +4,6 @@ Copyright (c) 2015 Microsoft Corporation --*/ -#include "util/lp/sparse_matrix.h" #include "math/simplex/sparse_matrix_def.h" #include "math/simplex/simplex.h" #include "math/simplex/simplex_def.h" diff --git a/src/test/var_subst.cpp b/src/test/var_subst.cpp index 09535ae8c..068e29930 100644 --- a/src/test/var_subst.cpp +++ b/src/test/var_subst.cpp @@ -50,8 +50,7 @@ void tst_instantiate(ast_manager & m, expr * f) { expr_ref_vector cnsts(m); for (unsigned i = 0; i < q->get_num_decls(); i++) cnsts.push_back(m.mk_fresh_const("a", q->get_decl_sort(i))); - expr_ref r(m); - instantiate(m, q, cnsts.c_ptr(), r); + expr_ref r = instantiate(m, q, cnsts.c_ptr()); TRACE("var_subst", tout << "quantifier:\n" << mk_pp(q, m) << "\nresult:\n" << mk_pp(r, m) << "\n";); } } @@ -83,7 +82,7 @@ void tst_subst(ast_manager& m) { sub1.push_back(x); sub1.push_back(y); // replace #1 -> #2, #2 -> #1 - subst(e2, 2, sub1.c_ptr(), e3); + e3 = subst(e2, 2, sub1.c_ptr()); std::cout << mk_pp(e2, m) << "\n"; std::cout << mk_pp(e3, m) << "\n"; std::cout << mk_pp(t1, m) << "\n"; @@ -91,7 +90,7 @@ void tst_subst(ast_manager& m) { // replace #2 -> #3, #3 -> #2 e2 = m.mk_forall(2, ss, names, e1); - subst(e2, 2, sub1.c_ptr(), e3); + e3 = subst(e2, 2, sub1.c_ptr()); std::cout << mk_pp(e2, m) << "\n"; std::cout << mk_pp(e3, m) << "\n"; std::cout << mk_pp(t2, m) << "\n"; diff --git a/src/util/cmd_context_types.h b/src/util/cmd_context_types.h index e82536704..564ae6325 100644 --- a/src/util/cmd_context_types.h +++ b/src/util/cmd_context_types.h @@ -55,8 +55,8 @@ class cmd_exception : public default_exception { } public: cmd_exception(char const * msg):default_exception(msg), m_line(-1), m_pos(-1) {} - cmd_exception(std::string const & msg):default_exception(msg), m_line(-1), m_pos(-1) {} - cmd_exception(std::string const & msg, int line, int pos):default_exception(msg), m_line(line), m_pos(pos) {} + cmd_exception(std::string && msg):default_exception(std::move(msg)), m_line(-1), m_pos(-1) {} + cmd_exception(std::string && msg, int line, int pos):default_exception(std::move(msg)), m_line(line), m_pos(pos) {} cmd_exception(char const * msg, symbol const & s): default_exception(compose(msg,s)),m_line(-1),m_pos(-1) {} cmd_exception(char const * msg, symbol const & s, int line, int pos): diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index 5ee49ef16..4ba3b3b5c 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -356,7 +356,7 @@ public: } } if (error) - throw exception(error_msg); + throw exception(std::move(error_msg)); } std::string get_value(params_ref const & ps, symbol const & p) { @@ -417,7 +417,7 @@ public: } } if (error) - throw exception(error_msg); + throw exception(std::move(error_msg)); return r; } @@ -509,7 +509,7 @@ public: } } if (error) - throw exception(error_msg); + throw exception(std::move(error_msg)); } void display_parameter(std::ostream & out, char const * name) { @@ -550,7 +550,7 @@ public: } } if (error) - throw exception(error_msg); + throw exception(std::move(error_msg)); } }; diff --git a/src/util/lp/CMakeLists.txt b/src/util/lp/CMakeLists.txt index 70c5f9e3b..bde6ed93a 100644 --- a/src/util/lp/CMakeLists.txt +++ b/src/util/lp/CMakeLists.txt @@ -1,33 +1,37 @@ z3_add_component(lp SOURCES + binary_heap_priority_queue.cpp + binary_heap_upair_queue.cpp + bound_propagator.cpp + core_solver_pretty_printer.cpp + dense_matrix.cpp + eta_matrix.cpp + indexed_vector.cpp + int_solver.cpp + lar_solver.cpp + lar_core_solver.cpp + lp_core_solver_base.cpp + lp_dual_core_solver.cpp + lp_dual_simplex.cpp + lp_primal_core_solver.cpp + lp_primal_simplex.cpp + lp_settings.cpp + lp_solver.cpp + lu.cpp lp_utils.cpp - binary_heap_priority_queue_instances.cpp - binary_heap_upair_queue_instances.cpp - lp_bound_propagator.cpp - core_solver_pretty_printer_instances.cpp - dense_matrix_instances.cpp - eta_matrix_instances.cpp - indexed_vector_instances.cpp - lar_core_solver_instances.cpp - lp_core_solver_base_instances.cpp - lp_dual_core_solver_instances.cpp - lp_dual_simplex_instances.cpp - lp_primal_core_solver_instances.cpp - lp_primal_simplex_instances.cpp - lp_settings_instances.cpp - lp_solver_instances.cpp - lu_instances.cpp - matrix_instances.cpp - permutation_matrix_instances.cpp - quick_xplain.cpp - row_eta_matrix_instances.cpp - scaler_instances.cpp - sparse_matrix_instances.cpp - square_dense_submatrix_instances.cpp - static_matrix_instances.cpp - random_updater_instances.cpp + matrix.cpp + nra_solver.cpp + permutation_matrix.cpp + random_updater.cpp + row_eta_matrix.cpp + scaler.cpp + square_dense_submatrix.cpp + square_sparse_matrix.cpp + static_matrix.cpp COMPONENT_DEPENDENCIES util + polynomial + nlsat PYG_FILES lp_params.pyg ) diff --git a/src/util/lp/active_set.h b/src/util/lp/active_set.h new file mode 100644 index 000000000..587570559 --- /dev/null +++ b/src/util/lp/active_set.h @@ -0,0 +1,76 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ + +#pragma once +#include "util/lp/binary_heap_priority_queue.h" +namespace lp { +class active_set { + std::unordered_set m_cs; + binary_heap_priority_queue m_q; + std::unordered_map m_id_to_constraint; +public: + std::unordered_set cs() const { return m_cs;} + + bool contains(const constraint* c) const { + return m_id_to_constraint.find(c->id()) != m_id_to_constraint.end(); + } + + bool is_empty() const { return m_cs.size() == 0; } + // low priority will be dequeued first + void add_constraint(constraint* c, int priority) { + if (contains(c)) + return; + m_cs.insert(c); + m_id_to_constraint[c->id()] = c; + m_q.enqueue(c->id(), priority); + } + + void clear() { + m_cs.clear(); + m_id_to_constraint.clear(); + m_q.clear(); + } + + + constraint* remove_constraint() { + if (m_cs.size() == 0) + return nullptr; + unsigned id = m_q.dequeue(); + auto it = m_id_to_constraint.find(id); + lp_assert(it != m_id_to_constraint.end()); + constraint* c = it->second; + m_cs.erase(c); + m_id_to_constraint.erase(it); + return c; + } + + unsigned size() const { + return static_cast(m_cs.size()); + } + + void remove_constraint(constraint * c) { + if (! contains(c)) return; + + m_cs.erase(c); + m_id_to_constraint.erase(c->id()); + m_q.remove(c->id()); + } +}; +} diff --git a/src/util/lp/binary_heap_priority_queue_instances.cpp b/src/util/lp/binary_heap_priority_queue.cpp similarity index 96% rename from src/util/lp/binary_heap_priority_queue_instances.cpp rename to src/util/lp/binary_heap_priority_queue.cpp index fca826a6b..89b9dba6f 100644 --- a/src/util/lp/binary_heap_priority_queue_instances.cpp +++ b/src/util/lp/binary_heap_priority_queue.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "util/lp/numeric_pair.h" -#include "util/lp/binary_heap_priority_queue.hpp" +#include "util/lp/binary_heap_priority_queue_def.h" namespace lp { template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); template unsigned binary_heap_priority_queue::dequeue(); diff --git a/src/util/lp/binary_heap_priority_queue.h b/src/util/lp/binary_heap_priority_queue.h index 8282ece9c..9a71fc01e 100644 --- a/src/util/lp/binary_heap_priority_queue.h +++ b/src/util/lp/binary_heap_priority_queue.h @@ -45,7 +45,7 @@ public: unsigned size() const { return m_heap_size; } binary_heap_priority_queue(): m_heap(1), m_heap_size(0) {} // the empty constructror // n is the initial queue capacity. - // The capacity will be enlarged two times automatically if needed + // The capacity will be enlarged each time twice if needed binary_heap_priority_queue(unsigned n); void clear() { @@ -75,7 +75,7 @@ public: /// return the first element of the queue and removes it from the queue unsigned dequeue(); unsigned peek() const { - SASSERT(m_heap_size > 0); + lp_assert(m_heap_size > 0); return m_heap[1]; } #ifdef Z3DEBUG diff --git a/src/util/lp/binary_heap_priority_queue.hpp b/src/util/lp/binary_heap_priority_queue_def.h similarity index 91% rename from src/util/lp/binary_heap_priority_queue.hpp rename to src/util/lp/binary_heap_priority_queue_def.h index e7378309d..8a39ecdfa 100644 --- a/src/util/lp/binary_heap_priority_queue.hpp +++ b/src/util/lp/binary_heap_priority_queue_def.h @@ -20,7 +20,7 @@ Revision History: #include "util/vector.h" #include "util/lp/binary_heap_priority_queue.h" namespace lp { -// this is the child place in the heap +// "i" is the child's place in the heap template void binary_heap_priority_queue::swap_with_parent(unsigned i) { unsigned parent = m_heap[i >> 1]; put_at(i >> 1, m_heap[i]); @@ -48,8 +48,8 @@ template void binary_heap_priority_queue::decrease_priority(unsi template bool binary_heap_priority_queue::is_consistent() const { for (int i = 0; i < m_heap_inverse.size(); i++) { int i_index = m_heap_inverse[i]; - SASSERT(i_index <= static_cast(m_heap_size)); - SASSERT(i_index == -1 || m_heap[i_index] == i); + lp_assert(i_index <= static_cast(m_heap_size)); + lp_assert(i_index == -1 || m_heap[i_index] == i); } for (unsigned i = 1; i < m_heap_size; i++) { unsigned ch = i << 1; @@ -71,7 +71,7 @@ template void binary_heap_priority_queue::remove(unsigned o) { if (o_in_heap == -1) { return; // nothing to do } - SASSERT(static_cast(o_in_heap) <= m_heap_size); + lp_assert(static_cast(o_in_heap) <= m_heap_size); if (static_cast(o_in_heap) < m_heap_size) { put_at(o_in_heap, m_heap[m_heap_size--]); if (m_priorities[m_heap[o_in_heap]] > priority_of_o) { @@ -88,11 +88,11 @@ template void binary_heap_priority_queue::remove(unsigned o) { } } } else { - SASSERT(static_cast(o_in_heap) == m_heap_size); + lp_assert(static_cast(o_in_heap) == m_heap_size); m_heap_size--; } m_heap_inverse[o] = -1; - // SASSERT(is_consistent()); + // lp_assert(is_consistent()); } // n is the initial queue capacity. // The capacity will be enlarged two times automatically if needed @@ -118,7 +118,7 @@ template void binary_heap_priority_queue::put_to_heap(unsigned i template void binary_heap_priority_queue::enqueue_new(unsigned o, const T& priority) { m_heap_size++; int i = m_heap_size; - SASSERT(o < m_priorities.size()); + lp_assert(o < m_priorities.size()); m_priorities[o] = priority; put_at(i, o); while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) { @@ -130,8 +130,12 @@ template void binary_heap_priority_queue::enqueue_new(unsigned o // In this case the priority will be changed and the queue adjusted. template void binary_heap_priority_queue::enqueue(unsigned o, const T & priority) { if (o >= m_priorities.size()) { - resize(o << 1); // make the size twice larger + if (o == 0) + resize(2); + else + resize(o << 1); // make the size twice larger } + if (m_heap_inverse[o] == -1) enqueue_new(o, priority); else @@ -150,7 +154,7 @@ template void binary_heap_priority_queue::change_priority_for_ex /// return the first element of the queue and removes it from the queue template unsigned binary_heap_priority_queue::dequeue_and_get_priority(T & priority) { - SASSERT(m_heap_size != 0); + lp_assert(m_heap_size != 0); int ret = m_heap[1]; priority = m_priorities[ret]; put_the_last_at_the_top_and_fix_the_heap(); @@ -184,7 +188,7 @@ template void binary_heap_priority_queue::put_the_last_at_the_to } /// return the first element of the queue and removes it from the queue template unsigned binary_heap_priority_queue::dequeue() { - SASSERT(m_heap_size > 0); + lp_assert(m_heap_size > 0); int ret = m_heap[1]; put_the_last_at_the_top_and_fix_the_heap(); m_heap_inverse[ret] = -1; diff --git a/src/util/lp/binary_heap_upair_queue_instances.cpp b/src/util/lp/binary_heap_upair_queue.cpp similarity index 95% rename from src/util/lp/binary_heap_upair_queue_instances.cpp rename to src/util/lp/binary_heap_upair_queue.cpp index 6d093b175..a521f7058 100644 --- a/src/util/lp/binary_heap_upair_queue_instances.cpp +++ b/src/util/lp/binary_heap_upair_queue.cpp @@ -17,7 +17,7 @@ Revision History: --*/ -#include "util/lp/binary_heap_upair_queue.hpp" +#include "util/lp/binary_heap_upair_queue_def.h" namespace lp { template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); diff --git a/src/util/lp/binary_heap_upair_queue.hpp b/src/util/lp/binary_heap_upair_queue_def.h similarity index 95% rename from src/util/lp/binary_heap_upair_queue.hpp rename to src/util/lp/binary_heap_upair_queue_def.h index d12be9707..e9f3d424a 100644 --- a/src/util/lp/binary_heap_upair_queue.hpp +++ b/src/util/lp/binary_heap_upair_queue_def.h @@ -29,7 +29,7 @@ template binary_heap_upair_queue::binary_heap_upair_queue(unsign template unsigned binary_heap_upair_queue::dequeue_available_spot() { - SASSERT(m_available_spots.empty() == false); + lp_assert(m_available_spots.empty() == false); unsigned ret = m_available_spots.back(); m_available_spots.pop_back(); return ret; @@ -69,7 +69,7 @@ template void binary_heap_upair_queue::enqueue(unsigned i, unsig m_pairs.resize(new_size); } ij_index = dequeue_available_spot(); - // SASSERT(ij_index void binary_heap_upair_queue::enqueue(unsigned i, unsig } template void binary_heap_upair_queue::dequeue(unsigned & i, unsigned &j) { - SASSERT(!m_q.is_empty()); + lp_assert(!m_q.is_empty()); unsigned ij_index = m_q.dequeue(); upair & p = m_pairs[ij_index]; i = p.first; diff --git a/src/util/lp/bound_analyzer_on_row.h b/src/util/lp/bound_analyzer_on_row.h index 52b8ece64..196551f20 100644 --- a/src/util/lp/bound_analyzer_on_row.h +++ b/src/util/lp/bound_analyzer_on_row.h @@ -19,37 +19,93 @@ Revision History: --*/ #pragma once #include "util/vector.h" -#include "util/lp/linear_combination_iterator.h" -#include "util/lp/implied_bound.h" -#include "util/lp/test_bound_analyzer.h" -#include -#include "util/lp/lp_bound_propagator.h" +#include "implied_bound.h" +#include "test_bound_analyzer.h" +#include "util/lp/bound_propagator.h" // We have an equality : sum by j of row[j]*x[j] = rs // We try to pin a var by pushing the total by using the variable bounds // In a loop we drive the partial sum down, denoting the variables of this process by _u. // In the same loop trying to pin variables by pushing the partial sum up, denoting the variable related to it by _l namespace lp { - +template // C plays a role of a container class bound_analyzer_on_row { + struct term_with_basis_col { + const C & m_row; + unsigned m_bj; + struct ival { + unsigned m_var; + const mpq & m_coeff; + ival(unsigned var, const mpq & val) : m_var(var), m_coeff(val) { + } + unsigned var() const { return m_var;} + const mpq & coeff() const { return m_coeff; } + }; + + term_with_basis_col(const C& row, unsigned bj) : m_row(row), m_bj(bj) {} + struct const_iterator { + // fields + typename C::const_iterator m_it; + unsigned m_bj; + - linear_combination_iterator & m_it; - lp_bound_propagator & m_bp; - unsigned m_row_or_term_index; - int m_column_of_u; // index of an unlimited from above monoid - // -1 means that such a value is not found, -2 means that at least two of such monoids were found - int m_column_of_l; // index of an unlimited from below monoid - impq m_rs; + //typedefs + + + typedef const_iterator self_type; + typedef ival value_type; + typedef ival reference; + typedef int difference_type; + typedef std::forward_iterator_tag iterator_category; + + reference operator*() const { + if (m_bj == static_cast(-1)) + return ival((*m_it).var(), (*m_it).coeff()); + return ival(m_bj, - 1); + } + self_type operator++() { self_type i = *this; operator++(1); return i; } + + self_type operator++(int) { + if (m_bj == static_cast(-1)) + m_it++; + else + m_bj = static_cast(-1); + return *this; + } + + // constructor + const_iterator(const typename C::const_iterator& it, unsigned bj) : + m_it(it), + m_bj(bj) + {} + bool operator==(const self_type &other) const { + return m_it == other.m_it && m_bj == other.m_bj ; + } + bool operator!=(const self_type &other) const { return !(*this == other); } + }; + const_iterator begin() const { + return const_iterator( m_row.begin(), m_bj); + } + const_iterator end() const { return const_iterator(m_row.end(), m_bj); } + }; + term_with_basis_col m_row; + bound_propagator & m_bp; + unsigned m_row_or_term_index; + int m_column_of_u; // index of an unlimited from above monoid + // -1 means that such a value is not found, -2 means that at least two of such monoids were found + int m_column_of_l; // index of an unlimited from below monoid + impq m_rs; public : // constructor bound_analyzer_on_row( - linear_combination_iterator &it, - const numeric_pair& rs, - unsigned row_or_term_index, - lp_bound_propagator & bp + const C & it, + unsigned bj, // basis column for the row + const numeric_pair& rs, + unsigned row_or_term_index, + bound_propagator & bp ) : - m_it(it), + m_row(it, bj), m_bp(bp), m_row_or_term_index(row_or_term_index), m_column_of_u(-1), @@ -60,11 +116,11 @@ public : unsigned j; void analyze() { - - mpq a; unsigned j; - while (((m_column_of_l != -2) || (m_column_of_u != -2)) && m_it.next(a, j)) - analyze_bound_on_var_on_coeff(j, a); - + for (const auto & c : m_row) { + if ((m_column_of_l == -2) && (m_column_of_u == -2)) + break; + analyze_bound_on_var_on_coeff(c.var(), c.coeff()); + } if (m_column_of_u >= 0) limit_monoid_u_from_below(); else if (m_column_of_u == -1) @@ -76,42 +132,42 @@ public : limit_all_monoids_from_above(); } - bool bound_is_available(unsigned j, bool low_bound) { - return (low_bound && low_bound_is_available(j)) || - (!low_bound && upper_bound_is_available(j)); + bool bound_is_available(unsigned j, bool lower_bound) { + return (lower_bound && lower_bound_is_available(j)) || + (!lower_bound && upper_bound_is_available(j)); } bool upper_bound_is_available(unsigned j) const { switch (m_bp.get_column_type(j)) - { - case column_type::fixed: - case column_type::boxed: - case column_type::upper_bound: - return true; - default: - return false; - } + { + case column_type::fixed: + case column_type::boxed: + case column_type::upper_bound: + return true; + default: + return false; + } } - bool low_bound_is_available(unsigned j) const { + bool lower_bound_is_available(unsigned j) const { switch (m_bp.get_column_type(j)) - { - case column_type::fixed: - case column_type::boxed: - case column_type::low_bound: - return true; - default: - return false; - } + { + case column_type::fixed: + case column_type::boxed: + case column_type::lower_bound: + return true; + default: + return false; + } } const impq & ub(unsigned j) const { - SASSERT(upper_bound_is_available(j)); + lp_assert(upper_bound_is_available(j)); return m_bp.get_upper_bound(j); } const impq & lb(unsigned j) const { - SASSERT(low_bound_is_available(j)); - return m_bp.get_low_bound(j); + lp_assert(lower_bound_is_available(j)); + return m_bp.get_lower_bound(j); } @@ -151,7 +207,7 @@ public : strict = !is_zero(ub(j).y); return a * ub(j).x; } - + strict = !is_zero(lb(j).y); return a * lb(j).x; } @@ -160,34 +216,32 @@ public : if (is_neg(a)) { return a * ub(j).x; } - + return a * lb(j).x; } - + void limit_all_monoids_from_above() { int strict = 0; mpq total; - SASSERT(is_zero(total)); - m_it.reset(); - mpq a; unsigned j; - while (m_it.next(a, j)) { + lp_assert(is_zero(total)); + for (const auto& p : m_row) { bool str; - total -= monoid_min(a, j, str); + total -= monoid_min(p.coeff(), p.var(), str); if (str) strict++; } - m_it.reset(); - while (m_it.next(a, j)) { + + for (const auto &p : m_row) { bool str; - bool a_is_pos = is_pos(a); - mpq bound = total / a + monoid_min_no_mult(a_is_pos, j, str); + bool a_is_pos = is_pos(p.coeff()); + mpq bound = total / p.coeff() + monoid_min_no_mult(a_is_pos, p.var(), str); if (a_is_pos) { - limit_j(j, bound, true, false, strict - static_cast(str) > 0); + limit_j(p.var(), bound, true, false, strict - static_cast(str) > 0); } else { - limit_j(j, bound, false, true, strict - static_cast(str) > 0); + limit_j(p.var(), bound, false, true, strict - static_cast(str) > 0); } } } @@ -195,52 +249,50 @@ public : void limit_all_monoids_from_below() { int strict = 0; mpq total; - SASSERT(is_zero(total)); - m_it.reset(); - mpq a; unsigned j; - while (m_it.next(a, j)) { + lp_assert(is_zero(total)); + for (const auto &p : m_row) { bool str; - total -= monoid_max(a, j, str); + total -= monoid_max(p.coeff(), p.var(), str); if (str) strict++; } - m_it.reset(); - while (m_it.next(a, j)) { + + for (const auto& p : m_row) { bool str; - bool a_is_pos = is_pos(a); - mpq bound = total / a + monoid_max_no_mult(a_is_pos, j, str); - bool astrict = strict - static_cast(str) > 0; + bool a_is_pos = is_pos(p.coeff()); + mpq bound = total / p.coeff() + monoid_max_no_mult(a_is_pos, p.var(), str); + bool astrict = strict - static_cast(str) > 0; if (a_is_pos) { - limit_j(j, bound, true, true, astrict); + limit_j(p.var(), bound, true, true, astrict); } else { - limit_j(j, bound, false, false, astrict); + limit_j(p.var(), bound, false, false, astrict); } } } - + void limit_monoid_u_from_below() { // we are going to limit from below the monoid m_column_of_u, // every other monoid is impossible to limit from below - mpq u_coeff, a; + mpq u_coeff; unsigned j; mpq bound = -m_rs.x; - m_it.reset(); bool strict = false; - while (m_it.next(a, j)) { + for (const auto& p : m_row) { + j = p.var(); if (j == static_cast(m_column_of_u)) { - u_coeff = a; + u_coeff = p.coeff(); continue; } bool str; - bound -= monoid_max(a, j, str); + bound -= monoid_max(p.coeff(), j, str); if (str) strict = true; } bound /= u_coeff; - + if (numeric_traits::is_pos(u_coeff)) { limit_j(m_column_of_u, bound, true, true, strict); } else { @@ -252,19 +304,19 @@ public : void limit_monoid_l_from_above() { // we are going to limit from above the monoid m_column_of_l, // every other monoid is impossible to limit from above - mpq l_coeff, a; + mpq l_coeff; unsigned j; mpq bound = -m_rs.x; bool strict = false; - m_it.reset(); - while (m_it.next(a, j)) { + for (const auto &p : m_row) { + j = p.var(); if (j == static_cast(m_column_of_l)) { - l_coeff = a; + l_coeff = p.coeff(); continue; } bool str; - bound -= monoid_min(a, j, str); + bound -= monoid_min(p.coeff(), j, str); if (str) strict = true; } @@ -275,51 +327,51 @@ public : limit_j(m_column_of_l, bound, false, true, strict); } } - + // // it is the coefficent before the bounded column // void provide_evidence(bool coeff_is_pos) { // /* // auto & be = m_ibounds.back(); - // bool low_bound = be.m_low_bound; + // bool lower_bound = be.m_lower_bound; // if (!coeff_is_pos) - // low_bound = !low_bound; - // auto it = m_it.clone(); + // lower_bound = !lower_bound; + // auto it = m_row.clone(); // mpq a; unsigned j; // while (it->next(a, j)) { // if (be.m_j == j) continue; - // SASSERT(bound_is_available(j, is_neg(a) ? low_bound : !low_bound)); + // lp_assert(bound_is_available(j, is_neg(a) ? lower_bound : !lower_bound)); // be.m_vector_of_bound_signatures.emplace_back(a, j, numeric_traits:: - // is_neg(a)? low_bound: !low_bound); + // is_neg(a)? lower_bound: !lower_bound); // } // delete it; // */ // } - void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_low_bound, bool strict){ - m_bp.try_add_bound(u, j, is_low_bound, coeff_before_j_is_pos, m_row_or_term_index, strict); + void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_lower_bound, bool strict){ + m_bp.try_add_bound(u, j, is_lower_bound, coeff_before_j_is_pos, m_row_or_term_index, strict); } - + void advance_u(unsigned j) { if (m_column_of_u == -1) m_column_of_u = j; else m_column_of_u = -2; } - + void advance_l(unsigned j) { if (m_column_of_l == -1) m_column_of_l = j; else m_column_of_l = -2; } - + void analyze_bound_on_var_on_coeff(int j, const mpq &a) { switch (m_bp.get_column_type(j)) { - case column_type::low_bound: + case column_type::lower_bound: if (numeric_traits::is_pos(a)) advance_u(j); - else + else advance_l(j); break; case column_type::upper_bound: @@ -337,14 +389,16 @@ public : } } - static void analyze_row(linear_combination_iterator &it, + static void analyze_row(const C & row, + unsigned bj, // basis column for the row const numeric_pair& rs, unsigned row_or_term_index, - lp_bound_propagator & bp + bound_propagator & bp ) { - bound_analyzer_on_row a(it, rs, row_or_term_index, bp); + bound_analyzer_on_row a(row, bj, rs, row_or_term_index, bp); a.analyze(); } }; } + diff --git a/src/util/lp/bound_propagator.cpp b/src/util/lp/bound_propagator.cpp new file mode 100644 index 000000000..c4fa2aefa --- /dev/null +++ b/src/util/lp/bound_propagator.cpp @@ -0,0 +1,58 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Lev Nachmanson +*/ +#include "util/lp/lar_solver.h" +namespace lp { +bound_propagator::bound_propagator(lar_solver & ls): + m_lar_solver(ls) {} +column_type bound_propagator::get_column_type(unsigned j) const { + return m_lar_solver.m_mpq_lar_core_solver.m_column_types()[j]; +} +const impq & bound_propagator::get_lower_bound(unsigned j) const { + return m_lar_solver.m_mpq_lar_core_solver.m_r_lower_bounds()[j]; +} +const impq & bound_propagator::get_upper_bound(unsigned j) const { + return m_lar_solver.m_mpq_lar_core_solver.m_r_upper_bounds()[j]; +} +void bound_propagator::try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) { + j = m_lar_solver.adjust_column_index_to_term_index(j); + if (m_lar_solver.is_term(j)) { + // lp treats terms as not having a free coefficient, restoring it below for the outside consumption + v += m_lar_solver.get_term(j).m_v; + } + + lconstraint_kind kind = is_low? GE : LE; + if (strict) + kind = static_cast(kind / 2); + + if (!bound_is_interesting(j, kind, v)) + return; + unsigned k; // index to ibounds + if (is_low) { + if (try_get_value(m_improved_lower_bounds, j, k)) { + auto & found_bound = m_ibounds[k]; + if (v > found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict)) { + found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); + TRACE("try_add_bound", m_lar_solver.print_implied_bound(found_bound, tout);); + } + } else { + m_improved_lower_bounds[j] = m_ibounds.size(); + m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); + TRACE("try_add_bound", m_lar_solver.print_implied_bound(m_ibounds.back(), tout);); + } + } else { // the upper bound case + if (try_get_value(m_improved_upper_bounds, j, k)) { + auto & found_bound = m_ibounds[k]; + if (v < found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict)) { + found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); + TRACE("try_add_bound", m_lar_solver.print_implied_bound(found_bound, tout);); + } + } else { + m_improved_upper_bounds[j] = m_ibounds.size(); + m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); + TRACE("try_add_bound", m_lar_solver.print_implied_bound(m_ibounds.back(), tout);); + } + } +} +} diff --git a/src/util/lp/bound_propagator.h b/src/util/lp/bound_propagator.h new file mode 100644 index 000000000..a1f0301aa --- /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_lower_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_lower_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, lp::constraint_index j) = 0; +}; +} diff --git a/src/util/lp/column_info.h b/src/util/lp/column_info.h index e4b449bbf..407f40dfc 100644 --- a/src/util/lp/column_info.h +++ b/src/util/lp/column_info.h @@ -30,11 +30,11 @@ inline bool is_valid(unsigned j) { return static_cast(j) >= 0;} template class column_info { std::string m_name; - bool m_low_bound_is_set; - bool m_low_bound_is_strict; + bool m_lower_bound_is_set; + bool m_lower_bound_is_strict; bool m_upper_bound_is_set; bool m_upper_bound_is_strict; - T m_low_bound; + T m_lower_bound; T m_upper_bound; T m_fixed_value; bool m_is_fixed; @@ -43,11 +43,11 @@ class column_info { public: bool operator==(const column_info & c) const { return m_name == c.m_name && - m_low_bound_is_set == c.m_low_bound_is_set && - m_low_bound_is_strict == c.m_low_bound_is_strict && + m_lower_bound_is_set == c.m_lower_bound_is_set && + m_lower_bound_is_strict == c.m_lower_bound_is_strict && m_upper_bound_is_set == c.m_upper_bound_is_set&& m_upper_bound_is_strict == c.m_upper_bound_is_strict&& - (!m_low_bound_is_set || m_low_bound == c.m_low_bound) && + (!m_lower_bound_is_set || m_lower_bound == c.m_low_bound) && (!m_upper_bound_is_set || m_upper_bound == c.m_upper_bound) && m_cost == c.m_cost && m_is_fixed == c.m_is_fixed && @@ -60,8 +60,8 @@ public: } // the default constructor column_info(): - m_low_bound_is_set(false), - m_low_bound_is_strict(false), + m_lower_bound_is_set(false), + m_lower_bound_is_strict(false), m_upper_bound_is_set (false), m_upper_bound_is_strict (false), m_is_fixed(false), @@ -70,8 +70,8 @@ public: {} column_info(unsigned column_index) : - m_low_bound_is_set(false), - m_low_bound_is_strict(false), + m_lower_bound_is_set(false), + m_lower_bound_is_strict(false), m_upper_bound_is_set (false), m_upper_bound_is_strict (false), m_is_fixed(false), @@ -81,11 +81,11 @@ public: column_info(const column_info & ci) { m_name = ci.m_name; - m_low_bound_is_set = ci.m_low_bound_is_set; - m_low_bound_is_strict = ci.m_low_bound_is_strict; + m_lower_bound_is_set = ci.m_lower_bound_is_set; + m_lower_bound_is_strict = ci.m_lower_bound_is_strict; m_upper_bound_is_set = ci.m_upper_bound_is_set; m_upper_bound_is_strict = ci.m_upper_bound_is_strict; - m_low_bound = ci.m_low_bound; + m_lower_bound = ci.m_lower_bound; m_upper_bound = ci.m_upper_bound; m_cost = ci.m_cost; m_fixed_value = ci.m_fixed_value; @@ -98,7 +98,7 @@ public: } column_type get_column_type() const { - return m_is_fixed? column_type::fixed : (m_low_bound_is_set? (m_upper_bound_is_set? column_type::boxed : column_type::low_bound) : (m_upper_bound_is_set? column_type::upper_bound: column_type::free_column)); + return m_is_fixed? column_type::fixed : (m_lower_bound_is_set? (m_upper_bound_is_set? column_type::boxed : column_type::lower_bound) : (m_upper_bound_is_set? column_type::upper_bound: column_type::free_column)); } column_type get_column_type_no_flipping() const { @@ -106,25 +106,25 @@ public: return column_type::fixed; } - if (m_low_bound_is_set) { - return m_upper_bound_is_set? column_type::boxed: column_type::low_bound; + if (m_lower_bound_is_set) { + return m_upper_bound_is_set? column_type::boxed: column_type::lower_bound; } // we are flipping the bounds! return m_upper_bound_is_set? column_type::upper_bound : column_type::free_column; } - T get_low_bound() const { - SASSERT(m_low_bound_is_set); - return m_low_bound; + T get_lower_bound() const { + lp_assert(m_lower_bound_is_set); + return m_lower_bound; } T get_upper_bound() const { - SASSERT(m_upper_bound_is_set); + lp_assert(m_upper_bound_is_set); return m_upper_bound; } - bool low_bound_is_set() const { - return m_low_bound_is_set; + bool lower_bound_is_set() const { + return m_lower_bound_is_set; } bool upper_bound_is_set() const { @@ -138,23 +138,23 @@ public: if (is_flipped()){ return m_upper_bound; } - return m_low_bound_is_set? m_low_bound : numeric_traits::zero(); + return m_lower_bound_is_set? m_lower_bound : numeric_traits::zero(); } bool is_flipped() { - return m_upper_bound_is_set && !m_low_bound_is_set; + return m_upper_bound_is_set && !m_lower_bound_is_set; } - bool adjusted_low_bound_is_set() { - return !is_flipped()? low_bound_is_set(): upper_bound_is_set(); + bool adjusted_lower_bound_is_set() { + return !is_flipped()? lower_bound_is_set(): upper_bound_is_set(); } bool adjusted_upper_bound_is_set() { - return !is_flipped()? upper_bound_is_set(): low_bound_is_set(); + return !is_flipped()? upper_bound_is_set(): lower_bound_is_set(); } T get_adjusted_upper_bound() { - return get_upper_bound() - get_low_bound(); + return get_upper_bound() - get_lower_bound(); } bool is_fixed() const { @@ -162,7 +162,7 @@ public: } bool is_free() { - return !m_low_bound_is_set && !m_upper_bound_is_set; + return !m_lower_bound_is_set && !m_upper_bound_is_set; } void set_fixed_value(T v) { @@ -171,7 +171,7 @@ public: } T get_fixed_value() const { - SASSERT(m_is_fixed); + lp_assert(m_is_fixed); return m_fixed_value; } @@ -191,9 +191,9 @@ public: return m_name; } - void set_low_bound(T const & l) { - m_low_bound = l; - m_low_bound_is_set = true; + void set_lower_bound(T const & l) { + m_lower_bound = l; + m_lower_bound_is_set = true; } void set_upper_bound(T const & l) { @@ -201,8 +201,8 @@ public: m_upper_bound_is_set = true; } - void unset_low_bound() { - m_low_bound_is_set = false; + void unset_lower_bound() { + m_lower_bound_is_set = false; } void unset_upper_bound() { @@ -213,8 +213,8 @@ public: m_is_fixed = false; } - bool low_bound_holds(T v) { - return !low_bound_is_set() || v >= m_low_bound -T(0.0000001); + bool lower_bound_holds(T v) { + return !lower_bound_is_set() || v >= m_lower_bound -T(0.0000001); } bool upper_bound_holds(T v) { @@ -222,36 +222,36 @@ public: } bool bounds_hold(T v) { - return low_bound_holds(v) && upper_bound_holds(v); + return lower_bound_holds(v) && upper_bound_holds(v); } bool adjusted_bounds_hold(T v) { - return adjusted_low_bound_holds(v) && adjusted_upper_bound_holds(v); + return adjusted_lower_bound_holds(v) && adjusted_upper_bound_holds(v); } - bool adjusted_low_bound_holds(T v) { - return !adjusted_low_bound_is_set() || v >= -T(0.0000001); + bool adjusted_lower_bound_holds(T v) { + return !adjusted_lower_bound_is_set() || v >= -T(0.0000001); } bool adjusted_upper_bound_holds(T v) { return !adjusted_upper_bound_is_set() || v <= get_adjusted_upper_bound() + T(0.000001); } bool is_infeasible() { - if ((!upper_bound_is_set()) || (!low_bound_is_set())) + if ((!upper_bound_is_set()) || (!lower_bound_is_set())) return false; // ok, both bounds are set - bool at_least_one_is_strict = upper_bound_is_strict() || low_bound_is_strict(); + bool at_least_one_is_strict = upper_bound_is_strict() || lower_bound_is_strict(); if (!at_least_one_is_strict) - return get_upper_bound() < get_low_bound(); + return get_upper_bound() < get_lower_bound(); // at least on bound is strict - return get_upper_bound() <= get_low_bound(); // the equality is impossible + return get_upper_bound() <= get_lower_bound(); // the equality is impossible } - bool low_bound_is_strict() const { - return m_low_bound_is_strict; + bool lower_bound_is_strict() const { + return m_lower_bound_is_strict; } - void set_low_bound_strict(bool val) { - m_low_bound_is_strict = val; + void set_lower_bound_strict(bool val) { + m_lower_bound_is_strict = val; } bool upper_bound_is_strict() const { diff --git a/src/util/lp/column_namer.h b/src/util/lp/column_namer.h index 97d371f48..e6e8e53a2 100644 --- a/src/util/lp/column_namer.h +++ b/src/util/lp/column_namer.h @@ -19,31 +19,19 @@ Revision History: --*/ #include -#include "util/lp/linear_combination_iterator.h" +#include "util/lp/static_matrix.h" namespace lp { class column_namer { public: virtual std::string get_column_name(unsigned j) const = 0; template - void print_linear_iterator(linear_combination_iterator* it, std::ostream & out) const { + void print_row(const row_strip & row, std::ostream & out) const { vector> coeff; - T a; - unsigned i; - while (it->next(a, i)) { - coeff.push_back(std::make_pair(a, i)); + for (auto & p : row) { + coeff.push_back(std::make_pair(p.coeff(), p.var())); } print_linear_combination_of_column_indices(coeff, out); } - template - void print_linear_iterator_indices_only(linear_combination_iterator* it, std::ostream & out) const { - vector> coeff; - T a; - unsigned i; - while (it->next(a, i)) { - coeff.emplace_back(a, i); - } - print_linear_combination_of_column_indices_only(coeff, out); - } template void print_linear_combination_of_column_indices_only(const vector> & coeffs, std::ostream & out) const { @@ -65,10 +53,34 @@ public: else if (val != numeric_traits::one()) out << T_to_string(val); - out << "_" << it.second; + out << "v" << it.second; } } + + template + void print_linear_combination_of_column_indices_std(const vector> & coeffs, std::ostream & out) const { + bool first = true; + for (const auto & it : coeffs) { + auto val = it.first; + if (first) { + first = false; + } else { + if (numeric_traits::is_pos(val)) { + out << " + "; + } else { + out << " - "; + val = -val; + } + } + if (val == -numeric_traits::one()) + out << " - "; + else if (val != numeric_traits::one()) + out << val; + + out << get_column_name(it.second); + } + } template void print_linear_combination_of_column_indices(const vector> & coeffs, std::ostream & out) const { bool first = true; diff --git a/src/util/lp/constraint.h b/src/util/lp/constraint.h new file mode 100644 index 000000000..84ec188d6 --- /dev/null +++ b/src/util/lp/constraint.h @@ -0,0 +1,99 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ + +#pragma once +namespace lp { +class constraint; // forward definition +struct constraint_hash { + size_t operator() (const constraint* c) const; +}; + +struct constraint_equal { + bool operator() (const constraint * a, const constraint * b) const; +}; + +class constraint { // we only have less or equal for the inequality sign, which is enough for integral variables + int m_id; + bool m_is_ineq; + polynomial m_poly; + mpq m_d; // the divider for the case of a divisibility constraint + std::unordered_set m_assert_origins; // these indices come from the client and get collected during tightening +public : + unsigned id() const { return m_id; } + const polynomial & poly() const { return m_poly; } + polynomial & poly() { return m_poly; } + std::unordered_set & assert_origins() { return m_assert_origins;} + const std::unordered_set & assert_origins() const { return m_assert_origins;} + bool is_lemma() const { return !is_assert(); } + bool is_assert() const { return m_assert_origins.size() == 1; } + bool is_ineq() const { return m_is_ineq; } + const mpq & divider() const { return m_d; } +public: + constraint( + unsigned id, + constraint_index assert_origin, + const polynomial & p, + bool is_ineq): + m_id(id), + m_is_ineq(is_ineq), + m_poly(p) + { // creates an assert + m_assert_origins.insert(assert_origin); + } + constraint( + unsigned id, + const std::unordered_set& origins, + const polynomial & p, + bool is_ineq): + m_id(id), + m_is_ineq(is_ineq), + m_poly(p), + m_assert_origins(origins) + {} + + + + constraint( + unsigned id, + const polynomial & p, + bool is_ineq): + m_id(id), + m_is_ineq(is_ineq), + m_poly(p) { // creates a lemma + } + +public: + constraint() {} + + const mpq & coeff(var_index j) const { + return m_poly.coeff(j); + } + const vector& coeffs() const { return m_poly.m_coeffs;} + + bool is_tight(unsigned j) const { + const mpq & a = m_poly.coeff(j); + return a == 1 || a == -1; + } + void add_predecessor(const constraint* p) { + lp_assert(p != nullptr); + for (auto m : p->assert_origins()) + m_assert_origins.insert(m); } +}; +} diff --git a/src/util/lp/conversion_helper.h b/src/util/lp/conversion_helper.h index f80b1c2c6..feb999743 100644 --- a/src/util/lp/conversion_helper.h +++ b/src/util/lp/conversion_helper.h @@ -22,8 +22,8 @@ Revision History: namespace lp { template struct conversion_helper { - static V get_low_bound(const column_info & ci) { - return V(ci.get_low_bound(), ci.low_bound_is_strict()? 1 : 0); + static V get_lower_bound(const column_info & ci) { + return V(ci.get_lower_bound(), ci.lower_bound_is_strict()? 1 : 0); } static V get_upper_bound(const column_info & ci) { @@ -37,20 +37,20 @@ struct conversion_helper { if (!ci.upper_bound_is_strict()) return ci.get_upper_bound().get_double(); double eps = 0.00001; - if (!ci.low_bound_is_set()) + if (!ci.lower_bound_is_set()) return ci.get_upper_bound().get_double() - eps; - eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double() / 1000, eps); + eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); return ci.get_upper_bound().get_double() - eps; } - static double get_low_bound(const column_info & ci) { - if (!ci.low_bound_is_strict()) - return ci.get_low_bound().get_double(); + static double get_lower_bound(const column_info & ci) { + if (!ci.lower_bound_is_strict()) + return ci.get_lower_bound().get_double(); double eps = 0.00001; if (!ci.upper_bound_is_set()) - return ci.get_low_bound().get_double() + eps; - eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double() / 1000, eps); - return ci.get_low_bound().get_double() + eps; + return ci.get_lower_bound().get_double() + eps; + eps = std::min((ci.get_upper_bound() - ci.get_lower_bound()).get_double() / 1000, eps); + return ci.get_lower_bound().get_double() + eps; } }; diff --git a/src/util/lp/core_solver_pretty_printer_instances.cpp b/src/util/lp/core_solver_pretty_printer.cpp similarity index 95% rename from src/util/lp/core_solver_pretty_printer_instances.cpp rename to src/util/lp/core_solver_pretty_printer.cpp index 0bd7f5559..3cba2240d 100644 --- a/src/util/lp/core_solver_pretty_printer_instances.cpp +++ b/src/util/lp/core_solver_pretty_printer.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "util/lp/numeric_pair.h" -#include "util/lp/core_solver_pretty_printer.hpp" +#include "util/lp/core_solver_pretty_printer_def.h" template lp::core_solver_pretty_printer::core_solver_pretty_printer(lp::lp_core_solver_base &, std::ostream & out); template void lp::core_solver_pretty_printer::print(); template lp::core_solver_pretty_printer::~core_solver_pretty_printer(); diff --git a/src/util/lp/core_solver_pretty_printer.h b/src/util/lp/core_solver_pretty_printer.h index 87c528792..2f2c61f7b 100644 --- a/src/util/lp/core_solver_pretty_printer.h +++ b/src/util/lp/core_solver_pretty_printer.h @@ -48,7 +48,7 @@ class core_solver_pretty_printer { std::string m_cost_title; std::string m_basis_heading_title; std::string m_x_title; - std::string m_low_bounds_title; + std::string m_lower_bounds_title; std::string m_upp_bounds_title; std::string m_exact_norm_title; std::string m_approx_norm_title; @@ -75,7 +75,7 @@ public: void init_column_widths(); - void adjust_width_with_low_bound(unsigned column, unsigned & w); + void adjust_width_with_lower_bound(unsigned column, unsigned & w); void adjust_width_with_upper_bound(unsigned column, unsigned & w); void adjust_width_with_bounds(unsigned column, unsigned & w); @@ -97,7 +97,7 @@ public: void print_x(); - std::string get_low_bound_string(unsigned j); + std::string get_lower_bound_string(unsigned j); std::string get_upp_bound_string(unsigned j); diff --git a/src/util/lp/core_solver_pretty_printer.hpp b/src/util/lp/core_solver_pretty_printer_def.h similarity index 93% rename from src/util/lp/core_solver_pretty_printer.hpp rename to src/util/lp/core_solver_pretty_printer_def.h index 4ae49a550..58ffbb481 100644 --- a/src/util/lp/core_solver_pretty_printer.hpp +++ b/src/util/lp/core_solver_pretty_printer_def.h @@ -38,7 +38,7 @@ core_solver_pretty_printer::core_solver_pretty_printer(lp_core_solver_base m_rs(ncols(), zero_of_type()), m_w_buff(core_solver.m_w), m_ed_buff(core_solver.m_ed) { - m_low_bounds_title = "low"; + m_lower_bounds_title = "low"; m_upp_bounds_title = "upp"; m_exact_norm_title = "exact cn"; m_approx_norm_title = "approx cn"; @@ -105,6 +105,8 @@ template void core_solver_pretty_printer::init_m_ string name = m_core_solver.column_name(column); for (unsigned row = 0; row < nrows(); row ++) { + m_A[row].resize(ncols(), ""); + m_signs[row].resize(ncols(),""); set_coeff( m_A[row], m_signs[row], @@ -139,9 +141,9 @@ template void core_solver_pretty_printer::init_co } } -template void core_solver_pretty_printer::adjust_width_with_low_bound(unsigned column, unsigned & w) { - if (!m_core_solver.low_bounds_are_set()) return; - w = std::max(w, (unsigned)T_to_string(m_core_solver.low_bound_value(column)).size()); +template void core_solver_pretty_printer::adjust_width_with_lower_bound(unsigned column, unsigned & w) { + if (!m_core_solver.lower_bounds_are_set()) return; + w = std::max(w, (unsigned)T_to_string(m_core_solver.lower_bound_value(column)).size()); } template void core_solver_pretty_printer::adjust_width_with_upper_bound(unsigned column, unsigned & w) { w = std::max(w, (unsigned)T_to_string(m_core_solver.upper_bound_value(column)).size()); @@ -151,11 +153,11 @@ template void core_solver_pretty_printer::adjust_ switch (m_core_solver.get_column_type(column)) { case column_type::fixed: case column_type::boxed: - adjust_width_with_low_bound(column, w); + adjust_width_with_lower_bound(column, w); adjust_width_with_upper_bound(column, w); break; - case column_type::low_bound: - adjust_width_with_low_bound(column, w); + case column_type::lower_bound: + adjust_width_with_lower_bound(column, w); break; case column_type::upper_bound: adjust_width_with_upper_bound(column, w); @@ -163,7 +165,7 @@ template void core_solver_pretty_printer::adjust_ case column_type::free_column: break; default: - SASSERT(false); + lp_assert(false); break; } } @@ -236,13 +238,13 @@ template void core_solver_pretty_printer::print_x m_out << std::endl; } -template std::string core_solver_pretty_printer::get_low_bound_string(unsigned j) { +template std::string core_solver_pretty_printer::get_lower_bound_string(unsigned j) { switch (m_core_solver.get_column_type(j)){ case column_type::boxed: - case column_type::low_bound: + case column_type::lower_bound: case column_type::fixed: - if (m_core_solver.low_bounds_are_set()) - return T_to_string(m_core_solver.low_bound_value(j)); + if (m_core_solver.lower_bounds_are_set()) + return T_to_string(m_core_solver.lower_bound_value(j)); else return std::string("0"); break; @@ -268,12 +270,12 @@ template void core_solver_pretty_printer::print_l if (ncols() == 0) { return; } - int blanks = m_title_width + 1 - static_cast(m_low_bounds_title.size()); - m_out << m_low_bounds_title; + int blanks = m_title_width + 1 - static_cast(m_lower_bounds_title.size()); + m_out << m_lower_bounds_title; print_blanks(blanks, m_out); for (unsigned i = 0; i < ncols(); i++) { - string s = get_low_bound_string(i); + string s = get_lower_bound_string(i); int blanks = m_column_widths[i] - static_cast(s.size()); print_blanks(blanks, m_out); m_out << s << " "; // the column interval @@ -372,7 +374,7 @@ template void core_solver_pretty_printer::print_g unsigned width = m_column_widths[col]; string s = row[col]; int number_of_blanks = width - static_cast(s.size()); - SASSERT(number_of_blanks >= 0); + lp_assert(number_of_blanks >= 0); print_blanks(number_of_blanks, m_out); m_out << s << ' '; if (col < row.size() - 1) { @@ -383,7 +385,7 @@ template void core_solver_pretty_printer::print_g string rs = T_to_string(rst); int nb = m_rs_width - static_cast(rs.size()); - SASSERT(nb >= 0); + lp_assert(nb >= 0); print_blanks(nb + 1, m_out); m_out << rs << std::endl; } diff --git a/src/util/lp/dense_matrix_instances.cpp b/src/util/lp/dense_matrix.cpp similarity index 98% rename from src/util/lp/dense_matrix_instances.cpp rename to src/util/lp/dense_matrix.cpp index 54b0d15d6..50f2598c8 100644 --- a/src/util/lp/dense_matrix_instances.cpp +++ b/src/util/lp/dense_matrix.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "util/lp/lp_settings.h" -#include "util/lp/dense_matrix.hpp" +#include "util/lp/dense_matrix_def.h" #ifdef Z3DEBUG #include "util/vector.h" template lp::dense_matrix lp::operator*(lp::matrix&, lp::matrix&); diff --git a/src/util/lp/dense_matrix.h b/src/util/lp/dense_matrix.h index e2ee54058..e1f34ecd9 100644 --- a/src/util/lp/dense_matrix.h +++ b/src/util/lp/dense_matrix.h @@ -46,7 +46,7 @@ public: dense_matrix(unsigned m, unsigned n); dense_matrix operator*=(matrix const & a) { - SASSERT(column_count() == a.row_count()); + lp_assert(column_count() == a.row_count()); dense_matrix c(row_count(), a.column_count()); for (unsigned i = 0; i < row_count(); i++) { for (unsigned j = 0; j < a.column_count(); j++) { @@ -100,7 +100,7 @@ public: void swap_rows(unsigned a, unsigned b); void multiply_row_by_constant(unsigned row, T & t); - + }; template dense_matrix operator* (matrix & a, matrix & b); diff --git a/src/util/lp/dense_matrix.hpp b/src/util/lp/dense_matrix_def.h similarity index 97% rename from src/util/lp/dense_matrix.hpp rename to src/util/lp/dense_matrix_def.h index a1f815109..f615c412d 100644 --- a/src/util/lp/dense_matrix.hpp +++ b/src/util/lp/dense_matrix_def.h @@ -23,7 +23,6 @@ Revision History: #include "util/lp/numeric_pair.h" #include "util/lp/dense_matrix.h" namespace lp { -template void print_vector(const vector & t, std::ostream & out); template dense_matrix::dense_matrix(unsigned m, unsigned n) : m_m(m), m_n(n), m_values(m * n, numeric_traits::zero()) { } @@ -185,7 +184,7 @@ template void dense_matrix::multiply_row_by_const template dense_matrix operator* (matrix & a, matrix & b){ - SASSERT(a.column_count() == b.row_count()); + lp_assert(a.column_count() == b.row_count()); dense_matrix ret(a.row_count(), b.column_count()); for (unsigned i = 0; i < ret.m_m; i++) for (unsigned j = 0; j< ret.m_n; j++) { diff --git a/src/util/lp/disjoint_intervals.h b/src/util/lp/disjoint_intervals.h deleted file mode 100644 index 5f4f31af6..000000000 --- a/src/util/lp/disjoint_intervals.h +++ /dev/null @@ -1,334 +0,0 @@ -/* - 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/eta_matrix_instances.cpp b/src/util/lp/eta_matrix.cpp similarity index 98% rename from src/util/lp/eta_matrix_instances.cpp rename to src/util/lp/eta_matrix.cpp index 87e12c913..4cb43d87f 100644 --- a/src/util/lp/eta_matrix_instances.cpp +++ b/src/util/lp/eta_matrix.cpp @@ -20,7 +20,7 @@ Revision History: #include #include "util/vector.h" #include "util/lp/numeric_pair.h" -#include "util/lp/eta_matrix.hpp" +#include "util/lp/eta_matrix_def.h" #ifdef Z3DEBUG template double lp::eta_matrix::get_elem(unsigned int, unsigned int) const; template lp::mpq lp::eta_matrix::get_elem(unsigned int, unsigned int) const; diff --git a/src/util/lp/eta_matrix.h b/src/util/lp/eta_matrix.h index abed6d06b..a811492f7 100644 --- a/src/util/lp/eta_matrix.h +++ b/src/util/lp/eta_matrix.h @@ -76,7 +76,7 @@ public: void push_back(unsigned row_index, T val ) { - SASSERT(row_index != m_column_index); + lp_assert(row_index != m_column_index); m_column_vector.push_back(row_index, val); } diff --git a/src/util/lp/eta_matrix.hpp b/src/util/lp/eta_matrix_def.h similarity index 95% rename from src/util/lp/eta_matrix.hpp rename to src/util/lp/eta_matrix_def.h index ae4ed712e..5c7661e24 100644 --- a/src/util/lp/eta_matrix.hpp +++ b/src/util/lp/eta_matrix_def.h @@ -75,7 +75,7 @@ void eta_matrix::apply_from_right(vector & w) { } w[m_column_index] = t; #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(clone_w, w, get_number_of_rows())); + // lp_assert(vectors_are_equal(clone_w, w, get_number_of_rows())); // delete clone_w; #endif } @@ -115,8 +115,8 @@ void eta_matrix::apply_from_right(indexed_vector & w) { } #ifdef Z3DEBUG - // SASSERT(w.is_OK()); - // SASSERT(vectors_are_equal(wcopy, w.m_data)); + // lp_assert(w.is_OK()); + // lp_assert(vectors_are_equal(wcopy, w.m_data)); #endif } #ifdef Z3DEBUG @@ -145,7 +145,7 @@ void eta_matrix::conjugate_by_permutation(permutation_matrix & p) { pair.first = p.get_rev(pair.first); } #ifdef Z3DEBUG - // SASSERT(deb == *this); + // lp_assert(deb == *this); #endif } } diff --git a/src/util/lp/explanation.h b/src/util/lp/explanation.h new file mode 100644 index 000000000..f811e1d0e --- /dev/null +++ b/src/util/lp/explanation.h @@ -0,0 +1,32 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +namespace lp { +struct explanation { + void clear() { m_explanation.clear(); } + vector> m_explanation; + void push_justification(constraint_index j, const mpq& v) { + m_explanation.push_back(std::make_pair(v, j)); + } + void push_justification(constraint_index j) { + m_explanation.push_back(std::make_pair(one_of_type(), j)); + } +}; +} diff --git a/src/util/lp/general_matrix.h b/src/util/lp/general_matrix.h new file mode 100644 index 000000000..715f2cb08 --- /dev/null +++ b/src/util/lp/general_matrix.h @@ -0,0 +1,259 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include +namespace lp { +class general_matrix { + // fields + permutation_matrix m_row_permutation; + permutation_matrix m_column_permutation; + vector> m_data; + +public: + unsigned adjust_row(unsigned row) const{ + return m_row_permutation[row]; + } + + void push_row(vector & v) { + m_data.push_back(v); + m_row_permutation.resize(m_data.size()); + m_column_permutation.resize(v.size()); + } + + unsigned adjust_column(unsigned col) const{ + return m_column_permutation.apply_reverse(col); + } + + unsigned adjust_row_inverse(unsigned row) const{ + return m_row_permutation.apply_reverse(row); + } + + unsigned adjust_column_inverse(unsigned col) const{ + return m_column_permutation[col]; + } + + + unsigned row_count() const { return m_data.size(); } + unsigned column_count() const { return m_data.size() > 0? m_data[0].size() : 0; } + + class ref_row { + general_matrix& m_matrix; + vector& m_row_data; + public: + ref_row(general_matrix& m, vector& row_data) : m_matrix(m), m_row_data(row_data) {} + mpq & operator[](unsigned col) { return m_row_data[m_matrix.adjust_column(col)]; } + }; + class ref_row_const { + const general_matrix& m_matrix; + const vector& m_row_data; + public: + ref_row_const(const general_matrix& m, const vector& row_data) : m_matrix(m), m_row_data(row_data) {} + const mpq& operator[](unsigned col) const { return m_row_data[m_matrix.adjust_column(col)]; } + }; + + ref_row operator[](unsigned i) { return ref_row(*this, m_data[adjust_row(i)]); } + ref_row_const operator[](unsigned i) const { return ref_row_const(*this, m_data[adjust_row(i)]); } + +#ifdef Z3DEBUG + void print(std::ostream & out, unsigned blanks = 0) const { + unsigned m = row_count(); + unsigned n = column_count(); + general_matrix g(m, n); + for (unsigned i = 0; i < m; i++) + for (unsigned j = 0; j < n; j++) + g[i][j] = (*this)[i][j]; + print_matrix(g.m_data, out, blanks); + } + void print(std::ostream & out, const char * ss) const { + std::string s(ss); + out << s; + print(out, static_cast(s.size())); + } + + void print_submatrix(std::ostream & out, unsigned k, unsigned blanks = 0) const { + general_matrix m(row_count() - k, column_count() - k); + for (unsigned i = k; i < row_count(); i++) { + for (unsigned j = k; j < column_count(); j++) + m[i-k][j-k] = (*this)[i][j]; + } + print_matrix(m.m_data, out, blanks); + } + +#endif + + void clear() { m_data.clear(); } + + bool row_is_initialized_correctly(const vector& row) { + lp_assert(row.size() == column_count()); + for (unsigned j = 0; j < row.size(); j ++) + lp_assert(is_zero(row[j])); + return true; + } + + template + void init_row_from_container(int i, const T & c, std::function column_fix) { + auto & row = m_data[adjust_row(i)]; + lp_assert(row_is_initialized_correctly(row)); + for (const auto & p : c) { + unsigned j = adjust_column(column_fix(p.var())); + row[j] = p.coeff(); + } + } + + void copy_column_to_indexed_vector(unsigned entering, indexed_vector &w ) const { + lp_assert(false); // not implemented + } + general_matrix operator*(const general_matrix & m) const { + lp_assert(m.row_count() == column_count()); + general_matrix ret(row_count(), m.column_count()); + for (unsigned i = 0; i < row_count(); i ++) { + for (unsigned j = 0; j < m.column_count(); j++) { + mpq a(0); + for (unsigned k = 0; k < column_count(); k++) + a += ((*this)[i][k])*m[k][j]; + ret[i][j] = a; + } + } + return ret; + } + + bool elements_are_equal(const general_matrix& m) const { + for (unsigned i = 0; i < row_count(); i++) + for (unsigned j = 0; j < column_count(); j++) + if ( (*this)[i][j] != m[i][j]) + return false; + return true; + } + + bool elements_are_equal_modulo(const general_matrix& m, const mpq & d) const { + for (unsigned i = 0; i < row_count(); i++) + for (unsigned j = 0; j < column_count(); j++) + if (!is_zero(((*this)[i][j] - m[i][j]) % d)) + return false; + return true; + } + bool operator==(const general_matrix& m) const { + return row_count() == m.row_count() && column_count() == m.column_count() && elements_are_equal(m); + } + + bool operator!=(const general_matrix& m) const { + return !(*this == m); + } + + bool equal_modulo(const general_matrix& m, const mpq & d) const { + return row_count() == m.row_count() && column_count() == m.column_count() && elements_are_equal_modulo(m, d); + } + + + vector operator*(const vector & x) const { + vector r; + lp_assert(x.size() == column_count()); + for (unsigned i = 0; i < row_count(); i++) { + mpq v(0); + for (unsigned j = 0; j < column_count(); j++) { + v += (*this)[i][j] * x[j]; + } + r.push_back(v); + } + return r; + } + + // bool create_upper_triangle(general_matrix& m, vector& x) { + // for (unsigned i = 1; i < m.row_count(); i++) { + // lp_assert(false); // to be continued + // } + // } + + // bool solve_A_x_equal_b(const general_matrix& m, vector& x, const vector& b) const { + // auto m_copy = m; + // // for square matrices + // lp_assert(row_count() == b.size()); + // lp_assert(x.size() == column_count()); + // lp_assert(row_count() == column_count()); + // x = b; + // create_upper_triangle(copy_of_m, x); + // solve_on_triangle(copy_of_m, x); + // } + // + + void transpose_rows(unsigned i, unsigned l) { + lp_assert(i != l); + m_row_permutation.transpose_from_right(i, l); + } + + void transpose_columns(unsigned j, unsigned k) { + lp_assert(j != k); + m_column_permutation.transpose_from_left(j, k); + } + + general_matrix(){} + general_matrix(unsigned n) : + m_row_permutation(n), + m_column_permutation(n), + m_data(n) + { + for (auto& v : m_data){ + v.resize(n); + } + } + + general_matrix(unsigned m, unsigned n) : + m_row_permutation(m), + m_column_permutation(n), + m_data(m) { + for (auto& v : m_data){ + v.resize(n); + } + } + + void shrink_to_rank(const svector& basis_rows) { + if (basis_rows.size() == row_count()) return; + vector> data; // todo : not efficient code + for (unsigned i : basis_rows) + data.push_back(m_data[i]); + m_data = data; + } + + // used for debug only + general_matrix take_first_n_columns(unsigned n) const { + lp_assert(n <= column_count()); + if (n == column_count()) + return *this; + general_matrix ret(row_count(), n); + for (unsigned i = 0; i < row_count(); i++) + for (unsigned j = 0; j < n; j++) + ret[i][j] = (*this)[i][j]; + return ret; + } + inline + friend vector operator*(const vector & f, const general_matrix& a) { + vector r(a.column_count()); + for (unsigned j = 0; j < a.column_count(); j ++) { + mpq t = zero_of_type(); + for (unsigned i = 0; i < a.row_count(); i++) { + t += f[i] * a[i][j]; + } + r[j] = t; + } + return r; + } +}; + +} diff --git a/src/util/lp/hash_helper.h b/src/util/lp/hash_helper.h deleted file mode 100644 index ab5fa844b..000000000 --- a/src/util/lp/hash_helper.h +++ /dev/null @@ -1,54 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include -#include -#include "util/numerics/mpq.h" -#ifdef __CLANG__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmismatched-tags" -#endif -namespace std { -template<> -struct hash { - inline size_t operator()(const lp::mpq & v) const { - return v.hash(); - } -}; -} - -template -inline void hash_combine(std::size_t & seed, const T & v) { - seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); -} - -namespace std { -template struct hash> { - inline size_t operator()(const pair & v) const { - size_t seed = 0; - hash_combine(seed, v.first); - hash_combine(seed, v.second); - return seed; - } -}; -} -#ifdef __CLANG__ -#pragma clang diagnostic pop -#endif diff --git a/src/util/lp/hnf.h b/src/util/lp/hnf.h new file mode 100644 index 000000000..3cdeac466 --- /dev/null +++ b/src/util/lp/hnf.h @@ -0,0 +1,623 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + Creates the Hermite Normal Form of a matrix in place. + We suppose that $A$ is an integral $m$ by $n$ matrix or rank $m$, where $n >= m$. + The paragraph below is applicable to the usage of HNF. +We have $H = AU$ where $H$ is in Hermite Normal Form +and $U$ is a unimodular matrix. We do not have an explicit + representation of $U$. For a given $i$ we need to find the $i$-th + row of $U^{-1}$. +Let $e_i$ be a vector of length $m$ with all elements equal to $0$ and +$1$ at $i$-th position. Then we need to find the row vector $e_iU^{-1}=t$. Noticing that $U^{-1} = H^{-1}A$, we have $e_iH^{-1}A=t$. +We find $e_iH^{-1} = f$ by solving $e_i = fH$ and then $fA$ gives us $t$. + +Author: + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include "util/lp/numeric_pair.h" +#include "util/ext_gcd.h" +namespace lp { +namespace hnf_calc { + + // d = u * a + v * b and the sum of abs(u) + abs(v) is minimal, d is positive +inline +void extended_gcd_minimal_uv(const mpq & a, const mpq & b, mpq & d, mpq & u, mpq & v) { + if (is_zero(a)) { + u = zero_of_type(); + v = one_of_type(); + d = b; + return; + } + if (is_zero(b)) { + u = one_of_type(); + v = zero_of_type(); + d = a; + return; + } +#if 1 + d = gcd(a, b, u, v); +#else + extended_gcd(a, b, d, u, v); +#endif + if (is_neg(d)) { + d = -d; + u = -u; + v = -v; + } + + if (d == a) { + u = one_of_type(); + v = zero_of_type(); + return; + } + if (d == -a) { + u = - one_of_type(); + v = zero_of_type(); + return; + } + + mpq a_over_d = abs(a) / d; + mpq r; + + mpq k = machine_div_rem(v, a_over_d, r); + if (is_neg(r)) { + r += a_over_d; + k -= one_of_type(); + } + + lp_assert(v == k * a_over_d + r); + + if (is_pos(b)) { + v = r - a_over_d; // v -= (k + 1) * a_over_d; + lp_assert(- a_over_d < v && v <= zero_of_type()); + + if (is_pos(a)) { + u += (k + 1) * (b / d); + lp_assert( one_of_type() <= u && u <= abs(b)/d); + } else { + u -= (k + 1) * (b / d); + lp_assert( one_of_type() <= -u && -u <= abs(b)/d); + } + } else { + v = r; // v -= k * a_over_d; + lp_assert(- a_over_d < -v && -v <= zero_of_type()); + if (is_pos(a)) { + u += k * (b / d); + lp_assert( one_of_type() <= u && u <= abs(b)/d); + } else { + u -= k * (b / d); + lp_assert( one_of_type() <= -u && -u <= abs(b)/d); + } + } + lp_assert(d == u * a + v * b); +} + + + +template +bool prepare_pivot_for_lower_triangle(M &m, unsigned r) { + for (unsigned i = r; i < m.row_count(); i++) { + for (unsigned j = r; j < m.column_count(); j++) { + if (!is_zero(m[i][j])) { + if (i != r) { + m.transpose_rows(i, r); + } + if (j != r) { + m.transpose_columns(j, r); + } + return true; + } + } + } + return false; +} + +template +void pivot_column_non_fractional(M &m, unsigned r, bool & overflow, const mpq & big_number) { + lp_assert(!is_zero(m[r][r])); + for (unsigned j = r + 1; j < m.column_count(); j++) { + for (unsigned i = r + 1; i < m.row_count(); i++) { + if ( + (m[i][j] = (r > 0) ? (m[r][r]*m[i][j] - m[i][r]*m[r][j]) / m[r-1][r-1] : + (m[r][r]*m[i][j] - m[i][r]*m[r][j])) + >= big_number) { + overflow = true; + return; + } + lp_assert(is_int(m[i][j])); + } + } +} + +// returns the rank of the matrix +template +unsigned to_lower_triangle_non_fractional(M &m, bool & overflow, const mpq& big_number) { + unsigned i = 0; + for (; i < m.row_count(); i++) { + if (!prepare_pivot_for_lower_triangle(m, i)) { + return i; + } + pivot_column_non_fractional(m, i, overflow, big_number); + if (overflow) + return 0; + } + lp_assert(i == m.row_count()); + return i; +} + +// returns gcd of values below diagonal i,i +template +mpq gcd_of_row_starting_from_diagonal(const M& m, unsigned i) { + mpq g = zero_of_type(); + unsigned j = i; + for (; j < m.column_count() && is_zero(g); j++) { + const auto & t = m[i][j]; + if (!is_zero(t)) + g = abs(t); + } + lp_assert(!is_zero(g)); + for (; j < m.column_count(); j++) { + const auto & t = m[i][j]; + if (!is_zero(t)) + g = gcd(g, t); + } + return g; +} + +// It fills "r" - the basic rows of m. +// The plan is to transform m to the lower triangular form by using non-fractional Gaussian Elimination by columns. +// Then the trailing after the diagonal elements of the following elements of the last non-zero row of the matrix, +// namely, m[r-1][r-1], m[r-1][r], ..., m[r-1]m[m.column_count() - 1] give the determinants of all minors of rank r. +// The gcd of these minors is the return value. + +template +mpq determinant_of_rectangular_matrix(const M& m, svector & basis_rows, const mpq& big_number) { + auto m_copy = m; + bool overflow = false; + unsigned rank = to_lower_triangle_non_fractional(m_copy, overflow, big_number); + if (overflow) + return big_number; + if (rank == 0) + return one_of_type(); + + for (unsigned i = 0; i < rank; i++) { + basis_rows.push_back(m_copy.adjust_row(i)); + } + TRACE("hnf_calc", tout << "basis_rows = "; print_vector(basis_rows, tout); m_copy.print(tout, "m_copy = ");); + return gcd_of_row_starting_from_diagonal(m_copy, rank - 1); +} +} // end of namespace hnf_calc + +template // M is the matrix type +class hnf { + // fields + +#ifdef Z3DEBUG + M m_H; + M m_U; + M m_U_reverse; + M m_A_orig; +#endif + M m_W; + vector m_buffer; + unsigned m_m; + unsigned m_n; + mpq m_d; // it is a positive number and a multiple of gcd of r-minors of m_A_orig, where r is the rank of m_A_orig + // we suppose that the rank of m_A is equal to row_count(), and that row_count() <= column_count(), that is m_A has the full rank + unsigned m_i; + unsigned m_j; + mpq m_R; + mpq m_half_R; + mpq mod_R_balanced(const mpq & a) const { + mpq t = a % m_R; + return t > m_half_R? t - m_R : (t < - m_half_R? t + m_R : t); + } + + mpq mod_R(const mpq & a) const { + mpq t = a % m_R; + t = is_neg(t) ? t + m_R : t; + CTRACE("hnf", is_neg(t), tout << "a=" << a << ", m_R= " << m_R << std::endl;); + return t; + + } + +#ifdef Z3DEBUG + void buffer_p_col_i_plus_q_col_j_H(const mpq & p, unsigned i, const mpq & q, unsigned j) { + for (unsigned k = i; k < m_m; k++) { + m_buffer[k] = p * m_H[k][i] + q * m_H[k][j]; + } + } +#endif + bool zeros_in_column_W_above(unsigned i) { + for (unsigned k = 0; k < i; k++) + if (!is_zero(m_W[k][i])) + return false; + return true; + } + + void buffer_p_col_i_plus_q_col_j_W_modulo(const mpq & p, const mpq & q) { + lp_assert(zeros_in_column_W_above(m_i)); + for (unsigned k = m_i; k < m_m; k++) { + m_buffer[k] = mod_R_balanced(mod_R_balanced(p * m_W[k][m_i]) + mod_R_balanced(q * m_W[k][m_j])); + } + } +#ifdef Z3DEBUG + void buffer_p_col_i_plus_q_col_j_U(const mpq & p, unsigned i, const mpq & q, unsigned j) { + for (unsigned k = 0; k < m_n; k++) { + m_buffer[k] = p * m_U[k][i] + q * m_U[k][j]; + } + } + + void pivot_column_i_to_column_j_H(mpq u, unsigned i, mpq v, unsigned j) { + lp_assert(is_zero(u * m_H[i][i] + v * m_H[i][j])); + m_H[i][j] = zero_of_type(); + for (unsigned k = i + 1; k < m_m; k ++) + m_H[k][j] = u * m_H[k][i] + v * m_H[k][j]; + + } +#endif + void pivot_column_i_to_column_j_W_modulo(mpq u, mpq v) { + lp_assert(is_zero((u * m_W[m_i][m_i] + v * m_W[m_i][m_j]) % m_R)); + m_W[m_i][m_j] = zero_of_type(); + for (unsigned k = m_i + 1; k < m_m; k ++) + m_W[k][m_j] = mod_R_balanced(mod_R_balanced(u * m_W[k][m_i]) + mod_R_balanced(v * m_W[k][m_j])); + } + +#ifdef Z3DEBUG + void pivot_column_i_to_column_j_U(mpq u, unsigned i, mpq v, unsigned j) { + for (unsigned k = 0; k < m_n; k ++) + m_U[k][j] = u * m_U[k][i] + v * m_U[k][j]; + + } + + void copy_buffer_to_col_i_H(unsigned i) { + for (unsigned k = i; k < m_m; k++) { + m_H[k][i] = m_buffer[k]; + } + } + void copy_buffer_to_col_i_U(unsigned i) { + for (unsigned k = 0; k < m_n; k++) + m_U[k][i] = m_buffer[k]; + } + + // multiply by (a, b) + // (c, d) + // from the left where i and j are the modified columns + // the [i][i] = a, and [i][j] = b for the matrix we multiply by + + + void multiply_U_reverse_from_left_by(unsigned i, unsigned j, const mpq & a, const mpq & b, const mpq & c, const mpq d) { + // the new i-th row goes to the buffer + for (unsigned k = 0; k < m_n; k++) { + m_buffer[k] = a * m_U_reverse[i][k] + b * m_U_reverse[j][k]; + } + + // calculate the new j-th row in place + for (unsigned k = 0; k < m_n; k++) { + m_U_reverse[j][k] = c * m_U_reverse[i][k] + d * m_U_reverse[j][k]; + } + + // copy the buffer into i-th row + for (unsigned k = 0; k < m_n; k++) { + m_U_reverse[i][k] = m_buffer[k]; + } + } + + void handle_column_ij_in_row_i(unsigned i, unsigned j) { + lp_assert(is_correct_modulo()); + const mpq& aii = m_H[i][i]; + const mpq& aij = m_H[i][j]; + mpq p,q,r; + extended_gcd(aii, aij, r, p, q); + mpq aii_over_r = aii / r; + mpq aij_over_r = aij / r; + + + buffer_p_col_i_plus_q_col_j_H(p, i, q, j); + pivot_column_i_to_column_j_H(- aij_over_r, i, aii_over_r, j); + copy_buffer_to_col_i_H(i); + + + buffer_p_col_i_plus_q_col_j_U(p, i, q, j); + pivot_column_i_to_column_j_U(- aij_over_r, i, aii_over_r, j); + copy_buffer_to_col_i_U(i); + + // U was multiplied from the right by (p, - aij_over_r) + // (q, aii_over_r ) + // We need to multiply U_reverse by (aii_over_r, aij_over_r) + // (-q , p) + // from the left + + multiply_U_reverse_from_left_by(i, j, aii_over_r, aij_over_r, -q, p); + lp_assert(is_correct_modulo()); + } + + + void switch_sign_for_column(unsigned i) { + for (unsigned k = i; k < m_m; k++) + m_H[k][i].neg(); + for (unsigned k = 0; k < m_n; k++) + m_U[k][i].neg(); + + // switch sign for the i-th row in the reverse m_U_reverse + for (unsigned k = 0; k < m_n; k++) + m_U_reverse[i][k].neg(); + + } + + void process_row_column(unsigned i, unsigned j){ + if (is_zero(m_H[i][j])) + return; + handle_column_ij_in_row_i(i, j); + } + + void replace_column_j_by_j_minus_u_col_i_H(unsigned i, unsigned j, const mpq & u) { + lp_assert(j < i); + for (unsigned k = i; k < m_m; k++) { + m_H[k][j] -= u * m_H[k][i]; + } + } + void replace_column_j_by_j_minus_u_col_i_U(unsigned i, unsigned j, const mpq & u) { + + lp_assert(j < i); + for (unsigned k = 0; k < m_n; k++) { + m_U[k][j] -= u * m_U[k][i]; + } + // Here we multiply from m_U from the right by the matrix ( 1, 0) + // ( -u, 1). + // To adjust the reverse we multiply it from the left by (1, 0) + // (u, 1) + + for (unsigned k = 0; k < m_n; k++) { + m_U_reverse[i][k] += u * m_U_reverse[j][k]; + } + + + } + + void work_on_columns_less_than_i_in_the_triangle(unsigned i) { + const mpq & mii = m_H[i][i]; + if (is_zero(mii)) return; + for (unsigned j = 0; j < i; j++) { + const mpq & mij = m_H[i][j]; + if (!is_pos(mij) && - mij < mii) + continue; + mpq u = ceil(mij / mii); + replace_column_j_by_j_minus_u_col_i_H(i, j, u); + replace_column_j_by_j_minus_u_col_i_U(i, j, u); + } + } + + void process_row(unsigned i) { + + lp_assert(is_correct_modulo()); + for (unsigned j = i + 1; j < m_n; j++) { + process_row_column(i, j); + } + if (i >= m_n) { + lp_assert(m_H == m_A_orig * m_U); + return; + } + if (is_neg(m_H[i][i])) + switch_sign_for_column(i); + work_on_columns_less_than_i_in_the_triangle(i); + lp_assert(is_correct_modulo()); + } + + void calculate() { + for (unsigned i = 0; i < m_m; i++) { + process_row(i); + } + } + + void prepare_U_and_U_reverse() { + m_U = M(m_H.column_count()); + for (unsigned i = 0; i < m_U.column_count(); i++) + m_U[i][i] = 1; + + m_U_reverse = m_U; + + lp_assert(m_H == m_A_orig * m_U); + } + + bool row_is_correct_form(unsigned i) const { + if (i >= m_n) + return true; + const mpq& hii = m_H[i][i]; + if (is_neg(hii)) + return false; + for (unsigned j = 0; j < i; j++) { + const mpq & hij = m_H[i][j]; + if (is_pos(hij)) + return false; + if (!is_zero(hii) && - hij >= hii) + return false; + } + + return true; + } + + bool is_correct_form() const { + for (unsigned i = 0; i < m_m; i++) + if (!row_is_correct_form(i)) + return false; + return true; + } + + bool is_correct() const { + return m_H == m_A_orig * m_U && is_unit_matrix(m_U * m_U_reverse); + } + + bool is_correct_modulo() const { + return m_H.equal_modulo(m_A_orig * m_U, m_d) && is_unit_matrix(m_U * m_U_reverse); + } + + bool is_correct_final() const { + if (!is_correct()) { + TRACE("hnf_calc", + tout << "m_H = "; m_H.print(tout, 17); + tout << "\nm_A_orig * m_U = "; (m_A_orig * m_U).print(tout, 17); + tout << "is_correct() does not hold" << std::endl;); + return false; + } + if (!is_correct_form()) { + TRACE("hnf_calc", tout << "is_correct_form() does not hold" << std::endl;); + return false; + } + return true; + } +public: + const M& H() const { return m_H;} + const M& U() const { return m_U;} + const M& U_reverse() const { return m_U_reverse; } +private: +#endif + void copy_buffer_to_col_i_W_modulo() { + for (unsigned k = m_i; k < m_m; k++) { + m_W[k][m_i] = m_buffer[k]; + } + } + + void replace_column_j_by_j_minus_u_col_i_W(unsigned j, const mpq & u) { + lp_assert(j < m_i); + for (unsigned k = m_i; k < m_m; k++) { + m_W[k][j] -= u * m_W[k][m_i]; + // m_W[k][j] = mod_R_balanced(m_W[k][j]); + } + } + + bool is_unit_matrix(const M& u) const { + unsigned m = u.row_count(); + unsigned n = u.column_count(); + if (m != n) return false; + for (unsigned i = 0; i < m; i ++) + for (unsigned j = 0; j < n; j++) { + if (i == j) { + if (one_of_type() != u[i][j]) + return false; + } else { + if (!is_zero(u[i][j])) + return false; + } + } + return true; + } + + + // follows Algorithm 2.4.8 of Henri Cohen's "A course on computational algebraic number theory", + // with some changes related to that we create a low triangle matrix + // with non-positive elements under the diagonal + void process_column_in_row_modulo() { + const mpq& aii = m_W[m_i][m_i]; + const mpq& aij = m_W[m_i][m_j]; + mpq d, p,q; + hnf_calc::extended_gcd_minimal_uv(aii, aij, d, p, q); + if (is_zero(d)) + return; + mpq aii_over_d = mod_R(aii / d); + mpq aij_over_d = mod_R(aij / d); + buffer_p_col_i_plus_q_col_j_W_modulo(p, q); + pivot_column_i_to_column_j_W_modulo(- aij_over_d, aii_over_d); + copy_buffer_to_col_i_W_modulo(); + } + + void fix_row_under_diagonal_W_modulo() { + mpq d, u, v; + if (is_zero(m_W[m_i][m_i])) { + m_W[m_i][m_i] = m_R; + u = one_of_type(); + d = m_R; + } else { + hnf_calc::extended_gcd_minimal_uv(m_W[m_i][m_i], m_R, d, u, v); + } + auto & mii = m_W[m_i][m_i]; + mii *= u; + mii = mod_R(mii); + if (is_zero(mii)) + mii = d; + + lp_assert(is_pos(mii)); + + // adjust column m_i + for (unsigned k = m_i + 1; k < m_m; k++) { + m_W[k][m_i] *= u; + m_W[k][m_i] = mod_R_balanced(m_W[k][m_i]); + } + + lp_assert(is_pos(mii)); + for (unsigned j = 0; j < m_i; j++) { + const mpq & mij = m_W[m_i][j]; + if (!is_pos(mij) && - mij < mii) + continue; + mpq q = ceil(mij / mii); + replace_column_j_by_j_minus_u_col_i_W(j, q); + } + } + + + void process_row_modulo() { + for (m_j = m_i + 1; m_j < m_n; m_j++) { + process_column_in_row_modulo(); + } + fix_row_under_diagonal_W_modulo(); + } + + void calculate_by_modulo() { + for (m_i = 0; m_i < m_m; m_i ++) { + process_row_modulo(); + lp_assert(is_pos(m_W[m_i][m_i])); + m_R /= m_W[m_i][m_i]; + lp_assert(is_int(m_R)); + m_half_R = floor(m_R / 2); + } + } + +public: + hnf(M & A, const mpq & d) : +#ifdef Z3DEBUG + m_H(A), + m_A_orig(A), +#endif + m_W(A), + m_buffer(std::max(A.row_count(), A.column_count())), + m_m(A.row_count()), + m_n(A.column_count()), + m_d(d), + m_R(m_d), + m_half_R(floor(m_R / 2)) + { + if (m_m == 0 || m_n == 0 || is_zero(m_d)) + return; +#ifdef Z3DEBUG + prepare_U_and_U_reverse(); + calculate(); + lp_assert(is_correct_final()); +#endif + calculate_by_modulo(); +#ifdef Z3DEBUG + CTRACE("hnf_calc", m_H != m_W, + tout << "A = "; m_A_orig.print(tout, 4); tout << std::endl; + tout << "H = "; m_H.print(tout, 4); tout << std::endl; + tout << "W = "; m_W.print(tout, 4); tout << std::endl;); + lp_assert (m_H == m_W); +#endif + } + + const M & W() const { return m_W; } + +}; + +} diff --git a/src/util/lp/hnf_cutter.h b/src/util/lp/hnf_cutter.h new file mode 100644 index 000000000..90cdd5a6d --- /dev/null +++ b/src/util/lp/hnf_cutter.h @@ -0,0 +1,232 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include "util/lp/lar_term.h" +#include "util/lp/hnf.h" +#include "util/lp/general_matrix.h" +#include "util/lp/var_register.h" +#include "util/lp/lia_move.h" +#include "util/lp/explanation.h" + +namespace lp { +class hnf_cutter { + var_register m_var_register; + general_matrix m_A; + vector m_terms; + svector m_constraints_for_explanation; + vector m_right_sides; + lp_settings & m_settings; + mpq m_abs_max; + bool m_overflow; +public: + + const mpq & abs_max() const { return m_abs_max; } + + hnf_cutter(lp_settings & settings) : m_settings(settings), + m_abs_max(zero_of_type()) {} + + unsigned terms_count() const { + return m_terms.size(); + } + + const vector& terms() const { return m_terms; } + const svector& constraints_for_explanation() const { + return m_constraints_for_explanation; + } + const vector & right_sides() const { return m_right_sides; } + void clear() { + // m_A will be filled from scratch in init_matrix_A + m_var_register.clear(); + m_terms.clear(); + m_constraints_for_explanation.clear(); + m_right_sides.clear(); + m_abs_max = zero_of_type(); + m_overflow = false; + } + void add_term(const lar_term* t, const mpq &rs, constraint_index ci) { + m_terms.push_back(t); + m_right_sides.push_back(rs); + m_constraints_for_explanation.push_back(ci); + for (const auto &p : *t) { + m_var_register.add_var(p.var()); + mpq t = abs(ceil(p.coeff())); + if (t > m_abs_max) + m_abs_max = t; + } + } + + void print(std::ostream & out) { + out << "terms = " << m_terms.size() << ", var = " << m_var_register.size() << std::endl; + } + + void initialize_row(unsigned i) { + m_A.init_row_from_container(i, * m_terms[i], [this](unsigned j) { return m_var_register.add_var(j);}); + } + + void init_matrix_A() { + m_A = general_matrix(terms_count(), vars().size()); + for (unsigned i = 0; i < terms_count(); i++) + initialize_row(i); + } + + // todo: as we need only one row i with non integral b[i] need to optimize later + void find_h_minus_1_b(const general_matrix& H, vector & b) { + // the solution will be put into b + for (unsigned i = 0; i < H.row_count() ;i++) { + for (unsigned j = 0; j < i; j++) { + b[i] -= H[i][j]*b[j]; + } + b[i] /= H[i][i]; + // consider return from here if b[i] is not an integer and return i + } + } + + vector create_b(const svector & basis_rows) { + if (basis_rows.size() == m_right_sides.size()) + return m_right_sides; + vector b; + for (unsigned i : basis_rows) { + b.push_back(m_right_sides[i]); + } + return b; + } + + int find_cut_row_index(const vector & b) { + int ret = -1; + int n = 0; + for (int i = 0; i < static_cast(b.size()); i++) { + if (is_int(b[i])) continue; + if (n == 0 ) { + lp_assert(ret == -1); + n = 1; + ret = i; + } else { + if (m_settings.random_next() % (++n) == 0) { + ret = i; + } + } + } + return ret; + } + + // fills e_i*H_minus_1 + void get_ei_H_minus_1(unsigned i, const general_matrix& H, vector & row) { + // we solve x = ei * H_min_1 + // or x * H = ei + unsigned m = H.row_count(); + for (unsigned k = i + 1; k < m; k++) { + row[k] = zero_of_type(); + } + row[i] = one_of_type() / H[i][i]; + for(int k = i - 1; k >= 0; k--) { + mpq t = zero_of_type(); + for (unsigned l = k + 1; l <= i; l++) { + t += H[l][k]*row[l]; + } + row[k] = -t / H[k][k]; + } + + // // test region + // vector ei(H.row_count(), zero_of_type()); + // ei[i] = one_of_type(); + // vector pr = row * H; + // pr.shrink(ei.size()); + // lp_assert(ei == pr); + // // end test region + + } + + void fill_term(const vector & row, lar_term& t) { + for (unsigned j = 0; j < row.size(); j++) { + if (!is_zero(row[j])) + t.add_monomial(row[j], m_var_register.local_to_external(j)); + } + } +#ifdef Z3DEBUG + vector transform_to_local_columns(const vector & x) const { + vector ret; + for (unsigned j = 0; j < vars().size(); j++) { + lp_assert(is_zero(x[m_var_register.local_to_external(j)].y)); + ret.push_back(x[m_var_register.local_to_external(j)].x); + } + return ret; + } +#endif + void shrink_explanation(const svector& basis_rows) { + svector new_expl; + for (unsigned i : basis_rows) { + new_expl.push_back(m_constraints_for_explanation[i]); + } + m_constraints_for_explanation = new_expl; + } + + bool overflow() const { return m_overflow; } + + lia_move create_cut(lar_term& t, mpq& k, explanation& ex, bool & upper + #ifdef Z3DEBUG + , + const vector & x0 + #endif + ) { + // we suppose that x0 has at least one non integer element + init_matrix_A(); + svector basis_rows; + mpq big_number = m_abs_max.expt(3); + mpq d = hnf_calc::determinant_of_rectangular_matrix(m_A, basis_rows, big_number); + + // std::cout << "max = " << m_abs_max << ", d = " << d << ", d/max = " << ceil (d /m_abs_max) << std::endl; + //std::cout << "max cube " << m_abs_max * m_abs_max * m_abs_max << std::endl; + + if (d >= big_number) { + return lia_move::undef; + } + + if (m_settings.get_cancel_flag()) + return lia_move::undef; + if (basis_rows.size() < m_A.row_count()) { + m_A.shrink_to_rank(basis_rows); + shrink_explanation(basis_rows); + } + + hnf h(m_A, d); + // general_matrix A_orig = m_A; + + vector b = create_b(basis_rows); + lp_assert(m_A * x0 == b); + // vector bcopy = b; + find_h_minus_1_b(h.W(), b); + // lp_assert(bcopy == h.W().take_first_n_columns(b.size()) * b); + int cut_row = find_cut_row_index(b); + if (cut_row == -1) + return lia_move::undef; + // the matrix is not square - we can get + // all integers in b's projection + + vector row(m_A.column_count()); + get_ei_H_minus_1(cut_row, h.W(), row); + vector f = row * m_A; + fill_term(f, t); + k = floor(b[cut_row]); + upper = true; + return lia_move::cut; + } + + svector vars() const { return m_var_register.vars(); } +}; +} diff --git a/src/util/lp/implied_bound.h b/src/util/lp/implied_bound.h index f1c711ffa..6c5f26cf2 100644 --- a/src/util/lp/implied_bound.h +++ b/src/util/lp/implied_bound.h @@ -24,34 +24,34 @@ namespace lp { struct implied_bound { mpq m_bound; unsigned m_j; // the column for which the bound has been found - bool m_is_low_bound; + bool m_is_lower_bound; bool m_coeff_before_j_is_pos; unsigned m_row_or_term_index; bool m_strict; - lconstraint_kind kind() const { - lconstraint_kind k = m_is_low_bound? GE : LE; + lconstraint_kind k = m_is_lower_bound? GE : LE; if (m_strict) k = static_cast(k / 2); return k; } bool operator==(const implied_bound & o) const { - return m_j == o.m_j && m_is_low_bound == o.m_is_low_bound && m_bound == o.m_bound && + return m_j == o.m_j && m_is_lower_bound == o.m_is_lower_bound && m_bound == o.m_bound && m_coeff_before_j_is_pos == o.m_coeff_before_j_is_pos && m_row_or_term_index == o.m_row_or_term_index && m_strict == o.m_strict; } implied_bound(){} implied_bound(const mpq & a, unsigned j, - bool low_bound, + bool lower_bound, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict): m_bound(a), m_j(j), - m_is_low_bound(low_bound), + m_is_lower_bound(lower_bound), m_coeff_before_j_is_pos(coeff_before_j_is_pos), m_row_or_term_index(row_or_term_index), - m_strict(strict) {} + m_strict(strict) { + } }; } diff --git a/src/util/lp/indexed_vector_instances.cpp b/src/util/lp/indexed_vector.cpp similarity index 70% rename from src/util/lp/indexed_vector_instances.cpp rename to src/util/lp/indexed_vector.cpp index 79c3ee1a1..180291705 100644 --- a/src/util/lp/indexed_vector_instances.cpp +++ b/src/util/lp/indexed_vector.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "util/vector.h" -#include "util/lp/indexed_vector.hpp" +#include "util/lp/indexed_vector_def.h" namespace lp { template void indexed_vector::clear(); template void indexed_vector::clear_all(); @@ -42,11 +42,12 @@ template void lp::indexed_vector::print(std::basic_ostream >::print(std::ostream&); #endif } -template void lp::print_vector(vector const&, std::ostream&); -template void lp::print_vector(vector const&, std::ostream&); -template void lp::print_vector(vector const&, std::ostream&); -template void lp::print_vector >(vector> const&, std::ostream&); +// template void lp::print_vector(vector const&, std::ostream&); +// template void lp::print_vector(vector const&, std::ostream&); +// template void lp::print_vector(vector const&, std::ostream&); +// template void lp::print_vector >(vector> const&, std::ostream&); template void lp::indexed_vector::resize(unsigned int); -template void lp::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream > &); -template void lp::print_vector >(vector> const&, std::ostream&); +// template void lp::print_vector< lp::mpq>(vector< lp::mpq> const &, std::basic_ostream > &); +// template void lp::print_vector >(vector> const&, std::ostream&); template void lp::indexed_vector >::erase_from_index(unsigned int); + diff --git a/src/util/lp/indexed_vector.h b/src/util/lp/indexed_vector.h index 3b1258ed7..d6ff4e76a 100644 --- a/src/util/lp/indexed_vector.h +++ b/src/util/lp/indexed_vector.h @@ -28,11 +28,9 @@ Revision History: #include namespace lp { -template void print_vector(const vector & t, std::ostream & out); -template void print_vector(const buffer & t, std::ostream & out); template void print_sparse_vector(const vector & t, std::ostream & out); -void print_vector(const vector & t, std::ostream & out); +void print_vector_as_doubles(const vector & t, std::ostream & out); template class indexed_vector { public: @@ -90,16 +88,7 @@ public: } void set_value(const T& value, unsigned index); - void set_value_as_in_dictionary(unsigned index) { - SASSERT(index < m_data.size()); - T & loc = m_data[index]; - if (is_zero(loc)) { - m_index.push_back(index); - loc = one_of_type(); // use as a characteristic function - } - } - void clear(); void clear_all(); const T& operator[] (unsigned i) const { @@ -175,6 +164,55 @@ public: } } } + + struct ival { + unsigned m_var; + const T & m_coeff; + ival(unsigned var, const T & val) : m_var(var), m_coeff(val) { + } + unsigned var() const { return m_var;} + const T & coeff() const { return m_coeff; } + }; + + struct const_iterator { + // fields + const unsigned *m_i; + const indexed_vector& m_v; + + //typedefs + + + typedef const_iterator self_type; + typedef ival value_type; + typedef const ival reference; + // typedef const column_cell* pointer; + typedef int difference_type; + typedef std::forward_iterator_tag iterator_category; + + reference operator*() const { + return ival(*m_i, m_v[*m_i]); + } + self_type operator++() { self_type i = *this; m_i++; return i; } + self_type operator++(int) { m_i++; return *this; } + + const_iterator(const unsigned* it, const indexed_vector& v) : + m_i(it), + m_v(v) + {} + bool operator==(const self_type &other) const { + return m_i == other.m_i; + } + bool operator!=(const self_type &other) const { return !(*this == other); } + }; + + const_iterator begin() const { + return const_iterator(m_index.begin(), *this); + } + + const_iterator end() const { + return const_iterator(m_index.end(), *this); + } + #ifdef Z3DEBUG bool is_OK() const; diff --git a/src/util/lp/indexed_vector.hpp b/src/util/lp/indexed_vector_def.h similarity index 82% rename from src/util/lp/indexed_vector.hpp rename to src/util/lp/indexed_vector_def.h index 73055d6da..2f7706089 100644 --- a/src/util/lp/indexed_vector.hpp +++ b/src/util/lp/indexed_vector_def.h @@ -22,21 +22,6 @@ Revision History: #include "util/lp/lp_settings.h" namespace lp { -template -void print_vector(const vector & t, std::ostream & out) { - for (unsigned i = 0; i < t.size(); i++) - out << t[i] << " "; - out << std::endl; -} - - -template -void print_vector(const buffer & t, std::ostream & out) { - for (unsigned i = 0; i < t.size(); i++) - out << t[i] << " "; - out << std::endl; -} - template void print_sparse_vector(const vector & t, std::ostream & out) { for (unsigned i = 0; i < t.size(); i++) { @@ -46,7 +31,7 @@ void print_sparse_vector(const vector & t, std::ostream & out) { out << std::endl; } -void print_vector(const vector & t, std::ostream & out) { +void print_vector_as_doubles(const vector & t, std::ostream & out) { for (unsigned i = 0; i < t.size(); i++) out << t[i].get_double() << std::setprecision(3) << " "; out << std::endl; @@ -56,13 +41,13 @@ template void indexed_vector::resize(unsigned data_size) { clear(); m_data.resize(data_size, numeric_traits::zero()); - SASSERT(is_OK()); + lp_assert(is_OK()); } template void indexed_vector::set_value(const T& value, unsigned index) { m_data[index] = value; - SASSERT(std::find(m_index.begin(), m_index.end(), index) == m_index.end()); + lp_assert(std::find(m_index.begin(), m_index.end(), index) == m_index.end()); m_index.push_back(index); } diff --git a/src/util/lp/indexer_of_constraints.h b/src/util/lp/indexer_of_constraints.h new file mode 100644 index 000000000..30976d496 --- /dev/null +++ b/src/util/lp/indexer_of_constraints.h @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ + +#pragma once +#include "util/lp/binary_heap_priority_queue.h" +namespace lp { + +class indexer_of_constraints { + binary_heap_priority_queue m_queue_of_released_indices; + unsigned m_max; +public: + indexer_of_constraints() :m_max(0) {} + unsigned get_new_index() { + unsigned ret; + if (m_queue_of_released_indices.is_empty()) { + ret = m_max++; + } + else { + ret = m_queue_of_released_indices.dequeue(); + } + return ret; + }; + void release_index(unsigned i) { + m_queue_of_released_indices.enqueue(i, i); + }; + unsigned max() const { return m_max; } +}; +} diff --git a/src/util/lp/init_lar_solver.h b/src/util/lp/init_lar_solver.h deleted file mode 100644 index 5d78c3ba7..000000000 --- a/src/util/lp/init_lar_solver.h +++ /dev/null @@ -1,591 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -// here we are inside lp::lar_solver class - -bool strategy_is_undecided() const { - return m_settings.simplex_strategy() == simplex_strategy_enum::undecided; -} - -var_index add_var(unsigned ext_j) { - var_index i; - SASSERT (ext_j < m_terms_start_index); - - if (ext_j >= m_terms_start_index) - throw 0; // todo : what is the right way to exit? - - if (try_get_val(m_ext_vars_to_columns, ext_j, i)) { - return i; - } - SASSERT(m_vars_to_ul_pairs.size() == A_r().column_count()); - i = A_r().column_count(); - m_vars_to_ul_pairs.push_back (ul_pair(static_cast(-1))); - add_non_basic_var_to_core_fields(ext_j); - SASSERT(sizes_are_correct()); - return i; -} - -void register_new_ext_var_index(unsigned ext_v) { - SASSERT(!contains(m_ext_vars_to_columns, ext_v)); - unsigned j = static_cast(m_ext_vars_to_columns.size()); - m_ext_vars_to_columns[ext_v] = j; - SASSERT(m_columns_to_ext_vars_or_term_indices.size() == j); - m_columns_to_ext_vars_or_term_indices.push_back(ext_v); -} - -void add_non_basic_var_to_core_fields(unsigned ext_j) { - register_new_ext_var_index(ext_j); - m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); - m_columns_with_changed_bound.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 add_new_var_to_core_fields_for_doubles(bool register_in_basis) { - unsigned j = A_d().column_count(); - A_d().add_column(); - SASSERT(m_mpq_lar_core_solver.m_d_x.size() == j); - // SASSERT(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); - SASSERT(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 add_new_var_to_core_fields_for_mpq(bool register_in_basis) { - unsigned j = A_r().column_count(); - A_r().add_column(); - SASSERT(m_mpq_lar_core_solver.m_r_x.size() == j); - // SASSERT(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); - SASSERT(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 add_term_undecided(const vector> & coeffs, - const mpq &m_v) { - m_terms.push_back(new lar_term(coeffs, m_v)); - m_orig_terms.push_back(new lar_term(coeffs, m_v)); - return m_terms_start_index + m_terms.size() - 1; -} - -// terms -var_index 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)); - m_orig_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_for_term(m_orig_terms.back(), ret); - if (m_settings.bound_propagation()) - m_rows_with_changed_bounds.insert(A_r().row_count() - 1); - } - SASSERT(m_ext_vars_to_columns.size() == A_r().column_count()); - return ret; -} - -void add_row_for_term(const lar_term * term, unsigned term_ext_index) { - SASSERT(sizes_are_correct()); - add_row_from_term_no_constraint(term, term_ext_index); - SASSERT(sizes_are_correct()); -} - -void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) { - register_new_ext_var_index(term_ext_index); - // j will be a new variable - unsigned j = A_r().column_count(); - ul_pair ul(j); - m_vars_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_x[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); -} - -void add_basic_var_to_core_fields() { - bool use_lu = m_mpq_lar_core_solver.need_to_presolve_with_double_solver(); - SASSERT(!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(); - add_new_var_to_core_fields_for_mpq(true); - if (use_lu) - add_new_var_to_core_fields_for_doubles(true); -} - -constraint_index add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) { - constraint_index ci = m_constraints.size(); - if (!is_term(j)) { // j is a var - 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); - } - SASSERT(sizes_are_correct()); - return ci; -} - -void 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: - SASSERT(false); // cannot be here - } -} - -void add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - SASSERT(is_term(j)); - unsigned adjusted_term_index = adjust_term_index(j); - unsigned term_j; - if (try_get_val(m_ext_vars_to_columns, j, term_j)) { - mpq rs = right_side - m_orig_terms[adjusted_term_index]->m_v; - m_constraints.push_back(new lar_term_constraint(m_orig_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_orig_terms[adjusted_term_index], kind, right_side); - } -} - - -void 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)); - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); -} - -void decide_on_strategy_and_adjust_initial_state() { - SASSERT(strategy_is_undecided()); - if (m_vars_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 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: - SASSERT(false); // not implemented - case simplex_strategy_enum::undecided: - adjust_initial_state_for_tableau_rows(); - break; - } -} - -void 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(); - SASSERT(m_mpq_lar_core_solver.m_d_x.size() == j); - // SASSERT(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); - SASSERT(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 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 fill_last_row_of_A_d(static_matrix & A, const lar_term* ls) { - SASSERT(A.row_count() > 0); - SASSERT(A.column_count() > 0); - unsigned last_row = A.row_count() - 1; - SASSERT(A.m_rows[last_row].empty()); - - for (auto & t : ls->m_coeffs) { - SASSERT(!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 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; - SASSERT(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound); - SASSERT(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; - SASSERT(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: - SASSERT(false); - - } - m_columns_with_changed_bound.insert(j); -} - -void update_upper_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - SASSERT(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 = 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 = 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: - SASSERT(false); - - } -} - -void update_boxed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - SASSERT(m_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 = INFEASIBLE; - SASSERT(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 = 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 = 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 = 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: - SASSERT(false); - - } -} -void update_low_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - SASSERT(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 = 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 = 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: - SASSERT(false); - - } -} - -void update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { - SASSERT(m_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])); - SASSERT(m_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 = 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 = 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 = 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 = 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 = 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 = INFEASIBLE; - m_infeasible_column_index = j; - set_low_bound_witness(j, ci); - } - break; - } - - default: - SASSERT(false); - - } -} - diff --git a/src/util/lp/int_set.h b/src/util/lp/int_set.h index 698b8bc49..058cf4114 100644 --- a/src/util/lp/int_set.h +++ b/src/util/lp/int_set.h @@ -35,7 +35,7 @@ public: return m_data[j] >= 0; } void insert(unsigned j) { - SASSERT(j < m_data.size()); + lp_assert(j < m_data.size()); if (contains(j)) return; m_data[j] = m_index.size(); m_index.push_back(j); diff --git a/src/util/lp/int_solver.cpp b/src/util/lp/int_solver.cpp new file mode 100644 index 000000000..0691b5887 --- /dev/null +++ b/src/util/lp/int_solver.cpp @@ -0,0 +1,1294 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Lev Nachmanson +*/ + +#include "util/lp/int_solver.h" +#include "util/lp/lar_solver.h" +#include "util/lp/lp_utils.h" +#include +#include "util/lp/monomial.h" +namespace lp { + + +void int_solver::trace_inf_rows() const { + TRACE("arith_int_rows", + unsigned num = m_lar_solver->A_r().column_count(); + for (unsigned v = 0; v < num; v++) { + if (is_int(v) && !get_value(v).is_int()) { + display_column(tout, v); + } + } + + num = 0; + for (unsigned i = 0; i < m_lar_solver->A_r().row_count(); i++) { + unsigned j = m_lar_solver->m_mpq_lar_core_solver.m_r_basis[i]; + if (column_is_int_inf(j)) { + num++; + m_lar_solver->print_row(m_lar_solver->A_r().m_rows[i], tout); + tout << "\n"; + } + } + tout << "num of int infeasible: " << num << "\n"; + ); +} + +bool int_solver::has_inf_int() const { + return m_lar_solver->has_inf_int(); +} + +int int_solver::find_inf_int_base_column() { + unsigned inf_int_count = 0; + int j = find_inf_int_boxed_base_column_with_smallest_range(inf_int_count); + if (j != -1) + return j; + if (inf_int_count == 0) + return -1; + unsigned k = random() % inf_int_count; + return get_kth_inf_int(k); +} + +int int_solver::get_kth_inf_int(unsigned k) const { + for (unsigned j : m_lar_solver->r_basis()) + if (column_is_int_inf(j) && k-- == 0) + return j; + lp_assert(false); + return -1; +} + +int int_solver::find_inf_int_nbasis_column() const { + for (unsigned j : m_lar_solver->r_nbasis()) + if (!column_is_int_inf(j)) + return j; + return -1; +} + +int int_solver::find_inf_int_boxed_base_column_with_smallest_range(unsigned & inf_int_count) { + inf_int_count = 0; + int result = -1; + mpq range; + mpq new_range; + mpq small_range_thresold(1024); + unsigned n = 0; + lar_core_solver & lcs = m_lar_solver->m_mpq_lar_core_solver; + + for (unsigned j : m_lar_solver->r_basis()) { + if (!column_is_int_inf(j)) + continue; + inf_int_count++; + if (!is_boxed(j)) + continue; + lp_assert(!is_fixed(j)); + new_range = lcs.m_r_upper_bounds()[j].x - lcs.m_r_lower_bounds()[j].x; + if (new_range > small_range_thresold) + continue; + if (result == -1 || new_range < range) { + result = j; + range = new_range; + n = 1; + } + else if (new_range == range && settings().random_next() % (++n) == 0) { + lp_assert(n > 1); + result = j; + } + } + return result; + +} + +bool int_solver::is_gomory_cut_target(const row_strip& row) { + // All non base variables must be at their bounds and assigned to rationals (that is, infinitesimals are not allowed). + unsigned j; + for (const auto & p : row) { + j = p.var(); + if (is_base(j)) continue; + if (!at_bound(j)) + return false; + if (!is_zero(get_value(j).y)) { + TRACE("gomory_cut", tout << "row is not gomory cut target:\n"; + display_column(tout, j); + tout << "infinitesimal: " << !is_zero(get_value(j).y) << "\n";); + return false; + } + } + return true; +} + + +void int_solver::real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0) { + TRACE("gomory_cut_detail_real", tout << "real\n";); + mpq new_a; + if (at_low(x_j)) { + if (a.is_pos()) { + new_a = a / one_minus_f_0; + } + else { + new_a = a / f_0; + new_a.neg(); + } + m_k->addmul(new_a, lower_bound(x_j).x); // is it a faster operation than + // k += lower_bound(x_j).x * new_a; + m_ex->push_justification(column_lower_bound_constraint(x_j), new_a); + } + else { + lp_assert(at_upper(x_j)); + if (a.is_pos()) { + new_a = a / f_0; + new_a.neg(); // the upper terms are inverted. + } + else { + new_a = a / one_minus_f_0; + } + m_k->addmul(new_a, upper_bound(x_j).x); // k += upper_bound(x_j).x * new_a; + m_ex->push_justification(column_upper_bound_constraint(x_j), new_a); + } + TRACE("gomory_cut_detail_real", tout << a << "*v" << x_j << " k: " << *m_k << "\n";); + m_t->add_monomial(new_a, x_j); +} + +constraint_index int_solver::column_upper_bound_constraint(unsigned j) const { + return m_lar_solver->get_column_upper_bound_witness(j); +} + +constraint_index int_solver::column_lower_bound_constraint(unsigned j) const { + return m_lar_solver->get_column_lower_bound_witness(j); +} + + +void int_solver::int_case_in_gomory_cut(const mpq & a, unsigned x_j, + mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0) { + lp_assert(is_int(x_j)); + lp_assert(!a.is_int()); + mpq f_j = fractional_part(a); + TRACE("gomory_cut_detail", + tout << a << " x_j" << x_j << " k = " << *m_k << "\n"; + tout << "f_j: " << f_j << "\n"; + tout << "f_0: " << f_0 << "\n"; + tout << "1 - f_0: " << 1 - f_0 << "\n"; + tout << "at_low(" << x_j << ") = " << at_low(x_j) << std::endl; + ); + lp_assert (!f_j.is_zero()); + mpq new_a; + if (at_low(x_j)) { + if (f_j <= one_minus_f_0) { + new_a = f_j / one_minus_f_0; + } + else { + new_a = (1 - f_j) / f_0; + } + m_k->addmul(new_a, lower_bound(x_j).x); + m_ex->push_justification(column_lower_bound_constraint(x_j), new_a); + } + else { + lp_assert(at_upper(x_j)); + if (f_j <= f_0) { + new_a = f_j / f_0; + } + else { + new_a = (mpq(1) - f_j) / one_minus_f_0; + } + new_a.neg(); // the upper terms are inverted + m_k->addmul(new_a, upper_bound(x_j).x); + m_ex->push_justification(column_upper_bound_constraint(x_j), new_a); + } + TRACE("gomory_cut_detail", tout << "new_a: " << new_a << " k: " << *m_k << "\n";); + m_t->add_monomial(new_a, x_j); + lcm_den = lcm(lcm_den, denominator(new_a)); +} + +lia_move int_solver::report_conflict_from_gomory_cut() { + TRACE("empty_pol",); + lp_assert(m_k->is_pos()); + // conflict 0 >= k where k is positive + m_k->neg(); // returning 0 <= -k + return lia_move::conflict; +} + +void int_solver::gomory_cut_adjust_t_and_k(vector> & pol, + lar_term & t, + mpq &k, + bool some_ints, + mpq & lcm_den) { + if (!some_ints) + return; + + t.clear(); + if (pol.size() == 1) { + unsigned v = pol[0].second; + lp_assert(is_int(v)); + bool k_is_int = k.is_int(); + const mpq& a = pol[0].first; + k /= a; + if (a.is_pos()) { // we have av >= k + if (!k_is_int) + k = ceil(k); + // switch size + t.add_monomial(- mpq(1), v); + k.neg(); + } else { + if (!k_is_int) + k = floor(k); + t.add_monomial(mpq(1), v); + } + } else if (some_ints) { + lcm_den = lcm(lcm_den, denominator(k)); + lp_assert(lcm_den.is_pos()); + if (!lcm_den.is_one()) { + // normalize coefficients of integer parameters to be integers. + for (auto & pi: pol) { + pi.first *= lcm_den; + SASSERT(!is_int(pi.second) || pi.first.is_int()); + } + k *= lcm_den; + } + // negate everything to return -pol <= -k + for (const auto & pi: pol) + t.add_monomial(-pi.first, pi.second); + k.neg(); + } +} + +bool int_solver::current_solution_is_inf_on_cut() const { + const auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x; + impq v = m_t->apply(x); + mpq sign = *m_upper ? one_of_type() : -one_of_type(); + CTRACE("current_solution_is_inf_on_cut", v * sign <= (*m_k) * sign, + tout << "m_upper = " << *m_upper << std::endl; + tout << "v = " << v << ", k = " << (*m_k) << std::endl; + ); + return v * sign > (*m_k) * sign; +} + +void int_solver::adjust_term_and_k_for_some_ints_case_gomory(mpq &lcm_den) { + lp_assert(!m_t->is_empty()); + auto pol = m_t->coeffs_as_vector(); + m_t->clear(); + if (pol.size() == 1) { + TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); + unsigned v = pol[0].second; + lp_assert(is_int(v)); + const mpq& a = pol[0].first; + (*m_k) /= a; + if (a.is_pos()) { // we have av >= k + if (!(*m_k).is_int()) + (*m_k) = ceil((*m_k)); + // switch size + m_t->add_monomial(- mpq(1), v); + (*m_k).neg(); + } else { + if (!(*m_k).is_int()) + (*m_k) = floor((*m_k)); + m_t->add_monomial(mpq(1), v); + } + } else { + TRACE("gomory_cut_detail", tout << "pol.size() > 1" << std::endl;); + lcm_den = lcm(lcm_den, denominator((*m_k))); + lp_assert(lcm_den.is_pos()); + if (!lcm_den.is_one()) { + // normalize coefficients of integer parameters to be integers. + for (auto & pi: pol) { + pi.first *= lcm_den; + SASSERT(!is_int(pi.second) || pi.first.is_int()); + } + (*m_k) *= lcm_den; + } + // negate everything to return -pol <= -(*m_k) + for (const auto & pi: pol) + m_t->add_monomial(-pi.first, pi.second); + (*m_k).neg(); + } + TRACE("gomory_cut_detail", tout << "k = " << (*m_k) << std::endl;); + lp_assert((*m_k).is_int()); +} + + + + +lia_move int_solver::mk_gomory_cut( unsigned inf_col, const row_strip & row) { + + lp_assert(column_is_int_inf(inf_col)); + + TRACE("gomory_cut", + tout << "applying cut at:\n"; m_lar_solver->print_row(row, tout); tout << std::endl; + for (auto & p : row) { + m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(p.var(), tout); + } + tout << "inf_col = " << inf_col << std::endl; + ); + + // gomory will be t <= k and the current solution has a property t > k + *m_k = 1; + mpq lcm_den(1); + unsigned x_j; + mpq a; + bool some_int_columns = false; + mpq f_0 = int_solver::fractional_part(get_value(inf_col)); + mpq one_min_f_0 = 1 - f_0; + for (auto & p : row) { + x_j = p.var(); + if (x_j == inf_col) + continue; + // make the format compatible with the format used in: Integrating Simplex with DPLL(T) + a = p.coeff(); + a.neg(); + if (is_real(x_j)) + real_case_in_gomory_cut(a, x_j, f_0, one_min_f_0); + else if (!a.is_int()) { // f_j will be zero and no monomial will be added + some_int_columns = true; + int_case_in_gomory_cut(a, x_j, lcm_den, f_0, one_min_f_0); + } + } + + if (m_t->is_empty()) + return report_conflict_from_gomory_cut(); + if (some_int_columns) + adjust_term_and_k_for_some_ints_case_gomory(lcm_den); + + lp_assert(current_solution_is_inf_on_cut()); + m_lar_solver->subs_term_columns(*m_t); + TRACE("gomory_cut", tout<<"precut:"; m_lar_solver->print_term(*m_t, tout); tout << " <= " << *m_k << std::endl;); + return lia_move::cut; +} + +int int_solver::find_free_var_in_gomory_row(const row_strip& row) { + unsigned j; + for (const auto & p : row) { + j = p.var(); + if (!is_base(j) && is_free(j)) + return static_cast(j); + } + return -1; +} + +lia_move int_solver::proceed_with_gomory_cut(unsigned j) { + const row_strip& row = m_lar_solver->get_row(row_of_basic_column(j)); + + if (-1 != find_free_var_in_gomory_row(row)) + return lia_move::undef; + + if (!is_gomory_cut_target(row)) + return lia_move::undef; + + *m_upper = true; + return mk_gomory_cut(j, row); +} + + +unsigned int_solver::row_of_basic_column(unsigned j) const { + return m_lar_solver->m_mpq_lar_core_solver.m_r_heading[j]; +} + +// template +// void int_solver::fill_cut_solver_for_constraint(constraint_index ci, cut_solver & cs) { +// const lar_base_constraint* c = m_lar_solver->constraints()[ci]; +// vector> coeffs; +// T rs; +// get_int_coeffs_from_constraint(c, coeffs, rs); +// vector explanation; +// explanation.push_back(ci); +// cs.add_ineq(coeffs, -rs, explanation); +// } + + +typedef monomial mono; + + +// this will allow to enable and disable tracking of the pivot rows +struct pivoted_rows_tracking_control { + lar_solver * m_lar_solver; + bool m_track_pivoted_rows; + pivoted_rows_tracking_control(lar_solver* ls) : + m_lar_solver(ls), + m_track_pivoted_rows(ls->get_track_pivoted_rows()) + { + TRACE("pivoted_rows", tout << "pivoted rows = " << ls->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;); + m_lar_solver->set_track_pivoted_rows(false); + } + ~pivoted_rows_tracking_control() { + TRACE("pivoted_rows", tout << "pivoted rows = " << m_lar_solver->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;); + m_lar_solver->set_track_pivoted_rows(m_track_pivoted_rows); + } +}; + + + +impq int_solver::get_cube_delta_for_term(const lar_term& t) const { + if (t.size() == 2) { + bool seen_minus = false; + bool seen_plus = false; + for(const auto & p : t) { + if (!is_int(p.var())) + goto usual_delta; + const mpq & c = p.coeff(); + if (c == one_of_type()) { + seen_plus = true; + } else if (c == -one_of_type()) { + seen_minus = true; + } else { + goto usual_delta; + } + } + if (seen_minus && seen_plus) + return zero_of_type(); + return impq(0, 1); + } + usual_delta: + mpq delta = zero_of_type(); + for (const auto & p : t) + if (is_int(p.var())) + delta += abs(p.coeff()); + + delta *= mpq(1, 2); + return impq(delta); +} + +bool int_solver::tighten_term_for_cube(unsigned i) { + unsigned ti = i + m_lar_solver->terms_start_index(); + if (!m_lar_solver->term_is_used_as_row(ti)) + return true; + const lar_term* t = m_lar_solver->terms()[i]; + impq delta = get_cube_delta_for_term(*t); + TRACE("cube", m_lar_solver->print_term_as_indices(*t, tout); tout << ", delta = " << delta;); + if (is_zero(delta)) + return true; + return m_lar_solver->tighten_term_bounds_by_delta(i, delta); +} + +bool int_solver::tighten_terms_for_cube() { + for (unsigned i = 0; i < m_lar_solver->terms().size(); i++) + if (!tighten_term_for_cube(i)) { + TRACE("cube", tout << "cannot tighten";); + return false; + } + return true; +} + +lia_move int_solver::find_cube() { + if (m_number_of_calls % settings().m_int_find_cube_period != 0) + return lia_move::undef; + + settings().st().m_cube_calls++; + TRACE("cube", + for (unsigned j = 0; j < m_lar_solver->A_r().column_count(); j++) + display_column(tout, j); + m_lar_solver->print_terms(tout); + ); + + lar_solver::scoped_push _sp(*m_lar_solver); + if (!tighten_terms_for_cube()) { + return lia_move::undef; + } + + lp_status st = m_lar_solver->find_feasible_solution(); + if (st != lp_status::FEASIBLE && st != lp_status::OPTIMAL) { + TRACE("cube", tout << "cannot find a feasiblie solution";); + _sp.pop(); + move_non_basic_columns_to_bounds(); + find_feasible_solution(); + // it can happen that we found an integer solution here + return !m_lar_solver->r_basis_has_inf_int()? lia_move::sat: lia_move::undef; + } + _sp.pop(); + m_lar_solver->round_to_integer_solution(); + settings().st().m_cube_success++; + return lia_move::sat; +} + +void int_solver::find_feasible_solution() { + m_lar_solver->find_feasible_solution(); + lp_assert(lp_status::OPTIMAL == m_lar_solver->get_status() || lp_status::FEASIBLE == m_lar_solver->get_status()); +} + +lia_move int_solver::run_gcd_test() { + if (settings().m_int_run_gcd_test) { + settings().st().m_gcd_calls++; + if (!gcd_test()) { + settings().st().m_gcd_conflicts++; + return lia_move::conflict; + } + } + return lia_move::undef; +} + +lia_move int_solver::gomory_cut() { + if ((m_number_of_calls) % settings().m_int_gomory_cut_period != 0) + return lia_move::undef; + + if (move_non_basic_columns_to_bounds()) { +#if Z3DEBUG + lp_status st = +#endif + m_lar_solver->find_feasible_solution(); +#if Z3DEBUG + lp_assert(st == lp_status::FEASIBLE || st == lp_status::OPTIMAL); +#endif + } + + int j = find_inf_int_base_column(); + if (j == -1) { + j = find_inf_int_nbasis_column(); + return j == -1? lia_move::sat : create_branch_on_column(j); + } + return proceed_with_gomory_cut(j); +} + + +void int_solver::try_add_term_to_A_for_hnf(unsigned i) { + mpq rs; + const lar_term* t = m_lar_solver->terms()[i]; + constraint_index ci; + if (!hnf_cutter_is_full() && m_lar_solver->get_equality_and_right_side_for_term_on_current_x(i, rs, ci)) { + m_hnf_cutter.add_term(t, rs, ci); + } +} + +bool int_solver::hnf_cutter_is_full() const { + return + m_hnf_cutter.terms_count() >= settings().limit_on_rows_for_hnf_cutter + || + m_hnf_cutter.vars().size() >= settings().limit_on_columns_for_hnf_cutter; +} + +lp_settings& int_solver::settings() { + return m_lar_solver->settings(); +} + +const lp_settings& int_solver::settings() const { + return m_lar_solver->settings(); +} + +bool int_solver::hnf_has_var_with_non_integral_value() const { + for (unsigned j : m_hnf_cutter.vars()) + if (get_value(j).is_int() == false) + return true; + return false; +} + +bool int_solver::init_terms_for_hnf_cut() { + m_hnf_cutter.clear(); + for (unsigned i = 0; i < m_lar_solver->terms().size() && !hnf_cutter_is_full(); i++) { + try_add_term_to_A_for_hnf(i); + } + return hnf_has_var_with_non_integral_value(); +} + +lia_move int_solver::make_hnf_cut() { + if (!init_terms_for_hnf_cut()) { + return lia_move::undef; + } + settings().st().m_hnf_cutter_calls++; + TRACE("hnf_cut", tout << "settings().st().m_hnf_cutter_calls = " << settings().st().m_hnf_cutter_calls;); +#ifdef Z3DEBUG + vector x0 = m_hnf_cutter.transform_to_local_columns(m_lar_solver->m_mpq_lar_core_solver.m_r_x); +#endif + lia_move r = m_hnf_cutter.create_cut(*m_t, *m_k, *m_ex, *m_upper +#ifdef Z3DEBUG + , x0 +#endif + ); + CTRACE("hnf_cut", r == lia_move::cut, tout<< "cut:"; m_lar_solver->print_term(*m_t, tout); tout << " <= " << *m_k << std::endl;); + if (r == lia_move::cut) { + lp_assert(current_solution_is_inf_on_cut()); + settings().st().m_hnf_cuts++; + m_ex->clear(); + for (unsigned i : m_hnf_cutter.constraints_for_explanation()) { + m_ex->push_justification(i); + } + } + return r; +} + +lia_move int_solver::hnf_cut() { + if ((m_number_of_calls) % settings().m_hnf_cut_period == 0) { + return make_hnf_cut(); + } + return lia_move::undef; +} + +lia_move int_solver::check(lar_term& t, mpq& k, explanation& ex, bool & upper) { + if (!has_inf_int()) return lia_move::sat; + + m_t = &t; m_k = &k; m_ex = &ex; m_upper = &upper; + lia_move r = run_gcd_test(); + if (r != lia_move::undef) return r; + + pivoted_rows_tracking_control pc(m_lar_solver); + + if(settings().m_int_pivot_fixed_vars_from_basis) + m_lar_solver->pivot_fixed_vars_from_basis(); + + r = patch_nbasic_columns(); + if (r != lia_move::undef) return r; + ++m_number_of_calls; + r = find_cube(); + if (r != lia_move::undef) return r; + + r = hnf_cut(); + if (r != lia_move::undef) return r; + + r = gomory_cut(); + return (r == lia_move::undef)? branch_or_sat() : r; +} + +lia_move int_solver::branch_or_sat() { + int j = find_any_inf_int_column_basis_first(); + return j == -1? lia_move::sat : create_branch_on_column(j); +} + +int int_solver::find_any_inf_int_column_basis_first() { + int j = find_inf_int_base_column(); + if (j != -1) + return j; + return find_inf_int_nbasis_column(); +} + +bool int_solver::move_non_basic_column_to_bounds(unsigned j) { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + auto & val = lcs.m_r_x[j]; + switch (lcs.m_column_types()[j]) { + case column_type::boxed: + if (val != lcs.m_r_lower_bounds()[j] && val != lcs.m_r_upper_bounds()[j]) { + if (random() % 2 == 0) + set_value_for_nbasic_column(j, lcs.m_r_lower_bounds()[j]); + else + set_value_for_nbasic_column(j, lcs.m_r_upper_bounds()[j]); + return true; + } + break; + case column_type::lower_bound: + if (val != lcs.m_r_lower_bounds()[j]) { + set_value_for_nbasic_column(j, lcs.m_r_lower_bounds()[j]); + return true; + } + break; + case column_type::upper_bound: + if (val != lcs.m_r_upper_bounds()[j]) { + set_value_for_nbasic_column(j, lcs.m_r_upper_bounds()[j]); + return true; + } + break; + default: + if (is_int(j) && !val.is_int()) { + set_value_for_nbasic_column(j, impq(floor(val))); + return true; + } + break; + } + return false; +} + +bool int_solver::move_non_basic_columns_to_bounds() { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + bool change = false; + for (unsigned j : lcs.m_r_nbasis) { + if (move_non_basic_column_to_bounds(j)) + change = true; + } + + if (settings().simplex_strategy() == simplex_strategy_enum::tableau_costs) + m_lar_solver->update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); + return change; +} + +void int_solver::set_value_for_nbasic_column_ignore_old_values(unsigned j, const impq & new_val) { + lp_assert(!is_base(j)); + auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x[j]; + auto delta = new_val - x; + x = new_val; + m_lar_solver->change_basic_columns_dependend_on_a_given_nb_column(j, delta); +} + + +void int_solver::set_value_for_nbasic_column(unsigned j, const impq & new_val) { + lp_assert(!is_base(j)); + auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x[j]; + auto delta = new_val - x; + x = new_val; + m_lar_solver->change_basic_columns_dependend_on_a_given_nb_column(j, delta); +} + +void int_solver::patch_nbasic_column(unsigned j, bool patch_only_int_vals) { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + impq & val = lcs.m_r_x[j]; + bool val_is_int = val.is_int(); + if (patch_only_int_vals && !val_is_int) + return; + + bool inf_l, inf_u; + impq l, u; + mpq m; + if (!get_freedom_interval_for_column(j, inf_l, l, inf_u, u, m)) { + return; + } + bool m_is_one = m.is_one(); + if (m.is_one() && val_is_int) + return; + // check whether value of j is already a multiple of m. + if (val_is_int && (val.x / m).is_int()) + return; + TRACE("patch_int", + tout << "TARGET j" << j << " -> ["; + if (inf_l) tout << "-oo"; else tout << l; + tout << ", "; + if (inf_u) tout << "oo"; else tout << u; + tout << "]"; + tout << ", m: " << m << ", val: " << val << ", is_int: " << m_lar_solver->column_is_int(j) << "\n";); + if (!inf_l) { + l = m_is_one ? ceil(l) : m * ceil(l / m); + if (inf_u || l <= u) { + TRACE("patch_int", + tout << "patching with l: " << l << '\n';); + set_value_for_nbasic_column(j, l); + } + else { + TRACE("patch_int", + tout << "not patching " << l << "\n";); + } + } + else if (!inf_u) { + u = m_is_one ? floor(u) : m * floor(u / m); + set_value_for_nbasic_column(j, u); + TRACE("patch_int", + tout << "patching with u: " << u << '\n';); + } + else { + set_value_for_nbasic_column(j, impq(0)); + TRACE("patch_int", + tout << "patching with 0\n";); + } +} + +lia_move int_solver::patch_nbasic_columns() { + settings().st().m_patches++; + lp_assert(is_feasible()); + for (unsigned j : m_lar_solver->m_mpq_lar_core_solver.m_r_nbasis) { + patch_nbasic_column(j, settings().m_int_patch_only_integer_values); + } + lp_assert(is_feasible()); + if (!has_inf_int()) { + settings().st().m_patches_success++; + return lia_move::sat; + } + return lia_move::undef; +} + +mpq get_denominators_lcm(const row_strip & row) { + mpq r(1); + for (auto & c : row) { + r = lcm(r, denominator(c.coeff())); + } + return r; +} + +bool int_solver::gcd_test_for_row(static_matrix> & A, unsigned i) { + mpq lcm_den = get_denominators_lcm(A.m_rows[i]); + mpq consts(0); + mpq gcds(0); + mpq least_coeff(0); + bool least_coeff_is_bounded = false; + unsigned j; + for (auto &c : A.m_rows[i]) { + j = c.var(); + const mpq& a = c.coeff(); + if (m_lar_solver->column_is_fixed(j)) { + mpq aux = lcm_den * a; + consts += aux * m_lar_solver->column_lower_bound(j).x; + } + else if (m_lar_solver->column_is_real(j)) { + return true; + } + else if (gcds.is_zero()) { + gcds = abs(lcm_den * a); + least_coeff = gcds; + least_coeff_is_bounded = m_lar_solver->column_is_bounded(j); + } + else { + mpq aux = abs(lcm_den * a); + gcds = gcd(gcds, aux); + if (aux < least_coeff) { + least_coeff = aux; + least_coeff_is_bounded = m_lar_solver->column_is_bounded(j); + } + else if (least_coeff_is_bounded && aux == least_coeff) { + least_coeff_is_bounded = m_lar_solver->column_is_bounded(j); + } + } + SASSERT(gcds.is_int()); + SASSERT(least_coeff.is_int()); + TRACE("gcd_test_bug", tout << "coeff: " << a << ", gcds: " << gcds + << " least_coeff: " << least_coeff << " consts: " << consts << "\n";); + + } + + if (gcds.is_zero()) { + // All variables are fixed. + // This theory guarantees that the assignment satisfies each row, and + // fixed integer variables are assigned to integer values. + return true; + } + + if (!(consts / gcds).is_int()) { + TRACE("gcd_test", tout << "row failed the GCD test:\n"; display_row_info(tout, i);); + fill_explanation_from_fixed_columns(A.m_rows[i]); + return false; + } + + if (least_coeff.is_one() && !least_coeff_is_bounded) { + SASSERT(gcds.is_one()); + return true; + } + + if (least_coeff_is_bounded) { + return ext_gcd_test(A.m_rows[i], least_coeff, lcm_den, consts); + } + return true; +} + + +void int_solver::add_to_explanation_from_fixed_or_boxed_column(unsigned j) { + constraint_index lc, uc; + m_lar_solver->get_bound_constraint_witnesses_for_column(j, lc, uc); + m_ex->m_explanation.push_back(std::make_pair(mpq(1), lc)); + m_ex->m_explanation.push_back(std::make_pair(mpq(1), uc)); +} +void int_solver::fill_explanation_from_fixed_columns(const row_strip & row) { + for (const auto & c : row) { + if (!m_lar_solver->column_is_fixed(c.var())) + continue; + add_to_explanation_from_fixed_or_boxed_column(c.var()); + } +} + +bool int_solver::gcd_test() { + auto & A = m_lar_solver->A_r(); // getting the matrix + for (unsigned i = 0; i < A.row_count(); i++) + if (!gcd_test_for_row(A, i)) + return false; + return true; +} + +bool int_solver::ext_gcd_test(const row_strip & row, + mpq const & least_coeff, + mpq const & lcm_den, + mpq const & consts) { + mpq gcds(0); + mpq l(consts); + mpq u(consts); + + mpq a; + unsigned j; + for (const auto & c : row) { + j = c.var(); + const mpq & a = c.coeff(); + if (m_lar_solver->column_is_fixed(j)) + continue; + SASSERT(!m_lar_solver->column_is_real(j)); + mpq ncoeff = lcm_den * a; + SASSERT(ncoeff.is_int()); + mpq abs_ncoeff = abs(ncoeff); + if (abs_ncoeff == least_coeff) { + SASSERT(m_lar_solver->column_is_bounded(j)); + if (ncoeff.is_pos()) { + // l += ncoeff * m_lar_solver->column_lower_bound(j).x; + l.addmul(ncoeff, m_lar_solver->column_lower_bound(j).x); + // u += ncoeff * m_lar_solver->column_upper_bound(j).x; + u.addmul(ncoeff, m_lar_solver->column_upper_bound(j).x); + } + else { + // l += ncoeff * upper_bound(j).get_rational(); + l.addmul(ncoeff, m_lar_solver->column_upper_bound(j).x); + // u += ncoeff * lower_bound(j).get_rational(); + u.addmul(ncoeff, m_lar_solver->column_lower_bound(j).x); + } + add_to_explanation_from_fixed_or_boxed_column(j); + } + else if (gcds.is_zero()) { + gcds = abs_ncoeff; + } + else { + gcds = gcd(gcds, abs_ncoeff); + } + SASSERT(gcds.is_int()); + } + + if (gcds.is_zero()) { + return true; + } + + mpq l1 = ceil(l/gcds); + mpq u1 = floor(u/gcds); + + if (u1 < l1) { + fill_explanation_from_fixed_columns(row); + return false; + } + + return true; + +} +/* +linear_combination_iterator * int_solver::get_column_iterator(unsigned j) { + if (m_lar_solver->use_tableau()) + return new iterator_on_column(m_lar_solver->A_r().m_columns[j], m_lar_solver->A_r()); + return new iterator_on_indexed_vector(m_lar_solver->get_column_in_lu_mode(j)); +} +*/ + + +int_solver::int_solver(lar_solver* lar_slv) : + m_lar_solver(lar_slv), + m_number_of_calls(0), + m_hnf_cutter(settings()) { + m_lar_solver->set_int_solver(this); +} + + +bool int_solver::has_low(unsigned j) const { + switch (m_lar_solver->m_mpq_lar_core_solver.m_column_types()[j]) { + case column_type::fixed: + case column_type::boxed: + case column_type::lower_bound: + return true; + default: + return false; + } +} + +bool int_solver::has_upper(unsigned j) const { + switch (m_lar_solver->m_mpq_lar_core_solver.m_column_types()[j]) { + case column_type::fixed: + case column_type::boxed: + case column_type::upper_bound: + return true; + default: + return false; + } +} + + +void set_lower(impq & l, + bool & inf_l, + impq const & v ) { + if (inf_l || v > l) { + l = v; + inf_l = false; + } +} + + +void set_upper(impq & u, + bool & inf_u, + impq const & v) { + if (inf_u || v < u) { + u = v; + inf_u = false; + } +} + +bool int_solver::get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m) { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + if (lcs.m_r_heading[j] >= 0) // the basic var + return false; + + impq const & xj = get_value(j); + + inf_l = true; + inf_u = true; + l = u = zero_of_type(); + m = mpq(1); + + if (has_low(j)) { + set_lower(l, inf_l, lower_bound(j)); + } + if (has_upper(j)) { + set_upper(u, inf_u, upper_bound(j)); + } + + mpq a; // the coefficient in the column + unsigned row_index; + lp_assert(settings().use_tableau()); + const auto & A = m_lar_solver->A_r(); + for (const auto &c : A.column(j)) { + row_index = c.var(); + const mpq & a = c.coeff(); + + unsigned i = lcs.m_r_basis[row_index]; + impq const & xi = get_value(i); + if (is_int(i) && is_int(j) && !a.is_int()) + m = lcm(m, denominator(a)); + if (a.is_neg()) { + if (has_low(i)) + set_lower(l, inf_l, xj + (xi - lcs.m_r_lower_bounds()[i]) / a); + + if (has_upper(i)) + set_upper(u, inf_u, xj + (xi - lcs.m_r_upper_bounds()[i]) / a); + } + else { + if (has_upper(i)) + set_lower(l, inf_l, xj + (xi - lcs.m_r_upper_bounds()[i]) / a); + if (has_low(i)) + set_upper(u, inf_u, xj + (xi - lcs.m_r_lower_bounds()[i]) / a); + } + if (!inf_l && !inf_u && l >= u) break; + } + + TRACE("freedom_interval", + tout << "freedom variable for:\n"; + tout << m_lar_solver->get_column_name(j); + tout << "["; + if (inf_l) tout << "-oo"; else tout << l; + tout << "; "; + if (inf_u) tout << "oo"; else tout << u; + tout << "]\n"; + tout << "val = " << get_value(j) << "\n"; + tout << "return " << (inf_l || inf_u || l <= u); + ); + return (inf_l || inf_u || l <= u); +} + +bool int_solver::is_int(unsigned j) const { + return m_lar_solver->column_is_int(j); +} + +bool int_solver::is_real(unsigned j) const { + return !is_int(j); +} + +bool int_solver::value_is_int(unsigned j) const { + return m_lar_solver->column_value_is_int(j); +} + + + +bool int_solver::is_feasible() const { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + lp_assert( + lcs.m_r_solver.calc_current_x_is_feasible_include_non_basis() == + lcs.m_r_solver.current_x_is_feasible()); + return lcs.m_r_solver.current_x_is_feasible(); +} +const impq & int_solver::get_value(unsigned j) const { + return m_lar_solver->m_mpq_lar_core_solver.m_r_x[j]; +} + +void int_solver::display_column(std::ostream & out, unsigned j) const { + m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(j, out); +} + +bool int_solver::column_is_int_inf(unsigned j) const { + return is_int(j) && (!value_is_int(j)); +} + +bool int_solver::is_base(unsigned j) const { + return m_lar_solver->m_mpq_lar_core_solver.m_r_heading[j] >= 0; +} + +bool int_solver::is_boxed(unsigned j) const { + return m_lar_solver->m_mpq_lar_core_solver.m_column_types[j] == column_type::boxed; +} + +bool int_solver::is_fixed(unsigned j) const { + return m_lar_solver->m_mpq_lar_core_solver.m_column_types[j] == column_type::fixed; +} + +bool int_solver::is_free(unsigned j) const { + return m_lar_solver->m_mpq_lar_core_solver.m_column_types[j] == column_type::free_column; +} + +bool int_solver::at_bound(unsigned j) const { + auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; + switch (mpq_solver.m_column_types[j] ) { + case column_type::fixed: + case column_type::boxed: + return + mpq_solver.m_lower_bounds[j] == get_value(j) || + mpq_solver.m_upper_bounds[j] == get_value(j); + case column_type::lower_bound: + return mpq_solver.m_lower_bounds[j] == get_value(j); + case column_type::upper_bound: + return mpq_solver.m_upper_bounds[j] == get_value(j); + default: + return false; + } +} + +bool int_solver::at_low(unsigned j) const { + auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; + switch (mpq_solver.m_column_types[j] ) { + case column_type::fixed: + case column_type::boxed: + case column_type::lower_bound: + return mpq_solver.m_lower_bounds[j] == get_value(j); + default: + return false; + } +} + +bool int_solver::at_upper(unsigned j) const { + auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; + switch (mpq_solver.m_column_types[j] ) { + case column_type::fixed: + case column_type::boxed: + case column_type::upper_bound: + return mpq_solver.m_upper_bounds[j] == get_value(j); + default: + return false; + } +} + +void int_solver::display_row_info(std::ostream & out, unsigned row_index) const { + auto & rslv = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; + for (auto &c: rslv.m_A.m_rows[row_index]) { + if (numeric_traits::is_pos(c.coeff())) + out << "+"; + out << c.coeff() << rslv.column_name(c.var()) << " "; + } + + for (auto& c: rslv.m_A.m_rows[row_index]) { + rslv.print_column_bound_info(c.var(), out); + } + rslv.print_column_bound_info(rslv.m_basis[row_index], out); +} + +unsigned int_solver::random() { + return m_lar_solver->get_core_solver().settings().random_next(); +} + +bool int_solver::shift_var(unsigned j, unsigned range) { + if (is_fixed(j) || is_base(j)) + return false; + + bool inf_l, inf_u; + impq l, u; + mpq m; + get_freedom_interval_for_column(j, inf_l, l, inf_u, u, m); + if (inf_l && inf_u) { + impq new_val = impq(random() % (range + 1)); + set_value_for_nbasic_column_ignore_old_values(j, new_val); + return true; + } + if (is_int(j)) { + if (!inf_l) { + l = ceil(l); + if (!m.is_one()) + l = m*ceil(l/m); + } + if (!inf_u) { + u = floor(u); + if (!m.is_one()) + u = m*floor(u/m); + } + } + if (!inf_l && !inf_u && l >= u) + return false; + if (inf_u) { + SASSERT(!inf_l); + impq delta = impq(random() % (range + 1)); + impq new_val = l + m*delta; + set_value_for_nbasic_column_ignore_old_values(j, new_val); + return true; + } + if (inf_l) { + SASSERT(!inf_u); + impq delta = impq(random() % (range + 1)); + impq new_val = u - m*delta; + set_value_for_nbasic_column_ignore_old_values(j, new_val); + return true; + } + if (!is_int(j)) { + SASSERT(!inf_l && !inf_u); + mpq delta = mpq(random() % (range + 1)); + impq new_val = l + ((delta * (u - l)) / mpq(range)); + set_value_for_nbasic_column_ignore_old_values(j, new_val); + return true; + } + else { + mpq r = (u.x - l.x) / m; + if (r < mpq(range)) + range = static_cast(r.get_uint64()); + impq new_val = l + m * (impq(random() % (range + 1))); + set_value_for_nbasic_column_ignore_old_values(j, new_val); + return true; + } +} + +bool int_solver::non_basic_columns_are_at_bounds() const { + auto & lcs = m_lar_solver->m_mpq_lar_core_solver; + for (unsigned j :lcs.m_r_nbasis) { + auto & val = lcs.m_r_x[j]; + switch (lcs.m_column_types()[j]) { + case column_type::boxed: + if (val != lcs.m_r_lower_bounds()[j] && val != lcs.m_r_upper_bounds()[j]) + return false; + break; + case column_type::lower_bound: + if (val != lcs.m_r_lower_bounds()[j]) + return false; + break; + case column_type::upper_bound: + if (val != lcs.m_r_upper_bounds()[j]) + return false; + break; + default: + if (is_int(j) && !val.is_int()) { + return false; + } + } + } + return true; +} + +const impq& int_solver::lower_bound(unsigned j) const { + return m_lar_solver->column_lower_bound(j); +} + +lia_move int_solver::create_branch_on_column(int j) { + TRACE("check_main_int", tout << "branching" << std::endl;); + lp_assert(m_t->is_empty()); + lp_assert(j != -1); + m_t->add_monomial(mpq(1), m_lar_solver->adjust_column_index_to_term_index(j)); + if (is_free(j)) { + *m_upper = true; + *m_k = mpq(0); + } else { + *m_upper = left_branch_is_more_narrow_than_right(j); + *m_k = *m_upper? floor(get_value(j)) : ceil(get_value(j)); + } + + TRACE("arith_int", tout << "branching v" << j << " = " << get_value(j) << "\n"; + display_column(tout, j); + tout << "k = " << *m_k << std::endl; + ); + return lia_move::branch; + +} + +bool int_solver::left_branch_is_more_narrow_than_right(unsigned j) { + switch (m_lar_solver->m_mpq_lar_core_solver.m_r_solver.m_column_types[j] ) { + case column_type::fixed: + return false; + case column_type::boxed: + { + auto k = floor(get_value(j)); + return k - lower_bound(j).x < upper_bound(j).x - (k + mpq(1)); + } + case column_type::lower_bound: + return true; + case column_type::upper_bound: + return false; + default: + return false; + } +} + +const impq& int_solver::upper_bound(unsigned j) const { + return m_lar_solver->column_upper_bound(j); +} + +bool int_solver::is_term(unsigned j) const { + return m_lar_solver->column_corresponds_to_term(j); +} + +unsigned int_solver::column_count() const { return m_lar_solver->column_count(); } + +} diff --git a/src/util/lp/int_solver.h b/src/util/lp/int_solver.h new file mode 100644 index 000000000..65c818d1e --- /dev/null +++ b/src/util/lp/int_solver.h @@ -0,0 +1,166 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include "util/lp/lp_settings.h" +#include "util/lp/static_matrix.h" +#include "util/lp/int_set.h" +#include "util/lp/lar_term.h" +#include "util/lp/lar_constraints.h" +#include "util/lp/hnf_cutter.h" +#include "util/lp/lia_move.h" +#include "util/lp/explanation.h" + +namespace lp { +class lar_solver; + +template +struct lp_constraint; + + +class int_solver { +public: + // fields + lar_solver *m_lar_solver; + unsigned m_number_of_calls; + lar_term *m_t; // the term to return in the cut + mpq *m_k; // the right side of the cut + explanation *m_ex; // the conflict explanation + bool *m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise + hnf_cutter m_hnf_cutter; + // methods + int_solver(lar_solver* lp); + + // main function to check that the 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 & upper); + lia_move check_(lar_term& t, mpq& k, explanation& ex, bool & upper); + bool move_non_basic_column_to_bounds(unsigned j); + lia_move check_wrapper(lar_term& t, mpq& k, explanation& ex); + bool is_base(unsigned j) const; + +private: + + // how to tighten bounds for integer variables. + + bool gcd_test_for_row(static_matrix> & A, unsigned i); + + // 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(); // returns false in case of failure. Creates a theory lemma in case of failure. + + bool branch(const lp_constraint & new_inequality); + bool ext_gcd_test(const row_strip& row, + mpq const & least_coeff, + mpq const & lcm_den, + mpq const & consts); + void fill_explanation_from_fixed_columns(const row_strip & row); + void add_to_explanation_from_fixed_or_boxed_column(unsigned j); + lia_move patch_nbasic_columns(); + bool get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m); + const impq & lower_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_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; + bool is_feasible() const; + const impq & get_value(unsigned j) const; + bool column_is_int_inf(unsigned j) const; + void trace_inf_rows() const; + lia_move branch_or_sat(); + int find_any_inf_int_column_basis_first(); + int find_inf_int_base_column(); + int find_inf_int_boxed_base_column_with_smallest_range(unsigned&); + int get_kth_inf_int(unsigned) const; + lp_settings& settings(); + const lp_settings& settings() const; + bool move_non_basic_columns_to_bounds(); + void branch_infeasible_int_var(unsigned); + lia_move mk_gomory_cut(unsigned inf_col, const row_strip& row); + lia_move report_conflict_from_gomory_cut(); + void adjust_term_and_k_for_some_ints_case_gomory(mpq& lcm_den); + lia_move proceed_with_gomory_cut(unsigned j); + int find_free_var_in_gomory_row(const row_strip& ); + bool is_gomory_cut_target(const row_strip&); + 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); + } + +public: + void display_column(std::ostream & out, unsigned j) const; + inline static + mpq fractional_part(const impq & n) { + lp_assert(is_rational(n)); + return n.x - floor(n.x); + } +private: + void real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0); + void int_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0); + constraint_index column_upper_bound_constraint(unsigned j) const; + constraint_index column_lower_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; +public: + bool shift_var(unsigned j, unsigned range); +private: + unsigned random(); + bool has_inf_int() const; + lia_move create_branch_on_column(int j); +public: + bool is_term(unsigned j) const; + bool left_branch_is_more_narrow_than_right(unsigned); + lia_move find_cube(); + bool tighten_terms_for_cube(); + bool tighten_term_for_cube(unsigned); + unsigned column_count() const; + bool all_columns_are_bounded() const; + impq get_cube_delta_for_term(const lar_term&) const; + void find_feasible_solution(); + int find_inf_int_nbasis_column() const; + lia_move run_gcd_test(); + lia_move gomory_cut(); + lia_move hnf_cut(); + lia_move make_hnf_cut(); + bool init_terms_for_hnf_cut(); + bool hnf_matrix_is_empty() const; + void try_add_term_to_A_for_hnf(unsigned term_index); + bool hnf_has_var_with_non_integral_value() const; + bool hnf_cutter_is_full() const; + void patch_nbasic_column(unsigned j, bool patch_only_int_vals); +}; +} diff --git a/src/util/lp/iterator_on_column.h b/src/util/lp/iterator_on_column.h deleted file mode 100644 index c9112a064..000000000 --- a/src/util/lp/iterator_on_column.h +++ /dev/null @@ -1,65 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/linear_combination_iterator.h" -#include "util/lp/static_matrix.h" -#include "util/lp/lar_term.h" -namespace lp { -template -struct iterator_on_column:linear_combination_iterator { - const vector& m_column; // the offset in term coeffs - const static_matrix & m_A; - int m_i; // the initial offset in the column - unsigned size() const override { return m_column.size(); } - iterator_on_column(const vector& column, const static_matrix & A) // the offset in term coeffs - : - m_column(column), - m_A(A), - m_i(-1) {} - - bool next(mpq & a, unsigned & i) override { - if (++m_i >= static_cast(m_column.size())) - return false; - - const column_cell& c = m_column[m_i]; - a = m_A.get_val(c); - i = c.m_i; - return true; - } - - bool next(unsigned & i) override { - if (++m_i >= static_cast(m_column.size())) - return false; - - const column_cell& c = m_column[m_i]; - i = c.m_i; - return true; - } - - void reset() override { - m_i = -1; - } - - linear_combination_iterator * clone() override { - iterator_on_column * r = new iterator_on_column(m_column, m_A); - return r; - } -}; -} diff --git a/src/util/lp/iterator_on_indexed_vector.h b/src/util/lp/iterator_on_indexed_vector.h deleted file mode 100644 index 6cb98b8f2..000000000 --- a/src/util/lp/iterator_on_indexed_vector.h +++ /dev/null @@ -1,53 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/linear_combination_iterator.h" -namespace lp { -template -struct iterator_on_indexed_vector:linear_combination_iterator { - const indexed_vector & m_v; - unsigned m_offset; - iterator_on_indexed_vector(const indexed_vector & v) : - m_v(v), - m_offset(0) - {} - unsigned size() const override { return m_v.m_index.size(); } - bool next(T & a, unsigned & i) override { - if (m_offset >= m_v.m_index.size()) - return false; - i = m_v.m_index[m_offset++]; - a = m_v.m_data[i]; - return true; - } - - bool next(unsigned & i) override { - if (m_offset >= m_v.m_index.size()) - return false; - i = m_v.m_index[m_offset++]; - return true; - } - void reset() override { - m_offset = 0; - } - linear_combination_iterator* clone() override { - return new iterator_on_indexed_vector(m_v); - } -}; -} diff --git a/src/util/lp/iterator_on_pivot_row.h b/src/util/lp/iterator_on_pivot_row.h deleted file mode 100644 index 721502bc2..000000000 --- a/src/util/lp/iterator_on_pivot_row.h +++ /dev/null @@ -1,59 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/iterator_on_indexed_vector.h" -namespace lp { -template -struct iterator_on_pivot_row:linear_combination_iterator { - bool m_basis_returned; - const indexed_vector & m_v; - unsigned m_basis_j; - iterator_on_indexed_vector m_it; - unsigned size() const override { return m_it.size(); } - iterator_on_pivot_row(const indexed_vector & v, unsigned basis_j) : - m_basis_returned(false), - m_v(v), m_basis_j(basis_j), m_it(v) {} - bool next(T & a, unsigned & i) override { - if (m_basis_returned == false) { - m_basis_returned = true; - a = one_of_type(); - i = m_basis_j; - return true; - } - return m_it.next(a, i); - } - bool next(unsigned & i) override { - if (m_basis_returned == false) { - m_basis_returned = true; - i = m_basis_j; - return true; - } - return m_it.next(i); - } - void reset() override { - m_basis_returned = false; - m_it.reset(); - } - linear_combination_iterator * clone() override { - iterator_on_pivot_row * r = new iterator_on_pivot_row(m_v, m_basis_j); - return r; - } -}; -} diff --git a/src/util/lp/iterator_on_row.h b/src/util/lp/iterator_on_row.h deleted file mode 100644 index 55fbda907..000000000 --- a/src/util/lp/iterator_on_row.h +++ /dev/null @@ -1,52 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/linear_combination_iterator.h" -namespace lp { -template -struct iterator_on_row:linear_combination_iterator { - const vector> & m_row; - unsigned m_i; // offset - iterator_on_row(const vector> & row) : m_row(row), m_i(0) - {} - unsigned size() const override { return m_row.size(); } - bool next(T & a, unsigned & i) override { - if (m_i == m_row.size()) - return false; - auto &c = m_row[m_i++]; - i = c.m_j; - a = c.get_val(); - return true; - } - bool next(unsigned & i) override { - if (m_i == m_row.size()) - return false; - auto &c = m_row[m_i++]; - i = c.m_j; - return true; - } - void reset() override { - m_i = 0; - } - linear_combination_iterator* clone() override { - return new iterator_on_row(m_row); - } -}; -} diff --git a/src/util/lp/iterator_on_term_with_basis_var.h b/src/util/lp/iterator_on_term_with_basis_var.h deleted file mode 100644 index 85d11cf36..000000000 --- a/src/util/lp/iterator_on_term_with_basis_var.h +++ /dev/null @@ -1,72 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/linear_combination_iterator.h" -#include "util/lp/numeric_pair.h" -#include "util/lp/lar_term.h" -namespace lp { -struct iterator_on_term_with_basis_var:linear_combination_iterator { - const lar_term & m_term; - std::unordered_map::const_iterator m_i; // the offset in term coeffs - bool m_term_j_returned; - unsigned m_term_j; - unsigned size() const override {return static_cast(m_term.m_coeffs.size() + 1);} - iterator_on_term_with_basis_var(const lar_term & t, unsigned term_j) : - m_term(t), - m_i(t.m_coeffs.begin()), - m_term_j_returned(false), - m_term_j(term_j) {} - - bool next(mpq & a, unsigned & i) override { - if (m_term_j_returned == false) { - m_term_j_returned = true; - a = - one_of_type(); - i = m_term_j; - return true; - } - if (m_i == m_term.m_coeffs.end()) - return false; - i = m_i->first; - a = m_i->second; - m_i++; - return true; - } - bool next(unsigned & i) override { - if (m_term_j_returned == false) { - m_term_j_returned = true; - i = m_term_j; - return true; - } - if (m_i == m_term.m_coeffs.end()) - return false; - i = m_i->first; - m_i++; - return true; - } - void reset() override { - m_term_j_returned = false; - m_i = m_term.m_coeffs.begin(); - } - linear_combination_iterator * clone() override { - iterator_on_term_with_basis_var * r = new iterator_on_term_with_basis_var(m_term, m_term_j); - return r; - } -}; -} diff --git a/src/util/lp/lar_constraints.h b/src/util/lp/lar_constraints.h index 5c33db8c6..ac15028bb 100644 --- a/src/util/lp/lar_constraints.h +++ b/src/util/lp/lar_constraints.h @@ -40,12 +40,11 @@ inline std::string lconstraint_kind_string(lconstraint_kind t) { case GT: return std::string(">"); case EQ: return std::string("="); } - SASSERT(false); + lp_unreachable(); return std::string(); // it is unreachable } -class lar_base_constraint { -public: +struct lar_base_constraint { lconstraint_kind m_kind; mpq m_right_side; virtual vector> get_left_side_coefficients() const = 0; @@ -88,7 +87,7 @@ public: : lar_base_constraint(kind, right_side), m_coeffs(left_side) {} lar_constraint(const lar_base_constraint & c) { - SASSERT(false); // should not be called : todo! + lp_assert(false); // should not be called : todo! } unsigned size() const override { diff --git a/src/util/lp/lar_core_solver_instances.cpp b/src/util/lp/lar_core_solver.cpp similarity index 86% rename from src/util/lp/lar_core_solver_instances.cpp rename to src/util/lp/lar_core_solver.cpp index a6a4048e5..95e516135 100644 --- a/src/util/lp/lar_core_solver_instances.cpp +++ b/src/util/lp/lar_core_solver.cpp @@ -22,4 +22,4 @@ Revision History: #include #include "util/vector.h" #include -#include "util/lp/lar_core_solver.hpp" +#include "util/lp/lar_core_solver_def.h" diff --git a/src/util/lp/lar_core_solver.h b/src/util/lp/lar_core_solver.h index 32229cf27..5f5518528 100644 --- a/src/util/lp/lar_core_solver.h +++ b/src/util/lp/lar_core_solver.h @@ -26,12 +26,9 @@ Revision History: #include "util/lp/indexed_vector.h" #include "util/lp/binary_heap_priority_queue.h" #include "util/lp/breakpoint.h" -#include "util/lp/stacked_unordered_set.h" #include "util/lp/lp_primal_core_solver.h" #include "util/lp/stacked_vector.h" #include "util/lp/lar_solution_signature.h" -#include "util/lp/iterator_on_column.h" -#include "util/lp/iterator_on_indexed_vector.h" #include "util/lp/stacked_value.h" namespace lp { @@ -50,7 +47,7 @@ public: stacked_vector m_column_types; // r - solver fields, for rational numbers vector> m_r_x; // the solution - stacked_vector> m_r_low_bounds; + stacked_vector> m_r_lower_bounds; stacked_vector> m_r_upper_bounds; static_matrix> m_r_A; stacked_vector m_r_pushed_basis; @@ -62,7 +59,7 @@ public: // d - solver fields, for doubles vector m_d_x; // the solution in doubles - vector m_d_low_bounds; + vector m_d_lower_bounds; vector m_d_upper_bounds; static_matrix m_d_A; stacked_vector m_d_pushed_basis; @@ -155,11 +152,11 @@ public: void fill_evidence(unsigned row); - + unsigned get_number_of_non_ints() const; void solve(); - bool low_bounds_are_set() const { return true; } + bool lower_bounds_are_set() const { return true; } const indexed_vector & get_pivot_row() const { return m_r_solver.m_pivot_row; @@ -183,16 +180,16 @@ public: } void push() { - SASSERT(m_r_solver.basis_heading_is_correct()); - SASSERT(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); - SASSERT(m_column_types.size() == m_r_A.column_count()); + lp_assert(m_r_solver.basis_heading_is_correct()); + lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); + lp_assert(m_column_types.size() == m_r_A.column_count()); m_stacked_simplex_strategy = settings().simplex_strategy(); m_stacked_simplex_strategy.push(); m_column_types.push(); // rational if (!settings().use_tableau()) m_r_A.push(); - m_r_low_bounds.push(); + m_r_lower_bounds.push(); m_r_upper_bounds.push(); if (!settings().use_tableau()) { push_vector(m_r_pushed_basis, m_r_basis); @@ -207,7 +204,7 @@ public: template void push_vector(stacked_vector & pushed_vector, const vector & vector) { - SASSERT(pushed_vector.size() <= vector.size()); + lp_assert(pushed_vector.size() <= vector.size()); for (unsigned i = 0; i < vector.size();i++) { if (i == pushed_vector.size()) { pushed_vector.push_back(vector[i]); @@ -234,7 +231,7 @@ public: // rationals if (!settings().use_tableau()) m_r_A.pop(k); - m_r_low_bounds.pop(k); + m_r_lower_bounds.pop(k); m_r_upper_bounds.pop(k); m_column_types.pop(k); @@ -257,8 +254,8 @@ public: pop_basis(k); m_stacked_simplex_strategy.pop(k); settings().simplex_strategy() = m_stacked_simplex_strategy; - SASSERT(m_r_solver.basis_heading_is_correct()); - SASSERT(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); + lp_assert(m_r_solver.basis_heading_is_correct()); + lp_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); } bool need_to_presolve_with_double_solver() const { @@ -276,11 +273,11 @@ public: bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair & delta) { auto & x = m_r_x[j]; switch (pos_type) { - case at_low_bound: - if (x == m_r_solver.m_low_bounds[j]) + case at_lower_bound: + if (x == m_r_solver.m_lower_bounds[j]) return false; - delta = m_r_solver.m_low_bounds[j] - x; - m_r_solver.m_x[j] = m_r_solver.m_low_bounds[j]; + delta = m_r_solver.m_lower_bounds[j] - x; + m_r_solver.m_x[j] = m_r_solver.m_lower_bounds[j]; break; case at_fixed: case at_upper_bound: @@ -300,30 +297,30 @@ public: delta = m_r_solver.m_upper_bounds[j] - x; x = m_r_solver.m_upper_bounds[j]; break; - case column_type::low_bound: - delta = m_r_solver.m_low_bounds[j] - x; - x = m_r_solver.m_low_bounds[j]; + case column_type::lower_bound: + delta = m_r_solver.m_lower_bounds[j] - x; + x = m_r_solver.m_lower_bounds[j]; break; case column_type::boxed: if (x > m_r_solver.m_upper_bounds[j]) { delta = m_r_solver.m_upper_bounds[j] - x; x += m_r_solver.m_upper_bounds[j]; } else { - delta = m_r_solver.m_low_bounds[j] - x; - x = m_r_solver.m_low_bounds[j]; + delta = m_r_solver.m_lower_bounds[j] - x; + x = m_r_solver.m_lower_bounds[j]; } break; case column_type::fixed: - delta = m_r_solver.m_low_bounds[j] - x; - x = m_r_solver.m_low_bounds[j]; + delta = m_r_solver.m_lower_bounds[j] - x; + x = m_r_solver.m_lower_bounds[j]; break; default: - SASSERT(false); + lp_assert(false); } break; default: - SASSERT(false); + lp_unreachable(); } m_r_solver.remove_column_from_inf_set(j); return true; @@ -332,7 +329,7 @@ public: void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) { - SASSERT(m_r_solver.inf_set_is_correct()); + lp_assert(m_r_solver.inf_set_is_correct()); for (auto &t : signature) { unsigned j = t.first; if (m_r_heading[j] >= 0) @@ -344,12 +341,11 @@ public: for (const auto & cc : m_r_solver.m_A.m_columns[j]){ unsigned i = cc.m_i; unsigned jb = m_r_solver.m_basis[i]; - m_r_solver.m_x[jb] -= delta * m_r_solver.m_A.get_val(cc); - m_r_solver.update_column_in_inf_set(jb); + m_r_solver.update_x_with_delta_and_track_feasibility(jb, - delta * m_r_solver.m_A.get_val(cc)); } - SASSERT(m_r_solver.A_mult_x_is_off() == false); + CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); } - SASSERT(m_r_solver.inf_set_is_correct()); + lp_assert(m_r_solver.inf_set_is_correct()); } @@ -357,11 +353,11 @@ public: void prepare_solver_x_with_signature(const lar_solution_signature & signature, lp_primal_core_solver & s) { for (auto &t : signature) { unsigned j = t.first; - SASSERT(m_r_heading[j] < 0); + lp_assert(m_r_heading[j] < 0); auto pos_type = t.second; switch (pos_type) { - case at_low_bound: - s.m_x[j] = s.m_low_bounds[j]; + case at_lower_bound: + s.m_x[j] = s.m_lower_bounds[j]; break; case at_fixed: case at_upper_bound: @@ -374,33 +370,33 @@ public: case not_at_bound: switch (m_column_types[j]) { case column_type::free_column: - SASSERT(false); // unreachable + lp_assert(false); // unreachable case column_type::upper_bound: s.m_x[j] = s.m_upper_bounds[j]; break; - case column_type::low_bound: - s.m_x[j] = s.m_low_bounds[j]; + case column_type::lower_bound: + s.m_x[j] = s.m_lower_bounds[j]; break; case column_type::boxed: if (settings().random_next() % 2) { - s.m_x[j] = s.m_low_bounds[j]; + s.m_x[j] = s.m_lower_bounds[j]; } else { s.m_x[j] = s.m_upper_bounds[j]; } break; case column_type::fixed: - s.m_x[j] = s.m_low_bounds[j]; + s.m_x[j] = s.m_lower_bounds[j]; break; default: - SASSERT(false); + lp_assert(false); } break; default: - SASSERT(false); + lp_unreachable(); } } - SASSERT(is_zero_vector(s.m_b)); + lp_assert(is_zero_vector(s.m_b)); s.solve_Ax_eq_b(); } @@ -433,7 +429,7 @@ public: // the queues of delayed indices std::queue entr_q, leav_q; auto * l = cs.m_factorization; - SASSERT(l->get_status() == LU_status::OK); + lp_assert(l->get_status() == LU_status::OK); for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { unsigned entering = trace_of_basis_change[i]; unsigned leaving = trace_of_basis_change[i+1]; @@ -461,8 +457,8 @@ public: continue; } } - SASSERT(cs.m_basis_heading[entering] < 0); - SASSERT(cs.m_basis_heading[leaving] >= 0); + lp_assert(cs.m_basis_heading[entering] < 0); + lp_assert(cs.m_basis_heading[leaving] >= 0); if (l->get_status() == LU_status::OK) { l->prepare_entering(entering, w); // to init vector w l->replace_column(zero_of_type(), w, cs.m_basis_heading[leaving]); @@ -486,7 +482,7 @@ public: void solve_on_signature_tableau(const lar_solution_signature & signature, const vector & changes_of_basis) { r_basis_is_OK(); - SASSERT(settings().use_tableau()); + lp_assert(settings().use_tableau()); bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading); if (!r) { // it is the case where m_d_solver gives a degenerated basis @@ -505,10 +501,10 @@ public: return; m_r_solver.stop_tracing_basis_changes(); // and now catch up in the double solver - SASSERT(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); + lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); } - SASSERT(r_basis_is_OK()); + lp_assert(r_basis_is_OK()); } bool adjust_x_of_column(unsigned j) { @@ -522,16 +518,16 @@ public: } m_r_solver.snap_column_to_bound_tableau(j); - SASSERT(m_r_solver.column_is_feasible(j)); + lp_assert(m_r_solver.column_is_feasible(j)); m_r_solver.m_inf_set.erase(j); */ - SASSERT(false); + lp_assert(false); return true; } bool catch_up_in_lu_tableau(const vector & trace_of_basis_change, const vector & basis_heading) { - SASSERT(r_basis_is_OK()); + lp_assert(r_basis_is_OK()); // the queues of delayed indices std::queue entr_q, leav_q; for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { @@ -561,8 +557,8 @@ public: continue; } } - SASSERT(m_r_solver.m_basis_heading[entering] < 0); - SASSERT(m_r_solver.m_basis_heading[leaving] >= 0); + lp_assert(m_r_solver.m_basis_heading[entering] < 0); + lp_assert(m_r_solver.m_basis_heading[leaving] >= 0); m_r_solver.change_basis_unconditionally(entering, leaving); if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) { // unroll the last step @@ -572,12 +568,12 @@ public: #endif m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]); #ifdef Z3DEBUG - SASSERT(t); + lp_assert(t); #endif return false; } } - SASSERT(r_basis_is_OK()); + lp_assert(r_basis_is_OK()); return true; } @@ -587,14 +583,14 @@ public: if (!m_r_solver.m_settings.use_tableau()) return true; for (unsigned j : m_r_solver.m_basis) { - SASSERT(m_r_solver.m_A.m_columns[j].size() == 1); - SASSERT(m_r_solver.m_A.get_val(m_r_solver.m_A.m_columns[j][0]) == one_of_type()); + lp_assert(m_r_solver.m_A.m_columns[j].size() == 1); + lp_assert(m_r_solver.m_A.get_val(m_r_solver.m_A.m_columns[j][0]) == one_of_type()); } for (unsigned j =0; j < m_r_solver.m_basis_heading.size(); j++) { if (m_r_solver.m_basis_heading[j] >= 0) continue; if (m_r_solver.m_column_types[j] == column_type::fixed) continue; - SASSERT(static_cast(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size()); - SASSERT( m_r_solver.m_basis_heading[j] <= -1); + lp_assert(static_cast(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size()); + lp_assert( m_r_solver.m_basis_heading[j] <= -1); } #endif return true; @@ -614,7 +610,6 @@ public: } if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back - // std::cout << "no_r_lu" << std::endl; catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver); m_r_solver.find_feasible_solution(); m_d_basis = m_r_basis; @@ -630,7 +625,7 @@ public: return; m_r_solver.stop_tracing_basis_changes(); // and now catch up in the double solver - SASSERT(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); + lp_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); } } @@ -656,7 +651,7 @@ public: template void extract_signature_from_lp_core_solver(const lp_primal_core_solver & solver, lar_solution_signature & signature) { signature.clear(); - SASSERT(signature.size() == 0); + lp_assert(signature.size() == 0); for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) { if (solver.m_basis_heading[j] < 0) { signature[j] = solver.get_non_basic_column_value_position(j); @@ -666,27 +661,27 @@ public: void get_bounds_for_double_solver() { unsigned n = m_n(); - m_d_low_bounds.resize(n); + m_d_lower_bounds.resize(n); m_d_upper_bounds.resize(n); double delta = find_delta_for_strict_boxed_bounds().get_double(); if (delta > 0.000001) delta = 0.000001; for (unsigned j = 0; j < n; j++) { - if (low_bound_is_set(j)) { - const auto & lb = m_r_solver.m_low_bounds[j]; - m_d_low_bounds[j] = lb.x.get_double() + delta * lb.y.get_double(); + if (lower_bound_is_set(j)) { + const auto & lb = m_r_solver.m_lower_bounds[j]; + m_d_lower_bounds[j] = lb.x.get_double() + delta * lb.y.get_double(); } if (upper_bound_is_set(j)) { const auto & ub = m_r_solver.m_upper_bounds[j]; m_d_upper_bounds[j] = ub.x.get_double() + delta * ub.y.get_double(); - SASSERT(!low_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_low_bounds[j])); + lp_assert(!lower_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_lower_bounds[j])); } } } void scale_problem_for_doubles( static_matrix& A, - vector & low_bounds, + vector & lower_bounds, vector & upper_bounds) { vector column_scale_vector; vector right_side_vector(A.column_count()); @@ -706,8 +701,8 @@ public: if (m_r_solver.column_has_upper_bound(j)) { upper_bounds[j] /= column_scale_vector[j]; } - if (m_r_solver.column_has_low_bound(j)) { - low_bounds[j] /= column_scale_vector[j]; + if (m_r_solver.column_has_lower_bound(j)) { + lower_bounds[j] /= column_scale_vector[j]; } } } @@ -734,17 +729,17 @@ public: } - bool low_bound_is_set(unsigned j) const { + bool lower_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: case column_type::upper_bound: return false; - case column_type::low_bound: + case column_type::lower_bound: case column_type::boxed: case column_type::fixed: return true; default: - SASSERT(false); + lp_assert(false); } return false; } @@ -752,27 +747,27 @@ public: bool upper_bound_is_set(unsigned j) const { switch (m_column_types[j]) { case column_type::free_column: - case column_type::low_bound: + case column_type::lower_bound: return false; case column_type::upper_bound: case column_type::boxed: case column_type::fixed: return true; default: - SASSERT(false); + lp_assert(false); } return false; } void update_delta(mpq& delta, numeric_pair const& l, numeric_pair const& u) const { - SASSERT(l <= u); + lp_assert(l <= u); if (l.x < u.x && l.y > u.y) { mpq delta1 = (u.x - l.x) / (l.y - u.y); if (delta1 < delta) { delta = delta1; } } - SASSERT(l.x + delta * l.y <= u.x + delta * u.y); + lp_assert(l.x + delta * l.y <= u.x + delta * u.y); } @@ -781,8 +776,7 @@ public: for (unsigned j = 0; j < m_r_A.column_count(); j++ ) { if (m_column_types()[j] != column_type::boxed) continue; - update_delta(delta, m_r_low_bounds[j], m_r_upper_bounds[j]); - + update_delta(delta, m_r_lower_bounds[j], m_r_upper_bounds[j]); } return delta; } @@ -791,8 +785,8 @@ public: mpq find_delta_for_strict_bounds(const mpq & initial_delta) const{ mpq delta = initial_delta; for (unsigned j = 0; j < m_r_A.column_count(); j++ ) { - if (low_bound_is_set(j)) - update_delta(delta, m_r_low_bounds[j], m_r_x[j]); + if (lower_bound_is_set(j)) + update_delta(delta, m_r_lower_bounds[j], m_r_x[j]); if (upper_bound_is_set(j)) update_delta(delta, m_r_x[j], m_r_upper_bounds[j]); } @@ -803,14 +797,38 @@ public: m_r_solver.init_column_row_non_zeroes(); } - linear_combination_iterator * get_column_iterator(unsigned j) { - if (settings().use_tableau()) { - return new iterator_on_column>(m_r_solver.m_A.m_columns[j], m_r_solver.m_A); - } else { - m_r_solver.solve_Bd(j); - return new iterator_on_indexed_vector(m_r_solver.m_ed); + bool column_is_fixed(unsigned j) const { + return m_column_types()[j] == column_type::fixed || + ( m_column_types()[j] == column_type::boxed && + m_r_solver.m_lower_bounds[j] == m_r_solver.m_upper_bounds[j]); + } + + const impq & lower_bound(unsigned j) const { + lp_assert(m_column_types()[j] == column_type::fixed || + m_column_types()[j] == column_type::boxed || + m_column_types()[j] == column_type::lower_bound); + return m_r_lower_bounds[j]; + } + + const impq & upper_bound(unsigned j) const { + lp_assert(m_column_types()[j] == column_type::fixed || + m_column_types()[j] == column_type::boxed || + m_column_types()[j] == column_type::upper_bound); + return m_r_upper_bounds[j]; + } + + + const bool column_is_bounded(unsigned j) const { + switch(m_column_types()[j]) { + case column_type::fixed: + case column_type::boxed: + return true; + default: + return false; } } - + + const vector& r_basis() const { return m_r_basis; } + const vector& r_nbasis() const { return m_r_nbasis; } }; } diff --git a/src/util/lp/lar_core_solver.hpp b/src/util/lp/lar_core_solver_def.h similarity index 79% rename from src/util/lp/lar_core_solver.hpp rename to src/util/lp/lar_core_solver_def.h index 62a5c7887..f9937e77a 100644 --- a/src/util/lp/lar_core_solver.hpp +++ b/src/util/lp/lar_core_solver_def.h @@ -54,7 +54,7 @@ lar_core_solver::lar_core_solver( m_r_heading, m_costs_dummy, m_column_types(), - m_r_low_bounds(), + m_r_lower_bounds(), m_r_upper_bounds(), settings, column_names), @@ -66,15 +66,15 @@ lar_core_solver::lar_core_solver( m_d_heading, m_d_costs_dummy, m_column_types(), - m_d_low_bounds, + m_d_lower_bounds, m_d_upper_bounds, settings, column_names){} void lar_core_solver::init_costs(bool first_time) { - SASSERT(false); // should not be called - // SASSERT(this->m_x.size() >= this->m_n()); - // SASSERT(this->m_column_types.size() >= this->m_n()); + lp_assert(false); // should not be called + // lp_assert(this->m_x.size() >= this->m_n()); + // lp_assert(this->m_column_types.size() >= this->m_n()); // if (first_time) // this->m_costs.resize(this->m_n()); // X inf = this->m_infeasibility; @@ -84,7 +84,7 @@ void lar_core_solver::init_costs(bool first_time) { // if (!(first_time || inf >= this->m_infeasibility)) { // LP_OUT(this->m_settings, "iter = " << this->total_iterations() << std::endl); // LP_OUT(this->m_settings, "inf was " << T_to_string(inf) << " and now " << T_to_string(this->m_infeasibility) << std::endl); - // SASSERT(false); + // lp_assert(false); // } // if (inf == this->m_infeasibility) // this->m_iters_with_no_cost_growing++; @@ -108,17 +108,17 @@ void lar_core_solver::init_cost_for_column(unsigned j) { if (x > this->m_upper_bounds[j]) { this->m_costs[j] = 1; this->m_infeasibility += x - this->m_upper_bounds[j]; - } else if (x < this->m_low_bounds[j]) { - this->m_infeasibility += this->m_low_bounds[j] - x; + } else if (x < this->m_lower_bounds[j]) { + this->m_infeasibility += this->m_lower_bounds[j] - x; this->m_costs[j] = -1; } else { this->m_costs[j] = numeric_traits::zero(); } break; - case low_bound: - if (x < this->m_low_bounds[j]) { + case lower_bound: + if (x < this->m_lower_bounds[j]) { this->m_costs[j] = -1; - this->m_infeasibility += this->m_low_bounds[j] - x; + this->m_infeasibility += this->m_lower_bounds[j] - x; } else { this->m_costs[j] = numeric_traits::zero(); } @@ -135,7 +135,7 @@ void lar_core_solver::init_cost_for_column(unsigned j) { this->m_costs[j] = numeric_traits::zero(); break; default: - SASSERT(false); + lp_assert(false); break; }*/ } @@ -154,7 +154,7 @@ int lar_core_solver::column_is_out_of_bounds(unsigned j) { return 1; } return 0; - case low_bound: + case lower_bound: if (this->x_below_low_bound(j)) { return -1; } @@ -168,30 +168,14 @@ int lar_core_solver::column_is_out_of_bounds(unsigned j) { return 0; break; }*/ - SASSERT(false); + lp_assert(false); return true; } void lar_core_solver::calculate_pivot_row(unsigned i) { - SASSERT(!m_r_solver.use_tableau()); - SASSERT(m_r_solver.m_pivot_row.is_OK()); - m_r_solver.m_pivot_row_of_B_1.clear(); - m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m()); - m_r_solver.m_pivot_row.clear(); - m_r_solver.m_pivot_row.resize(m_r_solver.m_n()); - if (m_r_solver.m_settings.use_tableau()) { - unsigned basis_j = m_r_solver.m_basis[i]; - for (auto & c : m_r_solver.m_A.m_rows[i]) { - if (c.m_j != basis_j) - m_r_solver.m_pivot_row.set_value(c.get_val(), c.m_j); - } - return; - } - - m_r_solver.calculate_pivot_row_of_B_1(i); - m_r_solver.calculate_pivot_row_when_pivot_row_of_B1_is_ready(i); + m_r_solver.calculate_pivot_row(i); } @@ -238,7 +222,7 @@ void lar_core_solver::calculate_pivot_row(unsigned i) { } void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { - SASSERT(m_r_solver.A_mult_x_is_off() == false); + CASSERT("A_off", m_r_solver.A_mult_x_is_off() == false); unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj); m_infeasible_linear_combination.clear(); @@ -271,34 +255,44 @@ void lar_core_solver::fill_not_improvable_zero_sum() { } } +unsigned lar_core_solver::get_number_of_non_ints() const { + unsigned n = 0; + for (auto & x : m_r_solver.m_x) { + if (x.is_int() == false) + n++; + } + return n; +} void lar_core_solver::solve() { - SASSERT(m_r_solver.non_basic_columns_are_set_correctly()); - SASSERT(m_r_solver.inf_set_is_correct()); - if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) { - m_r_solver.set_status(OPTIMAL); - return; - } + lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); + lp_assert(m_r_solver.inf_set_is_correct()); + TRACE("find_feas_stats", tout << "infeasibles = " << m_r_solver.m_inf_set.size() << ", int_infs = " << get_number_of_non_ints() << std::endl;); + if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) { + m_r_solver.set_status(lp_status::OPTIMAL); + return; + } ++settings().st().m_need_to_solve_inf; - SASSERT(!m_r_solver.A_mult_x_is_off()); - SASSERT((!settings().use_tableau()) || r_basis_is_OK()); + CASSERT("A_off", !m_r_solver.A_mult_x_is_off()); + lp_assert((!settings().use_tableau()) || r_basis_is_OK()); if (need_to_presolve_with_double_solver()) { prefix_d(); lar_solution_signature solution_signature; vector changes_of_basis = find_solution_signature_with_doubles(solution_signature); - if (m_d_solver.get_status() == TIME_EXHAUSTED) { - m_r_solver.set_status(TIME_EXHAUSTED); + if (m_d_solver.get_status() == lp_status::TIME_EXHAUSTED) { + m_r_solver.set_status(lp_status::TIME_EXHAUSTED); return; } if (settings().use_tableau()) solve_on_signature_tableau(solution_signature, changes_of_basis); else solve_on_signature(solution_signature, changes_of_basis); - SASSERT(!settings().use_tableau() || r_basis_is_OK()); + + lp_assert(!settings().use_tableau() || r_basis_is_OK()); } else { if (!settings().use_tableau()) { bool snapped = m_r_solver.snap_non_basic_x_to_bound(); - SASSERT(m_r_solver.non_basic_columns_are_set_correctly()); + lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); if (snapped) m_r_solver.solve_Ax_eq_b(); } @@ -306,16 +300,16 @@ void lar_core_solver::solve() { m_r_solver.find_feasible_solution(); else m_r_solver.solve(); - SASSERT(!settings().use_tableau() || r_basis_is_OK()); + lp_assert(!settings().use_tableau() || r_basis_is_OK()); } - if (m_r_solver.get_status() == INFEASIBLE) { + if (m_r_solver.get_status() == lp_status::INFEASIBLE) { fill_not_improvable_zero_sum(); - } else if (m_r_solver.get_status() != UNBOUNDED) { - m_r_solver.set_status(OPTIMAL); + } else if (m_r_solver.get_status() != lp_status::UNBOUNDED) { + m_r_solver.set_status(lp_status::OPTIMAL); } - SASSERT(r_basis_is_OK()); - SASSERT(m_r_solver.non_basic_columns_are_set_correctly()); - SASSERT(m_r_solver.inf_set_is_correct()); + lp_assert(r_basis_is_OK()); + lp_assert(m_r_solver.non_basic_columns_are_set_correctly()); + lp_assert(m_r_solver.inf_set_is_correct()); } diff --git a/src/util/lp/lar_solver.hpp b/src/util/lp/lar_solver.cpp similarity index 80% rename from src/util/lp/lar_solver.hpp rename to src/util/lp/lar_solver.cpp index 6846717af..6fec5b329 100644 --- a/src/util/lp/lar_solver.hpp +++ b/src/util/lp/lar_solver.cpp @@ -31,10 +31,6 @@ 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) {} @@ -151,9 +147,10 @@ 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, + unsigned j = m_mpq_lar_core_solver.m_r_basis[row_index]; // basis column for the row + bound_analyzer_on_row> + ra_pos(m_mpq_lar_core_solver.get_pivot_row(), + j, zero_of_type>(), row_index, bp @@ -163,14 +160,14 @@ void lar_solver::analyze_new_bounds_on_row( void lar_solver::analyze_new_bounds_on_row_tableau( unsigned row_index, - bound_propagator & bp - ) { + 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, + bound_analyzer_on_row>::analyze_row(A_r().m_rows[row_index], + static_cast(-1), zero_of_type>(), row_index, bp @@ -200,15 +197,8 @@ void lar_solver::calculate_implied_bounds_for_row(unsigned i, bound_propagator & } } - -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]; + unsigned ext_var_or_term = m_var_register.local_to_external(j); return ext_var_or_term < m_terms_start_index ? j : ext_var_or_term; } @@ -219,27 +209,20 @@ void lar_solver::propagate_bounds_on_a_term(const lar_term& t, bound_propagator 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 bound_sign = ib.m_is_lower_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(); + unsigned bound_j = ib.m_j; + if (is_term(bound_j)) { + bound_j = m_var_register.external_to_local(bound_j); } for (auto const& r : A_r().m_rows[i]) { unsigned j = r.m_j; + if (j == bound_j) continue; 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(); + auto witness = sign > 0? ul.upper_bound_witness(): ul.lower_bound_witness(); lp_assert(is_valid(witness)); bp.consume(a, witness); } @@ -249,7 +232,7 @@ void lar_solver::explain_implied_bound(implied_bound & ib, bound_propagator & bp bool lar_solver::term_is_used_as_row(unsigned term) const { lp_assert(is_term(term)); - return contains(m_ext_vars_to_columns, term); + return m_var_register.external_is_used(term); } void lar_solver::propagate_bounds_on_terms(bound_propagator & bp) { @@ -269,6 +252,8 @@ void lar_solver::propagate_bounds_for_touched_rows(bound_propagator & bp) { for (unsigned i : m_rows_with_changed_bounds.m_index) { calculate_implied_bounds_for_row(i, bp); + if (settings().get_cancel_flag()) + return; } m_rows_with_changed_bounds.clear(); if (!use_tableau()) { @@ -280,12 +265,7 @@ 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(); @@ -294,19 +274,12 @@ lp_status lar_solver::find_feasible_solution() { 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; } @@ -317,7 +290,6 @@ lp_status lar_solver::solve() { } m_columns_with_changed_bound.clear(); - lp_assert(inf_int_set_is_correct()); return m_status; } @@ -326,7 +298,7 @@ void lar_solver::fill_explanation_from_infeasible_column(vector::one(), ul.upper_bound_witness())); - evidence.push_back(std::make_pair(-numeric_traits::one(), ul.low_bound_witness())); + evidence.push_back(std::make_pair(-numeric_traits::one(), ul.lower_bound_witness())); } @@ -366,15 +338,11 @@ void lar_solver::shrink_inf_set_after_pop(unsigned n, int_set & set) { 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); + m_var_register.shrink(n); TRACE("arith_int", tout << "pop" << std::endl;); if (m_settings.use_tableau()) { pop_tableau(); @@ -384,18 +352,14 @@ void lar_solver::pop(unsigned 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]; @@ -403,6 +367,9 @@ void lar_solver::pop(unsigned k) { m_constraints.resize(m_constraint_count); m_term_count.pop(k); for (unsigned i = m_term_count; i < m_terms.size(); i++) { +#if Z3DEBUG_CHECK_UNIQUE_TERMS + m_set_of_terms.erase(m_terms[i]); +#endif delete m_terms[i]; } m_terms.resize(m_term_count); @@ -411,7 +378,6 @@ void lar_solver::pop(unsigned k) { 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 { @@ -422,18 +388,17 @@ vector lar_solver::get_all_constraint_indices() const { return ret; } -bool lar_solver::maximize_term_on_tableau(const vector> & term, +bool lar_solver::maximize_term_on_tableau(const lar_term & term, impq &term_max) { if (settings().simplex_strategy() == simplex_strategy_enum::undecided) decide_on_strategy_and_adjust_initial_state(); - + + m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::FEASIBLE); 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]; + term_max = term.apply(m_mpq_lar_core_solver.m_r_x); return true; } @@ -451,13 +416,13 @@ bool lar_solver::reduced_costs_are_zeroes_for_r_solver() const { return true; } -void lar_solver::set_costs_to_zero(const vector> & term) { +void lar_solver::set_costs_to_zero(const lar_term& 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; + for (const auto & p : term) { + unsigned j = p.var(); rslv.m_costs[j] = zero_of_type(); int i = rslv.m_basis_heading[j]; if (i < 0) @@ -477,25 +442,25 @@ void lar_solver::set_costs_to_zero(const vector> & ter lp_assert(costs_are_zeros_for_r_solver()); } -void lar_solver::prepare_costs_for_r_solver(const vector> & term) { +void lar_solver::prepare_costs_for_r_solver(const lar_term & 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; + for (const auto & p : term) { + unsigned j = p.var(); + rslv.m_costs[j] = p.coeff(); if (rslv.m_basis_heading[j] < 0) - rslv.m_d[j] += p.first; + rslv.m_d[j] += p.coeff(); else - rslv.update_reduced_cost_for_basic_column_cost_change(- p.first, j); + rslv.update_reduced_cost_for_basic_column_cost_change(- p.coeff(), j); } lp_assert(rslv.reduced_costs_are_correct_tableau()); } -bool lar_solver::maximize_term_on_corrected_r_solver(const vector> & term, +bool lar_solver::maximize_term_on_corrected_r_solver(lar_term & term, impq &term_max) { settings().backup_costs = false; switch (settings().simplex_strategy()) { @@ -525,14 +490,70 @@ bool lar_solver::maximize_term_on_corrected_r_solver(const vector> & term, +} + + +bool lar_solver::remove_from_basis(unsigned j) { + return m_mpq_lar_core_solver.m_r_solver.remove_from_basis(j); +} + +lar_term lar_solver::get_term_to_maximize(unsigned ext_j) const { + unsigned local_j; + if (m_var_register.external_is_used(ext_j, local_j)) { + lar_term r; + r. add_monomial(one_of_type(), local_j); + return r; + } + if (!is_term(ext_j) || adjust_term_index(ext_j) >= m_terms.size()) + return lar_term(); // return an empty term + return get_term(ext_j); +} + +lp_status lar_solver::maximize_term(unsigned ext_j, impq &term_max) { - lp_assert(m_mpq_lar_core_solver.m_r_solver.current_x_is_feasible()); + bool was_feasible = m_mpq_lar_core_solver.m_r_solver.calc_current_x_is_feasible_include_non_basis(); + impq prev_value; + lar_term term = get_term_to_maximize(ext_j); + if (term.is_empty()) { + return lp_status::UNBOUNDED; + } + + auto backup = m_mpq_lar_core_solver.m_r_x; + if (was_feasible) { + prev_value = term.apply(m_mpq_lar_core_solver.m_r_x); + } + 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); + if (!maximize_term_on_corrected_r_solver(term, term_max)) { + m_mpq_lar_core_solver.m_r_x = backup; + return lp_status::UNBOUNDED; + } + + impq opt_val = term_max; + + bool change = false; + for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_x.size(); j++) { + if (!column_is_int(j)) + continue; + if (column_value_is_integer(j)) + continue; + if (m_int_solver->is_base(j)) { + if (!remove_from_basis(j)) // consider a special version of remove_from_basis that would not remove inf_int columns + return lp_status::FEASIBLE; // it should not happen + } + m_int_solver->patch_nbasic_column(j, false); + if (!column_value_is_integer(j)) + return lp_status::FEASIBLE; + change = true; + } + if (change) { + term_max = term.apply(m_mpq_lar_core_solver.m_r_x); + } + if (was_feasible && term_max < prev_value) { + term_max = prev_value; + m_mpq_lar_core_solver.m_r_x = backup; + } + return term_max == opt_val? lp_status::OPTIMAL :lp_status::FEASIBLE; } @@ -558,9 +579,9 @@ void lar_solver::set_upper_bound_witness(var_index j, constraint_index ci) { m_columns_to_ul_pairs[j] = ul; } -void lar_solver::set_low_bound_witness(var_index j, constraint_index ci) { +void lar_solver::set_lower_bound_witness(var_index j, constraint_index ci) { ul_pair ul = m_columns_to_ul_pairs[j]; - ul.low_bound_witness() = ci; + ul.lower_bound_witness() = ci; m_columns_to_ul_pairs[j] = ul; } @@ -591,7 +612,8 @@ void lar_solver::substitute_terms_in_linear_expression(const vector & 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]; @@ -683,7 +704,6 @@ void lar_solver::change_basic_columns_dependend_on_a_given_nb_column(unsigned j, 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) { @@ -753,10 +773,8 @@ void lar_solver::solve_with_core_solver() { 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()); } @@ -812,18 +830,11 @@ void lar_solver::add_last_rows_to_lu(lp_primal_core_solver & s) { 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; } } @@ -885,7 +896,7 @@ void lar_solver::copy_from_mpq_matrix(static_matrix & matr) { 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()) { + if (ci.upper_bound_is_set() && ci.lower_bound_is_set() && ci.get_upper_bound() == ci.get_lower_bound() && !ci.is_fixed()) { ci.set_fixed_value(ci.get_upper_bound()); return true; } @@ -895,7 +906,7 @@ bool lar_solver::try_to_set_fixed(column_info & ci) { 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()) + if (ci.get_lower_bound() == ci.get_upper_bound()) ret = column_type::fixed; } return ret; @@ -904,10 +915,10 @@ column_type lar_solver::get_column_type(const column_info & ci) { 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()) + if (j >= m_var_register.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]); + return std::string("v") + T_to_string(m_var_register.local_to_external(j)); } bool lar_solver::all_constrained_variables_are_registered(const vector>& left_side) { @@ -926,7 +937,6 @@ bool lar_solver::all_constraints_hold() const { for (unsigned i = 0; i < m_constraints.size(); i++) { if (!constraint_holds(*m_constraints[i], var_map)) { - print_constraint(i, std::cout); return false; } } @@ -993,13 +1003,6 @@ bool lar_solver::the_left_sides_sum_to_zero(const vector::one()) + out << " - "; + else if (val != numeric_traits::one()) + out << T_to_string(val); + out << this->get_column_name(p.var()); + } + } void lar_solver::print_term_as_indices(lar_term const& term, std::ostream & out) const { @@ -1256,7 +1301,6 @@ void lar_solver::random_update(unsigned sz, var_index const * vars) { 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()); } @@ -1378,7 +1422,8 @@ void lar_solver::pop_tableau() { 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()) + unsigned size = m_var_register.size(); + while (A_r().column_count() > 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()); @@ -1386,7 +1431,6 @@ void lar_solver::pop_tableau() { } 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; @@ -1429,12 +1473,6 @@ void lar_solver::clean_inf_set_of_r_solver_after_pop() { } } -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++) { @@ -1462,22 +1500,13 @@ bool lar_solver::var_is_int(var_index v) const { } 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(); + return m_var_register.local_is_int(j); } 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 { @@ -1486,40 +1515,27 @@ bool lar_solver::strategy_is_undecided() const { 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; + var_index local_j; 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(); - } + if (m_var_register.external_is_used(ext_j, local_j)) + return local_j; lp_assert(m_columns_to_ul_pairs.size() == A_r().column_count()); - i = A_r().column_count(); + local_j = 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; + return local_j; } 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); + lp_assert(!m_var_register.external_is_used(ext_v)); + m_var_register.add_var(ext_v, is_int); } 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); @@ -1529,9 +1545,9 @@ 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 + // lp_assert(m_mpq_lar_core_solver.m_d_lower_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_lower_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) { @@ -1549,9 +1565,9 @@ 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 + // lp_assert(m_mpq_lar_core_solver.m_r_lower_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_lower_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); @@ -1573,17 +1589,55 @@ void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { var_index lar_solver::add_term_undecided(const vector> & coeffs, const mpq &m_v) { - m_terms.push_back(new lar_term(coeffs, m_v)); + push_and_register_term(new lar_term(coeffs, m_v)); return m_terms_start_index + m_terms.size() - 1; } +#if Z3DEBUG_CHECK_UNIQUE_TERMS +bool lar_solver::term_coeffs_are_ok(const vector> & coeffs, const mpq& v) { + if (coeffs.empty()) { + return is_zero(v); + } + + for (const auto & p : coeffs) { + if (column_is_real(p.second)) + return true; + } + + mpq g; + bool g_is_set = false; + for (const auto & p : coeffs) { + if (!p.first.is_int()) { + return false; + } + if (!g_is_set) { + g_is_set = true; + g = p.first; + } else { + g = gcd(g, p.first); + } + } + if (g == one_of_type()) + return true; + + return false; +} +#endif +void lar_solver::push_and_register_term(lar_term* t) { +#if Z3DEBUG_CHECK_UNIQUE_TERMS + lp_assert(m_set_of_terms.find(t) == m_set_of_terms.end()); + m_set_of_terms.insert(t); +#endif + m_terms.push_back(t); +} + // 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)); + push_and_register_term(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()) { @@ -1591,11 +1645,12 @@ var_index lar_solver::add_term(const vector> & coeffs, 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()); + lp_assert(m_var_register.size() == A_r().column_count()); return ret; } void lar_solver::add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) { + TRACE("dump_terms", print_term(*term, tout); tout << std::endl;); register_new_ext_var_index(term_ext_index, term_is_int(term)); // j will be a new variable unsigned j = A_r().column_count(); @@ -1603,8 +1658,8 @@ void lar_solver::add_row_from_term_no_constraint(const lar_term * term, unsigned 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, + A_r().fill_last_row_with_pivoting(*term, + j, 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()); } @@ -1614,7 +1669,6 @@ void lar_solver::add_row_from_term_no_constraint(const lar_term * term, unsigned 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() { @@ -1623,13 +1677,12 @@ void lar_solver::add_basic_var_to_core_fields() { 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 { +bool lar_solver::bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const { if (!column_is_int(j)) return true; return right_side.is_int(); @@ -1639,7 +1692,7 @@ constraint_index lar_solver::add_var_bound(var_index j, lconstraint_kind kind, c 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)); + lp_assert(bound_is_integer_for_integer_column(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); @@ -1648,7 +1701,6 @@ constraint_index lar_solver::add_var_bound(var_index j, lconstraint_kind kind, c 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; } @@ -1660,8 +1712,8 @@ void lar_solver::update_column_type_and_bound(var_index j, lconstraint_kind kind 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); + case column_type::lower_bound: + update_lower_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); @@ -1677,10 +1729,9 @@ void lar_solver::update_column_type_and_bound(var_index j, lconstraint_kind kind 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(); + // lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int()); + unsigned term_j; + if (m_var_register.external_is_used(j, term_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); @@ -1741,7 +1792,7 @@ 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_lower_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; @@ -1750,9 +1801,9 @@ void lar_solver::adjust_initial_state_for_lu() { 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 + // lp_assert(m_mpq_lar_core_solver.m_d_lower_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_lower_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) { @@ -1766,10 +1817,10 @@ void lar_solver::adjust_initial_state_for_lu() { } 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)) + for (unsigned i = 0; i < m_terms.size(); i++) { + if (m_var_register.external_is_used(i + m_terms_start_index)) continue; - add_row_from_term_no_constraint(m_terms[j], j + m_terms_start_index); + add_row_from_term_no_constraint(m_terms[i], i + m_terms_start_index); } } @@ -1808,19 +1859,19 @@ void lar_solver::update_free_column_type_and_bound(var_index j, lconstraint_kind case GT: y_of_bound = 1; case GE: - m_mpq_lar_core_solver.m_column_types[j] = column_type::low_bound; + m_mpq_lar_core_solver.m_column_types[j] = column_type::lower_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; + m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; } - set_low_bound_witness(j, constr_ind); + set_lower_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()); + m_mpq_lar_core_solver.m_r_lower_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); + set_lower_bound_witness(j, constr_ind); break; default: @@ -1852,15 +1903,15 @@ void lar_solver::update_upper_bound_column_type_and_bound(var_index j, lconstrai 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_mpq_lar_core_solver.m_r_lower_bounds[j] = low; + set_lower_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; + m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; } } break; @@ -1869,13 +1920,13 @@ void lar_solver::update_upper_bound_column_type_and_bound(var_index j, lconstrai 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); + set_lower_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_mpq_lar_core_solver.m_r_lower_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_lower_bound_witness(j, ci); set_upper_bound_witness(j, ci); m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; } @@ -1890,7 +1941,7 @@ void lar_solver::update_upper_bound_column_type_and_bound(var_index j, lconstrai } 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])); + 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_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j])); mpq y_of_bound(0); switch (kind) { case LT: @@ -1904,13 +1955,13 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin m_columns_with_changed_bound.insert(j); } - if (up < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + if (up < m_mpq_lar_core_solver.m_r_lower_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]) + if (m_mpq_lar_core_solver.m_r_lower_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j]) m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; } } @@ -1920,10 +1971,10 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin 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; + if (low > m_mpq_lar_core_solver.m_r_lower_bounds[j]) { + m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; m_columns_with_changed_bound.insert(j); - set_low_bound_witness(j, ci); + set_lower_bound_witness(j, ci); } if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { m_status = lp_status::INFEASIBLE; @@ -1937,7 +1988,7 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin case EQ: { auto v = numeric_pair(right_side, zero_of_type()); - if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); @@ -1945,11 +1996,11 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin 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); + set_lower_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); + m_mpq_lar_core_solver.m_r_lower_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; + set_lower_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); @@ -1963,8 +2014,8 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin } } -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); +void lar_solver::update_lower_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::lower_bound); mpq y_of_bound(0); switch (kind) { case LT: @@ -1976,12 +2027,12 @@ void lar_solver::update_low_bound_column_type_and_bound(var_index j, lconstraint 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]) { + if (up < m_mpq_lar_core_solver.m_r_lower_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; + m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_lower_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; } } break; @@ -1990,24 +2041,24 @@ void lar_solver::update_low_bound_column_type_and_bound(var_index j, lconstraint 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; + if (low > m_mpq_lar_core_solver.m_r_lower_bounds[j]) { + m_mpq_lar_core_solver.m_r_lower_bounds[j] = low; m_columns_with_changed_bound.insert(j); - set_low_bound_witness(j, ci); + set_lower_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]) { + if (v < m_mpq_lar_core_solver.m_r_lower_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); + m_mpq_lar_core_solver.m_r_lower_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; + set_lower_bound_witness(j, ci); set_upper_bound_witness(j, ci); m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; } @@ -2022,14 +2073,14 @@ void lar_solver::update_low_bound_column_type_and_bound(var_index j, lconstraint } 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())); + 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_lower_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_lower_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]) { + if (v <= m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); @@ -2037,7 +2088,7 @@ void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kin break; case LE: { - if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); @@ -2049,7 +2100,7 @@ void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kin 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); + set_lower_bound_witness(j, ci); } } break; @@ -2058,13 +2109,13 @@ void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kin 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); + set_lower_bound_witness(j, ci); } } break; case EQ: { - if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + if (v < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; m_infeasible_column_index = j; set_upper_bound_witness(j, ci); @@ -2072,7 +2123,7 @@ void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kin 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); + set_lower_bound_witness(j, ci); } break; } @@ -2083,6 +2134,147 @@ void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kin } } +bool lar_solver::column_corresponds_to_term(unsigned j) const { + return m_var_register.local_to_external(j) >= m_terms_start_index; +} + +var_index lar_solver::to_column(unsigned ext_j) const { + return m_var_register.external_to_local(ext_j); +} + +bool lar_solver::tighten_term_bounds_by_delta(unsigned term_index, const impq& delta) { + unsigned tj = term_index + m_terms_start_index; + unsigned j; + if (m_var_register.external_is_used(tj, j) == false) + return true; // the term is not a column so it has no bounds + auto & slv = m_mpq_lar_core_solver.m_r_solver; + TRACE("cube", tout << "delta = " << delta << std::endl; + m_int_solver->display_column(tout, j); ); + if (slv.column_has_upper_bound(j) && slv.column_has_lower_bound(j)) { + if (slv.m_upper_bounds[j] - delta < slv.m_lower_bounds[j] + delta) { + TRACE("cube", tout << "cannot tighten, delta = " << delta;); + return false; + } + } + TRACE("cube", tout << "can tighten";); + if (slv.column_has_upper_bound(j)) { + if (!is_zero(delta.y)) + add_var_bound(tj, lconstraint_kind::LT, slv.m_upper_bounds[j].x - delta.x); + else + add_var_bound(tj, lconstraint_kind::LE, slv.m_upper_bounds[j].x - delta.x); + } + if (slv.column_has_lower_bound(j)) { + if (!is_zero(delta.y)) + add_var_bound(tj, lconstraint_kind::GT, slv.m_lower_bounds[j].x + delta.x); + else + add_var_bound(tj, lconstraint_kind::GE, slv.m_lower_bounds[j].x + delta.x); + } + return true; +} + +void lar_solver::update_delta_for_terms(const impq & delta, unsigned j, const vector& terms_of_var) { + for (unsigned i : terms_of_var) { + lar_term & t = *m_terms[i]; + auto it = t.m_coeffs.find(j); + unsigned tj = to_column(i + m_terms_start_index); + TRACE("cube", + tout << "t.apply = " << t.apply(m_mpq_lar_core_solver.m_r_x) << ", m_mpq_lar_core_solver.m_r_x[tj]= " << m_mpq_lar_core_solver.m_r_x[tj];); + TRACE("cube", print_term_as_indices(t, tout); + tout << ", it->second = " << it->second; + tout << ", tj = " << tj << ", "; + m_int_solver->display_column(tout, tj); + ); + + m_mpq_lar_core_solver.m_r_x[tj] += it->second * delta; + lp_assert(t.apply(m_mpq_lar_core_solver.m_r_x) == m_mpq_lar_core_solver.m_r_x[tj]); + TRACE("cube", m_int_solver->display_column(tout, tj); ); + } +} + + +void lar_solver::fill_vars_to_terms(vector> & vars_to_terms) { + for (unsigned j = 0; j < m_terms.size(); j++) { + if (!term_is_used_as_row(j + m_terms_start_index)) + continue; + for (const auto & p : *m_terms[j]) { + if (p.var() >= vars_to_terms.size()) + vars_to_terms.resize(p.var() + 1); + vars_to_terms[p.var()].push_back(j); + } + } +} + +void lar_solver::round_to_integer_solution() { + vector> vars_to_terms; + fill_vars_to_terms(vars_to_terms); + + for (unsigned j = 0; j < column_count(); j++) { + if (column_is_int(j)) continue; + if (column_corresponds_to_term(j)) continue; + TRACE("cube", m_int_solver->display_column(tout, j);); + impq& v = m_mpq_lar_core_solver.m_r_x[j]; + if (v.is_int()) + continue; + impq flv = floor(v); + auto del = flv - v; // del is negative + if (del < - mpq(1, 2)) { + del = impq(one_of_type()) + del; + v = ceil(v); + } else { + v = flv; + } + TRACE("cube", m_int_solver->display_column(tout, j); tout << "v = " << v << " ,del = " << del;); + update_delta_for_terms(del, j, vars_to_terms[j]); + } +} + +bool lar_solver::get_equality_and_right_side_for_term_on_current_x(unsigned term_index, mpq & rs, constraint_index& ci) const { + unsigned tj = term_index + m_terms_start_index; + unsigned j; + bool is_int; + if (m_var_register.external_is_used(tj, j, is_int) == false) + return false; // the term does not have bound because it does not correspond to a column + if (!is_int) // todo - allow for the next version of hnf + return false; + impq term_val; + bool term_val_ready = false; + mpq b; + bool is_strict; + if (has_upper_bound(j, ci, b, is_strict) && !is_strict) { + lp_assert(b.is_int()); + term_val = terms()[term_index]->apply(m_mpq_lar_core_solver.m_r_x); + term_val_ready = true; + if (term_val.x == b) { + rs = b; + return true; + } + } + if (has_lower_bound(j, ci, b, is_strict) && !is_strict) { + if (!term_val_ready) + term_val = terms()[term_index]->apply(m_mpq_lar_core_solver.m_r_x); + lp_assert(b.is_int()); + + if (term_val.x == b) { + rs = b; + return true; + } + } + return false; +} + +void lar_solver::set_cut_strategy(unsigned cut_frequency) { + if (cut_frequency < 4) { // enable only gomory cut + settings().m_int_gomory_cut_period = 2; + settings().m_hnf_cut_period = 100000000; + } else if (cut_frequency == 4) { // enable all cuts and cube equally + settings().m_int_gomory_cut_period = 4; + settings().m_hnf_cut_period = 4; + } else { + // disable all heuristics + settings().m_int_gomory_cut_period = 10000000; + settings().m_hnf_cut_period = 100000000; + } +} } // namespace lp diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index c26333edb..f17aa4a0d 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -10,7 +10,7 @@ Abstract: Author: - + Nikolaj Bjorner (nbjorner) Lev Nachmanson (levnach) Revision History: @@ -34,1530 +34,548 @@ Revision History: #include "util/lp/lp_primal_core_solver.h" #include "util/lp/random_updater.h" #include -#include "util/lp/stacked_map.h" #include "util/lp/stacked_value.h" #include "util/lp/stacked_vector.h" -#include "util/lp/stacked_unordered_set.h" -#include "util/lp/iterator_on_pivot_row.h" #include "util/lp/implied_bound.h" #include "util/lp/bound_analyzer_on_row.h" -#include "util/lp/iterator_on_term_with_basis_var.h" -#include "util/lp/iterator_on_row.h" -#include "util/lp/quick_xplain.h" #include "util/lp/conversion_helper.h" +#include "util/lp/int_solver.h" +#include "util/lp/nra_solver.h" +#include "util/lp/bound_propagator.h" + namespace lp { + class lar_solver : public column_namer { +#if Z3DEBUG_CHECK_UNIQUE_TERMS + struct term_hasher { + std::size_t operator()(const lar_term *t) const + { + using std::size_t; + using std::hash; + using std::string; + size_t seed = 0; + for (const auto& p : t->m_coeffs) { + hash_combine(seed, p); + } + return seed; + } + }; + + struct term_ls_comparer { + bool operator()(const lar_term *a, const lar_term* b) const + { + // a is contained in b + for (auto & p : a->m_coeffs) { + auto t = b->m_coeffs.find(p.first); + if (t == b->m_coeffs.end()) + return false; + if (p.second != t->second) + return false; + } + // zz is contained in b + for (auto & p : b->m_coeffs) { + auto t = a->m_coeffs.find(p.first); + if (t == a->m_coeffs.end()) + return false; + if (p.second != t->second) + return false; + } + return true; + } + }; + std::unordered_set m_set_of_terms; +#endif + //////////////////// fields ////////////////////////// - lp_settings m_settings; - stacked_value m_status; - stacked_value m_simplex_strategy; - std::unordered_map m_ext_vars_to_columns; - vector m_columns_to_ext_vars_or_term_indices; - stacked_vector m_vars_to_ul_pairs; - vector m_constraints; - stacked_value m_constraint_count; + lp_settings m_settings; + lp_status m_status; + stacked_value m_simplex_strategy; + var_register m_var_register; + stacked_vector m_columns_to_ul_pairs; + vector m_constraints; +private: + stacked_value m_constraint_count; // the set of column indices j such that bounds have changed for j - int_set m_columns_with_changed_bound; - int_set m_rows_with_changed_bounds; - int_set m_basic_columns_with_changed_cost; - stacked_value m_infeasible_column_index; // such can be found at the initialization step - stacked_value m_term_count; - vector m_terms; - vector m_orig_terms; - const var_index m_terms_start_index; - indexed_vector m_column_buffer; + int_set m_columns_with_changed_bound; + int_set m_rows_with_changed_bounds; + int_set m_basic_columns_with_changed_cost; + stacked_value m_infeasible_column_index; // such can be found at the initialization step + stacked_value m_term_count; + vector m_terms; + const var_index m_terms_start_index; + indexed_vector m_column_buffer; public: - lar_core_solver m_mpq_lar_core_solver; - unsigned constraint_count() const { - return m_constraints.size(); + lar_core_solver m_mpq_lar_core_solver; +private: + int_solver * m_int_solver; + +public : + unsigned terms_start_index() const { return m_terms_start_index; } + const vector terms() const { return m_terms; } + const vector& constraints() const { + return m_constraints; } - const lar_base_constraint& get_constraint(unsigned ci) const { - return *(m_constraints[ci]); + void set_int_solver(int_solver * int_slv) { + m_int_solver = int_slv; } - + int_solver * get_int_solver() { + return m_int_solver; + } + unsigned constraint_count() const; + const lar_base_constraint& get_constraint(unsigned ci) const; ////////////////// methods //////////////////////////////// - static_matrix> & A_r() { return m_mpq_lar_core_solver.m_r_A;} - static_matrix> const & A_r() const { return m_mpq_lar_core_solver.m_r_A;} - static_matrix & A_d() { return m_mpq_lar_core_solver.m_d_A;} - static_matrix const & A_d() const { return m_mpq_lar_core_solver.m_d_A;} - + static_matrix> & A_r(); + static_matrix> const & A_r() const; + static_matrix & A_d(); + static_matrix const & A_d() const; + static bool valid_index(unsigned j){ return static_cast(j) >= 0;} + bool column_is_int(unsigned j) const; + bool column_value_is_int(unsigned j) const { + return m_mpq_lar_core_solver.m_r_x[j].is_int(); + } + const impq& get_column_value(unsigned j) const { + return m_mpq_lar_core_solver.m_r_x[j]; + } + bool is_term(var_index j) const; + bool column_is_fixed(unsigned j) const; public: - lp_settings & settings() { return m_settings;} - lp_settings const & settings() const { return m_settings;} + // init region + bool strategy_is_undecided() const; - void clear() {SASSERT(false); // not implemented - } + var_index add_var(unsigned ext_j, bool is_integer); + + void register_new_ext_var_index(unsigned ext_v, bool is_int); + + bool term_is_int(const lar_term * t) const; + + bool var_is_int(var_index v) const; + + void add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int); + + void add_new_var_to_core_fields_for_doubles(bool register_in_basis); + + void add_new_var_to_core_fields_for_mpq(bool register_in_basis); - lar_solver() : m_status(OPTIMAL), - m_infeasible_column_index(-1), - m_terms_start_index(1000000), - m_mpq_lar_core_solver(m_settings, *this) - {} - void set_propagate_bounds_on_pivoted_rows_mode(bool v) { - m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows = v? (& m_rows_with_changed_bounds) : nullptr; - } + // terms + var_index add_term(const vector> & coeffs, + const mpq &m_v); - virtual ~lar_solver(){ - for (auto c : m_constraints) - delete c; - for (auto t : m_terms) - delete t; - for (auto t : m_orig_terms) - delete t; - } + var_index add_term_undecided(const vector> & coeffs, + const mpq &m_v); -#include "util/lp/init_lar_solver.h" + bool term_coeffs_are_ok(const vector> & coeffs, const mpq& v); + void push_and_register_term(lar_term* t); - numeric_pair const& get_value(var_index vi) const { return m_mpq_lar_core_solver.m_r_x[vi]; } + void add_row_for_term(const lar_term * term, unsigned term_ext_index); - bool is_term(var_index j) const { - return j >= m_terms_start_index && j - m_terms_start_index < m_terms.size(); - } + void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index); - unsigned adjust_term_index(unsigned j) const { - SASSERT(is_term(j)); - return j - m_terms_start_index; - } + void add_basic_var_to_core_fields(); + + constraint_index add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) ; + + void update_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index); + + void add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); - bool use_lu() const { return m_settings.simplex_strategy() == simplex_strategy_enum::lu; } + void add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term, + lconstraint_kind kind, const mpq & right_side); - bool sizes_are_correct() const { - SASSERT(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_x.size()); - return true; - } - - - void 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_orig_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; - // for (auto & p : be.m_explanation) { - // out << p.first << " : "; - // print_constraint(p.second, out); - // } - - // m_mpq_lar_core_solver.m_r_solver.print_column_info(be.m_j< m_terms_start_index? be.m_j : adjust_term_index(be.m_j), out); - out << "end of implied bound" << std::endl; - } - - bool 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; - } - SASSERT(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_orig_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 decide_on_strategy_and_adjust_initial_state(); + + void adjust_initial_state(); + + void adjust_initial_state_for_lu(); + + void adjust_initial_state_for_tableau_rows(); + + // this fills the last row of A_d and sets the basis column: -1 in the last column of the row + void fill_last_row_of_A_d(static_matrix & A, const lar_term* ls); + + void update_free_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_ind); + + void update_upper_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); + + void update_boxed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); + void update_lower_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); + + void update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); + //end of init region + lp_settings & settings(); + + lp_settings const & settings() const; + + void clear(); + lar_solver(); + void set_track_pivoted_rows(bool v); + + bool get_track_pivoted_rows() const; + + virtual ~lar_solver(); + + unsigned adjust_term_index(unsigned j) const; + bool use_lu() const; + + bool sizes_are_correct() const; + + void print_implied_bound(const implied_bound& be, std::ostream & out) const; + + bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; + void analyze_new_bounds_on_row( - unsigned row_index, - lp_bound_propagator & bp) { - SASSERT(!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(); - } + unsigned row_index, + bound_propagator & bp); void analyze_new_bounds_on_row_tableau( - unsigned row_index, - lp_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]); - SASSERT(use_tableau()); - bound_analyzer_on_row::analyze_row(it, - zero_of_type>(), - row_index, - bp + unsigned row_index, + bound_propagator & bp ); - } + + + void substitute_basis_var_in_terms_for_row(unsigned i); + + void calculate_implied_bounds_for_row(unsigned i, bound_propagator & bp); + + unsigned adjust_column_index_to_term_index(unsigned j) const; + + void propagate_bounds_on_a_term(const lar_term& t, bound_propagator & bp, unsigned term_offset); - void 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 calculate_implied_bounds_for_row(unsigned i, lp_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); - } - } - - /* - void process_new_implied_evidence_for_low_bound( - implied_bound_explanation& implied_evidence, // not pushed yet - vector & bound_evidences, - std::unordered_map & improved_low_bounds) { - - unsigned existing_index; - if (try_get_val(improved_low_bounds, implied_evidence.m_j, existing_index)) { - // we are improving the existent bound - bound_evidences[existing_index] = fill_bound_evidence(implied_evidence); - } else { - improved_low_bounds[implied_evidence.m_j] = bound_evidences.size(); - bound_evidences.push_back(fill_bound_evidence(implied_evidence)); - } - } - - void fill_bound_evidence_on_term(implied_bound & ie, implied_bound& be) { - SASSERT(false); - } - void fill_implied_bound_on_row(implied_bound & ie, implied_bound& be) { - iterator_on_row it(A_r().m_rows[ie.m_row_or_term_index]); - mpq a; unsigned j; - bool toggle = ie.m_coeff_before_j_is_pos; - if (!ie.m_is_low_bound) - toggle = !toggle; - while (it.next(a, j)) { - if (j == ie.m_j) continue; - const ul_pair & ul = m_vars_to_ul_pairs[j]; - - if (is_neg(a)) { // so the monoid has a positive coeff on the right side - constraint_index witness = toggle ? ul.m_low_bound_witness : ul.m_upper_bound_witness; - SASSERT(is_valid(witness)); - be.m_explanation.emplace_back(a, witness); - } - } - } - */ - /* - implied_bound fill_implied_bound_for_low_bound(implied_bound& ie) { - implied_bound be(ie.m_j, ie.m_bound.y.is_zero() ? GE : GT, ie.m_bound.x); - if (is_term(ie.m_row_or_term_index)) { - fill_implied_bound_for_low_bound_on_term(ie, be); - } - else { - fill_implied_bound_for_low_bound_on_row(ie, be); - } - return be; - } - - implied_bound fill_implied_bound_for_upper_bound(implied_bound& implied_evidence) { - SASSERT(false); - - be.m_j = implied_evidence.m_j; - be.m_bound = implied_evidence.m_bound.x; - be.m_kind = implied_evidence.m_bound.y.is_zero() ? LE : LT; - for (auto t : implied_evidence.m_vector_of_bound_signatures) { - const ul_pair & ul = m_vars_to_ul_pairs[t.m_column_index]; - constraint_index witness = t.m_low_bound ? ul.m_low_bound_witness : ul.m_upper_bound_witness; - SASSERT(is_valid(witness)); - be.m_explanation.emplace_back(t.m_coeff, witness); - } - - } - */ - /* - void process_new_implied_evidence_for_upper_bound( - implied_bound& implied_evidence, - vector & implied_bounds, - std::unordered_map & improved_upper_bounds) { - unsigned existing_index; - if (try_get_val(improved_upper_bounds, implied_evidence.m_j, existing_index)) { - implied_bound & be = implied_bounds[existing_index]; - be.m_explanation.clear(); - // we are improving the existent bound improve the existing bound - be = fill_implied_bound_for_upper_bound(implied_evidence); - } else { - improved_upper_bounds[implied_evidence.m_j] = implied_bounds.size(); - implied_bounds.push_back(fill_implied_bound_for_upper_bound(implied_evidence)); - } - } - */ - // implied_bound * get_existing_ - - linear_combination_iterator * create_new_iter_from_term(unsigned term_index) const { - SASSERT(false); // not implemented - return nullptr; - // new linear_combination_iterator_on_vector(m_terms[adjust_term_index(term_index)]->coeffs_as_vector()); - } - - unsigned 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 propagate_bounds_on_a_term(const lar_term& t, lp_bound_propagator & bp, unsigned term_offset) { - SASSERT(false); // not implemented - } + void explain_implied_bound(implied_bound & ib, bound_propagator & bp); - void explain_implied_bound(implied_bound & ib, lp_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)) { - m_j = m_ext_vars_to_columns[m_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)) { - j = m_ext_vars_to_columns[j]; - } - int a_sign = is_pos(a)? 1: -1; - int sign = j_sign * a_sign; - const ul_pair & ul = m_vars_to_ul_pairs[j]; - auto witness = sign > 0? ul.upper_bound_witness(): ul.low_bound_witness(); - SASSERT(is_valid(witness)); - bp.consume(a, witness); - } - // SASSERT(implied_bound_is_correctly_explained(ib, explanation)); - } - - - bool term_is_used_as_row(unsigned term) const { - SASSERT(is_term(term)); - return contains(m_ext_vars_to_columns, term); - } - - void propagate_bounds_on_terms(lp_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); - } - } + bool term_is_used_as_row(unsigned term) const; + + void propagate_bounds_on_terms(bound_propagator & bp); // goes over touched rows and tries to induce bounds - void propagate_bounds_for_touched_rows(lp_bound_propagator & bp) { - if (!use_tableau()) - return; // ! todo : enable bound propagaion here. The current bug is that after the pop - // the changed terms become incorrect! + void propagate_bounds_for_touched_rows(bound_propagator & bp); - for (unsigned i : m_rows_with_changed_bounds.m_index) { - calculate_implied_bounds_for_row(i, bp); - if (settings().get_cancel_flag()) - return; - } - m_rows_with_changed_bounds.clear(); - if (!use_tableau()) { - propagate_bounds_on_terms(bp); - } - } + lp_status get_status() const; - lp_status get_status() const { return m_status;} + void set_status(lp_status s); - void set_status(lp_status s) {m_status = s;} + lp_status find_feasible_solution(); + + lp_status solve(); - lp_status find_feasible_solution() { - if (strategy_is_undecided()) - decide_on_strategy_and_adjust_initial_state(); + void fill_explanation_from_infeasible_column(explanation_t & evidence) const; - m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true; - return solve(); - } - - lp_status solve() { - if (m_status == INFEASIBLE) { - return m_status; - } - solve_with_core_solver(); - if (m_status != INFEASIBLE) { - if (m_settings.bound_propagation()) - detect_rows_with_changed_bounds(); - } - - m_columns_with_changed_bound.clear(); - return m_status; - } - - void 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_vars_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 get_total_iterations() const { return m_mpq_lar_core_solver.m_r_solver.total_iterations(); } + + unsigned get_total_iterations() const; // see http://research.microsoft.com/projects/z3/smt07.pdf // This method searches for a feasible solution with as many different values of variables, reverenced in vars, as it can find // Attention, after a call to this method the non-basic variables don't necesserarly stick to their bounds anymore - vector 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 push() { - m_simplex_strategy = m_settings.simplex_strategy(); - m_simplex_strategy.push(); - m_status.push(); - m_vars_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(); - } + vector get_list_of_all_var_indices() const; + void push(); - static void clean_large_elements_after_pop(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); - } + static void clean_popped_elements(unsigned n, int_set& set); - static void shrink_inf_set_after_pop(unsigned n, int_set & set) { - clean_large_elements_after_pop(n, set); - set.resize(n); - } + static void shrink_inf_set_after_pop(unsigned n, int_set & set); + + void pop(unsigned k); + class scoped_push { + lar_solver& m_solver; + bool m_pop; + public: + scoped_push(lar_solver& s):m_solver(s), m_pop(true) { s.push(); } + ~scoped_push() { if (m_pop) m_solver.pop(); } + void pop() { SASSERT(m_pop); m_solver.pop(); m_pop = false; } + }; + + vector get_all_constraint_indices() const; - void pop(unsigned k) { - int n_was = static_cast(m_ext_vars_to_columns.size()); - m_status.pop(k); - m_infeasible_column_index.pop(k); - unsigned n = m_vars_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); - if (m_settings.use_tableau()) { - pop_tableau(); - } - m_vars_to_ul_pairs.pop(k); + bool maximize_term_on_tableau(const lar_term & term, + impq &term_max); - m_mpq_lar_core_solver.pop(k); - clean_large_elements_after_pop(n, m_columns_with_changed_bound); - unsigned m = A_r().row_count(); - clean_large_elements_after_pop(m, m_rows_with_changed_bounds); - clean_inf_set_of_r_solver_after_pop(); - SASSERT(m_settings.simplex_strategy() == simplex_strategy_enum::undecided || - (!use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + bool costs_are_zeros_for_r_solver() const; + bool reduced_costs_are_zeroes_for_r_solver() const; + + void set_costs_to_zero(const lar_term & term); - - SASSERT(ax_is_correct()); - SASSERT(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]; - delete m_orig_terms[i]; - } - m_terms.resize(m_term_count); - m_orig_terms.resize(m_term_count); - m_simplex_strategy.pop(k); - m_settings.simplex_strategy() = m_simplex_strategy; - SASSERT(sizes_are_correct()); - SASSERT((!m_settings.use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } - - vector get_all_constraint_indices() const { - vector ret; - constraint_index i = 0; - while ( i < m_constraints.size()) - ret.push_back(i++); - return ret; - } - - bool 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() == 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 costs_are_zeros_for_r_solver() const { - for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_solver.m_costs.size(); j++) { - SASSERT(is_zero(m_mpq_lar_core_solver.m_r_solver.m_costs[j])); - } - return true; - } - bool 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++) { - SASSERT(is_zero(m_mpq_lar_core_solver.m_r_solver.m_d[j])); - } - return true; - } - - void 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 - SASSERT(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(); - - SASSERT(reduced_costs_are_zeroes_for_r_solver()); - SASSERT(costs_are_zeros_for_r_solver()); - } - - void prepare_costs_for_r_solver(const vector> & term) { - - auto & rslv = m_mpq_lar_core_solver.m_r_solver; - rslv.m_using_infeas_costs = false; - SASSERT(costs_are_zeros_for_r_solver()); - SASSERT(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); - } - SASSERT(rslv.reduced_costs_are_correct_tableau()); - } - - bool 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(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(OPTIMAL); - return ret; - } - - case simplex_strategy_enum::lu: - SASSERT(false); // not implemented - return false; - default: - SASSERT(false); // wrong mode - } - return false; - } + void prepare_costs_for_r_solver(const lar_term & term); + + bool maximize_term_on_corrected_r_solver(lar_term & term, + impq &term_max); // starting from a given feasible state look for the maximum of the term // return true if found and false if unbounded - bool maximize_term(const vector> & term, - impq &term_max) { - SASSERT(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); - } + lp_status maximize_term(unsigned ext_j , + impq &term_max); + + + + const lar_term & get_term(unsigned j) const; + + void pop_core_solver_params(); + + void pop_core_solver_params(unsigned k); + void set_upper_bound_witness(var_index j, constraint_index ci); - const lar_term & get_term(unsigned j) const { - SASSERT(j >= m_terms_start_index); - return *m_terms[j - m_terms_start_index]; - } - - void pop_core_solver_params() { - pop_core_solver_params(1); - } - - void pop_core_solver_params(unsigned k) { - A_r().pop(k); - A_d().pop(k); - } + void set_lower_bound_witness(var_index j, constraint_index ci); - void set_upper_bound_witness(var_index j, constraint_index ci) { - ul_pair ul = m_vars_to_ul_pairs[j]; - ul.upper_bound_witness() = ci; - m_vars_to_ul_pairs[j] = ul; - } - - void set_low_bound_witness(var_index j, constraint_index ci) { - ul_pair ul = m_vars_to_ul_pairs[j]; - ul.low_bound_witness() = ci; - m_vars_to_ul_pairs[j] = ul; - } + void substitute_terms_in_linear_expression( const vector>& left_side_with_terms, + vector> &left_side, mpq & free_coeff) const; - void substitute_terms(const mpq & mult, - const vector>& left_side_with_terms, - vector> &left_side, mpq & right_side) const { - for (auto & t : left_side_with_terms) { - if (t.second < m_terms_start_index) { - SASSERT(t.second < A_r().column_count()); - left_side.push_back(std::pair(mult * t.first, t.second)); - } else { - const lar_term & term = * m_terms[adjust_term_index(t.second)]; - substitute_terms(mult * t.first, left_side_with_terms, left_side, right_side); - right_side -= mult * term.m_v; - } - } - } + void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j); + void detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j); - void 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(); - SASSERT(m_column_buffer.size() == 0 && m_column_buffer.is_OK()); + bool use_tableau() const; - 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); - } + bool use_tableau_costs() const; + + void detect_rows_of_column_with_bound_change(unsigned j); + void adjust_x_of_column(unsigned j); + bool row_is_correct(unsigned i) const; + + bool ax_is_correct() const; - void 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 tableau_with_costs() const; - bool use_tableau() const { return m_settings.use_tableau(); } + bool costs_are_used() const; + + void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta); - bool use_tableau_costs() const { - return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; - } + void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j); - void 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; - } + + void detect_rows_with_changed_bounds_for_column(unsigned j); + + void detect_rows_with_changed_bounds(); - 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 update_x_and_inf_costs_for_columns_with_changed_bounds(); - void adjust_x_of_column(unsigned j) { - SASSERT(false); - } + void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); - bool 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); - } + + void solve_with_core_solver(); - bool ax_is_correct() const { - for (unsigned i = 0; i < A_r().row_count(); i++) { - if (!row_is_correct(i)) - return false; - } - return true; - } - - bool tableau_with_costs() const { - return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; - } - - bool costs_are_used() const { - return m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows; - } - - void change_basic_x_by_delta_on_column(unsigned j, const numeric_pair & delta) { - 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]; - m_mpq_lar_core_solver.m_r_x[bj] -= A_r().get_val(c) * delta; - if (tableau_with_costs()) { - m_basic_columns_with_changed_cost.insert(bj); - } - m_mpq_lar_core_solver.m_r_solver.update_column_in_inf_set(bj); - } - } 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_x[bj] -= m_column_buffer[i] * delta; - m_mpq_lar_core_solver.m_r_solver.update_column_in_inf_set(bj); - } - } - } - - void 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.update_column_in_inf_set(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.update_column_in_inf_set(j); - } - } else { - numeric_pair delta; - if (m_mpq_lar_core_solver.m_r_solver.make_column_feasible(j, delta)) - change_basic_x_by_delta_on_column(j, delta); - } - } - - - void 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 detect_rows_with_changed_bounds() { - for (auto j : m_columns_with_changed_bound.m_index) - detect_rows_with_changed_bounds_for_column(j); - } - - void 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 update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { - SASSERT(ax_is_correct()); - 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); - SASSERT(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } - } - - - void 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(); - m_mpq_lar_core_solver.solve(); - set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); - SASSERT(m_status != OPTIMAL || all_constraints_hold()); - } - - - numeric_pair 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 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) { - SASSERT(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; - } + + numeric_pair get_basic_var_value_from_row_directly(unsigned i); + + + + numeric_pair get_basic_var_value_from_row(unsigned i); template - void 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; - } - } + void add_last_rows_to_lu(lp_primal_core_solver & s); + + bool x_is_correct() const; - } + bool var_is_registered(var_index vj) const; - bool 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;; + unsigned constraint_stack_size() const; - } - - bool 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 constraint_stack_size() const { - return m_constraint_count.stack_size(); - } - - void fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls) { - SASSERT(A.row_count() > 0); - SASSERT(A.column_count() > 0); - unsigned last_row = A.row_count() - 1; - SASSERT(A.m_rows[last_row].size() == 0); - for (auto & t : ls->m_coeffs) { - SASSERT(!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)); - } + void fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls); template - void create_matrix_A(static_matrix & matr) { - SASSERT(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); - */ - } + void create_matrix_A(static_matrix & matr); template - void 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())); - } + void copy_from_mpq_matrix(static_matrix & matr); + + + bool try_to_set_fixed(column_info & ci); + + column_type get_column_type(const column_info & ci); + + std::string get_column_name(unsigned j) const; + + bool all_constrained_variables_are_registered(const vector>& left_side); + + constraint_index add_constraint(const vector>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm); + bool all_constraints_hold() const; + bool constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const; + bool the_relations_are_of_same_type(const vector> & evidence, lconstraint_kind & the_kind_of_sum) const; + + static void register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a); + static void register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j); + + + bool the_left_sides_sum_to_zero(const vector> & evidence) const; + + bool the_right_sides_do_not_sum_to_zero(const vector> & evidence); + + bool explanation_is_correct(const vector>& explanation) const; + + bool inf_explanation_is_correct() const; + + mpq sum_of_right_sides_of_explanation(const vector> & explanation) const; + + bool has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const; + + bool has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) const; + + bool has_value(var_index var, mpq& value) const; + + void get_infeasibility_explanation(vector> & explanation) const; + + void get_infeasibility_explanation_for_inf_sign( + vector> & explanation, + const vector> & inf_row, + int inf_sign) const; + + + + void get_model(std::unordered_map & variable_values) const; + + void get_model_do_not_care_about_diff_vars(std::unordered_map & variable_values) const; + + std::string get_variable_name(var_index vi) const; + + // ********** print region start + void print_constraint(constraint_index ci, std::ostream & out) const; + + void print_constraints(std::ostream& out) const ; + + void print_terms(std::ostream& out) const; + + void print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const; + + void print_term(lar_term const& term, std::ostream & out) const; + + void print_term_as_indices(lar_term const& term, std::ostream & out) const; + + mpq get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const; + + void print_constraint(const lar_base_constraint * c, std::ostream & out) const; + + void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list); + + void random_update(unsigned sz, var_index const * vars); + void pivot_fixed_vars_from_basis(); + void pop(); + bool column_represents_row_in_tableau(unsigned j); + void make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j); + void remove_last_row_and_column_from_tableau(unsigned j); + void remove_last_column_from_A(); + + void remove_last_column_from_basis_tableau(unsigned j); + void remove_last_column_from_tableau(); + void pop_tableau(); + void clean_inf_set_of_r_solver_after_pop(); + void shrink_explanation_to_minimum(vector> & explanation) const; + + + + bool column_value_is_integer(unsigned j) const { + return get_column_value(j).is_int(); + } + + bool column_is_real(unsigned j) const { + return !column_is_int(j); + } + + bool model_is_int_feasible() const; + + const impq & column_lower_bound(unsigned j) const { + return m_mpq_lar_core_solver.lower_bound(j); + } + + const impq & column_upper_bound(unsigned j) const { + return m_mpq_lar_core_solver.upper_bound(j); + } + + bool column_is_bounded(unsigned j) const { + return m_mpq_lar_core_solver.column_is_bounded(j); + } + + void get_bound_constraint_witnesses_for_column(unsigned j, constraint_index & lc, constraint_index & uc) const { + const ul_pair & ul = m_columns_to_ul_pairs[j]; + lc = ul.lower_bound_witness(); + uc = ul.upper_bound_witness(); + } + indexed_vector & get_column_in_lu_mode(unsigned j) { + 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); + return m_column_buffer; + } + + bool bound_is_integer_for_integer_column(unsigned j, const mpq & right_side) const; + + const row_strip & get_row(unsigned i) { + return A_r().m_rows[i]; + } + + + unsigned get_base_column_in_row(unsigned row_index) const { + return m_mpq_lar_core_solver.m_r_solver.get_base_column_in_row(row_index); + } + + constraint_index get_column_upper_bound_witness(unsigned j) const { + return m_columns_to_ul_pairs()[j].upper_bound_witness(); + } + + constraint_index get_column_lower_bound_witness(unsigned j) const { + return m_columns_to_ul_pairs()[j].lower_bound_witness(); + } + + void subs_term_columns(lar_term& t) { + vector> columns_to_subs; + for (const auto & m : t.m_coeffs) { + unsigned tj = adjust_column_index_to_term_index(m.first); + if (tj == m.first) continue; + columns_to_subs.push_back(std::make_pair(m.first, tj)); + } + for (const auto & p : columns_to_subs) { + auto it = t.m_coeffs.find(p.first); + lp_assert(it != t.m_coeffs.end()); + mpq v = it->second; + t.m_coeffs.erase(it); + t.m_coeffs[p.second] = v; } } - - bool 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; + bool has_int_var() const; + bool has_inf_int() const { + for (unsigned j = 0; j < column_count(); j++) { + if (column_is_int(j) && ! column_value_is_int(j)) + return true; } return false; } - column_type 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; + bool r_basis_has_inf_int() const { + for (unsigned j : r_basis()) { + if (column_is_int(j) && ! column_value_is_int(j)) + return true; } - return ret; - } - - std::string get_column_name(unsigned j) const override { - 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 all_constrained_variables_are_registered(const vector>& left_side) { - for (auto it : left_side) { - if (! var_is_registered(it.second)) - return false; - } - return true; - } - - constraint_index add_constraint(const vector>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm) { - /* - mpq rs = right_side_parm; - vector> left_side; - substitute_terms(one_of_type(), left_side_with_terms, left_side, rs); - SASSERT(left_side.size() > 0); - SASSERT(all_constrained_variables_are_registered(left_side)); - lar_constraint original_constr(left_side, kind_par, rs); - unsigned j; // j is the index of the basic variables corresponding to the left side - canonic_left_side ls = create_or_fetch_canonic_left_side(left_side, j); - mpq ratio = find_ratio_of_original_constraint_to_normalized(ls, original_constr); - auto kind = ratio.is_neg() ? flip_kind(kind_par) : kind_par; - rs/= ratio; - lar_normalized_constraint normalized_constraint(ls, ratio, kind, rs, original_constr); - m_constraints.push_back(normalized_constraint); - constraint_index constr_ind = m_constraints.size() - 1; - update_column_type_and_bound(j, kind, rs, constr_ind); - return constr_ind; - */ - SASSERT(false); // not implemented - return 0; - } - - bool all_constraints_hold() const { - if (m_settings.get_cancel_flag()) - return true; - std::unordered_map var_map; - get_model(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 constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const { - 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: - SASSERT(false); - } - return false; // it is unreachable - } - - - - - - - - bool 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; - } - - static void 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 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; - SASSERT(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 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; - SASSERT(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 explanation_is_correct(const vector>& explanation) const { -#ifdef Z3DEBUG - lconstraint_kind kind = EQ; // initialize it just to avoid a warning - SASSERT(the_relations_are_of_same_type(explanation, kind)); - SASSERT(the_left_sides_sum_to_zero(explanation)); - mpq rs = sum_of_right_sides_of_explanation(explanation); - switch (kind) { - case LE: SASSERT(rs < zero_of_type()); - break; - case LT: SASSERT(rs <= zero_of_type()); - break; - case GE: SASSERT(rs > zero_of_type()); - break; - case GT: SASSERT(rs >= zero_of_type()); - break; - case EQ: SASSERT(rs != zero_of_type()); - break; - default: - SASSERT(false); - return false; - } -#endif - return true; - } - - bool inf_explanation_is_correct() const { -#ifdef Z3DEBUG - vector> explanation; - get_infeasibility_explanation(explanation); - return explanation_is_correct(explanation); -#endif - return true; - } - - mpq 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; - SASSERT(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 has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) { - - if (var >= m_vars_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_vars_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 has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) { - - if (var >= m_vars_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_vars_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 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); - SASSERT(explanation_is_correct(explanation)); - } - - void 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_vars_to_ul_pairs[j]; - - constraint_index bound_constr_i = adj_sign < 0 ? ul.upper_bound_witness() : ul.low_bound_witness(); - SASSERT(bound_constr_i < m_constraints.size()); - explanation.push_back(std::make_pair(coeff, bound_constr_i)); - } - } - - - - void get_model(std::unordered_map & variable_values) const { - mpq delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(mpq(1, 2)); // start from 0.5 to have less clashes - SASSERT(m_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; - 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()); - } - - - std::string get_variable_name(var_index vi) const { - return get_column_name(vi); - } - - // ********** print region start - void 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 print_constraints(std::ostream& out) const { - for (auto c : m_constraints) { - print_constraint(c, out); - } - } - - void print_terms(std::ostream& out) const { - for (auto it : m_terms) { - print_term(*it, out); - out << "\n"; - } - } - - void 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 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); - } - - mpq 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); - SASSERT(vi != var_map.end()); - ret += it.first * vi->second; - } - return ret; - } - - void 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 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 random_update(unsigned sz, var_index const * vars) { - vector column_list; - fill_var_set_for_random_update(sz, vars, column_list); - random_updater ru(m_mpq_lar_core_solver, column_list); - ru.update(); - } - - - void try_pivot_fixed_vars_from_basis() { - m_mpq_lar_core_solver.m_r_solver.pivot_fixed_vars_from_basis(); - } - - void pop() { - pop(1); - } - - - bool column_represents_row_in_tableau(unsigned j) { - return m_vars_to_ul_pairs()[j].m_i != static_cast(-1); - } - - void 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 - SASSERT(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; - } - - SASSERT(non_zero_column_cell_index != -1); - SASSERT(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 remove_last_row_and_column_from_tableau(unsigned j) { - SASSERT(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); - } - SASSERT(last_row.size() == 0); - SASSERT(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 remove_last_column_from_tableau(unsigned j) { - SASSERT(j == A_r().column_count() - 1); - // the last column has to be empty - SASSERT(A_r().m_columns[j].size() == 0); - A_r().m_columns.pop_back(); - } - - void 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; - SASSERT(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; - SASSERT(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(); - SASSERT(rslv.m_basis.size() == A_r().row_count()); - SASSERT(rslv.basis_heading_is_correct()); - } - - void remove_column_from_tableau(unsigned j) { - auto& rslv = m_mpq_lar_core_solver.m_r_solver; - SASSERT(j == A_r().column_count() - 1); - SASSERT(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_tableau(j); - } - rslv.m_x.pop_back(); - rslv.m_d.pop_back(); - rslv.m_costs.pop_back(); - - remove_last_column_from_basis_tableau(j); - SASSERT(m_mpq_lar_core_solver.r_basis_is_OK()); - SASSERT(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); - } - - - void pop_tableau() { - SASSERT(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); - - SASSERT(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); - SASSERT(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 - for (unsigned j = A_r().column_count(); j-- > m_columns_to_ext_vars_or_term_indices.size();) - remove_column_from_tableau(j); - SASSERT(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); - SASSERT(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); - SASSERT(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); - } - - - - - void clean_inf_set_of_r_solver_after_pop() { - vector became_feas; - clean_large_elements_after_pop(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_x_by_delta_on_column(j, delta); - became_feas.push_back(j); - } - - for (unsigned j : became_feas) { - SASSERT(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) { - SASSERT(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); - SASSERT(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - } - } - - - void shrink_explanation_to_minimum(vector> & explanation) const { - // implementing quickXplain - quick_xplain::run(explanation, *this); - SASSERT(this->explanation_is_correct(explanation)); + return false; } + + lar_core_solver & get_core_solver() { return m_mpq_lar_core_solver; } + bool column_corresponds_to_term(unsigned) const; + void catch_up_in_updating_int_solver(); + var_index to_column(unsigned ext_j) const; + bool tighten_term_bounds_by_delta(unsigned, const impq&); + void round_to_integer_solution(); + void update_delta_for_terms(const impq & delta, unsigned j, const vector&); + void fill_vars_to_terms(vector> & vars_to_terms); + unsigned column_count() const { return A_r().column_count(); } + const vector & r_basis() const { return m_mpq_lar_core_solver.r_basis(); } + const vector & r_nbasis() const { return m_mpq_lar_core_solver.r_nbasis(); } + bool get_equality_and_right_side_for_term_on_current_x(unsigned i, mpq &rs, constraint_index& ci) const; + bool remove_from_basis(unsigned); + lar_term get_term_to_maximize(unsigned ext_j) const; + void set_cut_strategy(unsigned cut_frequency); }; } diff --git a/src/util/lp/lar_term.h b/src/util/lp/lar_term.h index 16b5a938d..519847848 100644 --- a/src/util/lp/lar_term.h +++ b/src/util/lp/lar_term.h @@ -1,22 +1,22 @@ /*++ -Copyright (c) 2017 Microsoft Corporation + Copyright (c) 2017 Microsoft Corporation -Module Name: + Module Name: - + -Abstract: + Abstract: - + -Author: + Author: - Lev Nachmanson (levnach) + Lev Nachmanson (levnach) -Revision History: + Revision History: ---*/ + --*/ #pragma once #include "util/lp/indexed_vector.h" namespace lp { @@ -25,7 +25,7 @@ struct lar_term { std::unordered_map m_coeffs; mpq m_v; lar_term() {} - void add_to_map(unsigned j, const mpq& c) { + void add_monomial(const mpq& c, unsigned j) { auto it = m_coeffs.find(j); if (it == m_coeffs.end()) { m_coeffs.emplace(j, c); @@ -36,6 +36,10 @@ struct lar_term { } } + bool is_empty() const { + return m_coeffs.size() == 0 && is_zero(m_v); + } + unsigned size() const { return static_cast(m_coeffs.size()); } const std::unordered_map & coeffs() const { @@ -45,7 +49,7 @@ struct lar_term { lar_term(const vector>& coeffs, const mpq & v) : m_v(v) { for (const auto & p : coeffs) { - add_to_map(p.second, p.first); + add_monomial(p.first, p.second); } } bool operator==(const lar_term & a) const { return false; } // take care not to create identical terms @@ -67,7 +71,7 @@ struct lar_term { if (it == m_coeffs.end()) return; const mpq & b = it->second; for (unsigned it_j :li.m_index) { - add_to_map(it_j, - b * li.m_data[it_j]); + add_monomial(- b * li.m_data[it_j], it_j); } m_coeffs.erase(it); } @@ -75,5 +79,61 @@ struct lar_term { bool contains(unsigned j) const { return m_coeffs.find(j) != m_coeffs.end(); } + + void negate() { + for (auto & t : m_coeffs) + t.second.neg(); + } + + template + T apply(const vector& x) const { + T ret = T(m_v); + for (const auto & t : m_coeffs) { + ret += t.second * x[t.first]; + } + return ret; + } + + void clear() { + m_coeffs.clear(); + m_v = zero_of_type(); + } + + struct ival { + unsigned m_var; + const mpq & m_coeff; + ival(unsigned var, const mpq & val) : m_var(var), m_coeff(val) { + } + unsigned var() const { return m_var;} + const mpq & coeff() const { return m_coeff; } + }; + + struct const_iterator { + //fields + std::unordered_map::const_iterator m_it; + + typedef const_iterator self_type; + typedef ival value_type; + typedef ival reference; + // typedef std::pair* pointer; + typedef int difference_type; + typedef std::forward_iterator_tag iterator_category; + + reference operator*() const { + return ival(m_it->first, m_it->second); + } + + self_type operator++() { self_type i = *this; m_it++; return i; } + self_type operator++(int) { m_it++; return *this; } + + const_iterator(std::unordered_map::const_iterator it) : m_it(it) {} + bool operator==(const self_type &other) const { + return m_it == other.m_it; + } + bool operator!=(const self_type &other) const { return !(*this == other); } + }; + + const_iterator begin() const { return m_coeffs.begin();} + const_iterator end() const { return m_coeffs.end(); } }; } diff --git a/src/util/lp/lia_move.h b/src/util/lp/lia_move.h new file mode 100644 index 000000000..ec4643e20 --- /dev/null +++ b/src/util/lp/lia_move.h @@ -0,0 +1,31 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +namespace lp { +enum class lia_move { + sat, + branch, + cut, + conflict, + continue_with_check, + undef, + unsat +}; +} diff --git a/src/util/lp/linear_combination_iterator.h b/src/util/lp/linear_combination_iterator.h deleted file mode 100644 index 417bdcf82..000000000 --- a/src/util/lp/linear_combination_iterator.h +++ /dev/null @@ -1,65 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -namespace lp { -template -struct linear_combination_iterator { - virtual bool next(T & a, unsigned & i) = 0; - virtual bool next(unsigned & i) = 0; - virtual void reset() = 0; - virtual linear_combination_iterator * clone() = 0; - virtual ~linear_combination_iterator(){} - virtual unsigned size() const = 0; -}; -template -struct linear_combination_iterator_on_vector : linear_combination_iterator { - vector> & m_vector; - int m_offset; - bool next(T & a, unsigned & i) { - if(m_offset >= m_vector.size()) - return false; - auto & p = m_vector[m_offset]; - a = p.first; - i = p.second; - m_offset++; - return true; - } - - bool next(unsigned & i) { - if(m_offset >= m_vector.size()) - return false; - auto & p = m_vector[m_offset]; - i = p.second; - m_offset++; - return true; - } - - void reset() {m_offset = 0;} - linear_combination_iterator * clone() { - return new linear_combination_iterator_on_vector(m_vector); - } - linear_combination_iterator_on_vector(vector> & vec): - m_vector(vec), - m_offset(0) - {} - unsigned size() const { return m_vector.size(); } -}; - -} diff --git a/src/util/lp/lp_bound_propagator.cpp b/src/util/lp/lp_bound_propagator.cpp deleted file mode 100644 index 53218fced..000000000 --- a/src/util/lp/lp_bound_propagator.cpp +++ /dev/null @@ -1,67 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "util/lp/lar_solver.h" -namespace lp { - lp_bound_propagator::lp_bound_propagator(lar_solver & ls): - m_lar_solver(ls) {} -column_type lp_bound_propagator::get_column_type(unsigned j) const { - return m_lar_solver.m_mpq_lar_core_solver.m_column_types()[j]; -} -const impq & lp_bound_propagator::get_low_bound(unsigned j) const { - return m_lar_solver.m_mpq_lar_core_solver.m_r_low_bounds()[j]; -} -const impq & lp_bound_propagator::get_upper_bound(unsigned j) const { - return m_lar_solver.m_mpq_lar_core_solver.m_r_upper_bounds()[j]; -} -void lp_bound_propagator::try_add_bound(const mpq & v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) { - unsigned term_j = m_lar_solver.adjust_column_index_to_term_index(j); - mpq w = v; - if (term_j != j) { - j = term_j; - w += m_lar_solver.get_term(term_j).m_v; // when terms are turned into the columns they "lose" the right side, at this moment they aquire it back - } - lconstraint_kind kind = is_low? GE : LE; - if (strict) - kind = static_cast(kind / 2); - - if (!bound_is_interesting(j, kind, w)) - return; - unsigned k; // index to ibounds - if (is_low) { - if (try_get_val(m_improved_low_bounds, j, k)) { - auto & found_bound = m_ibounds[k]; - if (w > found_bound.m_bound || (w == found_bound.m_bound && found_bound.m_strict == false && strict)) - found_bound = implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); - } else { - m_improved_low_bounds[j] = m_ibounds.size(); - m_ibounds.push_back(implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); - } - } else { // the upper bound case - if (try_get_val(m_improved_upper_bounds, j, k)) { - auto & found_bound = m_ibounds[k]; - if (w < found_bound.m_bound || (w == found_bound.m_bound && found_bound.m_strict == false && strict)) - found_bound = implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); - } else { - m_improved_upper_bounds[j] = m_ibounds.size(); - m_ibounds.push_back(implied_bound(w, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); - } - } -} -} diff --git a/src/util/lp/lp_bound_propagator.h b/src/util/lp/lp_bound_propagator.h deleted file mode 100644 index 76870f457..000000000 --- a/src/util/lp/lp_bound_propagator.h +++ /dev/null @@ -1,42 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/lp_settings.h" -namespace lp { -class lar_solver; -class lp_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: - lp_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(const 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/lp_core_solver_base_instances.cpp b/src/util/lp/lp_core_solver_base.cpp similarity index 97% rename from src/util/lp/lp_core_solver_base_instances.cpp rename to src/util/lp/lp_core_solver_base.cpp index f5853eecf..00c1322c2 100644 --- a/src/util/lp/lp_core_solver_base_instances.cpp +++ b/src/util/lp/lp_core_solver_base.cpp @@ -22,7 +22,7 @@ Revision History: #include #include "util/vector.h" #include -#include "util/lp/lp_core_solver_base.hpp" +#include "util/lp/lp_core_solver_base_def.h" template bool lp::lp_core_solver_base::A_mult_x_is_off() const; template bool lp::lp_core_solver_base::A_mult_x_is_off_on_index(const vector &) const; template bool lp::lp_core_solver_base::basis_heading_is_correct() const; @@ -144,3 +144,5 @@ template bool lp::lp_core_solver_base::inf_set_is_correct() co template bool lp::lp_core_solver_base >::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; template bool lp::lp_core_solver_base::infeasibility_costs_are_correct() const; +template void lp::lp_core_solver_base >::calculate_pivot_row(unsigned int); +template bool lp::lp_core_solver_base >::remove_from_basis(unsigned int); diff --git a/src/util/lp/lp_core_solver_base.h b/src/util/lp/lp_core_solver_base.h index fd115669c..b8b7b7c0d 100644 --- a/src/util/lp/lp_core_solver_base.h +++ b/src/util/lp/lp_core_solver_base.h @@ -28,6 +28,7 @@ Revision History: #include "util/lp/lu.h" #include "util/lp/permutation_matrix.h" #include "util/lp/column_namer.h" + namespace lp { template // X represents the type of the x variable and the bounds @@ -38,7 +39,17 @@ class lp_core_solver_base { private: lp_status m_status; public: - bool current_x_is_feasible() const { return m_inf_set.size() == 0; } + bool current_x_is_feasible() const { + TRACE("feas", + if (m_inf_set.size()) { + tout << "column " << m_inf_set.m_index[0] << " is infeasible" << std::endl; + print_column_info(m_inf_set.m_index[0], tout); + } else { + tout << "x is feasible\n"; + } + ); + return m_inf_set.size() == 0; + } bool current_x_is_infeasible() const { return m_inf_set.size() != 0; } int_set m_inf_set; bool m_using_infeas_costs; @@ -58,13 +69,13 @@ public: lp_settings & m_settings; vector m_y; // the buffer for yB = cb // a device that is able to solve Bx=c, xB=d, and change the basis - lu * m_factorization; + lu> * m_factorization; const column_namer & m_column_names; indexed_vector m_w; // the vector featuring in 24.3 of the Chvatal book vector m_d; // the vector of reduced costs indexed_vector m_ed; // the solution of B*m_ed = a const vector & m_column_types; - const vector & m_low_bounds; + const vector & m_lower_bounds; const vector & m_upper_bounds; vector m_column_norms; // the approximate squares of column norms that help choosing a profitable column vector m_copy_of_xB; @@ -74,6 +85,7 @@ public: bool m_tracing_basis_changes; int_set* m_pivoted_rows; bool m_look_for_feasible_solution_only; + void start_tracing_basis_changes() { m_trace_of_basis_change_vector.resize(0); m_tracing_basis_changes = true; @@ -108,7 +120,7 @@ public: lp_settings & settings, const column_namer& column_names, const vector & column_types, - const vector & low_bound_values, + const vector & lower_bound_values, const vector & upper_bound_values); void allocate_basis_heading(); @@ -197,11 +209,11 @@ public: bool need_to_pivot_to_basis_tableau() const { - SASSERT(m_A.is_correct()); + lp_assert(m_A.is_correct()); unsigned m = m_A.row_count(); for (unsigned i = 0; i < m; i++) { unsigned bj = m_basis[i]; - SASSERT(m_A.m_columns[bj].size() > 0); + lp_assert(m_A.m_columns[bj].size() > 0); if (m_A.m_columns[bj].size() > 1 || m_A.get_val(m_A.m_columns[bj][0]) != one_of_type()) return true; } return false; @@ -210,10 +222,9 @@ public: bool reduced_costs_are_correct_tableau() const { if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) return true; - SASSERT(m_A.is_correct()); + lp_assert(m_A.is_correct()); if (m_using_infeas_costs) { if (infeasibility_costs_are_correct() == false) { - std::cout << "infeasibility_costs_are_correct() does not hold" << std::endl; return false; } } @@ -222,9 +233,6 @@ public: for (unsigned j = 0; j < n; j++) { if (m_basis_heading[j] >= 0) { if (!is_zero(m_d[j])) { - - std::cout << "case a\n"; - print_column_info(j, std::cout); return false; } } else { @@ -233,8 +241,6 @@ public: d -= this->m_costs[this->m_basis[cc.m_i]] * this->m_A.get_val(cc); } if (m_d[j] != d) { - std::cout << "case b\n"; - print_column_info(j, std::cout); return false; } } @@ -253,14 +259,14 @@ public: } bool x_below_low_bound(unsigned p) const { - return below_bound(m_x[p], m_low_bounds[p]); + return below_bound(m_x[p], m_lower_bounds[p]); } bool infeasibility_costs_are_correct() const; bool infeasibility_cost_is_correct_for_column(unsigned j) const; - bool x_above_low_bound(unsigned p) const { - return above_bound(m_x[p], m_low_bounds[p]); + bool x_above_lower_bound(unsigned p) const { + return above_bound(m_x[p], m_lower_bounds[p]); } bool x_below_upper_bound(unsigned p) const { @@ -271,15 +277,15 @@ public: bool x_above_upper_bound(unsigned p) const { return above_bound(m_x[p], m_upper_bounds[p]); } - bool x_is_at_low_bound(unsigned j) const { - return at_bound(m_x[j], m_low_bounds[j]); + bool x_is_at_lower_bound(unsigned j) const { + return at_bound(m_x[j], m_lower_bounds[j]); } bool x_is_at_upper_bound(unsigned j) const { return at_bound(m_x[j], m_upper_bounds[j]); } bool x_is_at_bound(unsigned j) const { - return x_is_at_low_bound(j) || x_is_at_upper_bound(j); + return x_is_at_lower_bound(j) || x_is_at_upper_bound(j); } bool column_is_feasible(unsigned j) const; @@ -318,8 +324,8 @@ public: void fill_reduced_costs_from_m_y_by_rows(); void copy_rs_to_xB(vector & rs); - virtual bool low_bounds_are_set() const { return false; } - X low_bound_value(unsigned j) const { return m_low_bounds[j]; } + virtual bool lower_bounds_are_set() const { return false; } + X lower_bound_value(unsigned j) const { return m_lower_bounds[j]; } X upper_bound_value(unsigned j) const { return m_upper_bounds[j]; } column_type get_column_type(unsigned j) const {return m_column_types[j]; } @@ -329,7 +335,7 @@ public: } X bound_span(unsigned j) const { - return m_upper_bounds[j] - m_low_bounds[j]; + return m_upper_bounds[j] - m_lower_bounds[j]; } std::string column_name(unsigned column) const; @@ -357,21 +363,21 @@ public: case column_type::fixed: if (x_is_at_bound(j)) break; - m_x[j] = m_low_bounds[j]; + m_x[j] = m_lower_bounds[j]; return true; case column_type::boxed: if (x_is_at_bound(j)) break; // we should preserve x if possible // snap randomly if (m_settings.random_next() % 2 == 1) - m_x[j] = m_low_bounds[j]; + m_x[j] = m_lower_bounds[j]; else m_x[j] = m_upper_bounds[j]; return true; - case column_type::low_bound: - if (x_is_at_low_bound(j)) + case column_type::lower_bound: + if (x_is_at_lower_bound(j)) break; - m_x[j] = m_low_bounds[j]; + m_x[j] = m_lower_bounds[j]; return true; case column_type::upper_bound: if (x_is_at_upper_bound(j)) @@ -385,50 +391,47 @@ public: } bool make_column_feasible(unsigned j, numeric_pair & delta) { - SASSERT(m_basis_heading[j] < 0); + bool ret = false; + lp_assert(m_basis_heading[j] < 0); auto & x = m_x[j]; switch (m_column_types[j]) { case column_type::fixed: - SASSERT(m_low_bounds[j] == m_upper_bounds[j]); - if (x != m_low_bounds[j]) { - delta = m_low_bounds[j] - x; - x = m_low_bounds[j]; - return true; + lp_assert(m_lower_bounds[j] == m_upper_bounds[j]); + if (x != m_lower_bounds[j]) { + delta = m_lower_bounds[j] - x; + ret = true;; } break; case column_type::boxed: - if (x < m_low_bounds[j]) { - delta = m_low_bounds[j] - x; - x = m_low_bounds[j]; - return true; + if (x < m_lower_bounds[j]) { + delta = m_lower_bounds[j] - x; + ret = true;; } if (x > m_upper_bounds[j]) { delta = m_upper_bounds[j] - x; - x = m_upper_bounds[j]; - return true; + ret = true; } break; - case column_type::low_bound: - if (x < m_low_bounds[j]) { - delta = m_low_bounds[j] - x; - x = m_low_bounds[j]; - return true; + case column_type::lower_bound: + if (x < m_lower_bounds[j]) { + delta = m_lower_bounds[j] - x; + ret = true; } break; case column_type::upper_bound: if (x > m_upper_bounds[j]) { delta = m_upper_bounds[j] - x; - x = m_upper_bounds[j]; - return true; + ret = true; } break; - case column_type::free_column: - break; default: - SASSERT(false); break; } - return false; + if (ret) + add_delta_to_x_and_call_tracker(j, delta); + + return ret; + } @@ -444,6 +447,8 @@ public: void init_lu(); int pivots_in_column_and_row_are_different(int entering, int leaving) const; void pivot_fixed_vars_from_basis(); + bool remove_from_basis(unsigned j); + bool pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w); bool pivot_for_tableau_on_basis(); bool pivot_row_for_tableau_on_basis(unsigned row); void init_basic_part_of_basis_heading() { @@ -473,7 +478,7 @@ public: } void change_basis_unconditionally(unsigned entering, unsigned leaving) { - SASSERT(m_basis_heading[entering] < 0); + lp_assert(m_basis_heading[entering] < 0); int place_in_non_basis = -1 - m_basis_heading[entering]; if (static_cast(place_in_non_basis) >= m_nbasis.size()) { // entering variable in not in m_nbasis, we need to put it back; @@ -492,7 +497,8 @@ public: } void change_basis(unsigned entering, unsigned leaving) { - SASSERT(m_basis_heading[entering] < 0); + lp_assert(m_basis_heading[entering] < 0); + lp_assert(m_basis_heading[leaving] >= 0); int place_in_basis = m_basis_heading[leaving]; int place_in_non_basis = - m_basis_heading[entering] - 1; @@ -522,8 +528,8 @@ public: if (!this->x_is_at_bound(j)) return false; break; - case column_type::low_bound: - if (!this->x_is_at_low_bound(j)) + case column_type::lower_bound: + if (!this->x_is_at_lower_bound(j)) return false; break; case column_type::upper_bound: @@ -533,7 +539,7 @@ public: case column_type::free_column: break; default: - SASSERT(false); + lp_assert(false); break; } return true; @@ -541,7 +547,6 @@ public: bool non_basic_columns_are_set_correctly() const { for (unsigned j : this->m_nbasis) if (!column_is_feasible(j)) { - print_column_info(j, std::cout); return false; } return true; @@ -552,10 +557,10 @@ public: switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: - out << "(" << m_low_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl; + out << "(" << m_lower_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl; break; - case column_type::low_bound: - out << m_low_bounds[j] << std::endl; + case column_type::lower_bound: + out << m_lower_bounds[j] << std::endl; break; case column_type::upper_bound: out << m_upper_bounds[j] << std::endl; @@ -566,36 +571,38 @@ public: } void print_column_info(unsigned j, std::ostream & out) const { - out << "column_index = " << j << ", name = "<< column_name(j) << " type = " << column_type_to_string(m_column_types[j]) << std::endl; + out << "j = " << j << ", name = "<< column_name(j); switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: - out << "(" << m_low_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl; + out << " [" << m_lower_bounds[j] << ", " << m_upper_bounds[j] << "]"; break; - case column_type::low_bound: - out << m_low_bounds[j] << std::endl; + case column_type::lower_bound: + out << " [" << m_lower_bounds[j] << "," << "oo" << "]"; break; case column_type::upper_bound: - out << m_upper_bounds[j] << std::endl; + out << " [-oo, " << m_upper_bounds[j] << ']'; break; case column_type::free_column: + out << " [-oo, oo]"; break; default: - SASSERT(false); + lp_assert(false); } - std::cout << "basis heading = " << m_basis_heading[j] << std::endl; - std::cout << "x = " << m_x[j] << std::endl; - /* - std::cout << "cost = " << m_costs[j] << std::endl; - std:: cout << "m_d = " << m_d[j] << std::endl;*/ + // out << "basis heading = " << m_basis_heading[j] << std::endl; + out << " x = " << m_x[j]; + if (m_basis_heading[j] >= 0) + out << " base\n"; + else + out << " nbas\n"; } - bool column_is_free(unsigned j) { return this->m_column_type[j] == free; } + bool column_is_free(unsigned j) const { return this->m_column_type[j] == free; } - bool column_has_upper_bound(unsigned j) { + bool column_has_upper_bound(unsigned j) const { switch(m_column_types[j]) { case column_type::free_column: - case column_type::low_bound: + case column_type::lower_bound: return false; default: return true; @@ -605,13 +612,13 @@ public: bool bounds_for_boxed_are_set_correctly() const { for (unsigned j = 0; j < m_column_types.size(); j++) { if (m_column_types[j] != column_type::boxed) continue; - if (m_low_bounds[j] > m_upper_bounds[j]) + if (m_lower_bounds[j] > m_upper_bounds[j]) return false; } return true; } - bool column_has_low_bound(unsigned j) { + bool column_has_lower_bound(unsigned j) const { switch(m_column_types[j]) { case column_type::free_column: case column_type::upper_bound: @@ -671,26 +678,43 @@ public: return m_inf_set.contains(j); } - void update_column_in_inf_set(unsigned j) { - if (column_is_feasible(j)) { - m_inf_set.erase(j); - } else { - m_inf_set.insert(j); - } + void update_x_with_feasibility_tracking(unsigned j, const X & v) { + m_x[j] = v; + track_column_feasibility(j); + } + + void update_x_with_delta_and_track_feasibility(unsigned j, const X & del) { + m_x[j] += del; + track_column_feasibility(j); + } + + void update_x_and_call_tracker(unsigned j, const X & v) { + m_x[j] = v; + } + + void add_delta_to_x_and_call_tracker(unsigned j, const X & delta) { + m_x[j] += delta; + } + + void track_column_feasibility(unsigned j) { + if (column_is_feasible(j)) + remove_column_from_inf_set(j); + else + insert_column_into_inf_set(j); } void insert_column_into_inf_set(unsigned j) { m_inf_set.insert(j); - SASSERT(!column_is_feasible(j)); + lp_assert(!column_is_feasible(j)); } void remove_column_from_inf_set(unsigned j) { m_inf_set.erase(j); - SASSERT(column_is_feasible(j)); + lp_assert(column_is_feasible(j)); } bool costs_on_nbasis_are_zeros() const { - SASSERT(this->basis_heading_is_correct()); + lp_assert(this->basis_heading_is_correct()); for (unsigned j = 0; j < this->m_n(); j++) { if (this->m_basis_heading[j] < 0) - SASSERT(is_zero(this->m_costs[j])); + lp_assert(is_zero(this->m_costs[j])); } return true; } @@ -701,5 +725,10 @@ public: const unsigned & iters_with_no_cost_growing() const { return m_iters_with_no_cost_growing; } + + void calculate_pivot_row(unsigned i); + unsigned get_base_column_in_row(unsigned row_index) const { + return m_basis[row_index]; + } }; } diff --git a/src/util/lp/lp_core_solver_base.hpp b/src/util/lp/lp_core_solver_base_def.h similarity index 84% rename from src/util/lp/lp_core_solver_base.hpp rename to src/util/lp/lp_core_solver_base_def.h index b49dd0638..f20eaf042 100644 --- a/src/util/lp/lp_core_solver_base.hpp +++ b/src/util/lp/lp_core_solver_base_def.h @@ -35,11 +35,11 @@ lp_core_solver_base(static_matrix & A, lp_settings & settings, const column_namer& column_names, const vector & column_types, - const vector & low_bound_values, + const vector & lower_bound_values, const vector & upper_bound_values): m_total_iterations(0), m_iters_with_no_cost_growing(0), - m_status(FEASIBLE), + m_status(lp_status::FEASIBLE), m_inf_set(A.column_count()), m_using_infeas_costs(false), m_pivot_row_of_B_1(A.row_count()), @@ -59,7 +59,7 @@ lp_core_solver_base(static_matrix & A, m_d(m_n()), m_ed(m_m()), m_column_types(column_types), - m_low_bounds(low_bound_values), + m_lower_bounds(lower_bound_values), m_upper_bounds(upper_bound_values), m_column_norms(m_n()), m_copy_of_xB(m_m()), @@ -68,7 +68,7 @@ lp_core_solver_base(static_matrix & A, m_tracing_basis_changes(false), m_pivoted_rows(nullptr), m_look_for_feasible_solution_only(false) { - SASSERT(bounds_for_boxed_are_set_correctly()); + lp_assert(bounds_for_boxed_are_set_correctly()); init(); init_basis_heading_and_non_basic_columns_vector(); } @@ -76,7 +76,7 @@ lp_core_solver_base(static_matrix & A, template void lp_core_solver_base:: allocate_basis_heading() { // the rest of initilization will be handled by the factorization class init_basis_heading_and_non_basic_columns_vector(); - SASSERT(basis_heading_is_correct()); + lp_assert(basis_heading_is_correct()); } template void lp_core_solver_base:: init() { @@ -142,7 +142,7 @@ solve_yB(vector & y) { // } // } template void lp_core_solver_base::solve_Bd(unsigned entering, indexed_vector & column) { - SASSERT(!m_settings.use_tableau()); + lp_assert(!m_settings.use_tableau()); if (m_factorization == nullptr) { init_factorization(m_factorization, m_A, m_basis, m_settings); } @@ -152,19 +152,19 @@ template void lp_core_solver_base::solve_Bd(unsig template void lp_core_solver_base:: solve_Bd(unsigned entering) { - SASSERT(m_ed.is_OK()); + lp_assert(m_ed.is_OK()); m_factorization->solve_Bd(entering, m_ed, m_w); if (this->precise()) m_columns_nz[entering] = m_ed.m_index.size(); - SASSERT(m_ed.is_OK()); - SASSERT(m_w.is_OK()); + lp_assert(m_ed.is_OK()); + lp_assert(m_w.is_OK()); #ifdef Z3DEBUG // auto B = get_B(*m_factorization, m_basis); // vector a(m_m()); // m_A.copy_column_to_vector(entering, a); // vector cd(m_ed.m_data); // B.apply_from_left(cd, m_settings); - // SASSERT(vectors_are_equal(cd , a)); + // lp_assert(vectors_are_equal(cd , a)); #endif } @@ -223,16 +223,11 @@ restore_m_ed(T * buffer) { template bool lp_core_solver_base:: A_mult_x_is_off() const { - SASSERT(m_x.size() == m_A.column_count()); + lp_assert(m_x.size() == m_A.column_count()); if (numeric_traits::precise()) { - for (unsigned i = 0; i < m_m(); i++) { + for (unsigned i = 0; i < m_m(); i++) { X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); if (delta != numeric_traits::zero()) { - std::cout << "precise x is off ("; - std::cout << "m_b[" << i << "] = " << m_b[i] << " "; - std::cout << "left side = " << m_A.dot_product_with_row(i, m_x) << ' '; - std::cout << "delta = " << delta << ' '; - std::cout << "iters = " << total_iterations() << ")" << std::endl; return true; } } @@ -259,17 +254,12 @@ A_mult_x_is_off() const { } template bool lp_core_solver_base:: A_mult_x_is_off_on_index(const vector & index) const { - SASSERT(m_x.size() == m_A.column_count()); + lp_assert(m_x.size() == m_A.column_count()); if (numeric_traits::precise()) return false; #if RUN_A_MULT_X_IS_OFF_FOR_PRECESE for (unsigned i : index) { X delta = m_b[i] - m_A.dot_product_with_row(i, m_x); if (delta != numeric_traits::zero()) { - // std::cout << "x is off ("; - // std::cout << "m_b[" << i << "] = " << m_b[i] << " "; - // std::cout << "left side = " << m_A.dot_product_with_row(i, m_x) << ' '; - // std::cout << "delta = " << delta << ' '; - // std::cout << "iters = " << total_iterations() << ")" << std::endl; return true; } } @@ -299,13 +289,13 @@ A_mult_x_is_off_on_index(const vector & index) const { // from page 182 of Istvan Maros's book template void lp_core_solver_base:: calculate_pivot_row_of_B_1(unsigned pivot_row) { - SASSERT(! use_tableau()); - SASSERT(m_pivot_row_of_B_1.is_OK()); + lp_assert(! use_tableau()); + lp_assert(m_pivot_row_of_B_1.is_OK()); m_pivot_row_of_B_1.clear(); m_pivot_row_of_B_1.set_value(numeric_traits::one(), pivot_row); - SASSERT(m_pivot_row_of_B_1.is_OK()); + lp_assert(m_pivot_row_of_B_1.is_OK()); m_factorization->solve_yB_with_error_check_indexed(m_pivot_row_of_B_1, m_basis_heading, m_basis, m_settings); - SASSERT(m_pivot_row_of_B_1.is_OK()); + lp_assert(m_pivot_row_of_B_1.is_OK()); } @@ -391,15 +381,15 @@ set_non_basic_x_to_correct_bounds() { for (unsigned j : non_basis()) { switch (m_column_types[j]) { case column_type::boxed: - m_x[j] = m_d[j] < 0? m_upper_bounds[j]: m_low_bounds[j]; + m_x[j] = m_d[j] < 0? m_upper_bounds[j]: m_lower_bounds[j]; break; - case column_type::low_bound: - m_x[j] = m_low_bounds[j]; - SASSERT(column_is_dual_feasible(j)); + case column_type::lower_bound: + m_x[j] = m_lower_bounds[j]; + lp_assert(column_is_dual_feasible(j)); break; case column_type::upper_bound: m_x[j] = m_upper_bounds[j]; - SASSERT(column_is_dual_feasible(j)); + lp_assert(column_is_dual_feasible(j)); break; default: break; @@ -411,21 +401,18 @@ column_is_dual_feasible(unsigned j) const { switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: - return (x_is_at_low_bound(j) && d_is_not_negative(j)) || + return (x_is_at_lower_bound(j) && d_is_not_negative(j)) || (x_is_at_upper_bound(j) && d_is_not_positive(j)); - case column_type::low_bound: - return x_is_at_low_bound(j) && d_is_not_negative(j); + case column_type::lower_bound: + return x_is_at_lower_bound(j) && d_is_not_negative(j); case column_type::upper_bound: - LP_OUT(m_settings, "upper_bound type should be switched to low_bound" << std::endl); - SASSERT(false); // impossible case + lp_assert(false); // impossible case case column_type::free_column: return numeric_traits::is_zero(m_d[j]); default: - LP_OUT(m_settings, "column = " << j << std::endl); - LP_OUT(m_settings, "unexpected column type = " << column_type_to_string(m_column_types[j]) << std::endl); - SASSERT(false); + lp_unreachable(); } - SASSERT(false); + lp_unreachable(); return false; } template bool lp_core_solver_base:: @@ -484,14 +471,14 @@ template bool lp_core_solver_base::column_is_feas case column_type::boxed: if (this->above_bound(x, this->m_upper_bounds[j])) { return false; - } else if (this->below_bound(x, this->m_low_bounds[j])) { + } else if (this->below_bound(x, this->m_lower_bounds[j])) { return false; } else { return true; } break; - case column_type::low_bound: - if (this->below_bound(x, this->m_low_bounds[j])) { + case column_type::lower_bound: + if (this->below_bound(x, this->m_lower_bounds[j])) { return false; } else { return true; @@ -508,7 +495,7 @@ template bool lp_core_solver_base::column_is_feas return true; break; default: - SASSERT(false); + lp_unreachable(); } return false; // it is unreachable } @@ -530,9 +517,6 @@ template bool lp_core_solver_base::inf_set_is_cor bool is_feas = column_is_feasible(j); if (is_feas == belongs_to_set) { - print_column_info(j, std::cout); - std::cout << "belongs_to_set = " << belongs_to_set << std::endl; - std::cout <<( is_feas? "feas":"inf") << std::endl; return false; } } @@ -549,7 +533,7 @@ update_basis_and_x(int entering, int leaving, X const & tt) { if (!find_x_by_solving()) { restore_x(entering, tt); if(A_mult_x_is_off()) { - m_status = FLOATING_POINT_ERROR; + m_status = lp_status::FLOATING_POINT_ERROR; m_iters_with_no_cost_growing++; return false; } @@ -559,7 +543,7 @@ update_basis_and_x(int entering, int leaving, X const & tt) { if (m_factorization->get_status() != LU_status::OK) { std::stringstream s; // s << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations(); - m_status = FLOATING_POINT_ERROR; + m_status = lp_status::FLOATING_POINT_ERROR; return false; } return false; @@ -581,19 +565,19 @@ update_basis_and_x(int entering, int leaving, X const & tt) { init_lu(); if (m_factorization->get_status() != LU_status::OK) { if (m_look_for_feasible_solution_only && !precise()) { - m_status = UNSTABLE; + m_status = lp_status::UNSTABLE; delete m_factorization; m_factorization = nullptr; return false; } // LP_OUT(m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << total_iterations() << std::endl); restore_x_and_refactor(entering, leaving, tt); - if (m_status == FLOATING_POINT_ERROR) + if (m_status == lp_status::FLOATING_POINT_ERROR) return false; - SASSERT(!A_mult_x_is_off()); + CASSERT("A_off", !A_mult_x_is_off()); m_iters_with_no_cost_growing++; // LP_OUT(m_settings, "rolled back after failing of init_factorization()" << std::endl); - m_status = UNSTABLE; + m_status = lp_status::UNSTABLE; return false; } return true; @@ -602,7 +586,7 @@ update_basis_and_x(int entering, int leaving, X const & tt) { template bool lp_core_solver_base:: divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { - SASSERT(numeric_traits::precise()); + lp_assert(numeric_traits::precise()); int pivot_index = -1; auto & row = m_A.m_rows[pivot_row]; unsigned size = row.size(); @@ -629,7 +613,7 @@ divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col) { } template bool lp_core_solver_base:: pivot_column_tableau(unsigned j, unsigned piv_row_index) { - if (!divide_row_by_pivot(piv_row_index, j)) + if (!divide_row_by_pivot(piv_row_index, j)) return false; auto &column = m_A.m_columns[j]; int pivot_col_cell_index = -1; @@ -643,7 +627,7 @@ pivot_column_tableau(unsigned j, unsigned piv_row_index) { return false; if (pivot_col_cell_index != 0) { - SASSERT(column.size() > 1); + lp_assert(column.size() > 1); // swap the pivot column cell with the head cell auto c = column[0]; column[0] = column[pivot_col_cell_index]; @@ -654,7 +638,7 @@ pivot_column_tableau(unsigned j, unsigned piv_row_index) { } while (column.size() > 1) { auto & c = column.back(); - SASSERT(c.m_i != piv_row_index); + lp_assert(c.m_i != piv_row_index); if(! m_A.pivot_row_to_row_given_cell(piv_row_index, c, j)) { return false; } @@ -702,7 +686,7 @@ non_basis_is_correctly_represented_in_heading() const { } for (unsigned j = 0; j < m_A.column_count(); j++) { if (m_basis_heading[j] >= 0) { - SASSERT(static_cast(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j); + lp_assert(static_cast(m_basis_heading[j]) < m_A.row_count() && m_basis[m_basis_heading[j]] == j); } } return true; @@ -710,26 +694,22 @@ non_basis_is_correctly_represented_in_heading() const { template bool lp_core_solver_base:: basis_heading_is_correct() const { - SASSERT(m_basis_heading.size() == m_A.column_count()); - SASSERT(m_basis.size() == m_A.row_count()); - SASSERT(m_nbasis.size() <= m_A.column_count() - m_A.row_count()); // for the dual the size of non basis can be smaller + lp_assert(m_basis_heading.size() == m_A.column_count()); + lp_assert(m_basis.size() == m_A.row_count()); + lp_assert(m_nbasis.size() <= m_A.column_count() - m_A.row_count()); // for the dual the size of non basis can be smaller if (!basis_has_no_doubles()) { - // std::cout << "basis_has_no_doubles" << std::endl; return false; } if (!non_basis_has_no_doubles()) { - // std::cout << "non_basis_has_no_doubles" << std::endl; return false; } if (!basis_is_correctly_represented_in_heading()) { - // std::cout << "basis_is_correctly_represented_in_heading" << std::endl; return false; } if (!non_basis_is_correctly_represented_in_heading()) { - // std::cout << "non_basis_is_correctly_represented_in_heading" << std::endl; return false; } @@ -856,12 +836,12 @@ solve_Ax_eq_b() { template void lp_core_solver_base:: snap_non_basic_x_to_bound_and_free_to_zeroes() { for (unsigned j : non_basis()) { - SASSERT(j < m_x.size()); + lp_assert(j < m_x.size()); switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: - m_x[j] = m_low_bounds[j]; + case column_type::lower_bound: + m_x[j] = m_lower_bounds[j]; break; case column_type::upper_bound: m_x[j] = m_upper_bounds[j]; @@ -894,23 +874,23 @@ template non_basic_column_value_position lp_core_solver get_non_basic_column_value_position(unsigned j) const { switch (m_column_types[j]) { case column_type::fixed: - return x_is_at_low_bound(j)? at_fixed : not_at_bound; + return x_is_at_lower_bound(j)? at_fixed : not_at_bound; case column_type::free_column: return free_of_bounds; case column_type::boxed: - return x_is_at_low_bound(j)? at_low_bound :( + return x_is_at_lower_bound(j)? at_lower_bound :( x_is_at_upper_bound(j)? at_upper_bound: not_at_bound ); - case column_type::low_bound: - return x_is_at_low_bound(j)? at_low_bound : not_at_bound; + case column_type::lower_bound: + return x_is_at_lower_bound(j)? at_lower_bound : not_at_bound; case column_type::upper_bound: return x_is_at_upper_bound(j)? at_upper_bound : not_at_bound; default: - SASSERT(false); + lp_unreachable(); } - SASSERT(false); - return at_low_bound; + lp_unreachable(); + return at_lower_bound; } template void lp_core_solver_base::init_lu() { @@ -938,59 +918,80 @@ template void lp_core_solver_base::transpose_row transpose_basis(i, j); m_A.transpose_rows(i, j); } +// j is the new basic column, j_basic - the leaving column +template bool lp_core_solver_base::pivot_column_general(unsigned j, unsigned j_basic, indexed_vector & w) { + lp_assert(m_basis_heading[j] < 0); + lp_assert(m_basis_heading[j_basic] >= 0); + unsigned row_index = m_basis_heading[j_basic]; + if (m_settings.m_simplex_strategy == simplex_strategy_enum::lu) { + if (m_factorization->need_to_refactor()) { + init_lu(); + } + else { + m_factorization->prepare_entering(j, w); // to init vector w + m_factorization->replace_column(zero_of_type(), w, row_index); + } + if (m_factorization->get_status() != LU_status::OK) { + init_lu(); + return false; + } + else { + change_basis(j, j_basic); + } + } + else { // the tableau case + if (pivot_column_tableau(j, row_index)) + change_basis(j, j_basic); + else return false; + } + return true; +} template void lp_core_solver_base::pivot_fixed_vars_from_basis() { // run over basis and non-basis at the same time indexed_vector w(m_basis.size()); // the buffer unsigned i = 0; // points to basis - unsigned j = 0; // points to nonbasis - for (; i < m_basis.size() && j < m_nbasis.size(); i++) { - unsigned ii = m_basis[i]; - unsigned jj; + for (; i < m_basis.size(); i++) { + unsigned basic_j = m_basis[i]; - if (get_column_type(ii) != column_type::fixed) continue; - while (j < m_nbasis.size()) { - for (; j < m_nbasis.size(); j++) { - jj = m_nbasis[j]; - if (get_column_type(jj) != column_type::fixed) + if (get_column_type(basic_j) != column_type::fixed) continue; + T a; + unsigned j; + for (auto &c : m_A.m_rows[i]) { + j = c.var(); + if (j == basic_j) + continue; + if (get_column_type(j) != column_type::fixed) { + if (pivot_column_general(j, basic_j, w)) break; } - if (j >= m_nbasis.size()) - break; - j++; - if (m_factorization->need_to_refactor()) { - change_basis(jj, ii); - init_lu(); - } else { - m_factorization->prepare_entering(jj, w); // to init vector w - m_factorization->replace_column(zero_of_type(), w, m_basis_heading[ii]); - change_basis(jj, ii); - } - if (m_factorization->get_status() != LU_status::OK) { - change_basis(ii, jj); - init_lu(); - } else { - break; - } } - SASSERT(m_factorization->get_status()== LU_status::OK); } } +template bool lp_core_solver_base::remove_from_basis(unsigned basic_j) { + indexed_vector w(m_basis.size()); // the buffer + unsigned i = m_basis_heading[basic_j]; + for (auto &c : m_A.m_rows[i]) { + if (c.var() == basic_j) + continue; + if (pivot_column_general(c.var(), basic_j, w)) + return true; + } + return false; +} + + template bool lp_core_solver_base::infeasibility_costs_are_correct() const { if (! this->m_using_infeas_costs) return true; - SASSERT(costs_on_nbasis_are_zeros()); + lp_assert(costs_on_nbasis_are_zeros()); for (unsigned j :this->m_basis) { if (!infeasibility_cost_is_correct_for_column(j)) { - std::cout << "infeasibility_cost_is_correct_for_column does not hold\n"; - print_column_info(j, std::cout); return false; } if (!is_zero(m_d[j])) { - std::cout << "m_d is not zero\n"; - print_column_info(j, std::cout); return false; } } @@ -1012,7 +1013,7 @@ lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) } return is_zero(this->m_costs[j]); - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(j)) { return this->m_costs[j] == -r; } @@ -1026,9 +1027,31 @@ lp_core_solver_base::infeasibility_cost_is_correct_for_column(unsigned j) case column_type::free_column: return is_zero(this->m_costs[j]); default: - SASSERT(false); + lp_assert(false); return true; } } +template +void lp_core_solver_base::calculate_pivot_row(unsigned i) { + lp_assert(!use_tableau()); + lp_assert(m_pivot_row.is_OK()); + m_pivot_row_of_B_1.clear(); + m_pivot_row_of_B_1.resize(m_m()); + m_pivot_row.clear(); + m_pivot_row.resize(m_n()); + if (m_settings.use_tableau()) { + unsigned basic_j = m_basis[i]; + for (auto & c : m_A.m_rows[i]) { + if (c.m_j != basic_j) + m_pivot_row.set_value(c.get_val(), c.m_j); + } + return; + } + + calculate_pivot_row_of_B_1(i); + calculate_pivot_row_when_pivot_row_of_B1_is_ready(i); +} + + } diff --git a/src/util/lp/lp_dual_core_solver_instances.cpp b/src/util/lp/lp_dual_core_solver.cpp similarity index 97% rename from src/util/lp/lp_dual_core_solver_instances.cpp rename to src/util/lp/lp_dual_core_solver.cpp index db68bda65..bd8f605a7 100644 --- a/src/util/lp/lp_dual_core_solver_instances.cpp +++ b/src/util/lp/lp_dual_core_solver.cpp @@ -22,7 +22,7 @@ Revision History: #include #include "util/vector.h" #include -#include "util/lp/lp_dual_core_solver.hpp" +#include "util/lp/lp_dual_core_solver_def.h" template void lp::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); template void lp::lp_dual_core_solver::solve(); template lp::lp_dual_core_solver::lp_dual_core_solver(lp::static_matrix&, vector&, diff --git a/src/util/lp/lp_dual_core_solver.h b/src/util/lp/lp_dual_core_solver.h index 7aa572171..c620aa096 100644 --- a/src/util/lp/lp_dual_core_solver.h +++ b/src/util/lp/lp_dual_core_solver.h @@ -56,7 +56,7 @@ public: vector & heading, vector & costs, vector & column_type_array, - vector & low_bound_values, + vector & lower_bound_values, vector & upper_bound_values, lp_settings & settings, const column_namer & column_names): @@ -70,7 +70,7 @@ public: settings, column_names, column_type_array, - low_bound_values, + lower_bound_values, upper_bound_values), m_can_enter_basis(can_enter_basis), m_a_wave(this->m_m()), @@ -110,7 +110,7 @@ public: bool done(); - T get_edge_steepness_for_low_bound(unsigned p); + T get_edge_steepness_for_lower_bound(unsigned p); T get_edge_steepness_for_upper_bound(unsigned p); @@ -174,7 +174,7 @@ public: // it is positive if going from low bound to upper bound and negative if going from upper bound to low bound T signed_span_of_boxed(unsigned j) { - return this->x_is_at_low_bound(j)? this->bound_span(j): - this->bound_span(j); + return this->x_is_at_lower_bound(j)? this->bound_span(j): - this->bound_span(j); } void add_tight_breakpoints_and_q_to_flipped_set(); @@ -207,6 +207,6 @@ public: void solve(); - bool low_bounds_are_set() const override { return true; } + bool lower_bounds_are_set() const override { return true; } }; } diff --git a/src/util/lp/lp_dual_core_solver.hpp b/src/util/lp/lp_dual_core_solver_def.h similarity index 84% rename from src/util/lp/lp_dual_core_solver.hpp rename to src/util/lp/lp_dual_core_solver_def.h index 5d48fe24d..86e6231fc 100644 --- a/src/util/lp/lp_dual_core_solver.hpp +++ b/src/util/lp/lp_dual_core_solver_def.h @@ -38,7 +38,7 @@ template void lp_dual_core_solver::restore_non_ba while (j--) { if (this->m_basis_heading[j] >= 0 ) continue; if (m_can_enter_basis[j]) { - SASSERT(std::find(nb.begin(), nb.end(), j) == nb.end()); + lp_assert(std::find(nb.begin(), nb.end(), j) == nb.end()); nb.push_back(j); this->m_basis_heading[j] = - static_cast(nb.size()); } @@ -97,25 +97,25 @@ template void lp_dual_core_solver::start_with_ini } template bool lp_dual_core_solver::done() { - if (this->get_status() == OPTIMAL) { + if (this->get_status() == lp_status::OPTIMAL) { return true; } if (this->total_iterations() > this->m_settings.max_total_number_of_iterations) { // debug !!!! - this->set_status(ITERATIONS_EXHAUSTED); + this->set_status(lp_status::ITERATIONS_EXHAUSTED); return true; } return false; // todo, need to be more cases } -template T lp_dual_core_solver::get_edge_steepness_for_low_bound(unsigned p) { - SASSERT(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); - T del = this->m_x[p] - this->m_low_bounds[p]; +template T lp_dual_core_solver::get_edge_steepness_for_lower_bound(unsigned p) { + lp_assert(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); + T del = this->m_x[p] - this->m_lower_bounds[p]; del *= del; return del / this->m_betas[this->m_basis_heading[p]]; } template T lp_dual_core_solver::get_edge_steepness_for_upper_bound(unsigned p) { - SASSERT(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); + lp_assert(this->m_basis_heading[p] >= 0 && static_cast(this->m_basis_heading[p]) < this->m_m()); T del = this->m_x[p] - this->m_upper_bounds[p]; del *= del; return del / this->m_betas[this->m_basis_heading[p]]; @@ -127,7 +127,7 @@ template T lp_dual_core_solver::pricing_for_row(u case column_type::fixed: case column_type::boxed: if (this->x_below_low_bound(p)) { - T del = get_edge_steepness_for_low_bound(p); + T del = get_edge_steepness_for_lower_bound(p); return del; } if (this->x_above_upper_bound(p)) { @@ -135,9 +135,9 @@ template T lp_dual_core_solver::pricing_for_row(u return del; } return numeric_traits::zero(); - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(p)) { - T del = get_edge_steepness_for_low_bound(p); + T del = get_edge_steepness_for_lower_bound(p); return del; } return numeric_traits::zero(); @@ -150,12 +150,12 @@ template T lp_dual_core_solver::pricing_for_row(u return numeric_traits::zero(); break; case column_type::free_column: - SASSERT(numeric_traits::is_zero(this->m_d[p])); + lp_assert(numeric_traits::is_zero(this->m_d[p])); return numeric_traits::zero(); default: - SASSERT(false); + lp_unreachable(); } - SASSERT(false); + lp_unreachable(); return numeric_traits::zero(); } @@ -185,8 +185,8 @@ template void lp_dual_core_solver::pricing_loop(u } } while (i != initial_offset_in_rows && rows_left); if (m_r == -1) { - if (this->get_status() != UNSTABLE) { - this->set_status(OPTIMAL); + if (this->get_status() != lp_status::UNSTABLE) { + this->set_status(lp_status::OPTIMAL); } } else { m_p = this->m_basis[m_r]; @@ -196,10 +196,10 @@ template void lp_dual_core_solver::pricing_loop(u return; } // failure in advance_on_known_p - if (this->get_status() == FLOATING_POINT_ERROR) { + if (this->get_status() == lp_status::FLOATING_POINT_ERROR) { return; } - this->set_status(UNSTABLE); + this->set_status(lp_status::UNSTABLE); m_forbidden_rows.insert(m_r); } } @@ -224,9 +224,9 @@ template bool lp_dual_core_solver::advance_on_kno int pivot_compare_result = this->pivots_in_column_and_row_are_different(m_q, m_p); if (!pivot_compare_result){;} else if (pivot_compare_result == 2) { // the sign is changed, cannot continue - SASSERT(false); // not implemented yet + lp_unreachable(); // not implemented yet } else { - SASSERT(pivot_compare_result == 1); + lp_assert(pivot_compare_result == 1); this->init_lu(); } DSE_FTran(); @@ -243,38 +243,38 @@ template int lp_dual_core_solver::define_sign_of_ if (this->x_above_upper_bound(m_p)) { return 1; } - SASSERT(false); - case column_type::low_bound: + lp_unreachable(); + case column_type::lower_bound: if (this->x_below_low_bound(m_p)) { return -1; } - SASSERT(false); + lp_unreachable(); case column_type::upper_bound: if (this->x_above_upper_bound(m_p)) { return 1; } - SASSERT(false); + lp_unreachable(); default: - SASSERT(false); + lp_unreachable(); } - SASSERT(false); + lp_unreachable(); return 0; } template bool lp_dual_core_solver::can_be_breakpoint(unsigned j) { if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false; switch (this->m_column_types[j]) { - case column_type::low_bound: - SASSERT(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_low_bounds[j])); + case column_type::lower_bound: + lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_lower_bounds[j])); return m_sign_of_alpha_r * this->m_pivot_row[j] > 0; case column_type::upper_bound: - SASSERT(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j])); + lp_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j])); return m_sign_of_alpha_r * this->m_pivot_row[j] < 0; case column_type::boxed: { - bool low_bound = this->x_is_at_low_bound(j); + bool lower_bound = this->x_is_at_lower_bound(j); bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0; - return low_bound == grawing; + return lower_bound == grawing; } case column_type::fixed: // is always dual feasible so we ingore it return false; @@ -302,28 +302,28 @@ template T lp_dual_core_solver::get_delta() { switch (this->m_column_types[m_p]) { case column_type::boxed: if (this->x_below_low_bound(m_p)) { - return this->m_x[m_p] - this->m_low_bounds[m_p]; + return this->m_x[m_p] - this->m_lower_bounds[m_p]; } if (this->x_above_upper_bound(m_p)) { return this->m_x[m_p] - this->m_upper_bounds[m_p]; } - SASSERT(false); - case column_type::low_bound: + lp_unreachable(); + case column_type::lower_bound: if (this->x_below_low_bound(m_p)) { - return this->m_x[m_p] - this->m_low_bounds[m_p]; + return this->m_x[m_p] - this->m_lower_bounds[m_p]; } - SASSERT(false); + lp_unreachable(); case column_type::upper_bound: if (this->x_above_upper_bound(m_p)) { return get_edge_steepness_for_upper_bound(m_p); } - SASSERT(false); + lp_unreachable(); case column_type::fixed: return this->m_x[m_p] - this->m_upper_bounds[m_p]; default: - SASSERT(false); + lp_unreachable(); } - SASSERT(false); + lp_unreachable(); return zero_of_type(); } @@ -370,11 +370,11 @@ template void lp_dual_core_solver::update_betas() template void lp_dual_core_solver::apply_flips() { for (unsigned j : m_flipped_boxed) { - SASSERT(this->x_is_at_bound(j)); - if (this->x_is_at_low_bound(j)) { + lp_assert(this->x_is_at_bound(j)); + if (this->x_is_at_lower_bound(j)) { this->m_x[j] = this->m_upper_bounds[j]; } else { - this->m_x[j] = this->m_low_bounds[j]; + this->m_x[j] = this->m_lower_bounds[j]; } } } @@ -382,17 +382,17 @@ template void lp_dual_core_solver::apply_flips() template void lp_dual_core_solver::snap_xN_column_to_bounds(unsigned j) { switch (this->m_column_type[j]) { case column_type::fixed: - this->m_x[j] = this->m_low_bounds[j]; + this->m_x[j] = this->m_lower_bounds[j]; break; case column_type::boxed: - if (this->x_is_at_low_bound(j)) { - this->m_x[j] = this->m_low_bounds[j]; + if (this->x_is_at_lower_bound(j)) { + this->m_x[j] = this->m_lower_bounds[j]; } else { this->m_x[j] = this->m_upper_bounds[j]; } break; - case column_type::low_bound: - this->m_x[j] = this->m_low_bounds[j]; + case column_type::lower_bound: + this->m_x[j] = this->m_lower_bounds[j]; break; case column_type::upper_bound: this->m_x[j] = this->m_upper_bounds[j]; @@ -400,7 +400,7 @@ template void lp_dual_core_solver::snap_xN_column case column_type::free_column: break; default: - SASSERT(false); + lp_unreachable(); } } @@ -456,15 +456,15 @@ template bool lp_dual_core_solver::basis_change_a return false; } - SASSERT(d_is_correct()); + lp_assert(d_is_correct()); return true; } template void lp_dual_core_solver::recover_leaving() { switch (m_entering_boundary_position) { - case at_low_bound: + case at_lower_bound: case at_fixed: - this->m_x[m_q] = this->m_low_bounds[m_q]; + this->m_x[m_q] = this->m_lower_bounds[m_q]; break; case at_upper_bound: this->m_x[m_q] = this->m_upper_bounds[m_q]; @@ -472,7 +472,7 @@ template void lp_dual_core_solver::recover_leavin case free_of_bounds: this->m_x[m_q] = zero_of_type(); default: - SASSERT(false); + lp_unreachable(); } } @@ -481,12 +481,12 @@ template void lp_dual_core_solver::revert_to_prev this->change_basis_unconditionally(m_p, m_q); init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); // complete failure + this->set_status(lp_status::FLOATING_POINT_ERROR); // complete failure return; } recover_leaving(); if (!this->find_x_by_solving()) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); return; } recalculate_xB_and_d(); @@ -497,23 +497,23 @@ template void lp_dual_core_solver::revert_to_prev template bool lp_dual_core_solver::snap_runaway_nonbasic_column(unsigned j) { switch (this->m_column_types[j]) { case column_type::fixed: - case column_type::low_bound: - if (!this->x_is_at_low_bound(j)) { - this->m_x[j] = this->m_low_bounds[j]; + case column_type::lower_bound: + if (!this->x_is_at_lower_bound(j)) { + this->m_x[j] = this->m_lower_bounds[j]; return true; } break; case column_type::boxed: { - bool closer_to_low_bound = abs(this->m_low_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]); - if (closer_to_low_bound) { - if (!this->x_is_at_low_bound(j)) { - this->m_x[j] = this->m_low_bounds[j]; + bool closer_to_lower_bound = abs(this->m_lower_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]); + if (closer_to_lower_bound) { + if (!this->x_is_at_lower_bound(j)) { + this->m_x[j] = this->m_lower_bounds[j]; return true; } } else { if (!this->x_is_at_upper_bound(j)) { - this->m_x[j] = this->m_low_bounds[j]; + this->m_x[j] = this->m_lower_bounds[j]; return true; } } @@ -535,12 +535,6 @@ template bool lp_dual_core_solver::snap_runaway_n template bool lp_dual_core_solver::problem_is_dual_feasible() const { for (unsigned j : this->non_basis()){ if (!this->column_is_dual_feasible(j)) { - // std::cout << "column " << j << " is not dual feasible" << std::endl; - // std::cout << "m_d[" << j << "] = " << this->m_d[j] << std::endl; - // std::cout << "x[" << j << "] = " << this->m_x[j] << std::endl; - // std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl; - // std::cout << "bounds = " << this->m_low_bounds[j] << "," << this->m_upper_bounds[j] << std::endl; - // std::cout << "total_iterations = " << this->total_iterations() << std::endl; return false; } } @@ -566,10 +560,10 @@ template bool lp_dual_core_solver::delta_keeps_th } template void lp_dual_core_solver::set_status_to_tentative_dual_unbounded_or_dual_unbounded() { - if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) { - this->set_status(DUAL_UNBOUNDED); + if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { + this->set_status(lp_status::DUAL_UNBOUNDED); } else { - this->set_status(TENTATIVE_DUAL_UNBOUNDED); + this->set_status(lp_status::TENTATIVE_DUAL_UNBOUNDED); } } @@ -599,10 +593,10 @@ template bool lp_dual_core_solver::tight_breakpoi template T lp_dual_core_solver::calculate_harris_delta_on_breakpoint_set() { bool first_time = true; T ret = zero_of_type(); - SASSERT(m_breakpoint_set.size() > 0); + lp_assert(m_breakpoint_set.size() > 0); for (auto j : m_breakpoint_set) { T t; - if (this->x_is_at_low_bound(j)) { + if (this->x_is_at_lower_bound(j)) { t = abs((std::max(this->m_d[j], numeric_traits::zero()) + m_harris_tolerance) / this->m_pivot_row[j]); } else { t = abs((std::min(this->m_d[j], numeric_traits::zero()) - m_harris_tolerance) / this->m_pivot_row[j]); @@ -620,7 +614,7 @@ template T lp_dual_core_solver::calculate_harris_ template void lp_dual_core_solver::fill_tight_set_on_harris_delta(const T & harris_delta ){ m_tight_set.clear(); for (auto j : m_breakpoint_set) { - if (this->x_is_at_low_bound(j)) { + if (this->x_is_at_lower_bound(j)) { if (abs(std::max(this->m_d[j], numeric_traits::zero()) / this->m_pivot_row[j]) <= harris_delta){ m_tight_set.insert(j); } @@ -648,7 +642,7 @@ template void lp_dual_core_solver::find_q_on_tigh } } m_tight_set.erase(m_q); - SASSERT(m_q != -1); + lp_assert(m_q != -1); } template void lp_dual_core_solver::find_q_and_tight_set() { @@ -675,7 +669,7 @@ template bool lp_dual_core_solver::ratio_test() { set_status_to_tentative_dual_unbounded_or_dual_unbounded(); return false; } - this->set_status(FEASIBLE); + this->set_status(lp_status::FEASIBLE); find_q_and_tight_set(); if (!tight_breakpoinst_are_all_boxed()) break; T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign; @@ -731,19 +725,19 @@ template void lp_dual_core_solver::update_xb_afte template void lp_dual_core_solver::one_iteration() { unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving(); unsigned offset_in_rows = this->m_settings.random_next() % this->m_m(); - if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) { + if (this->get_status() == lp_status::TENTATIVE_DUAL_UNBOUNDED) { number_of_rows_to_try = this->m_m(); } else { - this->set_status(FEASIBLE); + this->set_status(lp_status::FEASIBLE); } pricing_loop(number_of_rows_to_try, offset_in_rows); - SASSERT(problem_is_dual_feasible()); + lp_assert(problem_is_dual_feasible()); } template void lp_dual_core_solver::solve() { // see the page 35 - SASSERT(d_is_correct()); - SASSERT(problem_is_dual_feasible()); - SASSERT(this->basis_heading_is_correct()); + lp_assert(d_is_correct()); + lp_assert(problem_is_dual_feasible()); + lp_assert(this->basis_heading_is_correct()); this->set_total_iterations(0); this->iters_with_no_cost_growing() = 0; do { @@ -751,7 +745,7 @@ template void lp_dual_core_solver::solve() { // s return; } one_iteration(); - } while (this->get_status() != FLOATING_POINT_ERROR && this->get_status() != DUAL_UNBOUNDED && this->get_status() != OPTIMAL && + } while (this->get_status() != lp_status::FLOATING_POINT_ERROR && this->get_status() != lp_status::DUAL_UNBOUNDED && this->get_status() != lp_status::OPTIMAL && this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements && this->total_iterations() <= this->m_settings.max_total_number_of_iterations); } diff --git a/src/util/lp/lp_dual_simplex_instances.cpp b/src/util/lp/lp_dual_simplex.cpp similarity index 92% rename from src/util/lp/lp_dual_simplex_instances.cpp rename to src/util/lp/lp_dual_simplex.cpp index 9e45849de..ff71b7b4f 100644 --- a/src/util/lp/lp_dual_simplex_instances.cpp +++ b/src/util/lp/lp_dual_simplex.cpp @@ -17,7 +17,7 @@ Revision History: --*/ -#include "util/lp/lp_dual_simplex.hpp" +#include "util/lp/lp_dual_simplex_def.h" template lp::mpq lp::lp_dual_simplex::get_current_cost() const; template void lp::lp_dual_simplex::find_maximal_solution(); template double lp::lp_dual_simplex::get_current_cost() const; diff --git a/src/util/lp/lp_dual_simplex.h b/src/util/lp/lp_dual_simplex.h index 15463fc34..59b1bc24c 100644 --- a/src/util/lp/lp_dual_simplex.h +++ b/src/util/lp/lp_dual_simplex.h @@ -28,7 +28,7 @@ template class lp_dual_simplex: public lp_solver { lp_dual_core_solver * m_core_solver; vector m_b_copy; - vector m_low_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver + vector m_lower_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver vector m_column_types_of_core_solver; vector m_column_types_of_logicals; vector m_can_enter_basis; diff --git a/src/util/lp/lp_dual_simplex.hpp b/src/util/lp/lp_dual_simplex_def.h similarity index 81% rename from src/util/lp/lp_dual_simplex.hpp rename to src/util/lp/lp_dual_simplex_def.h index 248dff448..e32a5e97f 100644 --- a/src/util/lp/lp_dual_simplex.hpp +++ b/src/util/lp/lp_dual_simplex_def.h @@ -22,61 +22,61 @@ namespace lp{ template void lp_dual_simplex::decide_on_status_after_stage1() { switch (m_core_solver->get_status()) { - case OPTIMAL: + case lp_status::OPTIMAL: if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { - this->m_status = FEASIBLE; + this->m_status = lp_status::FEASIBLE; } else { - this->m_status = UNBOUNDED; + this->m_status = lp_status::UNBOUNDED; } break; - case DUAL_UNBOUNDED: - SASSERT(false); - case ITERATIONS_EXHAUSTED: - this->m_status = ITERATIONS_EXHAUSTED; + case lp_status::DUAL_UNBOUNDED: + lp_unreachable(); + case lp_status::ITERATIONS_EXHAUSTED: + this->m_status = lp_status::ITERATIONS_EXHAUSTED; break; - case TIME_EXHAUSTED: - this->m_status = TIME_EXHAUSTED; + case lp_status::TIME_EXHAUSTED: + this->m_status = lp_status::TIME_EXHAUSTED; break; - case FLOATING_POINT_ERROR: - this->m_status = FLOATING_POINT_ERROR; + case lp_status::FLOATING_POINT_ERROR: + this->m_status = lp_status::FLOATING_POINT_ERROR; break; default: - SASSERT(false); + lp_unreachable(); } } template void lp_dual_simplex::fix_logical_for_stage2(unsigned j) { - SASSERT(j >= this->number_of_core_structurals()); + lp_assert(j >= this->number_of_core_structurals()); switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) { - case column_type::low_bound: - m_low_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::low_bound; + case column_type::lower_bound: + m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::lower_bound; m_can_enter_basis[j] = true; break; case column_type::fixed: - this->m_upper_bounds[j] = m_low_bounds[j] = numeric_traits::zero(); + this->m_upper_bounds[j] = m_lower_bounds[j] = numeric_traits::zero(); m_column_types_of_core_solver[j] = column_type::fixed; m_can_enter_basis[j] = false; break; default: - SASSERT(false); + lp_unreachable(); } } template void lp_dual_simplex::fix_structural_for_stage2(unsigned j) { column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; switch (ci->get_column_type()) { - case column_type::low_bound: - m_low_bounds[j] = numeric_traits::zero(); - m_column_types_of_core_solver[j] = column_type::low_bound; + case column_type::lower_bound: + m_lower_bounds[j] = numeric_traits::zero(); + m_column_types_of_core_solver[j] = column_type::lower_bound; m_can_enter_basis[j] = true; break; case column_type::fixed: case column_type::upper_bound: - SASSERT(false); + lp_unreachable(); case column_type::boxed: this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; - m_low_bounds[j] = numeric_traits::zero(); + m_lower_bounds[j] = numeric_traits::zero(); m_column_types_of_core_solver[j] = column_type::boxed; m_can_enter_basis[j] = true; break; @@ -85,7 +85,7 @@ template void lp_dual_simplex::fix_structural_for m_column_types_of_core_solver[j] = column_type::free_column; break; default: - SASSERT(false); + lp_unreachable(); } // T cost_was = this->m_costs[j]; this->set_scaled_cost(j); @@ -114,23 +114,23 @@ template void lp_dual_simplex::solve_for_stage2() m_core_solver->solve_yB(m_core_solver->m_y); m_core_solver->fill_reduced_costs_from_m_y_by_rows(); m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); - m_core_solver->set_status(FEASIBLE); + m_core_solver->set_status(lp_status::FEASIBLE); m_core_solver->solve(); switch (m_core_solver->get_status()) { - case OPTIMAL: - this->m_status = OPTIMAL; + case lp_status::OPTIMAL: + this->m_status = lp_status::OPTIMAL; break; - case DUAL_UNBOUNDED: - this->m_status = INFEASIBLE; + case lp_status::DUAL_UNBOUNDED: + this->m_status = lp_status::INFEASIBLE; break; - case TIME_EXHAUSTED: - this->m_status = TIME_EXHAUSTED; + case lp_status::TIME_EXHAUSTED: + this->m_status = lp_status::TIME_EXHAUSTED; break; - case FLOATING_POINT_ERROR: - this->m_status = FLOATING_POINT_ERROR; + case lp_status::FLOATING_POINT_ERROR: + this->m_status = lp_status::FLOATING_POINT_ERROR; break; default: - SASSERT(false); + lp_unreachable(); } this->m_second_stage_iterations = m_core_solver->total_iterations(); this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); @@ -144,7 +144,7 @@ template void lp_dual_simplex::fill_x_with_zeros( } template void lp_dual_simplex::stage1() { - SASSERT(m_core_solver == nullptr); + lp_assert(m_core_solver == nullptr); this->m_x.resize(this->m_A->column_count(), numeric_traits::zero()); if (this->m_settings.get_message_ostream() != nullptr) this->print_statistics_on_A(*this->m_settings.get_message_ostream()); @@ -158,7 +158,7 @@ template void lp_dual_simplex::stage1() { this->m_heading, this->m_costs, this->m_column_types_of_core_solver, - this->m_low_bounds, + this->m_lower_bounds, this->m_upper_bounds, this->m_settings, *this); @@ -166,7 +166,7 @@ template void lp_dual_simplex::stage1() { m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { // skipping stage 1 - m_core_solver->set_status(OPTIMAL); + m_core_solver->set_status(lp_status::OPTIMAL); m_core_solver->set_total_iterations(0); } else { m_core_solver->solve(); @@ -192,7 +192,7 @@ template void lp_dual_simplex::fill_first_stage_s } template column_type lp_dual_simplex::get_column_type(unsigned j) { - SASSERT(j < this->m_A->column_count()); + lp_assert(j < this->m_A->column_count()); if (j >= this->number_of_core_structurals()) { return m_column_types_of_logicals[j - this->number_of_core_structurals()]; } @@ -201,12 +201,12 @@ template column_type lp_dual_simplex::get_column_ template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { // see 4.7 in the dissertation of Achim Koberstein - SASSERT(this->m_core_solver_columns_to_external_columns.find(j) != + lp_assert(this->m_core_solver_columns_to_external_columns.find(j) != this->m_core_solver_columns_to_external_columns.end()); T free_bound = T(1e4); // see 4.8 unsigned jj = this->m_core_solver_columns_to_external_columns[j]; - SASSERT(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); + lp_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); column_info * ci = this->m_map_from_var_index_to_column_info[jj]; switch (ci->get_column_type()) { case column_type::upper_bound: { @@ -216,10 +216,10 @@ template void lp_dual_simplex::fill_costs_bounds_ throw_exception(s.str()); break; } - case column_type::low_bound: { + case column_type::lower_bound: { m_can_enter_basis[j] = true; this->set_scaled_cost(j); - this->m_low_bounds[j] = numeric_traits::zero(); + this->m_lower_bounds[j] = numeric_traits::zero(); this->m_upper_bounds[j] =numeric_traits::one(); break; } @@ -227,30 +227,30 @@ template void lp_dual_simplex::fill_costs_bounds_ m_can_enter_basis[j] = true; this->set_scaled_cost(j); this->m_upper_bounds[j] = free_bound; - this->m_low_bounds[j] = -free_bound; + this->m_lower_bounds[j] = -free_bound; break; } case column_type::boxed: m_can_enter_basis[j] = false; this->m_costs[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = this->m_low_bounds[j] = numeric_traits::zero(); // is it needed? + this->m_upper_bounds[j] = this->m_lower_bounds[j] = numeric_traits::zero(); // is it needed? break; default: - SASSERT(false); + lp_unreachable(); } m_column_types_of_core_solver[j] = column_type::boxed; } template void lp_dual_simplex::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { this->m_costs[j] = 0; - SASSERT(get_column_type(j) != column_type::upper_bound); - if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::low_bound))) { + lp_assert(get_column_type(j) != column_type::upper_bound); + if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::lower_bound))) { m_column_types_of_core_solver[j] = column_type::boxed; - this->m_low_bounds[j] = numeric_traits::zero(); + this->m_lower_bounds[j] = numeric_traits::zero(); this->m_upper_bounds[j] = numeric_traits::one(); } else { m_column_types_of_core_solver[j] = column_type::fixed; - this->m_low_bounds[j] = numeric_traits::zero(); + this->m_lower_bounds[j] = numeric_traits::zero(); this->m_upper_bounds[j] = numeric_traits::zero(); } } @@ -269,7 +269,7 @@ template void lp_dual_simplex::fill_costs_and_bou template void lp_dual_simplex::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, unsigned & slack_var, unsigned & artificial) { - SASSERT(row < this->row_count()); + lp_assert(row < this->row_count()); auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; // we need to bring the program to the form Ax = b T rs = this->m_b[row]; @@ -283,7 +283,7 @@ template void lp_dual_simplex::fill_first_stage_s break; case Greater_or_equal: - set_type_for_logical(slack_var, column_type::low_bound); + set_type_for_logical(slack_var, column_type::lower_bound); (*this->m_A)(row, slack_var) = - numeric_traits::one(); if (rs > 0) { // adding one artificial @@ -301,7 +301,7 @@ template void lp_dual_simplex::fill_first_stage_s break; case Less_or_equal: // introduce a non-negative slack variable - set_type_for_logical(slack_var, column_type::low_bound); + set_type_for_logical(slack_var, column_type::lower_bound); (*this->m_A)(row, slack_var) = numeric_traits::one(); if (rs < 0) { // adding one artificial @@ -328,7 +328,7 @@ template void lp_dual_simplex::augment_matrix_A_a m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); this->m_costs.resize(n); this->m_upper_bounds.resize(n); - this->m_low_bounds.resize(n); + this->m_lower_bounds.resize(n); m_can_enter_basis.resize(n); this->m_basis.resize(this->m_A->row_count()); } @@ -351,7 +351,7 @@ template void lp_dual_simplex::find_maximal_solut this->flip_costs(); // do it for now, todo ( remove the flipping) this->cleanup(); - if (this->m_status == INFEASIBLE) { + if (this->m_status == lp_status::INFEASIBLE) { return; } this->fill_matrix_A_and_init_right_side(); @@ -361,7 +361,7 @@ template void lp_dual_simplex::find_maximal_solut fill_first_stage_solver_fields(); copy_m_b_aside_and_set_it_to_zeros(); stage1(); - if (this->m_status == FEASIBLE) { + if (this->m_status == lp_status::FEASIBLE) { stage2(); } } diff --git a/src/util/lp/lp_primal_core_solver_instances.cpp b/src/util/lp/lp_primal_core_solver.cpp similarity index 92% rename from src/util/lp/lp_primal_core_solver_instances.cpp rename to src/util/lp/lp_primal_core_solver.cpp index fd5f42d67..f3eefa2b9 100644 --- a/src/util/lp/lp_primal_core_solver_instances.cpp +++ b/src/util/lp/lp_primal_core_solver.cpp @@ -23,8 +23,8 @@ Revision History: #include "util/vector.h" #include #include "util/lp/lar_solver.h" -#include "util/lp/lp_primal_core_solver.hpp" -#include "util/lp/lp_primal_core_solver_tableau.h" +#include "util/lp/lp_primal_core_solver_def.h" +#include "util/lp/lp_primal_core_solver_tableau_def.h" namespace lp { template void lp_primal_core_solver::find_feasible_solution(); diff --git a/src/util/lp/lp_primal_core_solver.h b/src/util/lp/lp_primal_core_solver.h index 139cf3ad5..26fc550b3 100644 --- a/src/util/lp/lp_primal_core_solver.h +++ b/src/util/lp/lp_primal_core_solver.h @@ -37,10 +37,9 @@ Revision History: #include "util/lp/breakpoint.h" #include "util/lp/binary_heap_priority_queue.h" #include "util/lp/int_set.h" -#include "util/lp/iterator_on_row.h" namespace lp { -// This core solver solves (Ax=b, low_bound_values \leq x \leq upper_bound_values, maximize costs*x ) +// This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) // The right side b is given implicitly by x and the basis template class lp_primal_core_solver:public lp_core_solver_base { @@ -85,7 +84,7 @@ public: // unsigned len = 100000000; // for (unsigned j : this->m_inf_set.m_index) { // int i = this->m_basis_heading[j]; - // SASSERT(i >= 0); + // lp_assert(i >= 0); // unsigned row_len = this->m_A.m_rows[i].size(); // if (row_len < len) { // choices.clear(); @@ -113,52 +112,52 @@ public: bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const { // sign = 1 means the x of the basis column of the row has to grow to become feasible, when the coeff before j is neg, or x - has to diminish when the coeff is pos // we have xbj = -aj * xj - SASSERT(this->m_basis_heading[j] < 0); - SASSERT(this->column_is_feasible(j)); + lp_assert(this->m_basis_heading[j] < 0); + lp_assert(this->column_is_feasible(j)); switch (this->m_column_types[j]) { case column_type::free_column: return true; case column_type::fixed: return false; - case column_type::low_bound: + case column_type::lower_bound: if (sign < 0) return true; - return !this->x_is_at_low_bound(j); + return !this->x_is_at_lower_bound(j); case column_type::upper_bound: if (sign > 0) return true; return !this->x_is_at_upper_bound(j); case column_type::boxed: if (sign < 0) - return !this->x_is_at_low_bound(j); + return !this->x_is_at_lower_bound(j); return !this->x_is_at_upper_bound(j); } - SASSERT(false); // cannot be here + lp_assert(false); // cannot be here return false; } bool needs_to_grow(unsigned bj) const { - SASSERT(!this->column_is_feasible(bj)); + lp_assert(!this->column_is_feasible(bj)); switch(this->m_column_types[bj]) { case column_type::free_column: return false; case column_type::fixed: - case column_type::low_bound: + case column_type::lower_bound: case column_type::boxed: return this-> x_below_low_bound(bj); default: return false; } - SASSERT(false); // unreachable + lp_assert(false); // unreachable return false; } int inf_sign_of_column(unsigned bj) const { - SASSERT(!this->column_is_feasible(bj)); + lp_assert(!this->column_is_feasible(bj)); switch(this->m_column_types[bj]) { case column_type::free_column: return 0; - case column_type::low_bound: + case column_type::lower_bound: return 1; case column_type::fixed: case column_type::boxed: @@ -166,7 +165,7 @@ public: default: return -1; } - SASSERT(false); // unreachable + lp_assert(false); // unreachable return 0; } @@ -174,15 +173,15 @@ public: bool monoid_can_decrease(const row_cell & rc) const { unsigned j = rc.m_j; - SASSERT(this->column_is_feasible(j)); + lp_assert(this->column_is_feasible(j)); switch (this->m_column_types[j]) { case column_type::free_column: return true; case column_type::fixed: return false; - case column_type::low_bound: + case column_type::lower_bound: if (is_pos(rc.get_val())) { - return this->x_above_low_bound(j); + return this->x_above_lower_bound(j); } return true; @@ -194,28 +193,28 @@ public: return this->x_below_upper_bound(j); case column_type::boxed: if (is_pos(rc.get_val())) { - return this->x_above_low_bound(j); + return this->x_above_lower_bound(j); } return this->x_below_upper_bound(j); default: return false; } - SASSERT(false); // unreachable + lp_assert(false); // unreachable return false; } bool monoid_can_increase(const row_cell & rc) const { unsigned j = rc.m_j; - SASSERT(this->column_is_feasible(j)); + lp_assert(this->column_is_feasible(j)); switch (this->m_column_types[j]) { case column_type::free_column: return true; case column_type::fixed: return false; - case column_type::low_bound: + case column_type::lower_bound: if (is_neg(rc.get_val())) { - return this->x_above_low_bound(j); + return this->x_above_lower_bound(j); } return true; @@ -227,14 +226,14 @@ public: return this->x_below_upper_bound(j); case column_type::boxed: if (is_neg(rc.get_val())) { - return this->x_above_low_bound(j); + return this->x_above_lower_bound(j); } return this->x_below_upper_bound(j); default: return false; } - SASSERT(false); // unreachable + lp_assert(false); // unreachable return false; } @@ -344,24 +343,24 @@ public: } void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - SASSERT(m < 0 && this->m_column_types[j] == column_type::upper_bound); + lp_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound); limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); } - void limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - SASSERT(m < 0 && this->m_column_types[j] == column_type::low_bound); - limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_low_bounds[j], theta, unlimited); + void limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { + lp_assert(m < 0 && this->m_column_types[j] == column_type::lower_bound); + limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); } - void limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - SASSERT(m > 0 && this->m_column_types[j] == column_type::low_bound); - limit_inf_on_low_bound_m_pos(m, this->m_x[j], this->m_low_bounds[j], theta, unlimited); + void limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(unsigned j, const T & m, X & theta, bool & unlimited) { + lp_assert(m > 0 && this->m_column_types[j] == column_type::lower_bound); + limit_inf_on_lower_bound_m_pos(m, this->m_x[j], this->m_lower_bounds[j], theta, unlimited); } void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { - SASSERT(m > 0 && this->m_column_types[j] == column_type::upper_bound); + lp_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); }; @@ -370,7 +369,7 @@ public: void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector & leavings, T m, X & t, T & abs_of_d_of_leaving); - vector m_low_bounds_dummy; // needed for the base class only + vector m_lower_bounds_dummy; // needed for the base class only X get_max_bound(vector & b); @@ -403,7 +402,7 @@ public: bool need_to_switch_costs() const { if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) return false; - // SASSERT(calc_current_x_is_feasible() == current_x_is_feasible()); + // lp_assert(calc_current_x_is_feasible() == current_x_is_feasible()); return this->current_x_is_feasible() == this->m_using_infeas_costs; } @@ -420,7 +419,7 @@ public: // returns the number of iterations unsigned solve(); - lu * factorization() {return this->m_factorization;} + lu> * factorization() {return this->m_factorization;} void delete_factorization(); @@ -445,7 +444,7 @@ public: void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, const X &theta ) { this->update_basis_and_x_tableau(entering, leaving, theta); - this->update_column_in_inf_set(entering); + this->track_column_feasibility(entering); } @@ -458,23 +457,23 @@ public: if (j == -1) return -1; - SASSERT(!this->column_is_feasible(j)); + lp_assert(!this->column_is_feasible(j)); switch (this->m_column_types[j]) { case column_type::fixed: case column_type::upper_bound: new_val_for_leaving = this->m_upper_bounds[j]; break; - case column_type::low_bound: - new_val_for_leaving = this->m_low_bounds[j]; + case column_type::lower_bound: + new_val_for_leaving = this->m_lower_bounds[j]; break; case column_type::boxed: if (this->x_above_upper_bound(j)) new_val_for_leaving = this->m_upper_bounds[j]; else - new_val_for_leaving = this->m_low_bounds[j]; + new_val_for_leaving = this->m_lower_bounds[j]; break; default: - SASSERT(false); + lp_assert(false); new_val_for_leaving = numeric_traits::zero(); // does not matter } return j; @@ -484,7 +483,7 @@ public: X new_val_for_leaving; int leaving = find_leaving_tableau_rows(new_val_for_leaving); if (leaving == -1) { - this->set_status(OPTIMAL); + this->set_status(lp_status::OPTIMAL); return; } @@ -500,14 +499,14 @@ public: T a_ent; int entering = find_beneficial_column_in_row_tableau_rows(this->m_basis_heading[leaving], a_ent); if (entering == -1) { - this->set_status(INFEASIBLE); + this->set_status(lp_status::INFEASIBLE); return; } X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent; advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta ); - SASSERT(this->m_x[leaving] == new_val_for_leaving); + lp_assert(this->m_x[leaving] == new_val_for_leaving); if (this->current_x_is_feasible()) - this->set_status(OPTIMAL); + this->set_status(lp_status::OPTIMAL); } void fill_breakpoints_array(unsigned entering); @@ -522,30 +521,30 @@ public: void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta); void decide_on_status_when_cannot_find_entering() { - SASSERT(!need_to_switch_costs()); - this->set_status(this->current_x_is_feasible()? OPTIMAL: INFEASIBLE); + lp_assert(!need_to_switch_costs()); + this->set_status(this->current_x_is_feasible()? lp_status::OPTIMAL: lp_status::INFEASIBLE); } // void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) { - // SASSERT(m < 0); - // SASSERT(this->m_column_type[j] == low_bound || this->m_column_type[j] == boxed); - // const X & eps = harris_eps_for_bound(this->m_low_bounds[j]); - // if (this->above_bound(this->m_x[j], this->m_low_bounds[j])) { - // theta = std::min((this->m_low_bounds[j] -this->m_x[j] - eps) / m, theta); + // lp_assert(m < 0); + // lp_assert(this->m_column_type[j] == lower_bound || this->m_column_type[j] == boxed); + // const X & eps = harris_eps_for_bound(this->m_lower_bounds[j]); + // if (this->above_bound(this->m_x[j], this->m_lower_bounds[j])) { + // theta = std::min((this->m_lower_bounds[j] -this->m_x[j] - eps) / m, theta); // if (theta < zero_of_type()) theta = zero_of_type(); // } // } void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { - SASSERT(m < 0); - const X& eps = harris_eps_for_bound(this->m_low_bounds[j]); - limit_theta((this->m_low_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); + lp_assert(m < 0); + const X& eps = harris_eps_for_bound(this->m_lower_bounds[j]); + limit_theta((this->m_lower_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); if (theta < zero_of_type()) theta = zero_of_type(); } bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller - SASSERT(m < 0); + lp_assert(m < 0); if (numeric_traits::precise()) { if (this->below_bound(x, bound)) return false; if (this->above_bound(x, bound)) { @@ -569,7 +568,7 @@ public: bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets larger - SASSERT(m > 0); + lp_assert(m > 0); if (numeric_traits::precise()) { if (this->above_bound(x, bound)) return false; if (this->below_bound(x, bound)) { @@ -591,17 +590,17 @@ public: return true; } - void limit_inf_on_low_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { + void limit_inf_on_lower_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { if (numeric_traits::precise()) { // x gets larger - SASSERT(m > 0); + lp_assert(m > 0); if (this->below_bound(x, bound)) { limit_theta((bound - x) / m, theta, unlimited); } } else { // x gets larger - SASSERT(m > 0); + lp_assert(m > 0); const X& eps = harris_eps_for_bound(bound); if (this->below_bound(x, bound)) { limit_theta((bound - x + eps) / m, theta, unlimited); @@ -611,7 +610,7 @@ public: void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { // x gets smaller - SASSERT(m < 0); + lp_assert(m < 0); const X& eps = harris_eps_for_bound(bound); if (this->above_bound(x, bound)) { limit_theta((bound - x - eps) / m, theta, unlimited); @@ -619,9 +618,9 @@ public: } void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // SASSERT(m > 0 && this->m_column_type[j] == column_type::boxed); + // lp_assert(m > 0 && this->m_column_type[j] == column_type::boxed); const X & x = this->m_x[j]; - const X & lbound = this->m_low_bounds[j]; + const X & lbound = this->m_lower_bounds[j]; if (this->below_bound(x, lbound)) { const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); @@ -639,14 +638,14 @@ public: } void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { - // SASSERT(m < 0 && this->m_column_type[j] == column_type::boxed); + // lp_assert(m < 0 && this->m_column_type[j] == column_type::boxed); const X & x = this->m_x[j]; const X & ubound = this->m_upper_bounds[j]; if (this->above_bound(x, ubound)) { const X& eps = harris_eps_for_bound(ubound); limit_theta((ubound - x - eps) / m, theta, unlimited); } else { - const X & lbound = this->m_low_bounds[j]; + const X & lbound = this->m_lower_bounds[j]; if (this->above_bound(x, lbound)){ const X& eps = harris_eps_for_bound(lbound); limit_theta((lbound - x - eps) / m, theta, unlimited); @@ -657,7 +656,7 @@ public: } } void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { - SASSERT(m > 0); + lp_assert(m > 0); const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]); if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); @@ -669,7 +668,7 @@ public: } void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { - SASSERT(m > 0); + lp_assert(m > 0); const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); if (theta < zero_of_type()) { @@ -679,7 +678,7 @@ public: // j is a basic column or the entering, in any case x[j] has to stay feasible. // m is the multiplier. updating t in a way that holds the following - // x[j] + t * m >= this->m_low_bounds[j]- harris_feasibility_tolerance ( if m < 0 ) + // x[j] + t * m >= this->m_lower_bounds[j]- harris_feasibility_tolerance ( if m < 0 ) // or // x[j] + t * m <= this->m_upper_bounds[j] + harris_feasibility_tolerance ( if m > 0) void limit_theta_on_basis_column(unsigned j, T m, X & theta, bool & unlimited) { @@ -696,15 +695,15 @@ public: limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(j, m, theta, unlimited); } break; - case column_type::low_bound: + case column_type::lower_bound: if (this->current_x_is_feasible()) { if (m < 0) limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); } else { if (m < 0) - limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(j, m, theta, unlimited); + limit_theta_on_basis_column_for_inf_case_m_neg_lower_bound(j, m, theta, unlimited); else - limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(j, m, theta, unlimited); + limit_theta_on_basis_column_for_inf_case_m_pos_lower_bound(j, m, theta, unlimited); } break; // case fixed: @@ -735,7 +734,7 @@ public: break; default: - SASSERT(false); + lp_unreachable(); } if (!unlimited && theta < zero_of_type()) { theta = zero_of_type(); @@ -770,7 +769,7 @@ public: void init_reduced_costs(); - bool low_bounds_are_set() const override { return true; } + bool lower_bounds_are_set() const override { return true; } int advance_on_sorted_breakpoints(unsigned entering, X & t); @@ -794,7 +793,7 @@ public: if (this->m_basis_heading[j] < 0) continue; if (!this->column_is_feasible(j)) - this->m_inf_set.insert(j); + this->insert_column_into_inf_set(j); } } @@ -807,7 +806,7 @@ public: if (this->x_above_upper_bound(j)) return 1; break; - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(j)) return -1; break; @@ -818,7 +817,7 @@ public: case column_type::free_column: return 0; default: - SASSERT(false); + lp_assert(false); } return 0; } @@ -842,18 +841,18 @@ public: case column_type::fixed: return 0; case column_type::boxed: - if (this->x_is_at_low_bound(j)) + if (this->x_is_at_lower_bound(j)) return 1; return -1; break; - case column_type::low_bound: + case column_type::lower_bound: return 1; break; case column_type::upper_bound: return -1; break; default: - SASSERT(false); + lp_assert(false); } return 0; @@ -879,7 +878,7 @@ public: // the delta is between the old and the new cost (old - new) void update_reduced_cost_for_basic_column_cost_change(const T & delta, unsigned j) { - SASSERT(this->m_basis_heading[j] >= 0); + lp_assert(this->m_basis_heading[j] >= 0); unsigned i = static_cast(this->m_basis_heading[j]); for (const row_cell & rc : this->m_A.m_rows[i]) { unsigned k = rc.m_j; @@ -906,7 +905,7 @@ public: vector & heading, vector & costs, const vector & column_type_array, - const vector & low_bound_values, + const vector & lower_bound_values, const vector & upper_bound_values, lp_settings & settings, const column_namer& column_names): @@ -919,7 +918,7 @@ public: settings, column_names, column_type_array, - low_bound_values, + lower_bound_values, upper_bound_values), m_beta(A.row_count()), m_epsilon_of_reduced_cost(T(1)/T(10000000)), @@ -930,7 +929,7 @@ public: } else { m_converted_harris_eps = zero_of_type(); } - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } // constructor @@ -954,12 +953,12 @@ public: settings, column_names, column_type_array, - m_low_bounds_dummy, + m_lower_bounds_dummy, upper_bound_values), m_beta(A.row_count()), m_converted_harris_eps(convert_struct::convert(this->m_settings.harris_feasibility_tolerance)) { - SASSERT(initial_x_is_correct()); - m_low_bounds_dummy.resize(A.column_count(), zero_of_type()); + lp_assert(initial_x_is_correct()); + m_lower_bounds_dummy.resize(A.column_count(), zero_of_type()); m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); #ifdef Z3DEBUG // check_correctness(); @@ -972,7 +971,7 @@ public: basis_set.insert(this->m_basis[i]); } for (unsigned j = 0; j < this->m_n(); j++) { - if (this->column_has_low_bound(j) && this->m_x[j] < numeric_traits::zero()) { + if (this->column_has_lower_bound(j) && this->m_x[j] < numeric_traits::zero()) { LP_OUT(this->m_settings, "low bound for variable " << j << " does not hold: this->m_x[" << j << "] = " << this->m_x[j] << " is negative " << std::endl); return false; } @@ -983,7 +982,7 @@ public: } if (basis_set.find(j) != basis_set.end()) continue; - if (this->m_column_types[j] == column_type::low_bound) { + if (this->m_column_types[j] == column_type::lower_bound) { if (numeric_traits::zero() != this->m_x[j]) { LP_OUT(this->m_settings, "only low bound is set for " << j << " but low bound value " << numeric_traits::zero() << " is not equal to " << this->m_x[j] << std::endl); return false; diff --git a/src/util/lp/lp_primal_core_solver.hpp b/src/util/lp/lp_primal_core_solver_def.h similarity index 86% rename from src/util/lp/lp_primal_core_solver.hpp rename to src/util/lp/lp_primal_core_solver_def.h index 5f81def51..1e9edbd31 100644 --- a/src/util/lp/lp_primal_core_solver.hpp +++ b/src/util/lp/lp_primal_core_solver_def.h @@ -25,12 +25,12 @@ Revision History: #include #include "util/lp/lp_primal_core_solver.h" namespace lp { -// This core solver solves (Ax=b, low_bound_values \leq x \leq upper_bound_values, maximize costs*x ) +// This core solver solves (Ax=b, lower_bound_values \leq x \leq upper_bound_values, maximize costs*x ) // The right side b is given implicitly by x and the basis template void lp_primal_core_solver::sort_non_basis_rational() { - SASSERT(numeric_traits::precise()); + lp_assert(numeric_traits::precise()); if (this->m_settings.use_tableau()) { std::sort(this->m_nbasis.begin(), this->m_nbasis.end(), [this](unsigned a, unsigned b) { unsigned ca = this->m_A.number_of_non_zeroes_in_column(a); @@ -84,12 +84,12 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_on_breakpoin bool ret; const T & d = this->m_d[j]; switch (this->m_column_types[j]) { - case column_type::low_bound: - SASSERT(this->x_is_at_low_bound(j)); + case column_type::lower_bound: + lp_assert(this->x_is_at_lower_bound(j)); ret = d < -m_epsilon_of_reduced_cost; break; case column_type::upper_bound: - SASSERT(this->x_is_at_upper_bound(j)); + lp_assert(this->x_is_at_upper_bound(j)); ret = d > m_epsilon_of_reduced_cost; break; case column_type::fixed: @@ -97,16 +97,16 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_on_breakpoin break; case column_type::boxed: { - bool low_bound = this->x_is_at_low_bound(j); - SASSERT(low_bound || this->x_is_at_upper_bound(j)); - ret = (low_bound && d < -m_epsilon_of_reduced_cost) || ((!low_bound) && d > m_epsilon_of_reduced_cost); + bool lower_bound = this->x_is_at_lower_bound(j); + lp_assert(lower_bound || this->x_is_at_upper_bound(j)); + ret = (lower_bound && d < -m_epsilon_of_reduced_cost) || ((!lower_bound) && d > m_epsilon_of_reduced_cost); } break; case column_type::free_column: ret = d > m_epsilon_of_reduced_cost || d < - m_epsilon_of_reduced_cost; break; default: - SASSERT(false); + lp_unreachable(); ret = false; break; } @@ -125,7 +125,7 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsign if (dj > m_epsilon_of_reduced_cost || dj < -m_epsilon_of_reduced_cost) return true; break; - case column_type::low_bound: + case column_type::lower_bound: if (dj > m_epsilon_of_reduced_cost) return true;; break; case column_type::upper_bound: @@ -137,19 +137,19 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis(unsign return true; break; } else if (dj < - m_epsilon_of_reduced_cost) { - if (this->m_x[j] > this->m_low_bounds[j] + this->bound_span(j)/2) + if (this->m_x[j] > this->m_lower_bounds[j] + this->bound_span(j)/2) return true; } break; default: - SASSERT(false); + lp_unreachable(); break; } return false; } template bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precise(unsigned j) const { - SASSERT (numeric_traits::precise()); + lp_assert (numeric_traits::precise()); if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search) return column_is_benefitial_for_entering_on_breakpoints(j); const T& dj = this->m_d[j]; @@ -159,9 +159,9 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precis if (!is_zero(dj)) return true; break; - case column_type::low_bound: + case column_type::lower_bound: if (dj > zero_of_type()) return true; - if (dj < 0 && this->m_x[j] > this->m_low_bounds[j]){ + if (dj < 0 && this->m_x[j] > this->m_lower_bounds[j]){ return true; } break; @@ -177,12 +177,12 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precis return true; break; } else if (dj < zero_of_type()) { - if (this->m_x[j] > this->m_low_bounds[j]) + if (this->m_x[j] > this->m_lower_bounds[j]) return true; } break; default: - SASSERT(false); + lp_unreachable(); break; } return false; @@ -190,7 +190,7 @@ bool lp_primal_core_solver::column_is_benefitial_for_entering_basis_precis template int lp_primal_core_solver::choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - SASSERT(numeric_traits::precise()); + lp_assert(numeric_traits::precise()); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { @@ -274,7 +274,7 @@ int lp_primal_core_solver::choose_entering_column(unsigned number_of_benef template int lp_primal_core_solver::advance_on_sorted_breakpoints(unsigned entering, X &t) { T slope_at_entering = this->m_d[entering]; breakpoint * last_bp = nullptr; - SASSERT(m_breakpoint_indices_queue.is_empty()==false); + lp_assert(m_breakpoint_indices_queue.is_empty()==false); while (m_breakpoint_indices_queue.is_empty() == false) { unsigned bi = m_breakpoint_indices_queue.dequeue(); breakpoint *b = &m_breakpoints[bi]; @@ -289,7 +289,7 @@ template int lp_primal_core_solver::advance_on_so } } } - SASSERT (last_bp != nullptr); + lp_assert (last_bp != nullptr); t = last_bp->m_delta; return last_bp->m_j; } @@ -297,13 +297,13 @@ template int lp_primal_core_solver::advance_on_so template int lp_primal_core_solver::find_leaving_and_t_with_breakpoints(unsigned entering, X & t){ - SASSERT(this->precise() == false); + lp_assert(this->precise() == false); fill_breakpoints_array(entering); return advance_on_sorted_breakpoints(entering, t); } template bool lp_primal_core_solver::get_harris_theta(X & theta) { - SASSERT(this->m_ed.is_OK()); + lp_assert(this->m_ed.is_OK()); bool unlimited = true; for (unsigned i : this->m_ed.m_index) { if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; @@ -360,13 +360,13 @@ template bool lp_primal_core_solver::try_jump_to_ if (m_sign_of_entering_delta > 0) { t = this->m_upper_bounds[entering] - this->m_x[entering]; if (unlimited || t <= theta){ - SASSERT(t >= zero_of_type()); + lp_assert(t >= zero_of_type()); return true; } } else { // m_sign_of_entering_delta == -1 - t = this->m_x[entering] - this->m_low_bounds[entering]; + t = this->m_x[entering] - this->m_lower_bounds[entering]; if (unlimited || t <= theta) { - SASSERT(t >= zero_of_type()); + lp_assert(t >= zero_of_type()); return true; } } @@ -375,16 +375,16 @@ template bool lp_primal_core_solver::try_jump_to_ if (m_sign_of_entering_delta > 0) { t = this->m_upper_bounds[entering] - this->m_x[entering]; if (unlimited || t <= theta){ - SASSERT(t >= zero_of_type()); + lp_assert(t >= zero_of_type()); return true; } } return false; - case column_type::low_bound: + case column_type::lower_bound: if (m_sign_of_entering_delta < 0) { - t = this->m_x[entering] - this->m_low_bounds[entering]; + t = this->m_x[entering] - this->m_lower_bounds[entering]; if (unlimited || t <= theta) { - SASSERT(t >= zero_of_type()); + lp_assert(t >= zero_of_type()); return true; } } @@ -404,7 +404,7 @@ try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t ) { return true; } // m_sign_of_entering_delta == -1 - t = this->m_x[entering] - this->m_low_bounds[entering]; + t = this->m_x[entering] - this->m_lower_bounds[entering]; return true; } @@ -420,7 +420,7 @@ template int lp_primal_core_solver::find_leaving_ do { unsigned i = this->m_ed.m_index[k]; const T & ed = this->m_ed[i]; - SASSERT(!numeric_traits::is_zero(ed)); + lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); if (!unlimited) { @@ -439,7 +439,7 @@ template int lp_primal_core_solver::find_leaving_ while (k != initial_k) { unsigned i = this->m_ed.m_index[k]; const T & ed = this->m_ed[i]; - SASSERT(!numeric_traits::is_zero(ed)); + lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; unlimited = true; limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); @@ -479,7 +479,7 @@ template int lp_primal_core_solver::find_leavi return find_leaving_and_t_with_breakpoints(entering, t); X theta; bool unlimited = get_harris_theta(theta); - SASSERT(unlimited || theta >= zero_of_type()); + lp_assert(unlimited || theta >= zero_of_type()); if (try_jump_to_another_bound_on_entering(entering, theta, t, unlimited)) return entering; if (unlimited) return -1; @@ -489,7 +489,7 @@ template int lp_primal_core_solver::find_leavi // m is the multiplier. updating t in a way that holds the following -// x[j] + t * m >= m_low_bounds[j] ( if m < 0 ) +// x[j] + t * m >= m_lower_bounds[j] ( if m < 0 ) // or // x[j] + t * m <= this->m_upper_bounds[j] ( if m > 0) template void @@ -501,7 +501,7 @@ lp_primal_core_solver::get_bound_on_variable_and_update_leaving_precisely( return; default:break; } - X tt = - (this->m_low_bounds[j] - this->m_x[j]) / m; + X tt = - (this->m_lower_bounds[j] - this->m_x[j]) / m; if (numeric_traits::is_neg(tt)) tt = zero_of_type(); if (leavings.size() == 0 || tt < t || (tt == t && m > abs_of_d_of_leaving)) { @@ -516,7 +516,7 @@ lp_primal_core_solver::get_bound_on_variable_and_update_leaving_precisely( } else if (m < 0){ switch (this->m_column_types[j]) { // check that j has an upper bound case column_type::free_column: - case column_type::low_bound: + case column_type::lower_bound: return; default:break; } @@ -548,7 +548,7 @@ template X lp_primal_core_solver::get_max_boun template void lp_primal_core_solver::check_Ax_equal_b() { dense_matrix d(this->m_A); T * ls = d.apply_from_left_with_different_dims(this->m_x); - SASSERT(vectors_are_equal(ls, this->m_b, this->m_m())); + lp_assert(vectors_are_equal(ls, this->m_b, this->m_m())); delete [] ls; } template void lp_primal_core_solver::check_the_bounds() { @@ -558,8 +558,8 @@ template void lp_primal_core_solver::check_the } template void lp_primal_core_solver::check_bound(unsigned i) { - SASSERT (!(this->column_has_low_bound(i) && (numeric_traits::zero() > this->m_x[i]))); - SASSERT (!(this->column_has_upper_bound(i) && (this->m_upper_bounds[i] < this->m_x[i]))); + lp_assert (!(this->column_has_lower_bound(i) && (numeric_traits::zero() > this->m_x[i]))); + lp_assert (!(this->column_has_upper_bound(i) && (this->m_upper_bounds[i] < this->m_x[i]))); } template void lp_primal_core_solver::check_correctness() { @@ -575,8 +575,8 @@ void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned e // the basis heading has changed already #ifdef Z3DEBUG auto & basis_heading = this->m_basis_heading; - SASSERT(basis_heading[entering] >= 0 && static_cast(basis_heading[entering]) < this->m_m()); - SASSERT(basis_heading[leaving] < 0); + lp_assert(basis_heading[entering] >= 0 && static_cast(basis_heading[entering]) < this->m_m()); + lp_assert(basis_heading[leaving] < 0); #endif T pivot = this->m_pivot_row[entering]; T dq = this->m_d[entering]/pivot; @@ -599,7 +599,7 @@ void lp_primal_core_solver::update_reduced_costs_from_pivot_row(unsigned e template int lp_primal_core_solver::refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) { if (numeric_traits::precise()) return 0; T reduced_at_entering_was = this->m_d[entering]; // can benefit from going over non-zeros of m_ed - SASSERT(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost); + lp_assert(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost); T refreshed_cost = this->m_costs[entering]; unsigned i = this->m_m(); while (i--) refreshed_cost -= this->m_costs[this->m_basis[i]] * this->m_ed[i]; @@ -634,7 +634,7 @@ template void lp_primal_core_solver::backup_an m_costs_backup = this->m_costs; } else { T cost_max = std::max(max_abs_in_vector(this->m_costs), T(1)); - SASSERT(m_costs_backup.size() == 0); + lp_assert(m_costs_backup.size() == 0); for (unsigned j = 0; j < this->m_costs.size(); j++) m_costs_backup.push_back(this->m_costs[j] /= cost_max); } @@ -664,16 +664,16 @@ template void lp_primal_core_solver::init_run( template void lp_primal_core_solver::calc_working_vector_beta_for_column_norms(){ - SASSERT(numeric_traits::precise() == false); - SASSERT(this->m_ed.is_OK()); - SASSERT(m_beta.is_OK()); + lp_assert(numeric_traits::precise() == false); + lp_assert(this->m_ed.is_OK()); + lp_assert(m_beta.is_OK()); m_beta = this->m_ed; this->m_factorization->solve_yB_with_error_check_indexed(m_beta, this->m_basis_heading, this->m_basis, this->m_settings); } template void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering, X & t) { - SASSERT(!this->A_mult_x_is_off() ); + CASSERT("A_off", !this->A_mult_x_is_off() ); this->update_x(entering, t * m_sign_of_entering_delta); if (this->A_mult_x_is_off_on_index(this->m_ed.m_index) && !this->find_x_by_solving()) { this->init_lu(); @@ -685,7 +685,7 @@ void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering } } if (this->m_using_infeas_costs) { - SASSERT(is_zero(this->m_costs[entering])); + lp_assert(is_zero(this->m_costs[entering])); init_infeasibility_costs_for_changed_basis_only(); } if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) @@ -698,10 +698,10 @@ void lp_primal_core_solver::advance_on_entering_equal_leaving(int entering } template void lp_primal_core_solver::advance_on_entering_and_leaving(int entering, int leaving, X & t) { - SASSERT(entering >= 0 && m_non_basis_list.back() == static_cast(entering)); - SASSERT(this->m_using_infeas_costs || t >= zero_of_type()); - SASSERT(leaving >= 0 && entering >= 0); - SASSERT(entering != leaving || !is_zero(t)); // otherwise nothing changes + lp_assert(entering >= 0 && m_non_basis_list.back() == static_cast(entering)); + lp_assert(this->m_using_infeas_costs || t >= zero_of_type()); + lp_assert(leaving >= 0 && entering >= 0); + lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes if (entering == leaving) { advance_on_entering_equal_leaving(entering, t); return; @@ -713,14 +713,14 @@ template void lp_primal_core_solver::advance_on_en int pivot_compare_result = this->pivots_in_column_and_row_are_different(entering, leaving); if (!pivot_compare_result){;} else if (pivot_compare_result == 2) { // the sign is changed, cannot continue - this->set_status(UNSTABLE); + this->set_status(lp_status::UNSTABLE); this->iters_with_no_cost_growing()++; return; } else { - SASSERT(pivot_compare_result == 1); + lp_assert(pivot_compare_result == 1); this->init_lu(); if (this->m_factorization == nullptr || this->m_factorization->get_status() != LU_status::OK) { - this->set_status(UNSTABLE); + this->set_status(lp_status::UNSTABLE); this->iters_with_no_cost_growing()++; return; } @@ -732,10 +732,10 @@ template void lp_primal_core_solver::advance_on_en t = -t; } if (!this->update_basis_and_x(entering, leaving, t)) { - if (this->get_status() == FLOATING_POINT_ERROR) + if (this->get_status() == lp_status::FLOATING_POINT_ERROR) return; if (this->m_look_for_feasible_solution_only) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); return; } init_reduced_costs(); @@ -748,7 +748,7 @@ template void lp_primal_core_solver::advance_on_en } if (this->current_x_is_feasible()) { - this->set_status(FEASIBLE); + this->set_status(lp_status::FEASIBLE); if (this->m_look_for_feasible_solution_only) return; } @@ -761,7 +761,7 @@ template void lp_primal_core_solver::advance_on_en } else { update_reduced_costs_from_pivot_row(entering, leaving); } - SASSERT(!need_to_switch_costs()); + lp_assert(!need_to_switch_costs()); std::list::iterator it = m_non_basis_list.end(); it--; * it = static_cast(leaving); @@ -769,13 +769,13 @@ template void lp_primal_core_solver::advance_on_en template void lp_primal_core_solver::advance_on_entering_precise(int entering) { - SASSERT(numeric_traits::precise()); - SASSERT(entering > -1); + lp_assert(numeric_traits::precise()); + lp_assert(entering > -1); this->solve_Bd(entering); X t; int leaving = find_leaving_and_t_precise(entering, t); if (leaving == -1) { - this->set_status(UNBOUNDED); + this->set_status(lp_status::UNBOUNDED); return; } advance_on_entering_and_leaving(entering, leaving, t); @@ -786,12 +786,12 @@ template void lp_primal_core_solver::advance_on_e advance_on_entering_precise(entering); return; } - SASSERT(entering > -1); + lp_assert(entering > -1); this->solve_Bd(entering); int refresh_result = refresh_reduced_cost_at_entering_and_check_that_it_is_off(entering); if (refresh_result) { if (this->m_look_for_feasible_solution_only) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); return; } @@ -806,7 +806,7 @@ template void lp_primal_core_solver::advance_on_e int leaving = find_leaving_and_t(entering, t); if (leaving == -1){ if (!this->current_x_is_feasible()) { - SASSERT(!numeric_traits::precise()); // we cannot have unbounded with inf costs + lp_assert(!numeric_traits::precise()); // we cannot have unbounded with inf costs // if (m_look_for_feasible_solution_only) { // this->m_status = INFEASIBLE; @@ -814,19 +814,19 @@ template void lp_primal_core_solver::advance_on_e // } - if (this->get_status() == UNSTABLE) { - this->set_status(FLOATING_POINT_ERROR); + if (this->get_status() == lp_status::UNSTABLE) { + this->set_status(lp_status::FLOATING_POINT_ERROR); return; } init_infeasibility_costs(); - this->set_status(UNSTABLE); + this->set_status(lp_status::UNSTABLE); return; } - if (this->get_status() == TENTATIVE_UNBOUNDED) { - this->set_status(UNBOUNDED); + if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED) { + this->set_status(lp_status::UNBOUNDED); } else { - this->set_status(TENTATIVE_UNBOUNDED); + this->set_status(lp_status::TENTATIVE_UNBOUNDED); } return; } @@ -840,7 +840,7 @@ template void lp_primal_core_solver::push_forw template unsigned lp_primal_core_solver::get_number_of_non_basic_column_to_try_for_enter() { unsigned ret = static_cast(this->m_nbasis.size()); - if (this->get_status() == TENTATIVE_UNBOUNDED) + if (this->get_status() == lp_status::TENTATIVE_UNBOUNDED) return ret; // we really need to find entering with a large reduced cost if (ret > 300) { ret = (unsigned)(ret * this->m_settings.percent_of_entering_to_check / 100); @@ -864,15 +864,15 @@ template void lp_primal_core_solver::print_column template unsigned lp_primal_core_solver::solve() { if (numeric_traits::precise() && this->m_settings.use_tableau()) return solve_with_tableau(); - + init_run(); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { - this->set_status(FEASIBLE); + this->set_status(lp_status::FEASIBLE); return 0; } if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); return 0; } do { @@ -880,10 +880,11 @@ template unsigned lp_primal_core_solver::solve() return this->total_iterations(); } one_iteration(); - SASSERT(!this->m_using_infeas_costs || this->costs_on_nbasis_are_zeros()); + + lp_assert(!this->m_using_infeas_costs || this->costs_on_nbasis_are_zeros()); switch (this->get_status()) { - case OPTIMAL: // double check that we are at optimum - case INFEASIBLE: + case lp_status::OPTIMAL: // double check that we are at optimum + case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; if (!numeric_traits::precise()) { @@ -892,7 +893,7 @@ template unsigned lp_primal_core_solver::solve() this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status (FLOATING_POINT_ERROR); + this->set_status (lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); @@ -900,7 +901,7 @@ template unsigned lp_primal_core_solver::solve() decide_on_status_when_cannot_find_entering(); break; } - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } else { // precise case if (this->m_look_for_feasible_solution_only) { // todo: keep the reduced costs correct all the time! init_reduced_costs(); @@ -908,31 +909,31 @@ template unsigned lp_primal_core_solver::solve() decide_on_status_when_cannot_find_entering(); break; } - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } } break; - case TENTATIVE_UNBOUNDED: + case lp_status::TENTATIVE_UNBOUNDED: this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); break; - case UNBOUNDED: + case lp_status::UNBOUNDED: if (this->current_x_is_infeasible()) { init_reduced_costs(); - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } break; - case UNSTABLE: - SASSERT(! (numeric_traits::precise())); + case lp_status::UNSTABLE: + lp_assert(! (numeric_traits::precise())); this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); @@ -941,13 +942,13 @@ template unsigned lp_primal_core_solver::solve() default: break; // do nothing } - } while (this->get_status() != FLOATING_POINT_ERROR + } while (this->get_status() != lp_status::FLOATING_POINT_ERROR && - this->get_status() != UNBOUNDED + this->get_status() != lp_status::UNBOUNDED && - this->get_status() != OPTIMAL + this->get_status() != lp_status::OPTIMAL && - this->get_status() != INFEASIBLE + this->get_status() != lp_status::INFEASIBLE && this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements && @@ -955,7 +956,7 @@ template unsigned lp_primal_core_solver::solve() && !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)); - SASSERT(this->get_status() == FLOATING_POINT_ERROR + lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR || this->current_x_is_feasible() == false || @@ -972,7 +973,7 @@ template void lp_primal_core_solver::delete_fa // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" template void lp_primal_core_solver::init_column_norms() { - SASSERT(numeric_traits::precise() == false); + lp_assert(numeric_traits::precise() == false); for (unsigned j = 0; j < this->m_n(); j++) { this->m_column_norms[j] = T(static_cast(this->m_A.m_columns[j].size() + 1)) @@ -982,7 +983,7 @@ template void lp_primal_core_solver::init_column_ // debug only template T lp_primal_core_solver::calculate_column_norm_exactly(unsigned j) { - SASSERT(numeric_traits::precise() == false); + lp_assert(numeric_traits::precise() == false); indexed_vector w(this->m_m()); this->m_A.copy_column_to_vector(j, w); vector d(this->m_m()); @@ -994,8 +995,8 @@ template T lp_primal_core_solver::calculate_colum } template void lp_primal_core_solver::update_or_init_column_norms(unsigned entering, unsigned leaving) { - SASSERT(numeric_traits::precise() == false); - SASSERT(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); + lp_assert(numeric_traits::precise() == false); + lp_assert(m_column_norm_update_counter <= this->m_settings.column_norms_update_frequency); if (m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { m_column_norm_update_counter = 0; init_column_norms(); @@ -1007,7 +1008,7 @@ template void lp_primal_core_solver::update_or // following Swietanowski - A new steepest ... template void lp_primal_core_solver::update_column_norms(unsigned entering, unsigned leaving) { - SASSERT(numeric_traits::precise() == false); + lp_assert(numeric_traits::precise() == false); T pivot = this->m_pivot_row[entering]; T g_ent = calculate_norm_of_entering_exactly() / pivot / pivot; if (!numeric_traits::precise()) { @@ -1042,8 +1043,8 @@ template T lp_primal_core_solver::calculate_no // calling it stage1 is too cryptic template void lp_primal_core_solver::find_feasible_solution() { this->m_look_for_feasible_solution_only = true; - SASSERT(this->non_basic_columns_are_set_correctly()); - this->set_status(UNKNOWN); + lp_assert(this->non_basic_columns_are_set_correctly()); + this->set_status(lp_status::UNKNOWN); solve(); } @@ -1087,15 +1088,15 @@ template void lp_primal_core_solver::fill_breakpo template bool lp_primal_core_solver::done() { - if (this->get_status() == OPTIMAL || this->get_status() == FLOATING_POINT_ERROR) return true; - if (this->get_status() == INFEASIBLE) { + if (this->get_status() == lp_status::OPTIMAL || this->get_status() == lp_status::FLOATING_POINT_ERROR) return true; + if (this->get_status() == lp_status::INFEASIBLE) { return true; } if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) { - this->get_status() = ITERATIONS_EXHAUSTED; return true; + this->get_status() = lp_status::ITERATIONS_EXHAUSTED; return true; } if (this->total_iterations() >= this->m_settings.max_total_number_of_iterations) { - this->get_status() = ITERATIONS_EXHAUSTED; return true; + this->get_status() = lp_status::ITERATIONS_EXHAUSTED; return true; } return false; } @@ -1110,8 +1111,8 @@ void lp_primal_core_solver::init_infeasibility_costs_for_changed_basis_onl template void lp_primal_core_solver::init_infeasibility_costs() { - SASSERT(this->m_x.size() >= this->m_n()); - SASSERT(this->m_column_types.size() >= this->m_n()); + lp_assert(this->m_x.size() >= this->m_n()); + lp_assert(this->m_column_types.size() >= this->m_n()); for (unsigned j = this->m_n(); j--;) init_infeasibility_cost_for_column(j); this->m_using_infeas_costs = true; @@ -1135,7 +1136,7 @@ lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const ret = numeric_traits::zero(); } break; - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(j)) { ret = -1; } else { @@ -1153,7 +1154,7 @@ lp_primal_core_solver::get_infeasibility_cost_for_column(unsigned j) const ret = numeric_traits::zero(); break; default: - SASSERT(false); + lp_assert(false); ret = numeric_traits::zero(); // does not matter break; } @@ -1189,7 +1190,7 @@ lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { this->m_costs[j] = numeric_traits::zero(); } break; - case column_type::low_bound: + case column_type::lower_bound: if (this->x_below_low_bound(j)) { this->m_costs[j] = -1; } else { @@ -1207,14 +1208,14 @@ lp_primal_core_solver::init_infeasibility_cost_for_column(unsigned j) { this->m_costs[j] = numeric_traits::zero(); break; default: - SASSERT(false); + lp_assert(false); break; } if (numeric_traits::is_zero(this->m_costs[j])) { - this->m_inf_set.erase(j); + this->remove_column_from_inf_set(j); } else { - this->m_inf_set.insert(j); + this->insert_column_into_inf_set(j); } if (!this->m_settings.use_breakpoints_in_feasibility_search) { this->m_costs[j] = - this->m_costs[j]; @@ -1227,18 +1228,18 @@ template void lp_primal_core_solver::print_column switch (this->m_column_type[j]) { case column_type::fixed: case column_type::boxed: - out << "( " << this->m_low_bounds[j] << " " << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl; + out << "( " << this->m_lower_bounds[j] << " " << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl; break; case column_type::upper_bound: out << "( _" << this->m_x[j] << " " << this->m_upper_bounds[j] << ")" << std::endl; break; - case column_type::low_bound: - out << "( " << this->m_low_bounds[j] << " " << this->m_x[j] << " " << "_ )" << std::endl; + case column_type::lower_bound: + out << "( " << this->m_lower_bounds[j] << " " << this->m_x[j] << " " << "_ )" << std::endl; break; case column_type::free_column: out << "( _" << this->m_x[j] << "_)" << std::endl; default: - SASSERT(false); + lp_unreachable(); } } @@ -1277,7 +1278,7 @@ template std::string lp_primal_core_solver::break case upper_break: return "upper_break"; case fixed_break: return "fixed_break"; default: - SASSERT(false); + lp_assert(false); break; } return "type is not found"; @@ -1290,7 +1291,7 @@ template void lp_primal_core_solver::print_breakp template void lp_primal_core_solver::init_reduced_costs() { - SASSERT(!this->use_tableau()); + lp_assert(!this->use_tableau()); if (this->current_x_is_infeasible() && !this->m_using_infeas_costs) { init_infeasibility_costs(); } else if (this->current_x_is_feasible() && this->m_using_infeas_costs) { @@ -1305,12 +1306,12 @@ void lp_primal_core_solver::init_reduced_costs() { template void lp_primal_core_solver::change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering) { if (b->m_j == entering) { - SASSERT(b->m_type != fixed_break && (!is_zero(b->m_delta))); + lp_assert(b->m_type != fixed_break && (!is_zero(b->m_delta))); slope_at_entering += m_sign_of_entering_delta; return; } - SASSERT(this->m_basis_heading[b->m_j] >= 0); + lp_assert(this->m_basis_heading[b->m_j] >= 0); unsigned i_row = this->m_basis_heading[b->m_j]; const T & d = - this->m_ed[i_row]; if (numeric_traits::is_zero(d)) return; @@ -1329,27 +1330,27 @@ template void lp_primal_core_solver::change_sl slope_at_entering += delta; break; default: - SASSERT(false); + lp_assert(false); } } template void lp_primal_core_solver::try_add_breakpoint_in_row(unsigned i) { - SASSERT(i < this->m_m()); + lp_assert(i < this->m_m()); const T & d = this->m_ed[i]; // the coefficient before m_entering in the i-th row if (d == 0) return; // the change of x[m_entering] will not change the corresponding basis x unsigned j = this->m_basis[i]; const X & x = this->m_x[j]; switch (this->m_column_types[j]) { case column_type::fixed: - try_add_breakpoint(j, x, d, fixed_break, this->m_low_bounds[j]); + try_add_breakpoint(j, x, d, fixed_break, this->m_lower_bounds[j]); break; case column_type::boxed: - try_add_breakpoint(j, x, d, low_break, this->m_low_bounds[j]); + try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); break; - case column_type::low_bound: - try_add_breakpoint(j, x, d, low_break, this->m_low_bounds[j]); + case column_type::lower_bound: + try_add_breakpoint(j, x, d, low_break, this->m_lower_bounds[j]); break; case column_type::upper_bound: try_add_breakpoint(j, x, d, upper_break, this->m_upper_bounds[j]); @@ -1357,7 +1358,7 @@ template void lp_primal_core_solver::try_add_b case column_type::free_column: break; default: - SASSERT(false); + lp_assert(false); break; } } @@ -1369,10 +1370,10 @@ template void lp_primal_core_solver::print_bound_ switch (this->m_column_types[j]) { case column_type::fixed: case column_type::boxed: - out << "[" << this->m_low_bounds[j] << "," << this->m_upper_bounds[j] << "]" << std::endl; + out << "[" << this->m_lower_bounds[j] << "," << this->m_upper_bounds[j] << "]" << std::endl; break; - case column_type::low_bound: - out << "[" << this->m_low_bounds[j] << ", inf" << std::endl; + case column_type::lower_bound: + out << "[" << this->m_lower_bounds[j] << ", inf" << std::endl; break; case column_type::upper_bound: out << "inf ," << this->m_upper_bounds[j] << "]" << std::endl; @@ -1381,7 +1382,7 @@ template void lp_primal_core_solver::print_bound_ out << "inf, inf" << std::endl; break; default: - SASSERT(false); + lp_assert(false); break; } } diff --git a/src/util/lp/lp_primal_core_solver_tableau.h b/src/util/lp/lp_primal_core_solver_tableau_def.h similarity index 81% rename from src/util/lp/lp_primal_core_solver_tableau.h rename to src/util/lp/lp_primal_core_solver_tableau_def.h index 0c56f0ab9..f85c131a3 100644 --- a/src/util/lp/lp_primal_core_solver_tableau.h +++ b/src/util/lp/lp_primal_core_solver_tableau_def.h @@ -28,14 +28,14 @@ template void lp_primal_core_solver::one_iteratio else { advance_on_entering_tableau(entering); } - SASSERT(this->inf_set_is_correct()); + lp_assert(this->inf_set_is_correct()); } template void lp_primal_core_solver::advance_on_entering_tableau(int entering) { X t; int leaving = find_leaving_and_t_tableau(entering, t); if (leaving == -1) { - this->set_status(UNBOUNDED); + this->set_status(lp_status::UNBOUNDED); return; } advance_on_entering_and_leaving_tableau(entering, leaving, t); @@ -52,7 +52,7 @@ template int lp_primal_core_solver::choose_enteri //this moment m_y = cB * B(-1) unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); - SASSERT(numeric_traits::precise()); + lp_assert(numeric_traits::precise()); if (number_of_benefitial_columns_to_go_over == 0) return -1; if (this->m_basis_sort_counter == 0) { @@ -100,25 +100,26 @@ template unsigned lp_primal_core_solver::solve_with_tableau() { init_run_tableau(); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { - this->set_status(FEASIBLE); + this->set_status(lp_status::FEASIBLE); return 0; } if ((!numeric_traits::precise()) && this->A_mult_x_is_off()) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); return 0; } do { if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->m_using_infeas_costs? "inf t" : "feas t"), * this->m_settings.get_message_ostream())) { return this->total_iterations(); } - if (this->m_settings.use_tableau_rows()) + if (this->m_settings.use_tableau_rows()) { one_iteration_tableau_rows(); + } else one_iteration_tableau(); switch (this->get_status()) { - case OPTIMAL: // double check that we are at optimum - case INFEASIBLE: + case lp_status::OPTIMAL: // double check that we are at optimum + case lp_status::INFEASIBLE: if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) break; if (!numeric_traits::precise()) { @@ -127,7 +128,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); @@ -135,7 +136,7 @@ unsigned lp_primal_core_solver::solve_with_tableau() { decide_on_status_when_cannot_find_entering(); break; } - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } else { // precise case if ((!this->infeasibility_costs_are_correct())) { init_reduced_costs_tableau(); // forcing recalc @@ -143,31 +144,31 @@ unsigned lp_primal_core_solver::solve_with_tableau() { decide_on_status_when_cannot_find_entering(); break; } - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } } break; - case TENTATIVE_UNBOUNDED: + case lp_status::TENTATIVE_UNBOUNDED: this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); break; - case UNBOUNDED: + case lp_status::UNBOUNDED: if (this->current_x_is_infeasible()) { init_reduced_costs(); - this->set_status(UNKNOWN); + this->set_status(lp_status::UNKNOWN); } break; - case UNSTABLE: - SASSERT(! (numeric_traits::precise())); + case lp_status::UNSTABLE: + lp_assert(! (numeric_traits::precise())); this->init_lu(); if (this->m_factorization->get_status() != LU_status::OK) { - this->set_status(FLOATING_POINT_ERROR); + this->set_status(lp_status::FLOATING_POINT_ERROR); break; } init_reduced_costs(); @@ -176,13 +177,13 @@ unsigned lp_primal_core_solver::solve_with_tableau() { default: break; // do nothing } - } while (this->get_status() != FLOATING_POINT_ERROR + } while (this->get_status() != lp_status::FLOATING_POINT_ERROR && - this->get_status() != UNBOUNDED + this->get_status() != lp_status::UNBOUNDED && - this->get_status() != OPTIMAL + this->get_status() != lp_status::OPTIMAL && - this->get_status() != INFEASIBLE + this->get_status() != lp_status::INFEASIBLE && this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements && @@ -193,13 +194,13 @@ unsigned lp_primal_core_solver::solve_with_tableau() { this->m_settings.get_cancel_flag() == false); if (this->m_settings.get_cancel_flag()) { - this->set_status(CANCELLED); + this->set_status(lp_status::CANCELLED); } - SASSERT( - this->get_status() == FLOATING_POINT_ERROR + lp_assert( + this->get_status() == lp_status::FLOATING_POINT_ERROR || - this->get_status() == CANCELLED + this->get_status() == lp_status::CANCELLED || this->current_x_is_feasible() == false || @@ -208,13 +209,13 @@ unsigned lp_primal_core_solver::solve_with_tableau() { } template void lp_primal_core_solver::advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t) { - SASSERT(this->A_mult_x_is_off() == false); - SASSERT(leaving >= 0 && entering >= 0); - SASSERT((this->m_settings.simplex_strategy() == + CASSERT("A_off", this->A_mult_x_is_off() == false); + lp_assert(leaving >= 0 && entering >= 0); + lp_assert((this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) || m_non_basis_list.back() == static_cast(entering)); - SASSERT(this->m_using_infeas_costs || !is_neg(t)); - SASSERT(entering != leaving || !is_zero(t)); // otherwise nothing changes + lp_assert(this->m_using_infeas_costs || !is_neg(t)); + lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes if (entering == leaving) { advance_on_entering_equal_leaving_tableau(entering, t); return; @@ -225,7 +226,7 @@ template void lp_primal_core_solver::advance_on_en t = -t; } this->update_basis_and_x_tableau(entering, leaving, t); - SASSERT(this->A_mult_x_is_off() == false); + CASSERT("A_off", this->A_mult_x_is_off() == false); this->iters_with_no_cost_growing() = 0; } else { this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); @@ -240,7 +241,7 @@ template void lp_primal_core_solver::advance_on_en this->init_reduced_costs_tableau(); } - SASSERT(!need_to_switch_costs()); + lp_assert(!need_to_switch_costs()); std::list::iterator it = m_non_basis_list.end(); it--; * it = static_cast(leaving); @@ -249,7 +250,7 @@ template void lp_primal_core_solver::advance_on_en template void lp_primal_core_solver::advance_on_entering_equal_leaving_tableau(int entering, X & t) { - SASSERT(!this->A_mult_x_is_off() ); + CASSERT("A_off", !this->A_mult_x_is_off() ); this->update_x_tableau(entering, t * m_sign_of_entering_delta); if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) return; @@ -270,7 +271,7 @@ template int lp_primal_core_solver::find_leaving_ const column_cell & c = col[k]; unsigned i = c.m_i; const T & ed = this->m_A.get_val(c); - SASSERT(!numeric_traits::is_zero(ed)); + lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); if (!unlimited) { @@ -289,7 +290,7 @@ template int lp_primal_core_solver::find_leaving_ const column_cell & c = col[k]; unsigned i = c.m_i; const T & ed = this->m_A.get_val(c); - SASSERT(!numeric_traits::is_zero(ed)); + lp_assert(!numeric_traits::is_zero(ed)); unsigned j = this->m_basis[i]; unlimited = true; limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); @@ -321,13 +322,12 @@ template int lp_primal_core_solver::find_leaving_ return m_leaving_candidates[k]; } template void lp_primal_core_solver::init_run_tableau() { - // print_matrix(&(this->m_A), std::cout); - SASSERT(this->A_mult_x_is_off() == false); - SASSERT(basis_columns_are_set_correctly()); + CASSERT("A_off", this->A_mult_x_is_off() == false); + lp_assert(basis_columns_are_set_correctly()); this->m_basis_sort_counter = 0; // to initiate the sort of the basis this->set_total_iterations(0); this->iters_with_no_cost_growing() = 0; - SASSERT(this->inf_set_is_correct()); + lp_assert(this->inf_set_is_correct()); if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) return; if (this->m_settings.backup_costs) @@ -341,13 +341,13 @@ template void lp_primal_core_solver::init_run_tab } if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) init_tableau_rows(); - SASSERT(this->reduced_costs_are_correct_tableau()); - SASSERT(!this->need_to_pivot_to_basis_tableau()); + lp_assert(this->reduced_costs_are_correct_tableau()); + lp_assert(!this->need_to_pivot_to_basis_tableau()); } template bool lp_primal_core_solver:: update_basis_and_x_tableau(int entering, int leaving, X const & tt) { - SASSERT(this->use_tableau()); + lp_assert(this->use_tableau()); update_x_tableau(entering, tt); this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); this->change_basis(entering, leaving); @@ -355,36 +355,34 @@ update_basis_and_x_tableau(int entering, int leaving, X const & tt) { } template void lp_primal_core_solver:: update_x_tableau(unsigned entering, const X& delta) { + this->add_delta_to_x_and_call_tracker(entering, delta); if (!this->m_using_infeas_costs) { - this->m_x[entering] += delta; for (const auto & c : this->m_A.m_columns[entering]) { unsigned i = c.m_i; - this->m_x[this->m_basis[i]] -= delta * this->m_A.get_val(c); - this->update_column_in_inf_set(this->m_basis[i]); + this->update_x_with_delta_and_track_feasibility(this->m_basis[i], - delta * this->m_A.get_val(c)); } } else { // m_using_infeas_costs == true - this->m_x[entering] += delta; - SASSERT(this->column_is_feasible(entering)); - SASSERT(this->m_costs[entering] == zero_of_type()); + lp_assert(this->column_is_feasible(entering)); + lp_assert(this->m_costs[entering] == zero_of_type()); // m_d[entering] can change because of the cost change for basic columns. for (const auto & c : this->m_A.m_columns[entering]) { unsigned i = c.m_i; unsigned j = this->m_basis[i]; - this->m_x[j] -= delta * this->m_A.get_val(c); + this->add_delta_to_x_and_call_tracker(j, -delta * this->m_A.get_val(c)); update_inf_cost_for_column_tableau(j); if (is_zero(this->m_costs[j])) - this->m_inf_set.erase(j); + this->remove_column_from_inf_set(j); else - this->m_inf_set.insert(j); + this->insert_column_into_inf_set(j); } } - SASSERT(this->A_mult_x_is_off() == false); + CASSERT("A_off", this->A_mult_x_is_off() == false); } template void lp_primal_core_solver:: update_inf_cost_for_column_tableau(unsigned j) { - SASSERT(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows); - SASSERT(this->m_using_infeas_costs); + lp_assert(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows); + lp_assert(this->m_using_infeas_costs); T new_cost = get_infeasibility_cost_for_column(j); T delta = this->m_costs[j] - new_cost; if (is_zero(delta)) diff --git a/src/util/lp/lp_primal_simplex_instances.cpp b/src/util/lp/lp_primal_simplex.cpp similarity index 96% rename from src/util/lp/lp_primal_simplex_instances.cpp rename to src/util/lp/lp_primal_simplex.cpp index 92e3a77ff..56d4a6134 100644 --- a/src/util/lp/lp_primal_simplex_instances.cpp +++ b/src/util/lp/lp_primal_simplex.cpp @@ -22,7 +22,7 @@ Revision History: #include #include "util/vector.h" #include -#include "util/lp/lp_primal_simplex.hpp" +#include "util/lp/lp_primal_simplex_def.h" template bool lp::lp_primal_simplex::bounds_hold(std::unordered_map, std::equal_to, std::allocator > > const&); template bool lp::lp_primal_simplex::row_constraints_hold(std::unordered_map, std::equal_to, std::allocator > > const&); template double lp::lp_primal_simplex::get_current_cost() const; diff --git a/src/util/lp/lp_primal_simplex.h b/src/util/lp/lp_primal_simplex.h index 06ea26f8f..d0f5d7d7d 100644 --- a/src/util/lp/lp_primal_simplex.h +++ b/src/util/lp/lp_primal_simplex.h @@ -26,12 +26,11 @@ Revision History: #include "util/lp/column_info.h" #include "util/lp/lp_primal_core_solver.h" #include "util/lp/lp_solver.h" -#include "util/lp/iterator_on_row.h" namespace lp { template class lp_primal_simplex: public lp_solver { lp_primal_core_solver * m_core_solver; - vector m_low_bounds; + vector m_lower_bounds; private: unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); } diff --git a/src/util/lp/lp_primal_simplex.hpp b/src/util/lp/lp_primal_simplex_def.h similarity index 89% rename from src/util/lp/lp_primal_simplex.hpp rename to src/util/lp/lp_primal_simplex_def.h index fd717ec7f..722e4bd79 100644 --- a/src/util/lp/lp_primal_simplex.hpp +++ b/src/util/lp/lp_primal_simplex_def.h @@ -76,14 +76,14 @@ template void lp_primal_simplex::fill_costs_and_x int row, unsigned & slack_var, unsigned & artificial) { - SASSERT(row >= 0 && row < this->row_count()); + lp_assert(row >= 0 && row < this->row_count()); auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; // we need to bring the program to the form Ax = b T rs = this->m_b[row]; T artificial_cost = - numeric_traits::one(); switch (constraint.m_relation) { case Equal: // no slack variable here - this->m_column_types[artificial] = column_type::low_bound; + this->m_column_types[artificial] = column_type::lower_bound; this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero this->m_basis[row] = artificial; if (rs >= 0) { @@ -97,13 +97,13 @@ template void lp_primal_simplex::fill_costs_and_x break; case Greater_or_equal: - this->m_column_types[slack_var] = column_type::low_bound; + this->m_column_types[slack_var] = column_type::lower_bound; (*this->m_A)(row, slack_var) = - numeric_traits::one(); if (rs > 0) { - SASSERT(numeric_traits::is_zero(this->m_x[slack_var])); + lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); // adding one artificial - this->m_column_types[artificial] = column_type::low_bound; + this->m_column_types[artificial] = column_type::lower_bound; (*this->m_A)(row, artificial) = numeric_traits::one(); this->m_costs[artificial] = artificial_cost; this->m_basis[row] = artificial; @@ -118,13 +118,13 @@ template void lp_primal_simplex::fill_costs_and_x break; case Less_or_equal: // introduce a non-negative slack variable - this->m_column_types[slack_var] = column_type::low_bound; + this->m_column_types[slack_var] = column_type::lower_bound; (*this->m_A)(row, slack_var) = numeric_traits::one(); if (rs < 0) { // adding one artificial - SASSERT(numeric_traits::is_zero(this->m_x[slack_var])); - this->m_column_types[artificial] = column_type::low_bound; + lp_assert(numeric_traits::is_zero(this->m_x[slack_var])); + this->m_column_types[artificial] = column_type::lower_bound; (*this->m_A)(row, artificial) = - numeric_traits::one(); this->m_costs[artificial] = artificial_cost; this->m_x[artificial] = - rs; @@ -192,12 +192,12 @@ template void lp_primal_simplex::fill_A_x_and_bas } template void lp_primal_simplex::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { - SASSERT(row < this->row_count()); + lp_assert(row < this->row_count()); auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row); - SASSERT(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); + lp_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); unsigned ext_row = ext_row_it->second; auto constr_it = this->m_constraints.find(ext_row); - SASSERT(constr_it != this->m_constraints.end()); + lp_assert(constr_it != this->m_constraints.end()); auto & constraint = constr_it->second; unsigned j = this->m_A->column_count(); // j is a slack variable this->m_A->add_column(); @@ -208,34 +208,34 @@ template void lp_primal_simplex::fill_A_x_and_bas this->m_x[j] = this->m_b[row]; (*this->m_A)(row, j) = numeric_traits::one(); this->m_column_types[j] = column_type::fixed; - this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type(); + this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); break; case Greater_or_equal: this->m_x[j] = - this->m_b[row]; (*this->m_A)(row, j) = - numeric_traits::one(); - this->m_column_types[j] = column_type::low_bound; + this->m_column_types[j] = column_type::lower_bound; this->m_upper_bounds[j] = zero_of_type(); break; case Less_or_equal: this->m_x[j] = this->m_b[row]; (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = column_type::low_bound; - this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type(); + this->m_column_types[j] = column_type::lower_bound; + this->m_upper_bounds[j] = m_lower_bounds[j] = zero_of_type(); break; default: - SASSERT(false); + lp_unreachable(); } } template void lp_primal_simplex::solve_with_total_inf() { int total_vars = this->m_A->column_count() + this->row_count(); if (total_vars == 0) { - this->m_status = OPTIMAL; + this->m_status = lp_status::OPTIMAL; return; } - m_low_bounds.clear(); - m_low_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero + m_lower_bounds.clear(); + m_lower_bounds.resize(total_vars, zero_of_type()); // low bounds are shifted ot zero this->m_x.resize(total_vars, numeric_traits::zero()); this->m_basis.resize(this->row_count()); this->m_costs.clear(); @@ -253,7 +253,7 @@ template void lp_primal_simplex::solve_with_total this->m_heading, this->m_costs, this->m_column_types, - m_low_bounds, + m_lower_bounds, this->m_upper_bounds, this->m_settings, *this); m_core_solver->solve(); @@ -278,7 +278,6 @@ template bool lp_primal_simplex::bounds_hold(std: } if (!it.second->bounds_hold(sol_it->second)) { - // std::cout << "bounds do not hold for " << it.second->get_name() << std::endl; it.second->bounds_hold(sol_it->second); return false; } @@ -296,10 +295,10 @@ template T lp_primal_simplex::get_row_value(unsig T ret = numeric_traits::zero(); for (auto & pair : it->second) { auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); - SASSERT(cit != this->m_map_from_var_index_to_column_info.end()); + lp_assert(cit != this->m_map_from_var_index_to_column_info.end()); column_info * ci = cit->second; auto sol_it = solution.find(ci->get_name()); - SASSERT(sol_it != solution.end()); + lp_assert(sol_it != solution.end()); T column_val = sol_it->second; if (out != nullptr) { (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; @@ -344,7 +343,7 @@ template bool lp_primal_simplex::row_constraint_h } return true;; } - SASSERT(false); + lp_unreachable(); return false; // it is unreachable } diff --git a/src/util/lp/lp_settings_instances.cpp b/src/util/lp/lp_settings.cpp similarity index 91% rename from src/util/lp/lp_settings_instances.cpp rename to src/util/lp/lp_settings.cpp index bd5a1515f..147319ed2 100644 --- a/src/util/lp/lp_settings_instances.cpp +++ b/src/util/lp/lp_settings.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include #include "util/vector.h" -#include "util/lp/lp_settings.hpp" +#include "util/lp/lp_settings_def.h" template bool lp::vectors_are_equal(vector const&, vector const&); template bool lp::vectors_are_equal(vector const&, vector const&); diff --git a/src/util/lp/lp_settings.h b/src/util/lp/lp_settings.h index cd9e78321..b2e785064 100644 --- a/src/util/lp/lp_settings.h +++ b/src/util/lp/lp_settings.h @@ -31,13 +31,16 @@ namespace lp { typedef unsigned var_index; typedef unsigned constraint_index; typedef unsigned row_index; + +typedef vector> explanation_t; + enum class column_type { free_column = 0, - low_bound = 1, - upper_bound = 2, - boxed = 3, - fixed = 4 - }; + lower_bound = 1, + upper_bound = 2, + boxed = 3, + fixed = 4 +}; enum class simplex_strategy_enum { undecided = 3, @@ -48,7 +51,7 @@ enum class simplex_strategy_enum { std::string column_type_to_string(column_type t); -enum lp_status { +enum class lp_status { UNKNOWN, INFEASIBLE, TENTATIVE_UNBOUNDED, @@ -81,7 +84,7 @@ inline std::ostream& operator<<(std::ostream& out, lp_status status) { lp_status lp_status_from_string(std::string status); -enum non_basic_column_value_position { at_low_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound }; +enum non_basic_column_value_position { at_lower_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound }; template bool is_epsilon_small(const X & v, const double& eps); // forward definition @@ -91,11 +94,22 @@ public: }; struct stats { + unsigned m_make_feasible; unsigned m_total_iterations; unsigned m_iters_with_no_cost_growing; unsigned m_num_factorizations; unsigned m_num_of_implied_bounds; unsigned m_need_to_solve_inf; + unsigned m_max_cols; + unsigned m_max_rows; + unsigned m_gcd_calls; + unsigned m_gcd_conflicts; + unsigned m_cube_calls; + unsigned m_cube_success; + unsigned m_patches; + unsigned m_patches_success; + unsigned m_hnf_cutter_calls; + unsigned m_hnf_cuts; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; @@ -115,53 +129,76 @@ private: }; default_lp_resource_limit m_default_resource_limit; - lp_resource_limit* m_resource_limit; + lp_resource_limit* m_resource_limit; // used for debug output - std::ostream* m_debug_out; + std::ostream* m_debug_out; // used for messages, for example, the computation progress messages - std::ostream* m_message_out; + std::ostream* m_message_out; - stats m_stats; - random_gen m_rand; + stats m_stats; + random_gen m_rand; public: - unsigned reps_in_scaler; + unsigned reps_in_scaler; // when the absolute value of an element is less than pivot_epsilon // in pivoting, we treat it as a zero - double pivot_epsilon; + double pivot_epsilon; // see Chatal, page 115 - double positive_price_epsilon; + double positive_price_epsilon; // a quatation "if some choice of the entering vairable leads to an eta matrix // whose diagonal element in the eta column is less than e2 (entering_diag_epsilon) in magnitude, the this choice is rejected ... - double entering_diag_epsilon; - int c_partial_pivoting; // this is the constant c from page 410 - unsigned depth_of_rook_search; - bool using_partial_pivoting; + double entering_diag_epsilon; + int c_partial_pivoting; // this is the constant c from page 410 + unsigned depth_of_rook_search; + bool using_partial_pivoting; // dissertation of Achim Koberstein // if Bx - b is different at any component more that refactor_epsilon then we refactor - double refactor_tolerance; - double pivot_tolerance; - double zero_tolerance; - double drop_tolerance; - double tolerance_for_artificials; - double can_be_taken_to_basis_tolerance; + double refactor_tolerance; + double pivot_tolerance; + double zero_tolerance; + double drop_tolerance; + double tolerance_for_artificials; + double can_be_taken_to_basis_tolerance; - unsigned percent_of_entering_to_check; // we try to find a profitable column in a percentage of the columns - bool use_scaling; - double scaling_maximum; - double scaling_minimum; - double harris_feasibility_tolerance; // page 179 of Istvan Maros - double ignore_epsilon_of_harris; - unsigned max_number_of_iterations_with_no_improvements; - unsigned max_total_number_of_iterations; - double time_limit; // the maximum time limit of the total run time in seconds + unsigned percent_of_entering_to_check; // we try to find a profitable column in a percentage of the columns + bool use_scaling; + double scaling_maximum; + double scaling_minimum; + double harris_feasibility_tolerance; // page 179 of Istvan Maros + double ignore_epsilon_of_harris; + unsigned max_number_of_iterations_with_no_improvements; + unsigned max_total_number_of_iterations; + double time_limit; // the maximum time limit of the total run time in seconds // dual section - double dual_feasibility_tolerance; // // page 71 of the PhD thesis of Achim Koberstein - double primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein - double relative_primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein - - bool m_bound_propagation; + double dual_feasibility_tolerance; // // page 71 of the PhD thesis of Achim Koberstein + double primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein + double relative_primal_feasibility_tolerance; // page 71 of the PhD thesis of Achim Koberstein + // end of dual section + bool m_bound_propagation; + bool presolve_with_double_solver_for_lar; + simplex_strategy_enum m_simplex_strategy; + int report_frequency; + bool print_statistics; + unsigned column_norms_update_frequency; + bool scale_with_ratio; + double density_threshold; + bool use_breakpoints_in_feasibility_search; + unsigned max_row_length_for_bound_propagation; + bool backup_costs; + unsigned column_number_threshold_for_using_lu_in_lar_solver; + unsigned m_int_gomory_cut_period; + unsigned m_int_find_cube_period; + unsigned m_hnf_cut_period; + bool m_int_run_gcd_test; + bool m_int_pivot_fixed_vars_from_basis; + bool m_int_patch_only_integer_values; + unsigned limit_on_rows_for_hnf_cutter; + unsigned limit_on_columns_for_hnf_cutter; + + unsigned random_next() { return m_rand(); } + void set_random_seed(unsigned s) { m_rand.set_seed(s); } + bool bound_progation() const { return m_bound_propagation; } @@ -172,15 +209,15 @@ public: lp_settings() : m_default_resource_limit(*this), m_resource_limit(&m_default_resource_limit), - m_debug_out( &std::cout), + m_debug_out(&std::cout), m_message_out(&std::cout), reps_in_scaler(20), pivot_epsilon(0.00000001), positive_price_epsilon(1e-7), - entering_diag_epsilon ( 1e-8), - c_partial_pivoting ( 10), // this is the constant c from page 410 - depth_of_rook_search ( 4), - using_partial_pivoting ( true), + entering_diag_epsilon (1e-8), + c_partial_pivoting (10), // this is the constant c from page 410 + depth_of_rook_search (4), + using_partial_pivoting (true), // dissertation of Achim Koberstein // if Bx - b is different at any component more that refactor_epsilon then we refactor refactor_tolerance ( 1e-4), @@ -189,7 +226,6 @@ public: drop_tolerance ( 1e-14), tolerance_for_artificials ( 1e-4), can_be_taken_to_basis_tolerance ( 0.00001), - percent_of_entering_to_check ( 5),// we try to find a profitable column in a percentage of the columns use_scaling ( true), scaling_maximum ( 1), @@ -214,7 +250,15 @@ public: use_breakpoints_in_feasibility_search(false), max_row_length_for_bound_propagation(300), backup_costs(true), - column_number_threshold_for_using_lu_in_lar_solver(4000) + column_number_threshold_for_using_lu_in_lar_solver(4000), + m_int_gomory_cut_period(4), + m_int_find_cube_period(4), + m_hnf_cut_period(4), + m_int_run_gcd_test(true), + m_int_pivot_fixed_vars_from_basis(false), + m_int_patch_only_integer_values(true), + limit_on_rows_for_hnf_cutter(75), + limit_on_columns_for_hnf_cutter(150) {} void set_resource_limit(lp_resource_limit& lim) { m_resource_limit = &lim; } @@ -284,8 +328,6 @@ public: return is_eps_small_general(t, tolerance_for_artificials); } // the method of lar solver to use - bool presolve_with_double_solver_for_lar; - simplex_strategy_enum m_simplex_strategy; simplex_strategy_enum simplex_strategy() const { return m_simplex_strategy; } @@ -307,20 +349,9 @@ public: return m_simplex_strategy == simplex_strategy_enum::tableau_rows; } - int report_frequency; - bool print_statistics; - unsigned column_norms_update_frequency; - bool scale_with_ratio; - double density_threshold; // need to tune it up, todo #ifdef Z3DEBUG static unsigned ddd; // used for debugging #endif - bool use_breakpoints_in_feasibility_search; - unsigned random_next() { return m_rand(); } - void random_seed(unsigned s) { m_rand.set_seed(s); } - unsigned max_row_length_for_bound_propagation; - bool backup_costs; - unsigned column_number_threshold_for_using_lu_in_lar_solver; }; // end of lp_settings class @@ -343,7 +374,7 @@ inline std::string T_to_string(const numeric_pair & t) { inline std::string T_to_string(const mpq & t) { std::ostringstream strs; - strs << t.get_double(); + strs << t; return strs.str(); } @@ -382,7 +413,7 @@ inline void print_blanks(int n, std::ostream & out) { // after a push of the last element we ensure that the vector increases // we also suppose that before the last push the vector was increasing inline void ensure_increasing(vector & v) { - SASSERT(v.size() > 0); + lp_assert(v.size() > 0); unsigned j = v.size() - 1; for (; j > 0; j-- ) if (v[j] <= v[j - 1]) { diff --git a/src/util/lp/lp_settings.hpp b/src/util/lp/lp_settings_def.h similarity index 71% rename from src/util/lp/lp_settings.hpp rename to src/util/lp/lp_settings_def.h index 659d47c62..02963718e 100644 --- a/src/util/lp/lp_settings.hpp +++ b/src/util/lp/lp_settings_def.h @@ -26,30 +26,30 @@ std::string column_type_to_string(column_type t) { switch (t) { case column_type::fixed: return "fixed"; case column_type::boxed: return "boxed"; - case column_type::low_bound: return "low_bound"; + case column_type::lower_bound: return "lower_bound"; case column_type::upper_bound: return "upper_bound"; case column_type::free_column: return "free_column"; - default: SASSERT(false); + default: lp_unreachable(); } return "unknown"; // it is unreachable } const char* lp_status_to_string(lp_status status) { switch (status) { - case UNKNOWN: return "UNKNOWN"; - case INFEASIBLE: return "INFEASIBLE"; - case UNBOUNDED: return "UNBOUNDED"; - case TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED"; - case DUAL_UNBOUNDED: return "DUAL_UNBOUNDED"; - case OPTIMAL: return "OPTIMAL"; - case FEASIBLE: return "FEASIBLE"; - case FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR"; - case TIME_EXHAUSTED: return "TIME_EXHAUSTED"; - case ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED"; - case EMPTY: return "EMPTY"; - case UNSTABLE: return "UNSTABLE"; + case lp_status::UNKNOWN: return "UNKNOWN"; + case lp_status::INFEASIBLE: return "INFEASIBLE"; + case lp_status::UNBOUNDED: return "UNBOUNDED"; + case lp_status::TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED"; + case lp_status::DUAL_UNBOUNDED: return "DUAL_UNBOUNDED"; + case lp_status::OPTIMAL: return "OPTIMAL"; + case lp_status::FEASIBLE: return "FEASIBLE"; + case lp_status::FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR"; + case lp_status::TIME_EXHAUSTED: return "TIME_EXHAUSTED"; + case lp_status::ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED"; + case lp_status::EMPTY: return "EMPTY"; + case lp_status::UNSTABLE: return "UNSTABLE"; default: - SASSERT(false); + lp_unreachable(); } return "UNKNOWN"; // it is unreachable } @@ -64,7 +64,7 @@ lp_status lp_status_from_string(std::string status) { if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED; if (status == "ITERATIONS_EXHAUSTED") return lp_status::ITERATIONS_EXHAUSTED; if (status == "EMPTY") return lp_status::EMPTY; - SASSERT(false); + lp_unreachable(); return lp_status::UNKNOWN; // it is unreachable } @@ -74,14 +74,12 @@ bool vectors_are_equal(T * a, vector &b, unsigned n) { if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { - // std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl; return false; } } } else { for (unsigned i = 0; i < n; i ++){ if (std::abs(numeric_traits::get_double(a[i] - b[i])) > 0.000001) { - // std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl; return false; } } @@ -97,7 +95,6 @@ bool vectors_are_equal(const vector & a, const vector &b) { if (numeric_traits::precise()) { for (unsigned i = 0; i < n; i ++){ if (!numeric_traits::is_zero(a[i] - b[i])) { - // std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl; return false; } } @@ -112,7 +109,6 @@ bool vectors_are_equal(const vector & a, const vector &b) { } if (fabs(da - db) > 0.000001) { - // std::cout << "a[" << i <<"] = " << a[i] << ", but " << "b[" << i <<"] = " << b[i] << std::endl; return false; } } diff --git a/src/util/lp/lp_solver_instances.cpp b/src/util/lp/lp_solver.cpp similarity index 93% rename from src/util/lp/lp_solver_instances.cpp rename to src/util/lp/lp_solver.cpp index d7caf9a18..ea88dd14c 100644 --- a/src/util/lp/lp_solver_instances.cpp +++ b/src/util/lp/lp_solver.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include -#include "util/lp/lp_solver.hpp" +#include "util/lp/lp_solver_def.h" template void lp::lp_solver::add_constraint(lp::lp_relation, double, unsigned int); template void lp::lp_solver::cleanup(); template void lp::lp_solver::count_slacks_and_artificials(); @@ -34,7 +34,6 @@ template void lp::lp_solver::print_statistics_on_A(std::ostream template bool lp::lp_solver::problem_is_empty(); template void lp::lp_solver::scale(); template void lp::lp_solver::set_scaled_cost(unsigned int); -template std::string lp::lp_solver::get_column_name(unsigned int) const; template lp::lp_solver::~lp_solver(); template void lp::lp_solver::add_constraint(lp::lp_relation, lp::mpq, unsigned int); template void lp::lp_solver::cleanup(); @@ -54,4 +53,3 @@ template void lp::lp_solver::scale(); template void lp::lp_solver::set_scaled_cost(unsigned int); template lp::lp_solver::~lp_solver(); template double lp::lp_solver::get_column_value_by_name(std::string) const; -template std::string lp::lp_solver::get_column_name(unsigned int) const; diff --git a/src/util/lp/lp_solver.h b/src/util/lp/lp_solver.h index c447b1870..f1fa15e00 100644 --- a/src/util/lp/lp_solver.h +++ b/src/util/lp/lp_solver.h @@ -28,7 +28,6 @@ Revision History: #include "util/lp/static_matrix.h" #include "util/lp/lp_core_solver_base.h" #include "util/lp/scaler.h" -#include "util/lp/linear_combination_iterator.h" #include "util/lp/bound_analyzer_on_row.h" namespace lp { enum lp_relation { @@ -114,9 +113,9 @@ public: // returns -1 if not found virtual int get_column_index_by_name(std::string name) const; - void set_low_bound(unsigned i, T bound) { + void set_lower_bound(unsigned i, T bound) { column_info *ci = get_or_create_column_info(i); - ci->set_low_bound(bound); + ci->set_lower_bound(bound); } void set_upper_bound(unsigned i, T bound) { @@ -124,8 +123,8 @@ public: ci->set_upper_bound(bound); } - void unset_low_bound(unsigned i) { - get_or_create_column_info(i)->unset_low_bound(); + void unset_lower_bound(unsigned i) { + get_or_create_column_info(i)->unset_lower_bound(); } void unset_upper_bound(unsigned i) { @@ -194,9 +193,9 @@ protected: void pin_vars_on_row_with_sign(std::unordered_map & row, T sign ); - bool get_minimal_row_value(std::unordered_map & row, T & low_bound); + bool get_minimal_row_value(std::unordered_map & row, T & lower_bound); - bool get_maximal_row_value(std::unordered_map & row, T & low_bound); + bool get_maximal_row_value(std::unordered_map & row, T & lower_bound); bool row_is_zero(std::unordered_map & row); @@ -244,7 +243,7 @@ protected: void count_slacks_and_artificials_for_row(unsigned i); - T low_bound_shift_for_row(unsigned i); + T lower_bound_shift_for_row(unsigned i); void fill_m_b(); diff --git a/src/util/lp/lp_solver.hpp b/src/util/lp/lp_solver_def.h similarity index 87% rename from src/util/lp/lp_solver.hpp rename to src/util/lp/lp_solver_def.h index 3bc83b316..10c7a6feb 100644 --- a/src/util/lp/lp_solver.hpp +++ b/src/util/lp/lp_solver_def.h @@ -47,7 +47,7 @@ template T lp_solver::get_column_cost_value(unsig return ci->get_cost() * get_column_value(j); } template void lp_solver::add_constraint(lp_relation relation, T right_side, unsigned row_index) { - SASSERT(m_constraints.find(row_index) == m_constraints.end()); + lp_assert(m_constraints.find(row_index) == m_constraints.end()); lp_constraint cs(right_side, relation); m_constraints[row_index] = cs; } @@ -173,29 +173,29 @@ template void lp_solver::pin_vars_on_row_with_sig column_info * ci = m_map_from_var_index_to_column_info[j]; T a = t.second; if (a * sign > numeric_traits::zero()) { - SASSERT(ci->upper_bound_is_set()); + lp_assert(ci->upper_bound_is_set()); ci->set_fixed_value(ci->get_upper_bound()); } else { - SASSERT(ci->low_bound_is_set()); - ci->set_fixed_value(ci->get_low_bound()); + lp_assert(ci->lower_bound_is_set()); + ci->set_fixed_value(ci->get_lower_bound()); } } } -template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & low_bound) { - low_bound = numeric_traits::zero(); +template bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & lower_bound) { + lower_bound = numeric_traits::zero(); for (auto & t : row) { T a = t.second; column_info * ci = m_map_from_var_index_to_column_info[t.first]; if (a > numeric_traits::zero()) { - if (ci->low_bound_is_set()) { - low_bound += ci->get_low_bound() * a; + if (ci->lower_bound_is_set()) { + lower_bound += ci->get_lower_bound() * a; } else { return false; } } else { if (ci->upper_bound_is_set()) { - low_bound += ci->get_upper_bound() * a; + lower_bound += ci->get_upper_bound() * a; } else { return false; } @@ -204,20 +204,20 @@ template bool lp_solver::get_minimal_row_value return true; } -template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & low_bound) { - low_bound = numeric_traits::zero(); +template bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & lower_bound) { + lower_bound = numeric_traits::zero(); for (auto & t : row) { T a = t.second; column_info * ci = m_map_from_var_index_to_column_info[t.first]; if (a < numeric_traits::zero()) { - if (ci->low_bound_is_set()) { - low_bound += ci->get_low_bound() * a; + if (ci->lower_bound_is_set()) { + lower_bound += ci->get_lower_bound() * a; } else { return false; } } else { if (ci->upper_bound_is_set()) { - low_bound += ci->get_upper_bound() * a; + lower_bound += ci->get_upper_bound() * a; } else { return false; } @@ -238,17 +238,17 @@ template bool lp_solver::row_e_is_obsolete(std T rs = m_constraints[row_index].m_rs; if (row_is_zero(row)) { if (!is_zero(rs)) - m_status = INFEASIBLE; + m_status = lp_status::INFEASIBLE; return true; } - T low_bound; - bool lb = get_minimal_row_value(row, low_bound); + T lower_bound; + bool lb = get_minimal_row_value(row, lower_bound); if (lb) { - T diff = low_bound - rs; + T diff = lower_bound - rs; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // low_bound > rs + m_settings.refactor_epsilon - m_status = INFEASIBLE; + // lower_bound > rs + m_settings.refactor_epsilon + m_status = lp_status::INFEASIBLE; return true; } if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ @@ -263,7 +263,7 @@ template bool lp_solver::row_e_is_obsolete(std T diff = rs - upper_bound; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { // upper_bound < rs - m_settings.refactor_tolerance - m_status = INFEASIBLE; + m_status = lp_status::INFEASIBLE; return true; } if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ @@ -279,7 +279,7 @@ template bool lp_solver::row_ge_is_obsolete(std: T rs = m_constraints[row_index].m_rs; if (row_is_zero(row)) { if (rs > zero_of_type()) - m_status = INFEASIBLE; + m_status = lp_status::INFEASIBLE; return true; } @@ -288,7 +288,7 @@ template bool lp_solver::row_ge_is_obsolete(std: T diff = rs - upper_bound; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { // upper_bound < rs - m_settings.refactor_tolerance - m_status = INFEASIBLE; + m_status = lp_status::INFEASIBLE; return true; } if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ @@ -301,18 +301,18 @@ template bool lp_solver::row_ge_is_obsolete(std: } template bool lp_solver::row_le_is_obsolete(std::unordered_map & row, unsigned row_index) { - T low_bound; + T lower_bound; T rs = m_constraints[row_index].m_rs; if (row_is_zero(row)) { if (rs < zero_of_type()) - m_status = INFEASIBLE; + m_status = lp_status::INFEASIBLE; return true; } - if (get_minimal_row_value(row, low_bound)) { - T diff = low_bound - rs; + if (get_minimal_row_value(row, lower_bound)) { + T diff = lower_bound - rs; if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ - // low_bound > rs + m_settings.refactor_tolerance + // lower_bound > rs + m_settings.refactor_tolerance m_status = lp_status::INFEASIBLE; return true; } @@ -343,7 +343,7 @@ template bool lp_solver::row_is_obsolete(std:: case lp_relation::Less_or_equal: return row_le_is_obsolete(row, row_index); } - SASSERT(false); + lp_unreachable(); return false; // it is unreachable } @@ -358,7 +358,7 @@ template void lp_solver::remove_fixed_or_zero_col vector removed; for (auto & col : row) { unsigned j = col.first; - SASSERT(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); + lp_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); column_info * ci = m_map_from_var_index_to_column_info[j]; if (ci->is_fixed()) { removed.push_back(j); @@ -427,7 +427,7 @@ template void lp_solver::map_external_columns_to_ } unsigned j = col.first; auto column_info_it = m_map_from_var_index_to_column_info.find(j); - SASSERT(column_info_it != m_map_from_var_index_to_column_info.end()); + lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); auto j_column = column_info_it->second->get_column_index(); if (!is_valid(j_column)) { // j is a newcomer @@ -450,14 +450,14 @@ template void lp_solver::fill_A_from_A_values() { m_A = new static_matrix(static_cast(m_A_values.size()), number_of_core_structurals()); for (auto & t : m_A_values) { auto row_it = m_external_rows_to_core_solver_rows.find(t.first); - SASSERT(row_it != m_external_rows_to_core_solver_rows.end()); + lp_assert(row_it != m_external_rows_to_core_solver_rows.end()); unsigned row = row_it->second; for (auto k : t.second) { auto column_info_it = m_map_from_var_index_to_column_info.find(k.first); - SASSERT(column_info_it != m_map_from_var_index_to_column_info.end()); + lp_assert(column_info_it != m_map_from_var_index_to_column_info.end()); column_info *ci = column_info_it->second; unsigned col = ci->get_column_index(); - SASSERT(is_valid(col)); + lp_assert(is_valid(col)); bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped(); if (!col_is_flipped) { (*m_A)(row, col) = k.second; @@ -471,7 +471,7 @@ template void lp_solver::fill_A_from_A_values() { template void lp_solver::fill_matrix_A_and_init_right_side() { map_external_rows_to_core_solver_rows(); map_external_columns_to_core_solver_columns(); - SASSERT(m_A == nullptr); + lp_assert(m_A == nullptr); fill_A_from_A_values(); m_b.resize(m_A->row_count()); } @@ -483,7 +483,7 @@ template void lp_solver::count_slacks_and_artific } template void lp_solver::count_slacks_and_artificials_for_row(unsigned i) { - SASSERT(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); + lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]]; switch (constraint.m_relation) { case Equal: @@ -504,7 +504,7 @@ template void lp_solver::count_slacks_and_artific } } -template T lp_solver::low_bound_shift_for_row(unsigned i) { +template T lp_solver::lower_bound_shift_for_row(unsigned i) { T ret = numeric_traits::zero(); auto row = this->m_A_values.find(i); @@ -519,10 +519,10 @@ template T lp_solver::low_bound_shift_for_row( template void lp_solver::fill_m_b() { for (int i = this->row_count() - 1; i >= 0; i--) { - SASSERT(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); + lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); unsigned external_i = this->m_core_solver_rows_to_external_rows[i]; auto & constraint = this->m_constraints[external_i]; - this->m_b[i] = constraint.m_rs - low_bound_shift_for_row(external_i); + this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i); } } @@ -545,7 +545,7 @@ template T lp_solver::get_column_value_with_core_ return v; } if (!ci->is_flipped()) { - return v + ci->get_low_bound(); + return v + ci->get_lower_bound(); } // the flipped case when there is only upper bound @@ -557,13 +557,13 @@ template T lp_solver::get_column_value_with_core_ template void lp_solver::set_scaled_cost(unsigned j) { // grab original costs but modify it with the column scales - SASSERT(j < this->m_column_scale.size()); + lp_assert(j < this->m_column_scale.size()); column_info * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; T cost = ci->get_cost(); if (ci->is_flipped()){ cost *= -1; } - SASSERT(ci->is_fixed() == false); + lp_assert(ci->is_fixed() == false); this->m_costs[j] = cost * this->m_column_scale[j]; } } diff --git a/src/util/lp/lp_utils.cpp b/src/util/lp/lp_utils.cpp index 46a82e9ec..98ab022d1 100644 --- a/src/util/lp/lp_utils.cpp +++ b/src/util/lp/lp_utils.cpp @@ -18,9 +18,10 @@ Revision History: --*/ #include "util/lp/lp_utils.h" - +#ifdef lp_for_z3 namespace lp { double numeric_traits::g_zero = 0.0; double numeric_traits::g_one = 1.0; } +#endif diff --git a/src/util/lp/lp_utils.h b/src/util/lp/lp_utils.h index fce9f4d02..e776092a2 100644 --- a/src/util/lp/lp_utils.h +++ b/src/util/lp/lp_utils.h @@ -22,8 +22,15 @@ Revision History: #include "util/lp/numeric_pair.h" #include "util/debug.h" #include +template +void print_vector(const C & t, std::ostream & out) { + for (const auto & p : t) + out << p << " "; + out << std::endl; +} + template -bool try_get_val(const std::unordered_map & map, const A& key, B & val) { +bool try_get_value(const std::unordered_map & map, const A& key, B & val) { const auto it = map.find(key); if (it == map.end()) return false; val = it->second; @@ -35,19 +42,34 @@ bool contains(const std::unordered_map & map, const A& key) { return map.find(key) != map.end(); } +#ifdef lp_for_z3 + +#ifdef Z3DEBUG +#define Z3DEBUG 1 +#endif + namespace lp { - inline void throw_exception(const std::string & str) { - throw default_exception(str); + + + inline void throw_exception(std::string && str) { + throw default_exception(std::move(str)); } typedef z3_exception exception; - template inline X zero_of_type() { return numeric_traits::zero(); } - template inline X one_of_type() { return numeric_traits::one(); } - template inline bool is_zero(const X & v) { return numeric_traits::is_zero(v); } - template inline bool is_pos(const X & v) { return numeric_traits::is_pos(v); } - template inline bool is_neg(const X & v) { return numeric_traits::is_neg(v); } +#define lp_assert(_x_) { SASSERT(_x_); } +inline void lp_unreachable() { lp_assert(false); } +template inline X zero_of_type() { return numeric_traits::zero(); } +template inline X one_of_type() { return numeric_traits::one(); } +template inline bool is_zero(const X & v) { return numeric_traits::is_zero(v); } +template inline bool is_pos(const X & v) { return numeric_traits::is_pos(v); } +template inline bool is_neg(const X & v) { return numeric_traits::is_neg(v); } +template inline bool is_int(const X & v) { return numeric_traits::is_int(v); } - template inline bool precise() { return numeric_traits::precise(); } +template inline X ceil_ratio(const X & a, const X & b) { return numeric_traits::ceil_ratio(a, b); } +template inline X floor_ratio(const X & a, const X & b) { return numeric_traits::floor_ratio(a, b); } + + +template inline bool precise() { return numeric_traits::precise(); } } namespace std { template<> @@ -84,3 +106,70 @@ struct hash> { }; } +#else // else of #if lp_for_z3 +#include +#include +//include "util/numerics/mpq.h" +//include "util/numerics/numeric_traits.h" +//include "util/numerics/double.h" + +#ifdef __CLANG__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +namespace std { +template<> +struct hash { + inline size_t operator()(const lp::mpq & v) const { + return v.hash(); + } +}; +} +namespace lp { +template inline bool precise() { return numeric_traits::precise();} +template inline X one_of_type() { return numeric_traits::one(); } +template inline bool is_zero(const X & v) { return numeric_traits::is_zero(v); } +template inline bool is_pos(const X & v) { return numeric_traits::is_pos(v); } +template inline bool is_int(const X & v) { return numeric_traits::is_int(v); } +template inline X ceil_ratio(const X & a, const X & b) { return numeric_traits::ceil_ratio(a, b); } +template inline X floor_ratio(const X & a, const X & b) { return numeric_traits::floor_ratio(v); } + + +template inline double get_double(const X & v) { return numeric_traits::get_double(v); } +template inline T zero_of_type() {return numeric_traits::zero();} +inline void throw_exception(std::string str) { throw exception(str); } +template inline T from_string(std::string const & ) { lp_unreachable();} +template <> double inline from_string(std::string const & str) { return atof(str.c_str());} +template <> mpq inline from_string(std::string const & str) { + return mpq(atof(str.c_str())); +} + +} // closing lp +template +inline void hash_combine(std::size_t & seed, const T & v) { + seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +namespace std { +template struct hash> { + inline size_t operator()(const pair & v) const { + size_t seed = 0; + hash_combine(seed, v.first); + hash_combine(seed, v.second); + return seed; + } +}; +template<> +struct hash> { + inline size_t operator()(const lp::numeric_pair & v) const { + size_t seed = 0; + hash_combine(seed, v.x); + hash_combine(seed, v.y); + return seed; + } +}; +} // std +#ifdef __CLANG__ +#pragma clang diagnostic pop +#endif +#endif diff --git a/src/util/lp/lu.cpp b/src/util/lp/lu.cpp new file mode 100644 index 000000000..e6df10908 --- /dev/null +++ b/src/util/lp/lu.cpp @@ -0,0 +1,84 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include +#include +#include +#include "util/vector.h" +#include "util/debug.h" +#include "util/lp/lu_def.h" +namespace lp { +template double dot_product(vector const&, vector const&); +template lu>::lu(static_matrix const&, vector&, lp_settings&); +template void lu>::push_matrix_to_tail(tail_matrix*); +template void lu>::replace_column(double, indexed_vector&, unsigned); +template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); +template lu>::~lu(); +template void lu>::push_matrix_to_tail(tail_matrix*); +template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); +template lu>::~lu(); +template void lu>::push_matrix_to_tail(tail_matrix*); +template void lu>::solve_Bd(unsigned int, indexed_vector&, indexed_vector&); +template lu>::~lu(); +template mpq dot_product(vector const&, vector const&); +template void init_factorization> + (lu>*&, static_matrix&, vector&, lp_settings&); +template void init_factorization> + (lu>*&, static_matrix&, vector&, lp_settings&); +template void init_factorization>(lu >*&, static_matrix&, vector&, lp_settings&); +#ifdef Z3DEBUG +template void print_matrix>(square_sparse_matrix&, std::ostream & out); +template void print_matrix>(static_matrix&, std::ostream&); +template void print_matrix >(static_matrix&, std::ostream&); +template void print_matrix>(static_matrix&, std::ostream & out); +template bool lu>::is_correct(const vector& basis); +template bool lu>::is_correct( vector const &); +template dense_matrix get_B>(lu>&, const vector& basis); +template dense_matrix get_B>(lu>&, vector const&); + +#endif + +template bool lu>::pivot_the_row(int); // NOLINT +template void lu>::init_vector_w(unsigned int, indexed_vector&); +template void lu>::solve_By(vector&); +template void lu>::solve_By_when_y_is_ready_for_X(vector&); +template void lu>::solve_yB_with_error_check(vector&, const vector& basis); +template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector const&, const vector & basis, const lp_settings&); +template void lu>::replace_column(mpq, indexed_vector&, unsigned); +template void lu>::solve_By(vector&); +template void lu>::solve_By_when_y_is_ready_for_X(vector&); +template void lu>::solve_yB_with_error_check(vector&, const vector& basis); +template void lu>::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); +template void lu >::solve_yB_with_error_check_indexed(indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); +template void lu >::init_vector_w(unsigned int, indexed_vector&); +template void lu >::replace_column(mpq, indexed_vector&, unsigned); +template void lu >::solve_Bd_faster(unsigned int, indexed_vector&); +template void lu >::solve_By(vector&); +template void lu >::solve_By_when_y_is_ready_for_X(vector&); +template void lu >::solve_yB_with_error_check(vector&, const vector& basis); +template void lu>::solve_By(indexed_vector&); +template void lu>::solve_By(indexed_vector&); +template void lu>::solve_yB_indexed(indexed_vector&); +template void lu >::solve_yB_indexed(indexed_vector&); +template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); +template void lu>::solve_By_for_T_indexed_only(indexed_vector&, lp_settings const&); +#ifdef Z3DEBUG +template void print_matrix>(tail_matrix&, std::ostream&); +#endif +} diff --git a/src/util/lp/lu.h b/src/util/lp/lu.h index f2cae7961..820786d87 100644 --- a/src/util/lp/lu.h +++ b/src/util/lp/lu.h @@ -8,7 +8,12 @@ Module Name: Abstract: - + for matrix B we have + t0*...*tn-1*B = Q*U*R + here ti are matrices corresponding to pivot operations, + including columns and rows swaps, + or a multiplication matrix row by a number + Q, R - permutations and U is an upper triangular matrix Author: Lev Nachmanson (levnach) @@ -24,7 +29,7 @@ Revision History: #include "util/debug.h" #include #include -#include "util/lp/sparse_matrix.h" +#include "util/lp/square_sparse_matrix.h" #include "util/lp/static_matrix.h" #include #include "util/lp/numeric_pair.h" @@ -36,18 +41,16 @@ Revision History: namespace lp { #ifdef Z3DEBUG template // print the nr x nc submatrix at the top left corner -void print_submatrix(sparse_matrix & m, unsigned mr, unsigned nc); +void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc); -template -void print_matrix(static_matrix &m, std::ostream & out); +template +void print_matrix(M &m, std::ostream & out); -template -void print_matrix(sparse_matrix& m, std::ostream & out); #endif template X dot_product(const vector & a, const vector & b) { - SASSERT(a.size() == b.size()); + lp_assert(a.size() == b.size()); auto r = zero_of_type(); for (unsigned i = 0; i < a.size(); i++) { r += a[i] * b[i]; @@ -114,7 +117,7 @@ public: m_i = p.apply_reverse(m_i); #ifdef Z3DEBUG - // SASSERT(*this == deb); + // lp_assert(*this == deb); #endif } }; // end of one_elem_on_diag @@ -123,17 +126,20 @@ enum class LU_status { OK, Degenerated}; // This class supports updates of the columns of B, and solves systems Bx=b,and yB=c // Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 -template +template class lu { LU_status m_status; public: + typedef typename M::coefftype T; + typedef typename M::argtype X; + // the fields unsigned m_dim; - static_matrix const &m_A; + const M & m_A; permutation_matrix m_Q; permutation_matrix m_R; permutation_matrix m_r_wave; - sparse_matrix m_U; + square_sparse_matrix m_U; square_dense_submatrix* m_dense_LU; vector *> m_tail; @@ -147,10 +153,11 @@ public: // constructor // if A is an m by n matrix then basis has length m and values in [0,n); the values are all different // they represent the set of m columns - lu(static_matrix const & A, + lu(const M & A, vector& basis, lp_settings & settings); - void debug_test_of_basis(static_matrix const & A, vector & basis); + lu(const M & A, lp_settings&); + void debug_test_of_basis(const M & A, vector & basis); void solve_Bd_when_w_is_ready(vector & d, indexed_vector& w ); void solve_By(indexed_vector & y); @@ -222,7 +229,7 @@ public: eta_matrix * get_eta_matrix_for_pivot(unsigned j); // we're processing the column j now - eta_matrix * get_eta_matrix_for_pivot(unsigned j, sparse_matrix& copy_of_U); + eta_matrix * get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U); // see page 407 of Chvatal unsigned transform_U_to_V_by_replacing_column(indexed_vector & w, unsigned leaving_column_of_U); @@ -245,7 +252,7 @@ public: } T B_(unsigned i, unsigned j, const vector& basis) { - return m_A.get_elem(i, basis[j]); + return m_A[i][basis[j]]; } unsigned dimension() { return m_dim; } @@ -261,11 +268,13 @@ public: void process_column(int j); bool is_correct(const vector& basis); + bool is_correct(); #ifdef Z3DEBUG dense_matrix tail_product(); dense_matrix get_left_side(const vector& basis); + dense_matrix get_left_side(); dense_matrix get_right_side(); #endif @@ -306,7 +315,7 @@ public: bool need_to_refactor() { return m_refactor_counter >= 200; } void adjust_dimension_with_matrix_A() { - SASSERT(m_A.row_count() >= m_dim); + lp_assert(m_A.row_count() >= m_dim); m_dim = m_A.row_count(); m_U.resize(m_dim); m_Q.resize(m_dim); @@ -320,7 +329,7 @@ public: unsigned m = m_A.row_count(); unsigned m_prev = m_U.dimension(); - SASSERT(m_A.column_count() == heading.size()); + lp_assert(m_A.column_count() == heading.size()); for (unsigned i = m_prev; i < m; i++) { for (const row_cell & c : m_A.m_rows[i]) { @@ -336,14 +345,14 @@ public: void add_last_rows_to_B(const vector & heading, const std::unordered_set & columns_to_replace) { unsigned m = m_A.row_count(); - SASSERT(m_A.column_count() == heading.size()); + lp_assert(m_A.column_count() == heading.size()); adjust_dimension_with_matrix_A(); m_w_for_extension.resize(m); // At this moment the LU is correct // for B extended by only by ones at the diagonal in the lower right corner for (unsigned j :columns_to_replace) { - SASSERT(heading[j] >= 0); + lp_assert(heading[j] >= 0); replace_column_with_only_change_at_last_rows(j, heading[j]); if (get_status() == LU_status::Degenerated) break; @@ -364,11 +373,14 @@ public: }; // end of lu -template -void init_factorization(lu* & factorization, static_matrix & m_A, vector & m_basis, lp_settings &m_settings); +template +void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings); #ifdef Z3DEBUG -template -dense_matrix get_B(lu& f, const vector& basis); +template +dense_matrix get_B(lu& f, const vector& basis); + +template +dense_matrix get_B(lu& f); #endif } diff --git a/src/util/lp/lu.hpp b/src/util/lp/lu_def.h similarity index 69% rename from src/util/lp/lu.hpp rename to src/util/lp/lu_def.h index 9d1532ac9..be4cd724d 100644 --- a/src/util/lp/lu.hpp +++ b/src/util/lp/lu_def.h @@ -26,8 +26,8 @@ Revision History: #include "util/lp/lu.h" namespace lp { #ifdef Z3DEBUG -template // print the nr x nc submatrix at the top left corner -void print_submatrix(sparse_matrix & m, unsigned mr, unsigned nc, std::ostream & out) { +template // print the nr x nc submatrix at the top left corner +void print_submatrix(square_sparse_matrix & m, unsigned mr, unsigned nc, std::ostream & out) { vector> A; vector widths; for (unsigned i = 0; i < m.row_count() && i < mr ; i++) { @@ -44,15 +44,14 @@ void print_submatrix(sparse_matrix & m, unsigned mr, unsigned nc, std::ost print_matrix_with_widths(A, widths, out); } -template -void print_matrix(static_matrix &m, std::ostream & out) { +template +void print_matrix(M &m, std::ostream & out) { vector> A; vector widths; - std::set> domain = m.get_domain(); for (unsigned i = 0; i < m.row_count(); i++) { A.push_back(vector()); for (unsigned j = 0; j < m.column_count(); j++) { - A[i].push_back(T_to_string(static_cast(m(i, j)))); + A[i].push_back(T_to_string(m[i][j])); } } @@ -63,23 +62,6 @@ void print_matrix(static_matrix &m, std::ostream & out) { print_matrix_with_widths(A, widths, out); } -template -void print_matrix(sparse_matrix& m, std::ostream & out) { - vector> A; - vector widths; - for (unsigned i = 0; i < m.row_count(); i++) { - A.push_back(vector()); - for (unsigned j = 0; j < m.column_count(); j++) { - A[i].push_back(T_to_string(static_cast(m(i, j)))); - } - } - - for (unsigned j = 0; j < m.column_count(); j++) { - widths.push_back(get_width_of_column(j, A)); - } - - print_matrix_with_widths(A, widths, out); -} #endif @@ -122,10 +104,10 @@ void one_elem_on_diag::apply_from_left_to_T(indexed_vector & w, lp_sett // This class supports updates of the columns of B, and solves systems Bx=b,and yB=c // Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5 -template -lu::lu(static_matrix const & A, - vector& basis, - lp_settings & settings): +template +lu::lu(const M& A, + vector& basis, + lp_settings & settings): m_status(LU_status::OK), m_dim(A.row_count()), m_A(A), @@ -137,42 +119,62 @@ lu::lu(static_matrix const & A, m_failure(false), m_row_eta_work_vector(A.row_count()), m_refactor_counter(0) { - SASSERT(!(numeric_traits::precise() && settings.use_tableau())); + lp_assert(!(numeric_traits::precise() && settings.use_tableau())); #ifdef Z3DEBUG debug_test_of_basis(A, basis); #endif ++m_settings.st().m_num_factorizations; create_initial_factorization(); #ifdef Z3DEBUG - // SASSERT(check_correctness()); + // lp_assert(check_correctness()); #endif } -template -void lu::debug_test_of_basis(static_matrix const & A, vector & basis) { +template +lu::lu(const M& A, + lp_settings & settings): + m_status(LU_status::OK), + m_dim(A.row_count()), + m_A(A), + m_Q(m_dim), + m_R(m_dim), + m_r_wave(m_dim), + m_U(A), // create the square matrix that eventually will be factorized + m_settings(settings), + m_failure(false), + m_row_eta_work_vector(A.row_count()), + m_refactor_counter(0) { + lp_assert(A.row_count() == A.column_count()); + create_initial_factorization(); +#ifdef Z3DEBUG + lp_assert(is_correct()); +#endif +} +template +void lu::debug_test_of_basis( M const & A, vector & basis) { std::set set; for (unsigned i = 0; i < A.row_count(); i++) { - SASSERT(basis[i]< A.column_count()); + lp_assert(basis[i]< A.column_count()); set.insert(basis[i]); } - SASSERT(set.size() == A.row_count()); + lp_assert(set.size() == A.row_count()); } - template - void lu::solve_By(indexed_vector & y) { - SASSERT(false); // not implemented +template +void lu::solve_By(indexed_vector & y) { + lp_assert(false); // not implemented // init_vector_y(y); // solve_By_when_y_is_ready(y); } -template -void lu::solve_By(vector & y) { +template +void lu::solve_By(vector & y) { init_vector_y(y); solve_By_when_y_is_ready_for_X(y); } -template -void lu::solve_By_when_y_is_ready_for_X(vector & y) { +template +void lu::solve_By_when_y_is_ready_for_X(vector & y) { if (numeric_traits::precise()) { m_U.solve_U_y(y); m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal @@ -189,8 +191,8 @@ void lu::solve_By_when_y_is_ready_for_X(vector & y) { } } -template -void lu::solve_By_when_y_is_ready_for_T(vector & y, vector & index) { +template +void lu::solve_By_when_y_is_ready_for_T(vector & y, vector & index) { if (numeric_traits::precise()) { m_U.solve_U_y(y); m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal @@ -214,8 +216,8 @@ void lu::solve_By_when_y_is_ready_for_T(vector & y, vector & } } -template -void lu::solve_By_for_T_indexed_only(indexed_vector & y, const lp_settings & settings) { +template +void lu::solve_By_for_T_indexed_only(indexed_vector & y, const lp_settings & settings) { if (numeric_traits::precise()) { vector active_rows; m_U.solve_U_y_indexed_only(y, settings, active_rows); @@ -226,8 +228,8 @@ void lu::solve_By_for_T_indexed_only(indexed_vector & y, const lp_setti m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal } -template -void lu::print_matrix_compact(std::ostream & f) { +template +void lu::print_matrix_compact(std::ostream & f) { f << "matrix_start" << std::endl; f << "nrows " << m_A.row_count() << std::endl; f << "ncolumns " << m_A.column_count() << std::endl; @@ -241,8 +243,8 @@ void lu::print_matrix_compact(std::ostream & f) { } f << "matrix_end" << std::endl; } -template -void lu::print(indexed_vector & w, const vector& basis) { +template +void lu< M>::print(indexed_vector & w, const vector& basis) { std::string dump_file_name("/tmp/lu"); remove(dump_file_name.c_str()); std::ofstream f(dump_file_name); @@ -256,8 +258,8 @@ void lu::print(indexed_vector & w, const vector& basis) { print_indexed_vector(w, f); f.close(); } -template -void lu::solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w) { +template +void lu< M>::solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector & w) { init_vector_w(a_column, w); if (w.m_index.size() * ratio_of_index_size_to_all_size() < d.m_data.size()) { // this const might need some tuning @@ -270,14 +272,14 @@ void lu::solve_Bd(unsigned a_column, indexed_vector & d, indexed_vector } } -template -void lu::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts the a_column into d +template +void lu< M>::solve_Bd_faster(unsigned a_column, indexed_vector & d) { // puts the a_column into d init_vector_w(a_column, d); solve_By_for_T_indexed_only(d, m_settings); } -template -void lu::solve_yB(vector& y) { +template +void lu< M>::solve_yB(vector& y) { // first solve yU = cb*R(-1) m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) m_U.solve_y_U(y); // got y*U=cb*R(-1) @@ -290,37 +292,37 @@ void lu::solve_yB(vector& y) { } } -template -void lu::solve_yB_indexed(indexed_vector& y) { - SASSERT(y.is_OK()); +template +void lu< M>::solve_yB_indexed(indexed_vector& y) { + lp_assert(y.is_OK()); // first solve yU = cb*R(-1) m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1) - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); m_U.solve_y_U_indexed(y, m_settings); // got y*U=cb*R(-1) - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); m_Q.apply_reverse_from_right_to_T(y); - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { #ifdef Z3DEBUG (*e)->set_number_of_columns(m_dim); #endif (*e)->apply_from_right(y); - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); } } -template -void lu::add_delta_to_solution(const vector& yc, vector& y){ +template +void lu< M>::add_delta_to_solution(const vector& yc, vector& y){ unsigned i = static_cast(y.size()); while (i--) y[i]+=yc[i]; } -template -void lu::add_delta_to_solution_indexed(indexed_vector& y) { +template +void lu< M>::add_delta_to_solution_indexed(indexed_vector& y) { // the delta sits in m_y_copy, put result into y - SASSERT(y.is_OK()); - SASSERT(m_y_copy.is_OK()); + lp_assert(y.is_OK()); + lp_assert(m_y_copy.is_OK()); m_ii.clear(); m_ii.resize(y.data_size()); for (unsigned i : y.m_index) @@ -330,7 +332,7 @@ void lu::add_delta_to_solution_indexed(indexed_vector& y) { if (m_ii[i] == 0) m_ii.set_value(1, i); } - SASSERT(m_ii.is_OK()); + lp_assert(m_ii.is_OK()); y.m_index.clear(); for (unsigned i : m_ii.m_index) { @@ -341,24 +343,24 @@ void lu::add_delta_to_solution_indexed(indexed_vector& y) { v = zero_of_type(); } - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); } -template -void lu::find_error_of_yB(vector& yc, const vector& y, const vector& m_basis) { +template +void lu< M>::find_error_of_yB(vector& yc, const vector& y, const vector& m_basis) { unsigned i = m_dim; while (i--) { yc[i] -= m_A.dot_product_with_column(y, m_basis[i]); } } -template -void lu::find_error_of_yB_indexed(const indexed_vector& y, const vector& heading, const lp_settings& settings) { +template +void lu< M>::find_error_of_yB_indexed(const indexed_vector& y, const vector& heading, const lp_settings& settings) { #if 0 == 1 // it is a non efficient version indexed_vector yc = m_y_copy; yc.m_index.clear(); - SASSERT(!numeric_traits::precise()); + lp_assert(!numeric_traits::precise()); { vector d_basis(y.m_data.size()); @@ -379,10 +381,10 @@ void lu::find_error_of_yB_indexed(const indexed_vector& y, const vector } } #endif - SASSERT(m_ii.is_OK()); + lp_assert(m_ii.is_OK()); m_ii.clear(); m_ii.resize(y.data_size()); - SASSERT(m_y_copy.is_OK()); + lp_assert(m_y_copy.is_OK()); // put the error into m_y_copy for (auto k : y.m_index) { auto & row = m_A.m_rows[k]; @@ -414,7 +416,7 @@ void lu::find_error_of_yB_indexed(const indexed_vector& y, const vector m_y_copy.set_value(v, k); } } - SASSERT(m_y_copy.is_OK()); + lp_assert(m_y_copy.is_OK()); } @@ -423,8 +425,8 @@ void lu::find_error_of_yB_indexed(const indexed_vector& y, const vector // solves y*B = y // y is the input -template -void lu::solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings & settings) { +template +void lu< M>::solve_yB_with_error_check_indexed(indexed_vector & y, const vector& heading, const vector & basis, const lp_settings & settings) { if (numeric_traits::precise()) { if (y.m_index.size() * ratio_of_index_size_to_all_size() * 3 < m_A.column_count()) { solve_yB_indexed(y); @@ -434,12 +436,12 @@ void lu::solve_yB_with_error_check_indexed(indexed_vector & y, const ve } return; } - SASSERT(m_y_copy.is_OK()); - SASSERT(y.is_OK()); + lp_assert(m_y_copy.is_OK()); + lp_assert(y.is_OK()); if (y.m_index.size() * ratio_of_index_size_to_all_size() < m_A.column_count()) { m_y_copy = y; solve_yB_indexed(y); - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); if (y.m_index.size() * ratio_of_index_size_to_all_size() >= m_A.column_count()) { find_error_of_yB(m_y_copy.m_data, y.m_data, basis); solve_yB(m_y_copy.m_data); @@ -451,7 +453,7 @@ void lu::solve_yB_with_error_check_indexed(indexed_vector & y, const ve solve_yB_indexed(m_y_copy); add_delta_to_solution_indexed(y); } - SASSERT(m_y_copy.is_OK()); + lp_assert(m_y_copy.is_OK()); } else { solve_yB_with_error_check(y.m_data, basis); y.restore_index_and_clean_from_data(); @@ -461,8 +463,8 @@ void lu::solve_yB_with_error_check_indexed(indexed_vector & y, const ve // solves y*B = y // y is the input -template -void lu::solve_yB_with_error_check(vector & y, const vector& basis) { +template +void lu< M>::solve_yB_with_error_check(vector & y, const vector& basis) { if (numeric_traits::precise()) { solve_yB(y); return; @@ -475,8 +477,8 @@ void lu::solve_yB_with_error_check(vector & y, const vector& add_delta_to_solution(yc, y); m_y_copy.clear_all(); } -template -void lu::apply_Q_R_to_U(permutation_matrix & r_wave) { +template +void lu< M>::apply_Q_R_to_U(permutation_matrix & r_wave) { m_U.multiply_from_right(r_wave); m_U.multiply_from_left_with_reverse(r_wave); } @@ -488,62 +490,62 @@ void lu::apply_Q_R_to_U(permutation_matrix & r_wave) { // solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering // variable -template -lu::~lu(){ +template +lu< M>::~lu(){ for (auto t : m_tail) { delete t; } } -template -void lu::init_vector_y(vector & y) { +template +void lu< M>::init_vector_y(vector & y) { apply_lp_list_to_y(y); m_Q.apply_reverse_from_left_to_X(y); } -template -void lu::perform_transformations_on_w(indexed_vector& w) { +template +void lu< M>::perform_transformations_on_w(indexed_vector& w) { apply_lp_list_to_w(w); m_Q.apply_reverse_from_left(w); - // TBD does not compile: SASSERT(numeric_traits::precise() || check_vector_for_small_values(w, m_settings)); + // TBD does not compile: lp_assert(numeric_traits::precise() || check_vector_for_small_values(w, m_settings)); } // see Chvatal 24.3 -template -void lu::init_vector_w(unsigned entering, indexed_vector & w) { +template +void lu< M>::init_vector_w(unsigned entering, indexed_vector & w) { w.clear(); m_A.copy_column_to_indexed_vector(entering, w); // w = a, the column perform_transformations_on_w(w); } -template -void lu::apply_lp_list_to_w(indexed_vector & w) { +template +void lu< M>::apply_lp_list_to_w(indexed_vector & w) { for (unsigned i = 0; i < m_tail.size(); i++) { m_tail[i]->apply_from_left_to_T(w, m_settings); - // TBD does not compile: SASSERT(check_vector_for_small_values(w, m_settings)); + // TBD does not compile: lp_assert(check_vector_for_small_values(w, m_settings)); } } -template -void lu::apply_lp_list_to_y(vector& y) { +template +void lu< M>::apply_lp_list_to_y(vector& y) { for (unsigned i = 0; i < m_tail.size(); i++) { m_tail[i]->apply_from_left(y, m_settings); } } -template -void lu::swap_rows(int j, int k) { +template +void lu< M>::swap_rows(int j, int k) { if (j != k) { m_Q.transpose_from_left(j, k); m_U.swap_rows(j, k); } } -template -void lu::swap_columns(int j, int pivot_column) { +template +void lu< M>::swap_columns(int j, int pivot_column) { if (j == pivot_column) return; m_R.transpose_from_right(j, pivot_column); m_U.swap_columns(j, pivot_column); } -template -bool lu::pivot_the_row(int row) { +template +bool lu< M>::pivot_the_row(int row) { eta_matrix * eta_matrix = get_eta_matrix_for_pivot(row); if (get_status() != LU_status::OK) { return false; @@ -560,8 +562,8 @@ bool lu::pivot_the_row(int row) { return true; } // we're processing the column j now -template -eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j) { +template +eta_matrix * lu< M>::get_eta_matrix_for_pivot(unsigned j) { eta_matrix *ret; if(!m_U.fill_eta_matrix(j, &ret)) { set_status(LU_status::Degenerated); @@ -569,16 +571,16 @@ eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j) { return ret; } // we're processing the column j now -template -eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j, sparse_matrix& copy_of_U) { +template +eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j, square_sparse_matrix& copy_of_U) { eta_matrix *ret; copy_of_U.fill_eta_matrix(j, &ret); return ret; } // see page 407 of Chvatal -template -unsigned lu::transform_U_to_V_by_replacing_column(indexed_vector & w, +template +unsigned lu::transform_U_to_V_by_replacing_column(indexed_vector & w, unsigned leaving_column) { unsigned column_to_replace = m_R.apply_reverse(leaving_column); m_U.replace_column(column_to_replace, w, m_settings); @@ -586,15 +588,15 @@ unsigned lu::transform_U_to_V_by_replacing_column(indexed_vector & w, } #ifdef Z3DEBUG -template -void lu::check_vector_w(unsigned entering) { +template +void lu::check_vector_w(unsigned entering) { T * w = new T[m_dim]; m_A.copy_column_to_vector(entering, w); check_apply_lp_lists_to_w(w); delete [] w; } -template -void lu::check_apply_matrix_to_vector(matrix *lp, T *w) { +template +void lu::check_apply_matrix_to_vector(matrix *lp, T *w) { if (lp != nullptr) { lp -> set_number_of_rows(m_dim); lp -> set_number_of_columns(m_dim); @@ -602,21 +604,21 @@ void lu::check_apply_matrix_to_vector(matrix *lp, T *w) { } } -template -void lu::check_apply_lp_lists_to_w(T * w) { +template +void lu::check_apply_lp_lists_to_w(T * w) { for (unsigned i = 0; i < m_tail.size(); i++) { check_apply_matrix_to_vector(m_tail[i], w); } permutation_matrix qr = m_Q.get_reverse(); apply_to_vector(qr, w); for (int i = m_dim - 1; i >= 0; i--) { - SASSERT(abs(w[i] - w[i]) < 0.0000001); + lp_assert(abs(w[i] - w[i]) < 0.0000001); } } #endif -template -void lu::process_column(int j) { +template +void lu::process_column(int j) { unsigned pi, pj; bool success = m_U.get_pivot_for_column(pi, pj, m_settings.c_partial_pivoting, j); if (!success) { @@ -637,8 +639,8 @@ void lu::process_column(int j) { m_failure = true; } } -template -bool lu::is_correct(const vector& basis) { +template +bool lu::is_correct(const vector& basis) { #ifdef Z3DEBUG if (get_status() != LU_status::OK) { return false; @@ -651,11 +653,25 @@ bool lu::is_correct(const vector& basis) { #endif } +template +bool lu::is_correct() { +#ifdef Z3DEBUG + if (get_status() != LU_status::OK) { + return false; + } + dense_matrix left_side = get_left_side(); + dense_matrix right_side = get_right_side(); + return left_side == right_side; +#else + return true; +#endif +} + #ifdef Z3DEBUG -template -dense_matrix lu::tail_product() { - SASSERT(tail_size() > 0); +template +dense_matrix lu::tail_product() { + lp_assert(tail_size() > 0); dense_matrix left_side = permutation_matrix(m_dim); for (unsigned i = 0; i < tail_size(); i++) { matrix* lp = get_lp_matrix(i); @@ -665,8 +681,8 @@ dense_matrix lu::tail_product() { } return left_side; } -template -dense_matrix lu::get_left_side(const vector& basis) { +template +dense_matrix lu::get_left_side(const vector& basis) { dense_matrix left_side = get_B(*this, basis); for (unsigned i = 0; i < tail_size(); i++) { matrix* lp = get_lp_matrix(i); @@ -676,8 +692,19 @@ dense_matrix lu::get_left_side(const vector& basis) { } return left_side; } -template -dense_matrix lu::get_right_side() { +template +dense_matrix lu::get_left_side() { + dense_matrix left_side = get_B(*this); + for (unsigned i = 0; i < tail_size(); i++) { + matrix* lp = get_lp_matrix(i); + lp->set_number_of_rows(m_dim); + lp->set_number_of_columns(m_dim); + left_side = ((*lp) * left_side); + } + return left_side; +} +template +dense_matrix lu::get_right_side() { auto ret = U() * R(); ret = Q() * ret; return ret; @@ -685,8 +712,8 @@ dense_matrix lu::get_right_side() { #endif // needed for debugging purposes -template -void lu::copy_w(T *buffer, indexed_vector & w) { +template +void lu::copy_w(T *buffer, indexed_vector & w) { unsigned i = m_dim; while (i--) { buffer[i] = w[i]; @@ -694,24 +721,24 @@ void lu::copy_w(T *buffer, indexed_vector & w) { } // needed for debugging purposes -template -void lu::restore_w(T *buffer, indexed_vector & w) { +template +void lu::restore_w(T *buffer, indexed_vector & w) { unsigned i = m_dim; while (i--) { w[i] = buffer[i]; } } -template -bool lu::all_columns_and_rows_are_active() { +template +bool lu::all_columns_and_rows_are_active() { unsigned i = m_dim; while (i--) { - SASSERT(m_U.col_is_active(i)); - SASSERT(m_U.row_is_active(i)); + lp_assert(m_U.col_is_active(i)); + lp_assert(m_U.row_is_active(i)); } return true; } -template -bool lu::too_dense(unsigned j) const { +template +bool lu::too_dense(unsigned j) const { unsigned r = m_dim - j; if (r < 5) return false; @@ -720,8 +747,8 @@ bool lu::too_dense(unsigned j) const { // return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j); return r * r * m_settings.density_threshold <= m_U.get_n_of_active_elems(); } -template -void lu::pivot_in_dense_mode(unsigned i) { +template +void lu::pivot_in_dense_mode(unsigned i) { int j = m_dense_LU->find_pivot_column_in_row(i); if (j == -1) { m_failure = true; @@ -733,8 +760,8 @@ void lu::pivot_in_dense_mode(unsigned i) { } m_dense_LU->pivot(i, m_settings); } -template -void lu::create_initial_factorization(){ +template +void lu::create_initial_factorization(){ m_U.prepare_for_factorization(); unsigned j; for (j = 0; j < m_dim; j++) { @@ -748,9 +775,9 @@ void lu::create_initial_factorization(){ } } if (j == m_dim) { - // TBD does not compile: SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - // SASSERT(is_correct()); - // SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + // TBD does not compile: lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + // lp_assert(is_correct()); + // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); return; } j++; @@ -763,16 +790,16 @@ void lu::create_initial_factorization(){ } } m_dense_LU->update_parent_matrix(m_settings); - SASSERT(m_dense_LU->is_L_matrix()); + lp_assert(m_dense_LU->is_L_matrix()); m_dense_LU->conjugate_by_permutation(m_Q); push_matrix_to_tail(m_dense_LU); m_refactor_counter = 0; - // SASSERT(is_correct()); - // SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + // lp_assert(is_correct()); + // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); } -template -void lu::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave) { +template +void lu::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & r_wave) { if (bump_start > bump_end) { set_status(LU_status::Degenerated); return; @@ -790,12 +817,12 @@ void lu::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_ m_U.multiply_from_right(r_wave); m_U.multiply_from_left_with_reverse(r_wave); } -template -void lu::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { +template +void lu::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { vector> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump)); for (auto & iv : last_row_vec) { if (is_zero(iv.m_value)) continue; - SASSERT(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value)); + lp_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value)); unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index); if (adjusted_col < lowest_row_of_the_bump) { m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col); @@ -805,8 +832,8 @@ void lu::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { } } -template -void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) { +template +void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) { // we have the system right side at m_row_eta_work_vector now // solve the system column wise for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) { @@ -816,14 +843,14 @@ void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned low vector> & row = m_U.get_row_values(aj); for (auto & iv : row) { unsigned col = m_U.adjust_column_inverse(iv.m_index); - SASSERT(col >= j || numeric_traits::is_zero(iv.m_value)); + lp_assert(col >= j || numeric_traits::is_zero(iv.m_value)); if (col == j) continue; if (numeric_traits::is_zero(iv.m_value)) { continue; } // the -v is for solving the system ( to zero the last row), and +v is for pivoting T delta = col < lowest_row_of_the_bump? -v * iv.m_value: v * iv.m_value; - SASSERT(numeric_traits::is_zero(delta) == false); + lp_assert(numeric_traits::is_zero(delta) == false); @@ -846,8 +873,8 @@ void lu::pivot_and_solve_the_system(unsigned replaced_column, unsigned low } // see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last // row at the same time -template -row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) { +template +row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) { if (replaced_column == lowest_row_of_the_bump) return nullptr; scan_last_row_to_work_vector(lowest_row_of_the_bump); pivot_and_solve_the_system(replaced_column, lowest_row_of_the_bump); @@ -861,9 +888,9 @@ row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned r } } #ifdef Z3DEBUG - auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump, m_dim); + auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump, m_dim); #else - auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump); + auto ret = new row_eta_matrix(replaced_column, lowest_row_of_the_bump); #endif for (auto j : m_row_eta_work_vector.m_index) { @@ -880,8 +907,8 @@ row_eta_matrix *lu::get_row_eta_matrix_and_set_row_vector(unsigned r return ret; } -template -void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, unsigned leaving_column_of_U){ +template +void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, unsigned leaving_column_of_U){ m_refactor_counter++; unsigned replaced_column = transform_U_to_V_by_replacing_column( w, leaving_column_of_U); unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column); @@ -900,15 +927,15 @@ void lu::replace_column(T pivot_elem_for_checking, indexed_vector & w, push_matrix_to_tail(row_eta); } calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump); - // SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); - // SASSERT(w.is_OK() && m_row_eta_work_vector.is_OK()); + // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + // lp_assert(w.is_OK() && m_row_eta_work_vector.is_OK()); } -template -void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){ +template +void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){ T diagonal_elem; if (replaced_column < lowest_row_of_the_bump) { diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump]; - // SASSERT(m_row_eta_work_vector.is_OK()); + // lp_assert(m_row_eta_work_vector.is_OK()); m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings); } else { diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently @@ -919,11 +946,11 @@ void lu::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned } calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem); - // SASSERT(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); + // lp_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings)); } -template -void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { +template +void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { auto l = new one_elem_on_diag(lowest_row_of_the_bump, diagonal_element); #ifdef Z3DEBUG l->set_number_of_columns(m_dim); @@ -933,26 +960,35 @@ void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bum l->conjugate_by_permutation(m_Q); } -template -void init_factorization(lu* & factorization, static_matrix & m_A, vector & m_basis, lp_settings &m_settings) { +template +void init_factorization(lu* & factorization, M & m_A, vector & m_basis, lp_settings &m_settings) { if (factorization != nullptr) delete factorization; - factorization = new lu(m_A, m_basis, m_settings); + factorization = new lu(m_A, m_basis, m_settings); // if (factorization->get_status() != LU_status::OK) // LP_OUT(m_settings, "failing in init_factorization" << std::endl); } #ifdef Z3DEBUG -template -dense_matrix get_B(lu& f, const vector& basis) { - SASSERT(basis.size() == f.dimension()); - SASSERT(basis.size() == f.m_U.dimension()); - dense_matrix B(f.dimension(), f.dimension()); +template +dense_matrix get_B(lu& f, const vector& basis) { + lp_assert(basis.size() == f.dimension()); + lp_assert(basis.size() == f.m_U.dimension()); + dense_matrix B(f.dimension(), f.dimension()); for (unsigned i = 0; i < f.dimension(); i++) for (unsigned j = 0; j < f.dimension(); j++) B.set_elem(i, j, f.B_(i, j, basis)); return B; } +template +dense_matrix get_B(lu& f) { + dense_matrix B(f.dimension(), f.dimension()); + for (unsigned i = 0; i < f.dimension(); i++) + for (unsigned j = 0; j < f.dimension(); j++) + B.set_elem(i, j, f.m_A[i][j]); + + return B; +} #endif } diff --git a/src/util/lp/lu_instances.cpp b/src/util/lp/lu_instances.cpp deleted file mode 100644 index 057895068..000000000 --- a/src/util/lp/lu_instances.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include -#include -#include "util/vector.h" -#include "util/debug.h" -#include "util/lp/lu.hpp" -template double lp::dot_product(vector const&, vector const&); -template lp::lu::lu(lp::static_matrix const&, vector&, lp::lp_settings&); -template void lp::lu::push_matrix_to_tail(lp::tail_matrix*); -template void lp::lu::replace_column(double, lp::indexed_vector&, unsigned); -template void lp::lu::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&); -template lp::lu::~lu(); -template void lp::lu::push_matrix_to_tail(lp::tail_matrix*); -template void lp::lu::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&); -template lp::lu::~lu(); -template void lp::lu >::push_matrix_to_tail(lp::tail_matrix >*); -template void lp::lu >::solve_Bd(unsigned int, lp::indexed_vector&, lp::indexed_vector&); -template lp::lu >::~lu(); -template lp::mpq lp::dot_product(vector const&, vector const&); -template void lp::init_factorization(lp::lu*&, lp::static_matrix&, vector&, lp::lp_settings&); -template void lp::init_factorization(lp::lu*&, lp::static_matrix&, vector&, lp::lp_settings&); -template void lp::init_factorization >(lp::lu >*&, lp::static_matrix >&, vector&, lp::lp_settings&); -#ifdef Z3DEBUG -template void lp::print_matrix(lp::sparse_matrix&, std::ostream & out); -template void lp::print_matrix(lp::static_matrix&, std::ostream&); -template void lp::print_matrix >(lp::static_matrix >&, std::ostream&); -template void lp::print_matrix(lp::static_matrix&, std::ostream & out); -template bool lp::lu::is_correct(const vector& basis); -template bool lp::lu >::is_correct( vector const &); -template lp::dense_matrix lp::get_B(lp::lu&, const vector& basis); -template lp::dense_matrix lp::get_B(lp::lu&, vector const&); - -#endif - -template bool lp::lu::pivot_the_row(int); // NOLINT -template void lp::lu::init_vector_w(unsigned int, lp::indexed_vector&); -template void lp::lu::solve_By(vector&); -template void lp::lu::solve_By_when_y_is_ready_for_X(vector&); -template void lp::lu::solve_yB_with_error_check(vector&, const vector& basis); -template void lp::lu::solve_yB_with_error_check_indexed(lp::indexed_vector&, vector const&, const vector & basis, const lp_settings&); -template void lp::lu::replace_column(lp::mpq, lp::indexed_vector&, unsigned); -template void lp::lu::solve_By(vector&); -template void lp::lu::solve_By_when_y_is_ready_for_X(vector&); -template void lp::lu::solve_yB_with_error_check(vector&, const vector& basis); -template void lp::lu::solve_yB_with_error_check_indexed(lp::indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); -template void lp::lu >::solve_yB_with_error_check_indexed(lp::indexed_vector&, vector< int > const&, const vector & basis, const lp_settings&); -template void lp::lu >::init_vector_w(unsigned int, lp::indexed_vector&); -template void lp::lu >::replace_column(lp::mpq, lp::indexed_vector&, unsigned); -template void lp::lu >::solve_Bd_faster(unsigned int, lp::indexed_vector&); -template void lp::lu >::solve_By(vector >&); -template void lp::lu >::solve_By_when_y_is_ready_for_X(vector >&); -template void lp::lu >::solve_yB_with_error_check(vector&, const vector& basis); -template void lp::lu::solve_By(lp::indexed_vector&); -template void lp::lu::solve_By(lp::indexed_vector&); -template void lp::lu::solve_yB_indexed(lp::indexed_vector&); -template void lp::lu::solve_yB_indexed(lp::indexed_vector&); -template void lp::lu >::solve_yB_indexed(lp::indexed_vector&); -template void lp::lu::solve_By_for_T_indexed_only(lp::indexed_vector&, lp::lp_settings const&); -template void lp::lu::solve_By_for_T_indexed_only(lp::indexed_vector&, lp::lp_settings const&); diff --git a/src/util/lp/matrix_instances.cpp b/src/util/lp/matrix.cpp similarity index 96% rename from src/util/lp/matrix_instances.cpp rename to src/util/lp/matrix.cpp index 8271a4d8a..2fb064111 100644 --- a/src/util/lp/matrix_instances.cpp +++ b/src/util/lp/matrix.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include "util/lp/lp_settings.h" #ifdef Z3DEBUG -#include "util/lp/matrix.hpp" +#include "util/lp/matrix_def.h" #include "util/lp/static_matrix.h" #include template void lp::print_matrix(lp::matrix const*, std::ostream & out); diff --git a/src/util/lp/matrix.h b/src/util/lp/matrix.h index f6374756f..063513287 100644 --- a/src/util/lp/matrix.h +++ b/src/util/lp/matrix.h @@ -49,11 +49,25 @@ void apply_to_vector(matrix & m, T * w); unsigned get_width_of_column(unsigned j, vector> & A); -void print_matrix_with_widths(vector> & A, vector & ws, std::ostream & out); -void print_string_matrix(vector> & A); +void print_matrix_with_widths(vector> & A, vector & ws, std::ostream & out, unsigned blanks = 0); +void print_string_matrix(vector> & A, std::ostream &, unsigned blanks_in_front = 0); template void print_matrix(matrix const * m, std::ostream & out); + +template +void print_matrix(const vector> & A, std::ostream & out, unsigned blanks_in_front = 0) { + vector> s(A.size()); + for (unsigned i = 0; i < A.size(); i++) { + for (const auto & v : A[i]) { + s[i].push_back(T_to_string(v)); + } + } + + print_string_matrix(s, out, blanks_in_front); +} + + } #endif diff --git a/src/util/lp/matrix.hpp b/src/util/lp/matrix_def.h similarity index 82% rename from src/util/lp/matrix.hpp rename to src/util/lp/matrix_def.h index 6eb82a9cc..ae5f05ad1 100644 --- a/src/util/lp/matrix.hpp +++ b/src/util/lp/matrix_def.h @@ -82,9 +82,11 @@ unsigned get_width_of_column(unsigned j, vector> & A) { return r; } -void print_matrix_with_widths(vector> & A, vector & ws, std::ostream & out) { +void print_matrix_with_widths(vector> & A, vector & ws, std::ostream & out, unsigned blanks_in_front) { for (unsigned i = 0; i < A.size(); i++) { for (unsigned j = 0; j < static_cast(A[i].size()); j++) { + if (i != 0 && j == 0) + print_blanks(blanks_in_front, out); print_blanks(ws[j] - static_cast(A[i][j].size()), out); out << A[i][j] << " "; } @@ -92,7 +94,7 @@ void print_matrix_with_widths(vector> & A, vector } } -void print_string_matrix(vector> & A, std::ostream & out) { +void print_string_matrix(vector> & A, std::ostream & out, unsigned blanks_in_front) { vector widths; if (A.size() > 0) @@ -100,10 +102,23 @@ void print_string_matrix(vector> & A, std::ostream & out) { widths.push_back(get_width_of_column(j, A)); } - print_matrix_with_widths(A, widths, out); + print_matrix_with_widths(A, widths, out, blanks_in_front); out << std::endl; } +template +void print_matrix(vector> & A, std::ostream & out, unsigned blanks_in_front = 0) { + vector> s(A.size()); + for (unsigned i = 0; i < A.size(); i++) { + for (const auto & v : A[i]) { + s[i].push_back(T_to_string(v)); + } + } + + print_string_matrix(s, out, blanks_in_front); +} + + template void print_matrix(matrix const * m, std::ostream & out) { vector> A(m->row_count()); diff --git a/src/util/lp/monomial.h b/src/util/lp/monomial.h new file mode 100644 index 000000000..5aa1eeb0c --- /dev/null +++ b/src/util/lp/monomial.h @@ -0,0 +1,33 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +namespace lp { +struct monomial { + mpq m_coeff; // the coefficient of the monomial + var_index m_var; // the variable index +public: + monomial(const mpq& coeff, var_index var) : m_coeff(coeff), m_var(var) {} + monomial(var_index var) : monomial(one_of_type(), var) {} + const mpq & coeff() const { return m_coeff; } + mpq & coeff() { return m_coeff; } + var_index var() const { return m_var; } + std::pair to_pair() const { return std::make_pair(coeff(), var());} +}; +} diff --git a/src/util/lp/mps_reader.h b/src/util/lp/mps_reader.h index 916e56322..09762cd5e 100644 --- a/src/util/lp/mps_reader.h +++ b/src/util/lp/mps_reader.h @@ -175,15 +175,15 @@ class mps_reader { if (m_line[i] == ' ') break; } - SASSERT(m_line.size() >= offset); - SASSERT(m_line.size() >> i); - SASSERT(i >= offset); + lp_assert(m_line.size() >= offset); + lp_assert(m_line.size() >> i); + lp_assert(i >= offset); return m_line.substr(offset, i - offset); } void set_boundary_for_column(unsigned col, bound * b, lp_solver * solver){ if (b == nullptr) { - solver->set_low_bound(col, numeric_traits::zero()); + solver->set_lower_bound(col, numeric_traits::zero()); return; } @@ -191,7 +191,7 @@ class mps_reader { return; } if (b->m_low_is_set) { - solver->set_low_bound(col, b->m_low); + solver->set_lower_bound(col, b->m_low); } if (b->m_upper_is_set) { solver->set_upper_bound(col, b->m_upper); @@ -303,7 +303,6 @@ class mps_reader { do { read_line(); if (m_line.find("RHS") == 0) { - // cout << "found RHS" << std::endl; break; } if (m_line.size() < 22) { @@ -512,7 +511,7 @@ class mps_reader { void create_or_update_bound() { const unsigned name_offset = 14; - SASSERT(m_line.size() >= 14); + lp_assert(m_line.size() >= 14); vector bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); if (bound_string.size() == 0) { @@ -618,7 +617,7 @@ class mps_reader { } for (auto s : row_with_range->m_row_columns) { - SASSERT(m_columns.find(s.first) != m_columns.end()); + lp_assert(m_columns.find(s.first) != m_columns.end()); other_bound_range_row->m_row_columns[s.first] = s.second; } } @@ -694,7 +693,7 @@ class mps_reader { if (row->m_name != m_cost_row_name) { solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index); for (auto s : row->m_row_columns) { - SASSERT(m_columns.find(s.first) != m_columns.end()); + lp_assert(m_columns.find(s.first) != m_columns.end()); solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second); } } else { @@ -729,7 +728,7 @@ class mps_reader { void set_solver_cost(row * row, lp_solver *solver) { for (auto s : row->m_row_columns) { std::string name = s.first; - SASSERT(m_columns.find(name) != m_columns.end()); + lp_assert(m_columns.find(name) != m_columns.end()); mps_reader::column * col = m_columns[name]; solver->set_cost_for_column(col->m_index, s.second); } @@ -738,7 +737,7 @@ class mps_reader { public: void set_message_stream(std::ostream * o) { - SASSERT(o != nullptr); + lp_assert(o != nullptr); m_message_stream = o; } vector column_names() { @@ -825,7 +824,7 @@ public: auto kind = get_lar_relation_from_row(row->m_type); vector> ls; for (auto s : row->m_row_columns) { - var_index i = solver->add_var(get_var_index(s.first)); + var_index i = solver->add_var(get_var_index(s.first), false); ls.push_back(std::make_pair(s.second, i)); } solver->add_constraint(ls, kind, row->m_right_side); @@ -843,20 +842,20 @@ public: void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) { vector> ls; - var_index i = solver->add_var(col->m_index); + var_index i = solver->add_var(col->m_index, false); ls.push_back(std::make_pair(numeric_traits::one(), i)); solver->add_constraint(ls, GE, b->m_low); } void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index); + var_index i = solver->add_var(col->m_index, false); vector> ls; ls.push_back(std::make_pair(numeric_traits::one(), i)); solver->add_constraint(ls, LE, b->m_upper); } void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) { - var_index i = solver->add_var(col->m_index); + var_index i = solver->add_var(col->m_index, false); vector> ls; ls.push_back(std::make_pair(numeric_traits::one(), i)); solver->add_constraint(ls, EQ, b->m_fixed_value); @@ -865,7 +864,7 @@ public: void fill_lar_solver_on_columns(lar_solver * solver) { for (auto s : m_columns) { mps_reader::column * col = s.second; - solver->add_var(col->m_index); + solver->add_var(col->m_index, false); auto b = col->m_bound; if (b == nullptr) return; diff --git a/src/util/lp/nra_solver.cpp b/src/util/lp/nra_solver.cpp new file mode 100644 index 000000000..b1ca67274 --- /dev/null +++ b/src/util/lp/nra_solver.cpp @@ -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, false); + 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", display(tout); 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/lp/numeric_pair.h b/src/util/lp/numeric_pair.h index a8a743a71..e98d76cbb 100644 --- a/src/util/lp/numeric_pair.h +++ b/src/util/lp/numeric_pair.h @@ -18,21 +18,29 @@ Revision History: --*/ #pragma once - +#define lp_for_z3 #include #include #include +#ifdef lp_for_z3 #include "../rational.h" #include "../sstream.h" #include "../z3_exception.h" - +#else + // include "util/numerics/mpq.h" + // include "util/numerics/numeric_traits.h" +#endif namespace lp { - typedef rational mpq; // rename rationals +#ifdef lp_for_z3 // rename rationals + typedef rational mpq; +#else + typedef lp::mpq mpq; +#endif template std::string T_to_string(const T & t); // forward definition - +#ifdef lp_for_z3 template class numeric_traits {}; template <> class numeric_traits { @@ -42,8 +50,25 @@ public: static unsigned one() { return 1; } static bool is_zero(unsigned v) { return v == 0; } static double get_double(unsigned const & d) { return d; } + static bool is_int(unsigned) {return true;} + static bool is_pos(unsigned) {return true;} }; +template <> class numeric_traits { +public: + static bool precise() { return true; } + static int const zero() { return 0; } + static int const one() { return 1; } + static bool is_zero(int v) { return v == 0; } + static double const get_double(int const & d) { return d; } + static bool is_int(int) {return true;} + static bool is_pos(int j) {return j > 0;} + static bool is_neg(int j) {return j < 0;} + static int ceil_ratio(int a, int b) { return static_cast(ceil(mpq(a, b)).get_int32());} + static int floor_ratio(int a, int b) { return static_cast(floor(mpq(a, b)).get_int32());} +}; + + template <> class numeric_traits { public: static bool precise() { return false; } @@ -71,14 +96,23 @@ template <> class numeric_traits { static rational from_string(std::string const & str) { return rational(str.c_str()); } static bool is_pos(const rational & d) {return d.is_pos();} static bool is_neg(const rational & d) {return d.is_neg();} + static bool is_int(const rational & d) {return d.is_int();} + static mpq ceil_ratio(const mpq & a, const mpq & b) { + return ceil(a / b); + } + static mpq floor_ratio(const mpq & a, const mpq & b) { + return floor(a / b); + } + }; +#endif template struct convert_struct { static X convert(const Y & y){ return X(y);} static bool is_epsilon_small(const X & x, const double & y) { return std::abs(numeric_traits::get_double(x)) < y; } - static bool below_bound_numeric(const X &, const X &, const Y &) { /*SASSERT(false);*/ return false;} - static bool above_bound_numeric(const X &, const X &, const Y &) { /*SASSERT(false);*/ return false; } + static bool below_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false;} + static bool above_bound_numeric(const X &, const X &, const Y &) { /*lp_unreachable();*/ return false; } }; @@ -148,7 +182,7 @@ struct numeric_pair { } numeric_pair operator/(const numeric_pair &) const { - // SASSERT(false); + // lp_unreachable(); } @@ -157,7 +191,7 @@ struct numeric_pair { } numeric_pair operator*(const numeric_pair & /*a*/) const { - // SASSERT(false); + // lp_unreachable(); } numeric_pair& operator+=(const numeric_pair & a) { @@ -203,6 +237,11 @@ struct numeric_pair { std::string to_string() const { return std::string("(") + T_to_string(x) + ", " + T_to_string(y) + ")"; } + + bool is_int() const { + return x.is_int() && y.is_zero(); + } + }; @@ -229,7 +268,7 @@ numeric_pair operator/(const numeric_pair & r, const X & a) { } // template bool precise() { return numeric_traits::precise();} -template double get_double(const lp::numeric_pair & ) { /* SASSERT(false); */ return 0;} +template double get_double(const lp::numeric_pair & ) { /* lp_unreachable(); */ return 0;} template class numeric_traits> { public: @@ -237,7 +276,7 @@ class numeric_traits> { static lp::numeric_pair zero() { return lp::numeric_pair(numeric_traits::zero(), numeric_traits::zero()); } static bool is_zero(const lp::numeric_pair & v) { return numeric_traits::is_zero(v.x) && numeric_traits::is_zero(v.y); } static double get_double(const lp::numeric_pair & v){ return numeric_traits::get_double(v.x); } // just return the double of the first coordinate - static double one() { /*SASSERT(false);*/ return 0;} + static double one() { /*lp_unreachable();*/ return 0;} static bool is_pos(const numeric_pair &p) { return numeric_traits::is_pos(p.x) || (numeric_traits::is_zero(p.x) && numeric_traits::is_pos(p.y)); @@ -246,7 +285,9 @@ class numeric_traits> { return numeric_traits::is_neg(p.x) || (numeric_traits::is_zero(p.x) && numeric_traits::is_neg(p.y)); } - + static bool is_int(const numeric_pair & p) { + return numeric_traits::is_int(p.x) && numeric_traits::is_zero(p.y); + } }; template <> @@ -267,11 +308,11 @@ struct convert_struct, double> { return convert_struct::is_epsilon_small(p.x, eps) && convert_struct::is_epsilon_small(p.y, eps); } static bool below_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { - // SASSERT(false); + // lp_unreachable(); return false; } static bool above_bound_numeric(const numeric_pair &, const numeric_pair &, const double &) { - // SASSERT(false); + // lp_unreachable(); return false; } }; @@ -328,4 +369,26 @@ struct convert_struct { template bool is_epsilon_small(const X & v, const double &eps) { return convert_struct::is_epsilon_small(v, eps);} template bool below_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::below_bound_numeric(x, bound, eps);} template bool above_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct::above_bound_numeric(x, bound, eps);} +template T floor(const numeric_pair & r) { + if (r.x.is_int()) { + if (r.y.is_nonneg()) { + return r.x; + } + return r.x - mpq::one(); + } + + return floor(r.x); +} + +template T ceil(const numeric_pair & r) { + if (r.x.is_int()) { + if (r.y.is_nonpos()) { + return r.x; + } + return r.x + mpq::one(); + } + + return ceil(r.x); +} + } diff --git a/src/util/lp/permutation_matrix_instances.cpp b/src/util/lp/permutation_matrix.cpp similarity index 99% rename from src/util/lp/permutation_matrix_instances.cpp rename to src/util/lp/permutation_matrix.cpp index 692d32337..8b57cfe00 100644 --- a/src/util/lp/permutation_matrix_instances.cpp +++ b/src/util/lp/permutation_matrix.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include #include "util/vector.h" -#include "util/lp/permutation_matrix.hpp" +#include "util/lp/permutation_matrix_def.h" #include "util/lp/numeric_pair.h" template void lp::permutation_matrix::apply_from_right(vector&); template void lp::permutation_matrix::init(unsigned int); diff --git a/src/util/lp/permutation_matrix.h b/src/util/lp/permutation_matrix.h index 8e664bba0..d1422ef86 100644 --- a/src/util/lp/permutation_matrix.h +++ b/src/util/lp/permutation_matrix.h @@ -101,7 +101,7 @@ class permutation_matrix : public tail_matrix { void apply_reverse_from_right_to_X(vector & w); void set_val(unsigned i, unsigned pi) { - SASSERT(i < size() && pi < size()); m_permutation[i] = pi; m_rev[pi] = i; } + lp_assert(i < size() && pi < size()); m_permutation[i] = pi; m_rev[pi] = i; } void transpose_from_left(unsigned i, unsigned j); @@ -132,8 +132,6 @@ class permutation_matrix : public tail_matrix { unsigned size() const { return static_cast(m_rev.size()); } - unsigned * values() const { return m_permutation.c_ptr(); } - void resize(unsigned size) { unsigned old_size = m_permutation.size(); m_permutation.resize(size); diff --git a/src/util/lp/permutation_matrix.hpp b/src/util/lp/permutation_matrix_def.h similarity index 91% rename from src/util/lp/permutation_matrix.hpp rename to src/util/lp/permutation_matrix_def.h index be96ca99f..76af12ed8 100644 --- a/src/util/lp/permutation_matrix.hpp +++ b/src/util/lp/permutation_matrix_def.h @@ -64,8 +64,7 @@ void permutation_matrix::apply_from_left(vector & w, lp_settings & ) { // L * deb_w = clone_vector(w, row_count()); // deb.apply_from_left(deb_w); #endif - // std::cout << " apply_from_left " << std::endl; - SASSERT(m_X_buffer.size() == w.size()); + lp_assert(m_X_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_X_buffer[i] = w[m_permutation[i]]; @@ -75,7 +74,7 @@ void permutation_matrix::apply_from_left(vector & w, lp_settings & ) { w[i] = m_X_buffer[i]; } #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(deb_w, w, row_count())); + // lp_assert(vectors_are_equal(deb_w, w, row_count())); // delete [] deb_w; #endif } @@ -101,7 +100,7 @@ template void permutation_matrix::apply_from_righ // T * deb_w = clone_vector(w, row_count()); // deb.apply_from_right(deb_w); #endif - SASSERT(m_T_buffer.size() == w.size()); + lp_assert(m_T_buffer.size() == w.size()); for (unsigned i = 0; i < size(); i++) { m_T_buffer[i] = w[m_rev[i]]; } @@ -110,7 +109,7 @@ template void permutation_matrix::apply_from_righ w[i] = m_T_buffer[i]; } #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(deb_w, w, row_count())); + // lp_assert(vectors_are_equal(deb_w, w, row_count())); // delete [] deb_w; #endif } @@ -132,9 +131,9 @@ template void permutation_matrix::apply_from_righ unsigned pj = m_permutation[j]; w.set_value(buffer[i], pj); } - SASSERT(w.is_OK()); + lp_assert(w.is_OK()); #ifdef Z3DEBUG - SASSERT(vectors_are_equal(wcopy, w.m_data)); + lp_assert(vectors_are_equal(wcopy, w.m_data)); #endif } @@ -181,7 +180,7 @@ void permutation_matrix::apply_reverse_from_left(indexed_vector & w) { w.m_index[i] = j; } #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(deb_w, w.m_data, row_count())); + // lp_assert(vectors_are_equal(deb_w, w.m_data, row_count())); // delete [] deb_w; #endif } @@ -189,7 +188,7 @@ void permutation_matrix::apply_reverse_from_left(indexed_vector & w) { template void permutation_matrix::apply_reverse_from_left_to_T(vector & w) { // the result will be w = p(-1) * w - SASSERT(m_T_buffer.size() == w.size()); + lp_assert(m_T_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_T_buffer[m_permutation[i]] = w[i]; @@ -202,7 +201,7 @@ void permutation_matrix::apply_reverse_from_left_to_T(vector & w) { template void permutation_matrix::apply_reverse_from_left_to_X(vector & w) { // the result will be w = p(-1) * w - SASSERT(m_X_buffer.size() == w.size()); + lp_assert(m_X_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_X_buffer[m_permutation[i]] = w[i]; @@ -216,7 +215,7 @@ void permutation_matrix::apply_reverse_from_left_to_X(vector & w) { template void permutation_matrix::apply_reverse_from_right_to_T(vector & w) { // the result will be w = w * p(-1) - SASSERT(m_T_buffer.size() == w.size()); + lp_assert(m_T_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_T_buffer[i] = w[m_permutation[i]]; @@ -234,7 +233,7 @@ void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & // vector wcopy(w.m_data); // apply_reverse_from_right_to_T(wcopy); #endif - SASSERT(w.is_OK()); + lp_assert(w.is_OK()); vector tmp; vector tmp_index(w.m_index); for (auto i : w.m_index) { @@ -247,15 +246,15 @@ void permutation_matrix::apply_reverse_from_right_to_T(indexed_vector & w.set_value(tmp[k], m_rev[j]); } - // SASSERT(w.is_OK()); - // SASSERT(vectors_are_equal(w.m_data, wcopy)); + // lp_assert(w.is_OK()); + // lp_assert(vectors_are_equal(w.m_data, wcopy)); } template void permutation_matrix::apply_reverse_from_right_to_X(vector & w) { // the result will be w = w * p(-1) - SASSERT(m_X_buffer.size() == w.size()); + lp_assert(m_X_buffer.size() == w.size()); unsigned i = size(); while (i-- > 0) { m_X_buffer[i] = w[m_permutation[i]]; @@ -268,7 +267,7 @@ void permutation_matrix::apply_reverse_from_right_to_X(vector & w) { template void permutation_matrix::transpose_from_left(unsigned i, unsigned j) { // the result will be this = (i,j)*this - SASSERT(i < size() && j < size() && i != j); + lp_assert(i < size() && j < size() && i != j); auto pi = m_rev[i]; auto pj = m_rev[j]; set_val(pi, j); @@ -277,7 +276,7 @@ template void permutation_matrix::transpose_from_ template void permutation_matrix::transpose_from_right(unsigned i, unsigned j) { // the result will be this = this * (i,j) - SASSERT(i < size() && j < size() && i != j); + lp_assert(i < size() && j < size() && i != j); auto pi = m_permutation[i]; auto pj = m_permutation[j]; set_val(i, pj); @@ -286,7 +285,7 @@ template void permutation_matrix::transpose_from_ template void permutation_matrix::multiply_by_permutation_from_left(permutation_matrix & p) { m_work_array = m_permutation; - SASSERT(p.size() == size()); + lp_assert(p.size() == size()); unsigned i = size(); while (i-- > 0) { set_val(i, m_work_array[p[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation @@ -296,7 +295,7 @@ template void permutation_matrix::multiply_by_per // this is multiplication in the matrix sense template void permutation_matrix::multiply_by_permutation_from_right(permutation_matrix & p) { m_work_array = m_permutation; - SASSERT(p.size() == size()); + lp_assert(p.size() == size()); unsigned i = size(); while (i-- > 0) set_val(i, p[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation @@ -304,7 +303,7 @@ template void permutation_matrix::multiply_by_per } template void permutation_matrix::multiply_by_reverse_from_right(permutation_matrix & q){ // todo : condensed permutations ? - SASSERT(q.size() == size()); + lp_assert(q.size() == size()); m_work_array = m_permutation; // the result is this = this*q(-1) unsigned i = size(); diff --git a/src/util/lp/polynomial.h b/src/util/lp/polynomial.h new file mode 100644 index 000000000..f4f9615ea --- /dev/null +++ b/src/util/lp/polynomial.h @@ -0,0 +1,115 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +namespace lp { +struct polynomial { + static mpq m_local_zero; + // the polynomial evaluates to m_coeffs + m_a + vector m_coeffs; + mpq m_a; // the free coefficient + polynomial(const vector& p, const mpq & a) : m_coeffs(p), m_a(a) {} + polynomial(const vector& p) : polynomial(p, zero_of_type()) {} + polynomial(): m_a(zero_of_type()) {} + polynomial(const polynomial & p) : m_coeffs(p.m_coeffs), m_a(p.m_a) {} + + const mpq & coeff(var_index j) const { + for (const auto & t : m_coeffs) { + if (j == t.var()) { + return t.coeff(); + } + } + return m_local_zero; + } + + polynomial & operator+=(const polynomial & p) { + m_a += p.m_a; + for (const auto & t: p.m_coeffs) + *this += monomial(t.coeff(), t.var()); + return *this; + } + + void add(const mpq & c, const polynomial &p) { + m_a += p.m_a * c; + + for (const auto & t: p.m_coeffs) + *this += monomial(c * t.coeff(), t.var()); + } + + void clear() { + m_coeffs.clear(); + m_a = zero_of_type(); + } + + bool is_empty() const { return m_coeffs.size() == 0 && numeric_traits::is_zero(m_a); } + + unsigned number_of_monomials() const { return m_coeffs.size();} + + void add(const monomial &m ){ + if (is_zero(m.coeff())) return; + for (unsigned k = 0; k < m_coeffs.size(); k++) { + auto & l = m_coeffs[k]; + if (m.var() == l.var()) { + l.coeff() += m.coeff(); + if (l.coeff() == 0) + m_coeffs.erase(m_coeffs.begin() + k); + return; + } + } + m_coeffs.push_back(m); + lp_assert(is_correct()); + } + + polynomial & operator+=(const monomial &m ){ + add(m); + return *this; + } + + polynomial & operator+=(const mpq &c ){ + m_a += c; + return *this; + } + + + bool is_correct() const { + std::unordered_set s; + for (auto & l : m_coeffs) { + if (l.coeff() == 0) + return false; + s.insert(l.var()); + } + return m_coeffs.size() == s.size(); + } + + bool var_coeff_is_unit(unsigned j) const { + const mpq & a = coeff(j); + return a == 1 || a == -1; + } + + template // c plays a role of a map from indices to impq + mpq value(const c& v) const { + mpq r = m_a; + for (auto & p : m_coeffs) + r += v[p.var()].x * p.coeff(); + return r; + } + + const vector & coeffs() const { return m_coeffs; } +}; +} diff --git a/src/util/lp/quick_xplain.cpp b/src/util/lp/quick_xplain.cpp deleted file mode 100644 index f9506c056..000000000 --- a/src/util/lp/quick_xplain.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "util/lp/lar_solver.h" -namespace lp { -quick_xplain::quick_xplain(vector> & explanation, const lar_solver & ls, lar_solver & qsol) : - m_explanation(explanation), - m_parent_solver(ls), - m_qsol(qsol) { -} -void quick_xplain::add_constraint_to_qsol(unsigned j) { - auto & lar_c = m_constraints_in_local_vars[j]; - auto ls = lar_c.get_left_side_coefficients(); - auto ci = m_qsol.add_constraint(ls, lar_c.m_kind, lar_c.m_right_side); - m_local_ci_to_constraint_offsets[ci] = j; -} - -void quick_xplain::copy_constraint_and_add_constraint_vars(const lar_constraint& lar_c) { - vector < std::pair> ls; - for (auto & p : lar_c.get_left_side_coefficients()) { - unsigned j = p.second; - unsigned lj = m_qsol.add_var(j); - ls.push_back(std::make_pair(p.first, lj)); - } - m_constraints_in_local_vars.push_back(lar_constraint(ls, lar_c.m_kind, lar_c.m_right_side)); - -} - -bool quick_xplain::infeasible() { - m_qsol.solve(); - return m_qsol.get_status() == INFEASIBLE; -} - -// u - unexplored constraints -// c and x are assumed, in other words, all constrains of x and c are already added to m_qsol -void quick_xplain::minimize(const vector& u) { - unsigned k = 0; - unsigned initial_stack_size = m_qsol.constraint_stack_size(); - for (; k < u.size();k++) { - m_qsol.push(); - add_constraint_to_qsol(u[k]); - if (infeasible()) - break; - } - m_x.insert(u[k]); - unsigned m = k / 2; // the split - if (m < k) { - m_qsol.pop(k + 1 - m); - add_constraint_to_qsol(u[k]); - if (!infeasible()) { - vector un; - for (unsigned j = m; j < k; j++) - un.push_back(u[j]); - minimize(un); - } - } - if (m > 0) { - SASSERT(m_qsol.constraint_stack_size() >= initial_stack_size); - m_qsol.pop(m_qsol.constraint_stack_size() - initial_stack_size); - for (auto j : m_x) - add_constraint_to_qsol(j); - if (!infeasible()) { - vector un; - for (unsigned j = 0; j < m; j++) - un.push_back(u[j]); - minimize(un); - } - } -} - - -void quick_xplain::run(vector> & explanation, const lar_solver & ls){ - if (explanation.size() <= 2) return; - lar_solver qsol; - SASSERT(ls.explanation_is_correct(explanation)); - quick_xplain q(explanation, ls, qsol); - q.solve(); -} - -void quick_xplain::copy_constraints_to_local_constraints() { - for (auto & p : m_explanation) { - const auto & lar_c = m_parent_solver.get_constraint(p.second); - m_local_constraint_offset_to_external_ci.push_back(p.second); - copy_constraint_and_add_constraint_vars(lar_c); - } -} - -bool quick_xplain::is_feasible(const vector & x, unsigned k) const { - lar_solver l; - for (unsigned i : x) { - if (i == k) - continue; - vector < std::pair> ls; - const lar_constraint & c = m_constraints_in_local_vars[i]; - for (auto & p : c.get_left_side_coefficients()) { - unsigned lj = l.add_var(p.second); - ls.push_back(std::make_pair(p.first, lj)); - } - l.add_constraint(ls, c.m_kind, c.m_right_side); - } - l.solve(); - return l.get_status() != INFEASIBLE; -} - -bool quick_xplain::x_is_minimal() const { - vector x; - for (auto j : m_x) - x.push_back(j); - - for (unsigned k = 0; k < x.size(); k++) { - SASSERT(is_feasible(x, x[k])); - } - return true; -} - -void quick_xplain::solve() { - copy_constraints_to_local_constraints(); - m_qsol.push(); - SASSERT(m_qsol.constraint_count() == 0); - vector u; - for (unsigned k = 0; k < m_constraints_in_local_vars.size(); k++) - u.push_back(k); - minimize(u); - while (m_qsol.constraint_count() > 0) - m_qsol.pop(); - for (unsigned i : m_x) - add_constraint_to_qsol(i); - m_qsol.solve(); - SASSERT(m_qsol.get_status() == INFEASIBLE); - m_qsol.get_infeasibility_explanation(m_explanation); - SASSERT(m_qsol.explanation_is_correct(m_explanation)); - SASSERT(x_is_minimal()); - for (auto & p : m_explanation) { - p.second = this->m_local_constraint_offset_to_external_ci[m_local_ci_to_constraint_offsets[p.second]]; - } -} -} diff --git a/src/util/lp/quick_xplain.h b/src/util/lp/quick_xplain.h deleted file mode 100644 index 902fa08cd..000000000 --- a/src/util/lp/quick_xplain.h +++ /dev/null @@ -1,48 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -#include "util/vector.h" -#include - -namespace lp { - class lar_solver; // forward definition - - class quick_xplain { - std::unordered_set m_x; // the minimal set of constraints, the core - it is empty at the begining - vector m_constraints_in_local_vars; - vector> & m_explanation; - const lar_solver& m_parent_solver; - lar_solver & m_qsol; - vector m_local_constraint_offset_to_external_ci; - std::unordered_map m_local_ci_to_constraint_offsets; - quick_xplain(vector> & explanation, const lar_solver & parent_lar_solver, lar_solver & qsol); - void minimize(const vector & u); - void add_constraint_to_qsol(unsigned j); - void copy_constraint_and_add_constraint_vars(const lar_constraint& lar_c); - void copy_constraints_to_local_constraints(); - bool infeasible(); - bool is_feasible(const vector & x, unsigned k) const; - bool x_is_minimal() const; - public: - static void run(vector> & explanation,const lar_solver & ls); - void solve(); - }; -} diff --git a/src/util/lp/random_updater_instances.cpp b/src/util/lp/random_updater.cpp similarity index 80% rename from src/util/lp/random_updater_instances.cpp rename to src/util/lp/random_updater.cpp index 4f9b880c0..564885ea0 100644 --- a/src/util/lp/random_updater_instances.cpp +++ b/src/util/lp/random_updater.cpp @@ -17,4 +17,5 @@ Revision History: --*/ -#include "util/lp/random_updater.hpp" +#include "util/lp/random_updater_def.h" + diff --git a/src/util/lp/random_updater.h b/src/util/lp/random_updater.h index 6b03ad941..e5067f2e5 100644 --- a/src/util/lp/random_updater.h +++ b/src/util/lp/random_updater.h @@ -25,72 +25,23 @@ Revision History: #include #include #include "util/lp/lp_settings.h" -#include "util/lp/linear_combination_iterator.h" // see http://research.microsoft.com/projects/z3/smt07.pdf // The class searches for a feasible solution with as many different values of variables as it can find namespace lp { template struct numeric_pair; // forward definition -class lar_core_solver; // forward definition +class lar_solver; // forward definition class random_updater { - struct interval { - bool upper_bound_is_set; - numeric_pair upper_bound; - bool low_bound_is_set; - numeric_pair low_bound; - interval() : upper_bound_is_set(false), - low_bound_is_set(false) {} - - void set_low_bound(const numeric_pair & v) { - if (low_bound_is_set) { - low_bound = std::max(v, low_bound); - } else { - low_bound = v; - low_bound_is_set = true; - } - } - void set_upper_bound(const numeric_pair & v) { - if (upper_bound_is_set) { - upper_bound = std::min(v, upper_bound); - } else { - upper_bound = v; - upper_bound_is_set = true; - } - } - bool is_empty() const {return - upper_bound_is_set && low_bound_is_set && low_bound >= upper_bound; - } - - bool low_bound_holds(const numeric_pair & a) const { - return low_bound_is_set == false || a >= low_bound; - } - bool upper_bound_holds(const numeric_pair & a) const { - return upper_bound_is_set == false || a <= upper_bound; - } - - bool contains(const numeric_pair & a) const { - return low_bound_holds(a) && upper_bound_holds(a); - } - std::string lbs() { return low_bound_is_set ? T_to_string(low_bound):std::string("inf");} - std::string rbs() { return upper_bound_is_set? T_to_string(upper_bound):std::string("inf");} - std::string to_str() { return std::string("[")+ lbs() + ", " + rbs() + "]";} - }; std::set m_var_set; - lar_core_solver & m_core_solver; - unsigned range; - linear_combination_iterator* m_column_j; // the actual column - interval find_shift_interval(unsigned j); - interval get_interval_of_non_basic_var(unsigned j); + lar_solver & m_lar_solver; + unsigned m_range; void add_column_to_sets(unsigned j); - void random_shift_var(unsigned j); + bool random_shift_var(unsigned j); std::unordered_map, unsigned> m_values; // it maps a value to the number of time it occurs - void diminish_interval_to_leave_basic_vars_feasible(numeric_pair &nb_x, interval & inter); - void shift_var(unsigned j, interval & r); - void diminish_interval_for_basic_var(numeric_pair &nb_x, unsigned j, mpq & a, interval & r); - numeric_pair get_random_from_interval(interval & r); - void add_value(numeric_pair& v); - void remove_value(numeric_pair & v); + bool shift_var(unsigned j); + void add_value(const numeric_pair& v); + void remove_value(const numeric_pair & v); public: - random_updater(lar_core_solver & core_solver, const vector & column_list); + random_updater(lar_solver & solver, const vector & column_list); void update(); }; } diff --git a/src/util/lp/random_updater.hpp b/src/util/lp/random_updater.hpp deleted file mode 100644 index 5bbcdf27c..000000000 --- a/src/util/lp/random_updater.hpp +++ /dev/null @@ -1,222 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include "util/lp/random_updater.h" -#include "util/lp/static_matrix.h" -#include "util/lp/lar_solver.h" -#include "util/vector.h" -namespace lp { - - - -random_updater::random_updater( - lar_core_solver & lar_core_solver, - const vector & column_indices) : - m_core_solver(lar_core_solver), - range(100000) { - for (unsigned j : column_indices) - add_column_to_sets(j); -} - -random_updater::interval random_updater::get_interval_of_non_basic_var(unsigned j) { - interval ret; - switch (m_core_solver.get_column_type(j)) { - case column_type::free_column: - break; - case column_type::low_bound: - ret.set_low_bound(m_core_solver.m_r_low_bounds[j]); - break; - case column_type::upper_bound: - ret.set_upper_bound(m_core_solver.m_r_upper_bounds[j]); - break; - case column_type::boxed: - case column_type::fixed: - ret.set_low_bound(m_core_solver.m_r_low_bounds[j]); - ret.set_upper_bound(m_core_solver.m_r_upper_bounds[j]); - break; - default: - SASSERT(false); - } - return ret; -} - -void random_updater::diminish_interval_for_basic_var(numeric_pair& nb_x, unsigned j, - mpq & a, - interval & r) { - SASSERT(m_core_solver.m_r_heading[j] >= 0); - numeric_pair delta; - SASSERT(a != zero_of_type()); - switch (m_core_solver.get_column_type(j)) { - case column_type::free_column: - break; - case column_type::low_bound: - delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j]; - SASSERT(delta >= zero_of_type>()); - if (a > 0) { - r.set_upper_bound(nb_x + delta / a); - } else { - r.set_low_bound(nb_x + delta / a); - } - break; - case column_type::upper_bound: - delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j]; - SASSERT(delta >= zero_of_type>()); - if (a > 0) { - r.set_low_bound(nb_x - delta / a); - } else { - r.set_upper_bound(nb_x - delta / a); - } - break; - case column_type::boxed: - if (a > 0) { - delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j]; - SASSERT(delta >= zero_of_type>()); - r.set_upper_bound(nb_x + delta / a); - delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j]; - SASSERT(delta >= zero_of_type>()); - r.set_low_bound(nb_x - delta / a); - } else { // a < 0 - delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j]; - SASSERT(delta >= zero_of_type>()); - r.set_upper_bound(nb_x - delta / a); - delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j]; - SASSERT(delta >= zero_of_type>()); - r.set_low_bound(nb_x + delta / a); - } - break; - case column_type::fixed: - r.set_low_bound(nb_x); - r.set_upper_bound(nb_x); - break; - default: - SASSERT(false); - } -} - - -void random_updater::diminish_interval_to_leave_basic_vars_feasible(numeric_pair &nb_x, interval & r) { - m_column_j->reset(); - unsigned i; - mpq a; - while (m_column_j->next(a, i)) { - diminish_interval_for_basic_var(nb_x, m_core_solver.m_r_basis[i], a, r); - if (r.is_empty()) - break; - } -} - -random_updater::interval random_updater::find_shift_interval(unsigned j) { - interval ret = get_interval_of_non_basic_var(j); - diminish_interval_to_leave_basic_vars_feasible(m_core_solver.m_r_x[j], ret); - return ret; -} - -void random_updater::shift_var(unsigned j, interval & r) { - SASSERT(r.contains(m_core_solver.m_r_x[j])); - SASSERT(m_core_solver.m_r_solver.column_is_feasible(j)); - auto old_x = m_core_solver.m_r_x[j]; - remove_value(old_x); - auto new_val = m_core_solver.m_r_x[j] = get_random_from_interval(r); - add_value(new_val); - - SASSERT(r.contains(m_core_solver.m_r_x[j])); - SASSERT(m_core_solver.m_r_solver.column_is_feasible(j)); - auto delta = m_core_solver.m_r_x[j] - old_x; - - unsigned i; - m_column_j->reset(); - mpq a; - while(m_column_j->next(a, i)) { - unsigned bj = m_core_solver.m_r_basis[i]; - m_core_solver.m_r_x[bj] -= a * delta; - SASSERT(m_core_solver.m_r_solver.column_is_feasible(bj)); - } - SASSERT(m_core_solver.m_r_solver.A_mult_x_is_off() == false); -} - -numeric_pair random_updater::get_random_from_interval(interval & r) { - unsigned rand = m_core_solver.settings().random_next(); - if ((!r.low_bound_is_set) && (!r.upper_bound_is_set)) - return numeric_pair(rand % range, 0); - if (r.low_bound_is_set && (!r.upper_bound_is_set)) - return r.low_bound + numeric_pair(rand % range, 0); - if ((!r.low_bound_is_set) && r.upper_bound_is_set) - return r.upper_bound - numeric_pair(rand % range, 0); - SASSERT(r.low_bound_is_set && r.upper_bound_is_set); - return r.low_bound + (rand % range) * (r.upper_bound - r.low_bound)/ range; -} - -void random_updater::random_shift_var(unsigned j) { - m_column_j = m_core_solver.get_column_iterator(j); - if (m_column_j->size() >= 50) { - delete m_column_j; - return; - } - interval interv = find_shift_interval(j); - if (interv.is_empty()) { - delete m_column_j; - return; - } - - shift_var(j, interv); - delete m_column_j; -} - -void random_updater::update() { - for (auto j : m_var_set) { - if (m_var_set.size() <= m_values.size()) { - break; // we are done - } - random_shift_var(j); - } -} - -void random_updater::add_value(numeric_pair& v) { - auto it = m_values.find(v); - if (it == m_values.end()) { - m_values[v] = 1; - } else { - it->second++; - } -} - -void random_updater::remove_value(numeric_pair& v) { - std::unordered_map, unsigned>::iterator it = m_values.find(v); - SASSERT(it != m_values.end()); - it->second--; - if (it->second == 0) - m_values.erase((std::unordered_map, unsigned>::const_iterator)it); -} - -void random_updater::add_column_to_sets(unsigned j) { - if (m_core_solver.m_r_heading[j] < 0) { - m_var_set.insert(j); - add_value(m_core_solver.m_r_x[j]); - } else { - unsigned row = m_core_solver.m_r_heading[j]; - for (auto row_c : m_core_solver.m_r_A.m_rows[row]) { - unsigned cj = row_c.m_j; - if (m_core_solver.m_r_heading[cj] < 0) { - m_var_set.insert(cj); - add_value(m_core_solver.m_r_x[cj]); - } - } - } -} -} diff --git a/src/util/lp/random_updater_def.h b/src/util/lp/random_updater_def.h new file mode 100644 index 000000000..419335aa3 --- /dev/null +++ b/src/util/lp/random_updater_def.h @@ -0,0 +1,95 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include "util/lp/random_updater.h" +#include "util/lp/static_matrix.h" +#include "util/lp/lar_solver.h" +#include "util/vector.h" +namespace lp { + + + +random_updater::random_updater( + lar_solver & lar_solver, + const vector & column_indices) : + m_lar_solver(lar_solver), + m_range(100000) { + for (unsigned j : column_indices) + add_column_to_sets(j); +} + + +bool random_updater::shift_var(unsigned v) { + return m_lar_solver.get_int_solver()->shift_var(v, m_range); +} + +bool random_updater::random_shift_var(unsigned j) { + if (m_lar_solver.A_r().m_columns.size() >= 50) { + return false; + } + + return shift_var(j); +} + +void random_updater::update() { + for (auto j : m_var_set) { + if (m_var_set.size() <= m_values.size()) { + break; // we are done + } + auto old_x = m_lar_solver.get_column_value(j); + if (random_shift_var(j)) { + remove_value(old_x); + add_value(m_lar_solver.get_column_value(j)); + } + } +} + +void random_updater::add_value(const numeric_pair& v) { + auto it = m_values.find(v); + if (it == m_values.end()) { + m_values[v] = 1; + } else { + it->second++; + } +} + +void random_updater::remove_value(const numeric_pair& v) { + std::unordered_map, unsigned>::iterator it = m_values.find(v); + lp_assert(it != m_values.end()); + it->second--; + if (it->second == 0) + m_values.erase((std::unordered_map, unsigned>::const_iterator)it); +} + +void random_updater::add_column_to_sets(unsigned j) { + if (m_lar_solver.get_core_solver().m_r_heading[j] < 0) { + m_var_set.insert(j); + add_value(m_lar_solver.get_core_solver().m_r_x[j]); + } else { + unsigned row = m_lar_solver.get_core_solver().m_r_heading[j]; + for (auto & row_c : m_lar_solver.get_core_solver().m_r_A.m_rows[row]) { + unsigned cj = row_c.m_j; + if (m_lar_solver.get_core_solver().m_r_heading[cj] < 0) { + m_var_set.insert(cj); + add_value(m_lar_solver.get_core_solver().m_r_x[cj]); + } + } + } +} +} diff --git a/src/util/lp/row_eta_matrix_instances.cpp b/src/util/lp/row_eta_matrix.cpp similarity index 98% rename from src/util/lp/row_eta_matrix_instances.cpp rename to src/util/lp/row_eta_matrix.cpp index 3c4ab9bed..c74ce9186 100644 --- a/src/util/lp/row_eta_matrix_instances.cpp +++ b/src/util/lp/row_eta_matrix.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include #include "util/vector.h" -#include "util/lp/row_eta_matrix.hpp" +#include "util/lp/row_eta_matrix_def.h" #include "util/lp/lu.h" namespace lp { template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); diff --git a/src/util/lp/row_eta_matrix.h b/src/util/lp/row_eta_matrix.h index 2bc0ac24e..497e7935c 100644 --- a/src/util/lp/row_eta_matrix.h +++ b/src/util/lp/row_eta_matrix.h @@ -70,7 +70,7 @@ public: } void push_back(unsigned row_index, T val ) { - SASSERT(row_index != m_row); + lp_assert(row_index != m_row); m_row_vector.push_back(row_index, val); } diff --git a/src/util/lp/row_eta_matrix.hpp b/src/util/lp/row_eta_matrix_def.h similarity index 92% rename from src/util/lp/row_eta_matrix.hpp rename to src/util/lp/row_eta_matrix_def.h index 969b4af7d..46b187a24 100644 --- a/src/util/lp/row_eta_matrix.hpp +++ b/src/util/lp/row_eta_matrix_def.h @@ -34,7 +34,7 @@ void row_eta_matrix::apply_from_left(vector & w, lp_settings &) { } // w[m_row] = w_at_row; // #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(clone_w, w, m_dimension)); + // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); // delete [] clone_w; // #endif } @@ -58,7 +58,7 @@ void row_eta_matrix::apply_from_left_local_to_T(indexed_vector & w, lp_ auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); w.m_index.erase(it); } - // TBD: SASSERT(check_vector_for_small_values(w, settings)); + // TBD: lp_assert(check_vector_for_small_values(w, settings)); } template @@ -80,7 +80,7 @@ void row_eta_matrix::apply_from_left_local_to_X(indexed_vector & w, lp_ auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); w.m_index.erase(it); } - // TBD: does not compile SASSERT(check_vector_for_small_values(w, settings)); + // TBD: does not compile lp_assert(check_vector_for_small_values(w, settings)); } template @@ -96,14 +96,14 @@ void row_eta_matrix::apply_from_right(vector & w) { w[it.first] += w_row * it.second; } #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(clone_w, w, m_dimension)); + // lp_assert(vectors_are_equal(clone_w, w, m_dimension)); // delete clone_w; #endif } template void row_eta_matrix::apply_from_right(indexed_vector & w) { - SASSERT(w.is_OK()); + lp_assert(w.is_OK()); const T & w_row = w[m_row]; if (numeric_traits::is_zero(w_row)) return; #ifdef Z3DEBUG @@ -145,7 +145,7 @@ void row_eta_matrix::apply_from_right(indexed_vector & w) { } } #ifdef Z3DEBUG - // SASSERT(vectors_are_equal(wcopy, w.m_data)); + // lp_assert(vectors_are_equal(wcopy, w.m_data)); #endif } @@ -166,7 +166,7 @@ void row_eta_matrix::conjugate_by_permutation(permutation_matrix & p for (unsigned i = static_cast(columns.size()); i-- > 0;) m_row_vector.m_data[i].first = p.get_rev(columns[i]); #ifdef Z3DEBUG - // SASSERT(deb == *this); + // lp_assert(deb == *this); #endif } #ifdef Z3DEBUG diff --git a/src/util/lp/scaler_instances.cpp b/src/util/lp/scaler.cpp similarity index 89% rename from src/util/lp/scaler_instances.cpp rename to src/util/lp/scaler.cpp index ba02321ea..dcb4cd308 100644 --- a/src/util/lp/scaler_instances.cpp +++ b/src/util/lp/scaler.cpp @@ -17,6 +17,6 @@ Revision History: --*/ -#include "util/lp/scaler.hpp" +#include "util/lp/scaler_def.h" template bool lp::scaler::scale(); template bool lp::scaler::scale(); diff --git a/src/util/lp/scaler.h b/src/util/lp/scaler.h index 509671528..f98be83bd 100644 --- a/src/util/lp/scaler.h +++ b/src/util/lp/scaler.h @@ -46,7 +46,7 @@ public: m_scaling_maximum(scaling_maximum), m_column_scale(column_scale), m_settings(settings) { - SASSERT(m_column_scale.size() == 0); + lp_assert(m_column_scale.size() == 0); m_column_scale.resize(m_A.column_count(), numeric_traits::one()); } diff --git a/src/util/lp/scaler.hpp b/src/util/lp/scaler_def.h similarity index 90% rename from src/util/lp/scaler.hpp rename to src/util/lp/scaler_def.h index ea8dc98c4..2710f89bf 100644 --- a/src/util/lp/scaler.hpp +++ b/src/util/lp/scaler_def.h @@ -56,7 +56,7 @@ template T scaler::A_max() const { template T scaler::get_A_ratio() const { T min = A_min(); T max = A_max(); - SASSERT(!m_settings.abs_val_is_smaller_than_zero_tolerance(min)); + lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(min)); T ratio = max / min; return ratio; } @@ -66,7 +66,7 @@ template T scaler::get_max_ratio_on_rows() con unsigned i = m_A.row_count(); while (i--) { T den = m_A.get_min_abs_in_row(i); - SASSERT(!m_settings.abs_val_is_smaller_than_zero_tolerance(den)); + lp_assert(!m_settings.abs_val_is_smaller_than_zero_tolerance(den)); T t = m_A.get_max_abs_in_row(i)/ den; if (t > ret) ret = t; @@ -93,7 +93,7 @@ template void scaler::scale_rows_with_geometri while (i--) { T max = m_A.get_max_abs_in_row(i); T min = m_A.get_min_abs_in_row(i); - SASSERT(max > zero_of_type() && min > zero_of_type()); + lp_assert(max > zero_of_type() && min > zero_of_type()); if (is_zero(max) || is_zero(min)) continue; T gm = T(sqrt(numeric_traits::get_double(max*min))); @@ -219,18 +219,18 @@ template void scaler::scale_row(unsigned i) { if (numeric_traits::is_zero(row_max)) { return; } - if (numeric_traits::get_double(row_max) < m_scaling_minimum) { + if (row_max < m_scaling_minimum) { do { alpha *= 2; row_max *= 2; - } while (numeric_traits::get_double(row_max) < m_scaling_minimum); + } while (row_max < m_scaling_minimum); m_A.multiply_row(i, alpha); m_b[i] *= alpha; - } else if (numeric_traits::get_double(row_max) > m_scaling_maximum) { + } else if (row_max > m_scaling_maximum) { do { alpha /= 2; row_max /= 2; - } while (numeric_traits::get_double(row_max) > m_scaling_maximum); + } while (row_max > m_scaling_maximum); m_A.multiply_row(i, alpha); m_b[i] *= alpha; } @@ -243,17 +243,16 @@ template void scaler::scale_column(unsigned i) if (numeric_traits::is_zero(column_max)){ return; // the column has zeros only } - - if (numeric_traits::get_double(column_max) < m_scaling_minimum) { + if (column_max < m_scaling_minimum) { do { alpha *= 2; column_max *= 2; - } while (numeric_traits::get_double(column_max) < m_scaling_minimum); - } else if (numeric_traits::get_double(column_max) > m_scaling_maximum) { + } while (column_max < m_scaling_minimum); + } else if (column_max > m_scaling_maximum) { do { alpha /= 2; column_max /= 2; - } while (numeric_traits::get_double(column_max) > m_scaling_maximum); + } while (column_max > m_scaling_maximum); } else { return; } diff --git a/src/util/lp/signature_bound_evidence.h b/src/util/lp/signature_bound_evidence.h index e4eeb328d..7e09f720f 100644 --- a/src/util/lp/signature_bound_evidence.h +++ b/src/util/lp/signature_bound_evidence.h @@ -25,14 +25,14 @@ struct bound_signature { unsigned m_i; bool m_at_low; bound_signature(unsigned i, bool at_low) :m_i(i), m_at_low(m_at_low) {} - bool at_upper_bound() const { return !m_at_low_bound;} - bool at_low_bound() const { return m_at_low;} + bool at_upper_bound() const { return !m_at_lower_bound;} + bool at_lower_bound() const { return m_at_low;} }; template struct signature_bound_evidence { vector m_evidence; unsigned m_j; // found new bound - bool m_low_bound; + bool m_lower_bound; X m_bound; }; } diff --git a/src/util/lp/sparse_matrix_instances.cpp b/src/util/lp/sparse_matrix_instances.cpp deleted file mode 100644 index 64a555bb2..000000000 --- a/src/util/lp/sparse_matrix_instances.cpp +++ /dev/null @@ -1,116 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#include -#include "util/vector.h" -#include "util/lp/lp_settings.h" -#include "util/lp/lu.h" -#include "util/lp/sparse_matrix.hpp" -#include "util/lp/dense_matrix.h" -namespace lp { -template double sparse_matrix::dot_product_with_row(unsigned int, vector const&) const; -template void sparse_matrix::add_new_element(unsigned int, unsigned int, const double&); -template void sparse_matrix::divide_row_by_constant(unsigned int, const double&, lp_settings&); -template bool sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); -template const double & sparse_matrix::get(unsigned int, unsigned int) const; -template unsigned sparse_matrix::get_number_of_nonzeroes() const; -template bool sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned sparse_matrix::lowest_row_in_column(unsigned int); -template bool sparse_matrix::pivot_row_to_row(unsigned int, const double&, unsigned int, lp_settings&); -template bool sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); -template void sparse_matrix::prepare_for_factorization(); -template void sparse_matrix::remove_element(vector >&, indexed_value&); -template void sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void sparse_matrix::set(unsigned int, unsigned int, double); -template void sparse_matrix::set_max_in_row(vector >&); -template bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); -template void sparse_matrix::solve_y_U(vector&) const; -template sparse_matrix::sparse_matrix(static_matrix const&, vector&); -template sparse_matrix::sparse_matrix(unsigned int); -template void sparse_matrix::add_new_element(unsigned int, unsigned int, const mpq&); -template void sparse_matrix::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); -template bool sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); -template mpq const & sparse_matrix::get(unsigned int, unsigned int) const; -template unsigned sparse_matrix::get_number_of_nonzeroes() const; -template bool sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned sparse_matrix::lowest_row_in_column(unsigned int); -template bool sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); -template void sparse_matrix::prepare_for_factorization(); -template void sparse_matrix::remove_element(vector> &, indexed_value&); -template void sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void sparse_matrix::set_max_in_row(vector>&); -template bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); -template void sparse_matrix::solve_y_U(vector&) const; -template sparse_matrix::sparse_matrix(static_matrix const&, vector&); -template void sparse_matrix>::add_new_element(unsigned int, unsigned int, const mpq&); -template void sparse_matrix>::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); -template bool sparse_matrix>::fill_eta_matrix(unsigned int, eta_matrix >**); -template const mpq & sparse_matrix>::get(unsigned int, unsigned int) const; -template unsigned sparse_matrix>::get_number_of_nonzeroes() const; -template bool sparse_matrix>::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); -template unsigned sparse_matrix>::lowest_row_in_column(unsigned int); -template bool sparse_matrix>::pivot_with_eta(unsigned int, eta_matrix >*, lp_settings&); -template void sparse_matrix>::prepare_for_factorization(); -template void sparse_matrix>::remove_element(vector>&, indexed_value&); -template void sparse_matrix>::replace_column(unsigned int, indexed_vector&, lp_settings&); -template void sparse_matrix>::set_max_in_row(vector>&); -template bool sparse_matrix>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); -template bool sparse_matrix>::shorten_active_matrix(unsigned int, eta_matrix >*); -template void sparse_matrix>::solve_y_U(vector&) const; -template sparse_matrix>::sparse_matrix(static_matrix > const&, vector&); -template void sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings &); -template void sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings&); -template void sparse_matrix>::double_solve_U_y(indexed_vector&, const lp_settings&); -template void sparse_matrix >::double_solve_U_y >(indexed_vector>&, const lp_settings&); -template void lp::sparse_matrix::solve_U_y_indexed_only(lp::indexed_vector&, const lp_settings&, vector &); -template void lp::sparse_matrix::solve_U_y_indexed_only(lp::indexed_vector&, const lp_settings &, vector &); -#ifdef Z3DEBUG -template bool sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -template bool sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -template bool sparse_matrix >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; -#endif -} -template void lp::sparse_matrix >::solve_U_y_indexed_only(lp::indexed_vector&, const lp_settings &, vector &); -template void lp::sparse_matrix::solve_U_y(vector&); -template void lp::sparse_matrix::double_solve_U_y(vector&); -template void lp::sparse_matrix::solve_U_y(vector&); -template void lp::sparse_matrix::double_solve_U_y(vector&); -template void lp::sparse_matrix >::solve_U_y >(vector >&); -template void lp::sparse_matrix >::double_solve_U_y >(vector >&); -template void lp::sparse_matrix::find_error_in_solution_U_y_indexed(lp::indexed_vector&, lp::indexed_vector&, const vector &); -template double lp::sparse_matrix::dot_product_with_row(unsigned int, lp::indexed_vector const&) const; -template void lp::sparse_matrix::find_error_in_solution_U_y_indexed(lp::indexed_vector&, lp::indexed_vector&, const vector &); -template lp::mpq lp::sparse_matrix::dot_product_with_row(unsigned int, lp::indexed_vector const&) const; -template void lp::sparse_matrix >::find_error_in_solution_U_y_indexed(lp::indexed_vector&, lp::indexed_vector&, const vector &); -template lp::mpq lp::sparse_matrix >::dot_product_with_row(unsigned int, lp::indexed_vector const&) const; -template void lp::sparse_matrix >::find_error_in_solution_U_y_indexed >(lp::indexed_vector >&, lp::indexed_vector >&, const vector &); -template lp::numeric_pair lp::sparse_matrix >::dot_product_with_row >(unsigned int, lp::indexed_vector > const&) const; -template void lp::sparse_matrix::extend_and_sort_active_rows(vector const&, vector&); - -template void lp::sparse_matrix >::extend_and_sort_active_rows(vector const&, vector&); - -template void lp::sparse_matrix >::solve_U_y(vector&); -template void lp::sparse_matrix >::double_solve_U_y(vector&); -template void lp::sparse_matrix< lp::mpq,lp::numeric_pair< lp::mpq> >::set(unsigned int,unsigned int, lp::mpq); -template void lp::sparse_matrix::solve_y_U_indexed(lp::indexed_vector&, const lp_settings & ); -template void lp::sparse_matrix::solve_y_U_indexed(lp::indexed_vector&, const lp_settings &); -template void lp::sparse_matrix >::solve_y_U_indexed(lp::indexed_vector&, const lp_settings &); - diff --git a/src/util/lp/sparse_vector.h b/src/util/lp/sparse_vector.h index 51639674c..67ea97a9d 100644 --- a/src/util/lp/sparse_vector.h +++ b/src/util/lp/sparse_vector.h @@ -42,7 +42,7 @@ public: } #endif void divide(T const & a) { - SASSERT(!lp_settings::is_eps_small_general(a, 1e-12)); + lp_assert(!lp_settings::is_eps_small_general(a, 1e-12)); for (auto & t : m_data) { t.second /= a; } } diff --git a/src/util/lp/square_dense_submatrix_instances.cpp b/src/util/lp/square_dense_submatrix.cpp similarity index 87% rename from src/util/lp/square_dense_submatrix_instances.cpp rename to src/util/lp/square_dense_submatrix.cpp index e1df0036e..802f3d74a 100644 --- a/src/util/lp/square_dense_submatrix_instances.cpp +++ b/src/util/lp/square_dense_submatrix.cpp @@ -19,15 +19,15 @@ Revision History: --*/ #include #include "util/vector.h" -#include "util/lp/square_dense_submatrix.hpp" -template void lp::square_dense_submatrix::init(lp::sparse_matrix*, unsigned int); -template lp::square_dense_submatrix::square_dense_submatrix(lp::sparse_matrix*, unsigned int); +#include "util/lp/square_dense_submatrix_def.h" +template void lp::square_dense_submatrix::init(lp::square_sparse_matrix*, unsigned int); +template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); template bool lp::square_dense_submatrix::is_L_matrix() const; template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); template int lp::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; template void lp::square_dense_submatrix::pivot(unsigned int, lp::lp_settings&); -template lp::square_dense_submatrix >::square_dense_submatrix(lp::sparse_matrix >*, unsigned int); +template lp::square_dense_submatrix >::square_dense_submatrix(lp::square_sparse_matrix >*, unsigned int); template void lp::square_dense_submatrix >::update_parent_matrix(lp::lp_settings&); template bool lp::square_dense_submatrix >::is_L_matrix() const; template void lp::square_dense_submatrix >::conjugate_by_permutation(lp::permutation_matrix >&); @@ -40,7 +40,7 @@ template void lp::square_dense_submatrix::apply_from_right(vecto template void lp::square_dense_submatrix::apply_from_left_local(lp::indexed_vector&, lp::lp_settings&); template void lp::square_dense_submatrix::apply_from_left_to_vector(vector&); -template lp::square_dense_submatrix::square_dense_submatrix(lp::sparse_matrix*, unsigned int); +template lp::square_dense_submatrix::square_dense_submatrix(lp::square_sparse_matrix*, unsigned int); template void lp::square_dense_submatrix::update_parent_matrix(lp::lp_settings&); template bool lp::square_dense_submatrix::is_L_matrix() const; template void lp::square_dense_submatrix::conjugate_by_permutation(lp::permutation_matrix&); diff --git a/src/util/lp/square_dense_submatrix.h b/src/util/lp/square_dense_submatrix.h index d43b2eadd..0c6bbdec3 100644 --- a/src/util/lp/square_dense_submatrix.h +++ b/src/util/lp/square_dense_submatrix.h @@ -34,7 +34,7 @@ Revision History: #include "util/lp/lp_settings.h" #include "util/lp/eta_matrix.h" #include "util/lp/binary_heap_upair_queue.h" -#include "util/lp/sparse_matrix.h" +#include "util/lp/square_sparse_matrix.h" namespace lp { template class square_dense_submatrix : public tail_matrix { @@ -45,11 +45,11 @@ class square_dense_submatrix : public tail_matrix { ref(unsigned i, square_dense_submatrix & s) : m_i_offset((i - s.m_index_start) * s.m_dim), m_s(s){} T & operator[] (unsigned j) { - SASSERT(j >= m_s.m_index_start); + lp_assert(j >= m_s.m_index_start); return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; } const T & operator[] (unsigned j) const { - SASSERT(j >= m_s.m_index_start); + lp_assert(j >= m_s.m_index_start); return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start]; } }; @@ -57,7 +57,7 @@ public: unsigned m_index_start; unsigned m_dim; vector m_v; - sparse_matrix * m_parent; + square_sparse_matrix * m_parent; permutation_matrix m_row_permutation; indexed_vector m_work_vector; public: @@ -66,15 +66,15 @@ public: square_dense_submatrix() {} - square_dense_submatrix (sparse_matrix *parent_matrix, unsigned index_start); + square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start); - void init(sparse_matrix *parent_matrix, unsigned index_start); + void init(square_sparse_matrix *parent_matrix, unsigned index_start); bool is_dense() const override { return true; } ref operator[] (unsigned i) { - SASSERT(i >= m_index_start); - SASSERT(i < m_parent->dimension()); + lp_assert(i >= m_index_start); + lp_assert(i < m_parent->dimension()); return ref(i, *this); } @@ -163,7 +163,7 @@ public: } } } - SASSERT(wcopy.is_OK()); + lp_assert(wcopy.is_OK()); apply_from_right(w.m_data); w.m_index.clear(); if (numeric_traits::precise()) { @@ -182,11 +182,11 @@ public: } } #else - SASSERT(w.is_OK()); - SASSERT(m_work_vector.is_OK()); + lp_assert(w.is_OK()); + lp_assert(m_work_vector.is_OK()); m_work_vector.resize(w.data_size()); m_work_vector.clear(); - SASSERT(m_work_vector.is_OK()); + lp_assert(m_work_vector.is_OK()); unsigned end = m_index_start + m_dim; for (unsigned k : w.m_index) { // find j such that k = adjust_row_inverse(j) @@ -203,7 +203,7 @@ public: } } m_work_vector.clean_up(); - SASSERT(m_work_vector.is_OK()); + lp_assert(m_work_vector.is_OK()); w = m_work_vector; #endif } diff --git a/src/util/lp/square_dense_submatrix.hpp b/src/util/lp/square_dense_submatrix_def.h similarity index 93% rename from src/util/lp/square_dense_submatrix.hpp rename to src/util/lp/square_dense_submatrix_def.h index cbf69c5dd..54f6e5327 100644 --- a/src/util/lp/square_dense_submatrix.hpp +++ b/src/util/lp/square_dense_submatrix_def.h @@ -21,7 +21,7 @@ Revision History: #include "util/lp/square_dense_submatrix.h" namespace lp { template -square_dense_submatrix::square_dense_submatrix (sparse_matrix *parent_matrix, unsigned index_start) : +square_dense_submatrix::square_dense_submatrix (square_sparse_matrix *parent_matrix, unsigned index_start) : m_index_start(index_start), m_dim(parent_matrix->dimension() - index_start), m_v(m_dim * m_dim), @@ -33,14 +33,14 @@ square_dense_submatrix::square_dense_submatrix (sparse_matrix *paren unsigned row = parent_matrix->adjust_row(i); for (auto & iv : parent_matrix->get_row_values(row)) { unsigned j = parent_matrix->adjust_column_inverse(iv.m_index); - SASSERT(j>= m_index_start); + lp_assert(j>= m_index_start); m_v[row_offset + j] = iv.m_value; } row_offset += m_dim; } } -template void square_dense_submatrix::init(sparse_matrix *parent_matrix, unsigned index_start) { +template void square_dense_submatrix::init(square_sparse_matrix *parent_matrix, unsigned index_start) { m_index_start = index_start; m_dim = parent_matrix->dimension() - index_start; m_v.resize(m_dim * m_dim); @@ -58,7 +58,7 @@ template void square_dense_submatrix::init(sparse template int square_dense_submatrix::find_pivot_column_in_row(unsigned i) const { int j = -1; T max = zero_of_type(); - SASSERT(i >= m_index_start); + lp_assert(i >= m_index_start); unsigned row_start = (i - m_index_start) * m_dim; for (unsigned k = i; k < m_parent->dimension(); k++) { unsigned col = adjust_column(k); // this is where the column is in the row @@ -79,14 +79,14 @@ template void square_dense_submatrix::pivot(un } template void square_dense_submatrix::pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings) { - SASSERT(i < row); + lp_assert(i < row); unsigned pj = adjust_column(i); // the pivot column unsigned pjd = pj - m_index_start; unsigned pivot_row_offset = (i-m_index_start)*m_dim; T pivot = m_v[pivot_row_offset + pjd]; unsigned row_offset= (row-m_index_start)*m_dim; T m = m_v[row_offset + pjd]; - SASSERT(!is_zero(pivot)); + lp_assert(!is_zero(pivot)); m_v[row_offset + pjd] = -m * pivot; // creating L matrix for (unsigned j = m_index_start; j < m_parent->dimension(); j++) { if (j == pj) { @@ -109,7 +109,7 @@ template void square_dense_submatrix::divide_r unsigned pj = adjust_column(i); // the pivot column unsigned irow_offset = (i - m_index_start) * m_dim; T pivot = m_v[irow_offset + pj - m_index_start]; - SASSERT(!is_zero(pivot)); + lp_assert(!is_zero(pivot)); for (unsigned k = m_index_start; k < m_parent->dimension(); k++) { if (k == pj){ m_v[irow_offset++] = one_of_type() / pivot; // creating the L matrix diagonal @@ -173,7 +173,7 @@ template void square_dense_submatrix::push_new template template L square_dense_submatrix::row_by_vector_product(unsigned i, const vector & v) { - SASSERT(i >= m_index_start); + lp_assert(i >= m_index_start); unsigned row_in_subm = i - m_index_start; unsigned row_offset = row_in_subm * m_dim; @@ -186,7 +186,7 @@ L square_dense_submatrix::row_by_vector_product(unsigned i, const vector template L square_dense_submatrix::column_by_vector_product(unsigned j, const vector & v) { - SASSERT(j >= m_index_start); + lp_assert(j >= m_index_start); unsigned offset = j - m_index_start; L r = zero_of_type(); @@ -197,7 +197,7 @@ L square_dense_submatrix::column_by_vector_product(unsigned j, const vecto template template L square_dense_submatrix::row_by_indexed_vector_product(unsigned i, const indexed_vector & v) { - SASSERT(i >= m_index_start); + lp_assert(i >= m_index_start); unsigned row_in_subm = i - m_index_start; unsigned row_offset = row_in_subm * m_dim; @@ -264,8 +264,8 @@ void square_dense_submatrix::apply_from_left_local(indexed_vector & w, #ifdef Z3DEBUG // cout << "w final" << endl; // print_vector(w.m_data); - // SASSERT(vectors_are_equal(deb_w, w.m_data)); - // SASSERT(w.is_OK()); + // lp_assert(vectors_are_equal(deb_w, w.m_data)); + // lp_assert(w.is_OK()); #endif } @@ -295,16 +295,16 @@ void square_dense_submatrix::apply_from_left_to_vector(vector & w) { #ifdef Z3DEBUG // cout << "w final" << endl; // print_vector(w.m_data); - // SASSERT(vectors_are_equal(deb_w, w)); + // lp_assert(vectors_are_equal(deb_w, w)); #endif } template bool square_dense_submatrix::is_L_matrix() const { #ifdef Z3DEBUG - SASSERT(m_row_permutation.is_identity()); + lp_assert(m_row_permutation.is_identity()); for (unsigned i = 0; i < m_parent->dimension(); i++) { if (i < m_index_start) { - SASSERT(m_column_permutation[i] == i); + lp_assert(m_column_permutation[i] == i); continue; } unsigned row_offs = (i-m_index_start)*m_dim; @@ -312,9 +312,9 @@ template bool square_dense_submatrix::is_L_mat unsigned j = m_index_start + k; unsigned jex = adjust_column_inverse(j); if (jex > i) { - SASSERT(is_zero(m_v[row_offs + k])); + lp_assert(is_zero(m_v[row_offs + k])); } else if (jex == i) { - SASSERT(!is_zero(m_v[row_offs + k])); + lp_assert(!is_zero(m_v[row_offs + k])); } } } @@ -342,7 +342,7 @@ template void square_dense_submatrix::apply_from_ } w = t; #ifdef Z3DEBUG - // SASSERT(vector_are_equal(deb_w, w)); + // lp_assert(vector_are_equal(deb_w, w)); #endif } diff --git a/src/util/lp/square_sparse_matrix.cpp b/src/util/lp/square_sparse_matrix.cpp new file mode 100644 index 000000000..6c65cd863 --- /dev/null +++ b/src/util/lp/square_sparse_matrix.cpp @@ -0,0 +1,119 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#include +#include "util/vector.h" +#include "util/lp/lp_settings.h" +#include "util/lp/lu.h" +#include "util/lp/square_sparse_matrix_def.h" +#include "util/lp/dense_matrix.h" +namespace lp { +template double square_sparse_matrix::dot_product_with_row(unsigned int, vector const&) const; +template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const double&); +template void square_sparse_matrix::divide_row_by_constant(unsigned int, const double&, lp_settings&); +template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); +template const double & square_sparse_matrix::get(unsigned int, unsigned int) const; +template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; +template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); +template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); +template bool square_sparse_matrix::pivot_row_to_row(unsigned int, const double&, unsigned int, lp_settings&); +template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); +template void square_sparse_matrix::prepare_for_factorization(); +template void square_sparse_matrix::remove_element(vector >&, indexed_value&); +template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); +template void square_sparse_matrix::set(unsigned int, unsigned int, double); +template void square_sparse_matrix::set_max_in_row(vector >&); +template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); +template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); +template void square_sparse_matrix::solve_y_U(vector&) const; +template square_sparse_matrix::square_sparse_matrix(unsigned int, unsigned); +template void square_sparse_matrix::add_new_element(unsigned int, unsigned int, const mpq&); +template void square_sparse_matrix::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); +template bool square_sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); +template mpq const & square_sparse_matrix::get(unsigned int, unsigned int) const; +template unsigned square_sparse_matrix::get_number_of_nonzeroes() const; +template bool square_sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); +template unsigned square_sparse_matrix::lowest_row_in_column(unsigned int); +template bool square_sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); +template void square_sparse_matrix::prepare_for_factorization(); +template void square_sparse_matrix::remove_element(vector> &, indexed_value&); +template void square_sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); +template void square_sparse_matrix::set_max_in_row(vector>&); +template bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); +template bool square_sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); +template void square_sparse_matrix::solve_y_U(vector&) const; +template void square_sparse_matrix>::add_new_element(unsigned int, unsigned int, const mpq&); +template void square_sparse_matrix>::divide_row_by_constant(unsigned int, const mpq&, lp_settings&); +template bool square_sparse_matrix>::fill_eta_matrix(unsigned int, eta_matrix >**); +template const mpq & square_sparse_matrix>::get(unsigned int, unsigned int) const; +template unsigned square_sparse_matrix>::get_number_of_nonzeroes() const; +template bool square_sparse_matrix>::get_pivot_for_column(unsigned int&, unsigned int&, int, unsigned int); +template unsigned square_sparse_matrix>::lowest_row_in_column(unsigned int); +template bool square_sparse_matrix>::pivot_with_eta(unsigned int, eta_matrix >*, lp_settings&); +template void square_sparse_matrix>::prepare_for_factorization(); +template void square_sparse_matrix>::remove_element(vector>&, indexed_value&); +template void square_sparse_matrix>::replace_column(unsigned int, indexed_vector&, lp_settings&); +template void square_sparse_matrix>::set_max_in_row(vector>&); +template bool square_sparse_matrix>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); +template bool square_sparse_matrix>::shorten_active_matrix(unsigned int, eta_matrix >*); +template void square_sparse_matrix>::solve_y_U(vector&) const; +template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings &); +template void square_sparse_matrix::double_solve_U_y(indexed_vector&, const lp_settings&); +template void square_sparse_matrix>::double_solve_U_y(indexed_vector&, const lp_settings&); +template void square_sparse_matrix >::double_solve_U_y >(indexed_vector>&, const lp_settings&); +template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings&, vector &); +template void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); +#ifdef Z3DEBUG +template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; +template bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; +template bool square_sparse_matrix >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; +#endif + +template void square_sparse_matrix >::solve_U_y_indexed_only(indexed_vector&, const lp_settings &, vector &); +template void square_sparse_matrix::solve_U_y(vector&); +template void square_sparse_matrix::double_solve_U_y(vector&); +template void square_sparse_matrix::solve_U_y(vector&); +template void square_sparse_matrix::double_solve_U_y(vector&); +template void square_sparse_matrix >::solve_U_y >(vector >&); +template void square_sparse_matrix >::double_solve_U_y >(vector >&); +template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); +template double square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; +template void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); +template mpq square_sparse_matrix::dot_product_with_row(unsigned int, indexed_vector const&) const; +template void square_sparse_matrix >::find_error_in_solution_U_y_indexed(indexed_vector&, indexed_vector&, const vector &); +template mpq square_sparse_matrix >::dot_product_with_row(unsigned int, indexed_vector const&) const; +template void square_sparse_matrix >::find_error_in_solution_U_y_indexed >(indexed_vector >&, indexed_vector >&, const vector &); +template numeric_pair square_sparse_matrix >::dot_product_with_row >(unsigned int, indexed_vector > const&) const; +template void square_sparse_matrix::extend_and_sort_active_rows(vector const&, vector&); + +template void square_sparse_matrix >::extend_and_sort_active_rows(vector const&, vector&); + +template void square_sparse_matrix >::solve_U_y(vector&); +template void square_sparse_matrix >::double_solve_U_y(vector&); +template void square_sparse_matrix< mpq,numeric_pair< mpq> >::set(unsigned int,unsigned int, mpq); +template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings & ); +template void square_sparse_matrix::solve_y_U_indexed(indexed_vector&, const lp_settings &); +template void square_sparse_matrix >::solve_y_U_indexed(indexed_vector&, const lp_settings &); + +template square_sparse_matrix::square_sparse_matrix(static_matrix const&, vector&); +template square_sparse_matrix::square_sparse_matrix (static_matrix const&, vector&); +template square_sparse_matrix >::square_sparse_matrix(static_matrix > const&, vector&); +} +template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); +template void lp::square_sparse_matrix::copy_from_input_on_basis >(lp::static_matrix const&, vector&); diff --git a/src/util/lp/sparse_matrix.h b/src/util/lp/square_sparse_matrix.h similarity index 91% rename from src/util/lp/sparse_matrix.h rename to src/util/lp/square_sparse_matrix.h index 44111a9fd..d84a8a289 100644 --- a/src/util/lp/sparse_matrix.h +++ b/src/util/lp/square_sparse_matrix.h @@ -39,7 +39,7 @@ Revision History: namespace lp { // it is a square matrix template -class sparse_matrix +class square_sparse_matrix #ifdef Z3DEBUG : public matrix #endif @@ -96,29 +96,46 @@ public: return m_column_permutation[col]; } - void copy_column_from_static_matrix(unsigned col, static_matrix const &A, unsigned col_index_in_the_new_matrix); - void copy_B(static_matrix const &A, vector & basis); + template + void copy_column_from_input(unsigned input_column, const M& A, unsigned j); + template + void copy_column_from_input_with_possible_zeros(const M& A, unsigned j); + + template + void copy_from_input(const M& A); + template + void copy_from_input_on_basis(const M& A, vector & basis); public: - // constructor that copies columns of the basis from A - sparse_matrix(static_matrix const &A, vector & basis); + + // constructors + template + square_sparse_matrix(const M &A, vector& basis); + template + square_sparse_matrix(const M &A); + + square_sparse_matrix(unsigned dim, unsigned); // the second parameter is needed to distinguish this + // constructor from the one above + + + class ref_matrix_element { - sparse_matrix & m_matrix; + square_sparse_matrix & m_matrix; unsigned m_row; unsigned m_col; public: - ref_matrix_element(sparse_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} + ref_matrix_element(square_sparse_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} ref_matrix_element & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; } ref_matrix_element & operator=(ref_matrix_element const & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } operator T () const { return m_matrix.get(m_row, m_col); } }; class ref_row { - sparse_matrix & m_matrix; + square_sparse_matrix & m_matrix; unsigned m_row; public: - ref_row(sparse_matrix & m, unsigned row) : m_matrix(m), m_row(row) {} + ref_row(square_sparse_matrix & m, unsigned row) : m_matrix(m), m_row(row) {} ref_matrix_element operator[](unsigned col) const { return ref_matrix_element(m_matrix, m_row, col); } }; @@ -154,11 +171,6 @@ public: return m_columns[col].m_values; } - // constructor creating a zero matrix of dim*dim - sparse_matrix(unsigned dim); - - - unsigned dimension() const {return static_cast(m_row_permutation.size());} #ifdef Z3DEBUG @@ -221,19 +233,19 @@ public: void multiply_from_right(permutation_matrix& p) { // m_dense = m_dense * p; m_column_permutation.multiply_by_permutation_from_right(p); - // SASSERT(*this == m_dense); + // lp_assert(*this == m_dense); } void multiply_from_left(permutation_matrix& p) { // m_dense = p * m_dense; m_row_permutation.multiply_by_permutation_from_left(p); - // SASSERT(*this == m_dense); + // lp_assert(*this == m_dense); } void multiply_from_left_with_reverse(permutation_matrix& p) { // m_dense = p * m_dense; m_row_permutation.multiply_by_permutation_reverse_from_left(p); - // SASSERT(*this == m_dense); + // lp_assert(*this == m_dense); } // adding delta columns at the end of the matrix @@ -242,17 +254,13 @@ public: void delete_column(int i); void swap_columns(unsigned a, unsigned b) { - // cout << "swaapoiiin" << std::endl; - // dense_matrix d(*this); m_column_permutation.transpose_from_left(a, b); - // d.swap_columns(a, b); - // SASSERT(*this == d); } void swap_rows(unsigned a, unsigned b) { m_row_permutation.transpose_from_right(a, b); // m_dense.swap_rows(a, b); - // SASSERT(*this == m_dense); + // lp_assert(*this == m_dense); } void divide_row_by_constant(unsigned i, const T & t, lp_settings & settings); @@ -408,7 +416,7 @@ public: void process_index_recursively_for_y_U(unsigned j, vector & sorted_rows); void resize(unsigned new_dim) { unsigned old_dim = dimension(); - SASSERT(new_dim >= old_dim); + lp_assert(new_dim >= old_dim); for (unsigned j = old_dim; j < new_dim; j++) { m_rows.push_back(vector>()); m_columns.push_back(col_header()); diff --git a/src/util/lp/sparse_matrix.hpp b/src/util/lp/square_sparse_matrix_def.h similarity index 74% rename from src/util/lp/sparse_matrix.hpp rename to src/util/lp/square_sparse_matrix_def.h index d2040d313..791bdb6ae 100644 --- a/src/util/lp/sparse_matrix.hpp +++ b/src/util/lp/square_sparse_matrix_def.h @@ -19,38 +19,61 @@ Revision History: --*/ #include "util/vector.h" -#include "util/lp/sparse_matrix.h" +#include "util/lp/square_sparse_matrix.h" #include #include namespace lp { template -void sparse_matrix::copy_column_from_static_matrix(unsigned col, static_matrix const &A, unsigned col_index_in_the_new_matrix) { - vector const & A_col_vector = A.m_columns[col]; - unsigned size = static_cast(A_col_vector.size()); - vector> & new_column_vector = m_columns[col_index_in_the_new_matrix].m_values; - for (unsigned l = 0; l < size; l++) { - column_cell const & col_cell = A_col_vector[l]; +template +void square_sparse_matrix::copy_column_from_input(unsigned input_column, const M& A, unsigned j) { + vector> & new_column_vector = m_columns[j].m_values; + for (const auto & c : A.column(input_column)) { unsigned col_offset = static_cast(new_column_vector.size()); - vector> & row_vector = m_rows[col_cell.m_i]; + vector> & row_vector = m_rows[c.var()]; unsigned row_offset = static_cast(row_vector.size()); - const T & val = A.get_val(col_cell); - new_column_vector.push_back(indexed_value(val, col_cell.m_i, row_offset)); - row_vector.push_back(indexed_value(val, col_index_in_the_new_matrix, col_offset)); + new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); + row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); m_n_of_active_elems++; } } template -void sparse_matrix::copy_B(static_matrix const &A, vector & basis) { - unsigned m = A.row_count(); // this should be the size of basis +template +void square_sparse_matrix::copy_column_from_input_with_possible_zeros(const M& A, unsigned j) { + vector> & new_column_vector = m_columns[j].m_values; + for (const auto & c : A.column(j)) { + if (is_zero(c.coeff())) + continue; + unsigned col_offset = static_cast(new_column_vector.size()); + vector> & row_vector = m_rows[c.var()]; + unsigned row_offset = static_cast(row_vector.size()); + new_column_vector.push_back(indexed_value(c.coeff(), c.var(), row_offset)); + row_vector.push_back(indexed_value(c.coeff(), j, col_offset)); + m_n_of_active_elems++; + } +} + +template +template +void square_sparse_matrix::copy_from_input_on_basis(const M & A, vector & basis) { + unsigned m = A.row_count(); for (unsigned j = m; j-- > 0;) { - copy_column_from_static_matrix(basis[j], A, j); + copy_column_from_input(basis[j], A, j); + } +} +template +template +void square_sparse_matrix::copy_from_input(const M & A) { + unsigned m = A.row_count(); + for (unsigned j = m; j-- > 0;) { + copy_column_from_input_with_possible_zeros(A, j); } } // constructor that copies columns of the basis from A template -sparse_matrix::sparse_matrix(static_matrix const &A, vector & basis) : +template +square_sparse_matrix::square_sparse_matrix(const M &A, vector & basis) : m_n_of_active_elems(0), m_pivot_queue(A.row_count()), m_row_permutation(A.row_count()), @@ -59,11 +82,26 @@ sparse_matrix::sparse_matrix(static_matrix const &A, vector -void sparse_matrix::set_with_no_adjusting_for_row(unsigned row, unsigned col, T val) { // should not be used in efficient code +template +square_sparse_matrix::square_sparse_matrix(const M &A) : + m_n_of_active_elems(0), + m_pivot_queue(A.row_count()), + m_row_permutation(A.row_count()), + m_column_permutation(A.row_count()), + m_work_pivot_vector(A.row_count(), -1), + m_processed(A.row_count()) { + init_row_headers(); + init_column_headers(); + copy_from_input(A); +} + + +template +void square_sparse_matrix::set_with_no_adjusting_for_row(unsigned row, unsigned col, T val) { // should not be used in efficient code vector> & row_vec = m_rows[row]; for (auto & iv : row_vec) { if (iv.m_index == col) { @@ -76,7 +114,7 @@ void sparse_matrix::set_with_no_adjusting_for_row(unsigned row, unsigned c } template -void sparse_matrix::set_with_no_adjusting_for_col(unsigned row, unsigned col, T val) { // should not be used in efficient code +void square_sparse_matrix::set_with_no_adjusting_for_col(unsigned row, unsigned col, T val) { // should not be used in efficient code vector> & col_vec = m_columns[col].m_values; for (auto & iv : col_vec) { if (iv.m_index == row) { @@ -90,23 +128,23 @@ void sparse_matrix::set_with_no_adjusting_for_col(unsigned row, unsigned c template -void sparse_matrix::set_with_no_adjusting(unsigned row, unsigned col, T val) { // should not be used in efficient code +void square_sparse_matrix::set_with_no_adjusting(unsigned row, unsigned col, T val) { // should not be used in efficient code set_with_no_adjusting_for_row(row, col, val); set_with_no_adjusting_for_col(row, col, val); } template -void sparse_matrix::set(unsigned row, unsigned col, T val) { // should not be used in efficient code - SASSERT(row < dimension() && col < dimension()); +void square_sparse_matrix::set(unsigned row, unsigned col, T val) { // should not be used in efficient code + lp_assert(row < dimension() && col < dimension()); // m_dense.set_elem(row, col, val); row = adjust_row(row); col = adjust_column(col); set_with_no_adjusting(row, col, val); - // SASSERT(*this == m_dense); + // lp_assert(*this == m_dense); } template -T const & sparse_matrix::get_not_adjusted(unsigned row, unsigned col) const { +T const & square_sparse_matrix::get_not_adjusted(unsigned row, unsigned col) const { for (indexed_value const & iv : m_rows[row]) { if (iv.m_index == col) { return iv.m_value; @@ -116,7 +154,7 @@ T const & sparse_matrix::get_not_adjusted(unsigned row, unsigned col) cons } template -T const & sparse_matrix::get(unsigned row, unsigned col) const { // should not be used in efficient code +T const & square_sparse_matrix::get(unsigned row, unsigned col) const { // should not be used in efficient code row = adjust_row(row); auto & row_chunk = m_rows[row]; col = adjust_column(col); @@ -130,7 +168,7 @@ T const & sparse_matrix::get(unsigned row, unsigned col) const { // should // constructor creating a zero matrix of dim*dim template -sparse_matrix::sparse_matrix(unsigned dim) : +square_sparse_matrix::square_sparse_matrix(unsigned dim, unsigned ) : m_pivot_queue(dim), // dim will be the initial size of the queue m_row_permutation(dim), m_column_permutation(dim), @@ -141,21 +179,21 @@ sparse_matrix::sparse_matrix(unsigned dim) : } template -void sparse_matrix::init_row_headers() { +void square_sparse_matrix::init_row_headers() { for (unsigned l = 0; l < m_row_permutation.size(); l++) { m_rows.push_back(vector>()); } } template -void sparse_matrix::init_column_headers() { // we alway have only square sparse_matrix +void square_sparse_matrix::init_column_headers() { // we alway have only square square_sparse_matrix for (unsigned l = 0; l < m_row_permutation.size(); l++) { m_columns.push_back(col_header()); } } template -unsigned sparse_matrix::lowest_row_in_column(unsigned j) { +unsigned square_sparse_matrix::lowest_row_in_column(unsigned j) { auto & mc = get_column_values(adjust_column(j)); unsigned ret = 0; for (auto & iv : mc) { @@ -168,7 +206,7 @@ unsigned sparse_matrix::lowest_row_in_column(unsigned j) { } template -void sparse_matrix::remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset) { +void square_sparse_matrix::remove_element(vector> & row_vals, unsigned row_offset, vector> & column_vals, unsigned column_offset) { if (column_offset != column_vals.size() - 1) { auto & column_iv = column_vals[column_offset] = column_vals.back(); // copy from the tail column_iv_other(column_iv).m_other = column_offset; @@ -187,14 +225,14 @@ void sparse_matrix::remove_element(vector> & row_vals, un } template -void sparse_matrix::remove_element(vector> & row_chunk, indexed_value & row_el_iv) { +void square_sparse_matrix::remove_element(vector> & row_chunk, indexed_value & row_el_iv) { auto & column_chunk = get_column_values(row_el_iv.m_index); indexed_value & col_el_iv = column_chunk[row_el_iv.m_other]; remove_element(row_chunk, col_el_iv.m_other, column_chunk, row_el_iv.m_other); } template -void sparse_matrix::put_max_index_to_0(vector> & row_vals, unsigned max_index) { +void square_sparse_matrix::put_max_index_to_0(vector> & row_vals, unsigned max_index) { if (max_index == 0) return; indexed_value * max_iv = & row_vals[max_index]; indexed_value * start_iv = & row_vals[0]; @@ -209,7 +247,7 @@ void sparse_matrix::put_max_index_to_0(vector> & row_vals } template -void sparse_matrix::set_max_in_row(vector> & row_vals) { +void square_sparse_matrix::set_max_in_row(vector> & row_vals) { if (row_vals.size() == 0) return; T max_val = abs(row_vals[0].m_value); @@ -225,7 +263,7 @@ void sparse_matrix::set_max_in_row(vector> & row_vals) { } template -bool sparse_matrix::pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings) { +bool square_sparse_matrix::pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings) { const T& pivot = eta_matrix->get_diagonal_element(); for (auto & it : eta_matrix->m_column_vector.m_data) { if (!pivot_row_to_row(i, it.second, it.first, settings)) { @@ -243,7 +281,7 @@ bool sparse_matrix::pivot_with_eta(unsigned i, eta_matrix *eta_matri // returns the offset of the pivot column in the row template -void sparse_matrix::scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column) { +void square_sparse_matrix::scan_row_to_work_vector_and_remove_pivot_column(unsigned row, unsigned pivot_column) { auto & rvals = m_rows[row]; unsigned size = rvals.size(); for (unsigned j = 0; j < size; j++) { @@ -260,7 +298,7 @@ void sparse_matrix::scan_row_to_work_vector_and_remove_pivot_column(unsign #ifdef Z3DEBUG template -vector sparse_matrix::get_full_row(unsigned i) const { +vector square_sparse_matrix::get_full_row(unsigned i) const { vector r; for (unsigned j = 0; j < column_count(); j++) r.push_back(get(i, j)); @@ -275,9 +313,9 @@ vector sparse_matrix::get_full_row(unsigned i) const { // After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, // Returns false if the resulting row is all zeroes, and true otherwise template -bool sparse_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ) { - SASSERT(i < dimension() && i0 < dimension()); - SASSERT(i != i0); +bool square_sparse_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, lp_settings & settings ) { + lp_assert(i < dimension() && i0 < dimension()); + lp_assert(i != i0); unsigned pivot_col = adjust_column(i); i = adjust_row(i); i0 = adjust_row(i0); @@ -334,7 +372,7 @@ bool sparse_matrix::pivot_row_to_row(unsigned i, const T& alpha, unsigned // set the max val as well // returns false if the resulting row is all zeroes, and true otherwise template -bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, +bool square_sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, lp_settings & settings) { remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(i0, work_vec, settings); // all non-zero elements in m_work_pivot_vector are new @@ -342,7 +380,7 @@ bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adj if (numeric_traits::is_zero(work_vec[j])) { continue; } - SASSERT(!settings.abs_val_is_smaller_than_drop_tolerance(work_vec[j])); + lp_assert(!settings.abs_val_is_smaller_than_drop_tolerance(work_vec[j])); add_new_element(i0, adjust_column(j), work_vec[j]); work_vec[j] = numeric_traits::zero(); } @@ -358,7 +396,7 @@ bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adj template -void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements(unsigned row) { +void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements(unsigned row) { auto & row_vals = m_rows[row]; for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing // elements from row_vals @@ -377,7 +415,7 @@ void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements // work_vec here has not adjusted column indices template -void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings) { +void square_sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings) { auto & row_vals = m_rows[row]; for (unsigned k = static_cast(row_vals.size()); k-- > 0;) { // we cannot simply run the iterator since we are removing // elements from row_vals @@ -387,7 +425,7 @@ void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements T val = work_vec[rj]; if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { remove_element(row_vals, row_el_iv); - SASSERT(numeric_traits::is_zero(val)); + lp_assert(numeric_traits::is_zero(val)); } else { m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value = val); work_vec[rj] = numeric_traits::zero(); @@ -398,7 +436,7 @@ void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements // adding delta columns at the end of the matrix template -void sparse_matrix::add_columns_at_the_end(unsigned delta) { +void square_sparse_matrix::add_columns_at_the_end(unsigned delta) { for (unsigned i = 0; i < delta; i++) { col_header col_head; m_columns.push_back(col_head); @@ -407,8 +445,8 @@ void sparse_matrix::add_columns_at_the_end(unsigned delta) { } template -void sparse_matrix::delete_column(int i) { - SASSERT(i < dimension()); +void square_sparse_matrix::delete_column(int i) { + lp_assert(i < dimension()); for (auto cell = m_columns[i].m_head; cell != nullptr;) { auto next_cell = cell->m_down; kill_cell(cell); @@ -417,8 +455,8 @@ void sparse_matrix::delete_column(int i) { } template -void sparse_matrix::divide_row_by_constant(unsigned i, const T & t, lp_settings & settings) { - SASSERT(!settings.abs_val_is_smaller_than_zero_tolerance(t)); +void square_sparse_matrix::divide_row_by_constant(unsigned i, const T & t, lp_settings & settings) { + lp_assert(!settings.abs_val_is_smaller_than_zero_tolerance(t)); i = adjust_row(i); for (auto & iv : m_rows[i]) { T &v = iv.m_value; @@ -434,7 +472,7 @@ void sparse_matrix::divide_row_by_constant(unsigned i, const T & t, lp_set // solving x * this = y, and putting the answer into y // the matrix here has to be upper triangular template -void sparse_matrix::solve_y_U(vector & y) const { // works by rows +void square_sparse_matrix::solve_y_U(vector & y) const { // works by rows #ifdef Z3DEBUG // T * rs = clone_vector(y, dimension()); #endif @@ -455,7 +493,7 @@ void sparse_matrix::solve_y_U(vector & y) const { // works by rows // dense_matrix deb(*this); // T * clone_y = clone_vector(y, dimension()); // deb.apply_from_right(clone_y); - // SASSERT(vectors_are_equal(rs, clone_y, dimension())); + // lp_assert(vectors_are_equal(rs, clone_y, dimension())); // delete [] clone_y; // delete [] rs; #endif @@ -464,7 +502,7 @@ void sparse_matrix::solve_y_U(vector & y) const { // works by rows // solving x * this = y, and putting the answer into y // the matrix here has to be upper triangular template -void sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_settings & settings) { +void square_sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_settings & settings) { #if 0 && Z3DEBUG vector ycopy(y.m_data); if (numeric_traits::precise() == false) @@ -489,10 +527,10 @@ void sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_sett y.m_data[j] = zero_of_type(); } - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); #if 0 && Z3DEBUG if (numeric_traits::precise() == false) - SASSERT(vectors_are_equal(ycopy, y.m_data)); + lp_assert(vectors_are_equal(ycopy, y.m_data)); #endif } @@ -525,7 +563,7 @@ void sparse_matrix::solve_y_U_indexed(indexed_vector & y, const lp_sett template template -void sparse_matrix::find_error_in_solution_U_y(vector& y_orig, vector & y) { +void square_sparse_matrix::find_error_in_solution_U_y(vector& y_orig, vector & y) { unsigned i = dimension(); while (i--) { y_orig[i] -= dot_product_with_row(i, y); @@ -534,7 +572,7 @@ void sparse_matrix::find_error_in_solution_U_y(vector& y_orig, vector template -void sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows) { +void square_sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector& y_orig, indexed_vector & y, const vector& sorted_active_rows) { for (unsigned i: sorted_active_rows) y_orig.add_value_at_index(i, -dot_product_with_row(i, y)); // cannot round up here!!! // y_orig can contain very small values @@ -543,7 +581,7 @@ void sparse_matrix::find_error_in_solution_U_y_indexed(indexed_vector& template template -void sparse_matrix::add_delta_to_solution(const vector& del, vector & y) { +void square_sparse_matrix::add_delta_to_solution(const vector& del, vector & y) { unsigned i = dimension(); while (i--) { y[i] += del[i]; @@ -551,21 +589,21 @@ void sparse_matrix::add_delta_to_solution(const vector& del, vector } template template -void sparse_matrix::add_delta_to_solution(const indexed_vector& del, indexed_vector & y) { -// SASSERT(del.is_OK()); - // SASSERT(y.is_OK()); +void square_sparse_matrix::add_delta_to_solution(const indexed_vector& del, indexed_vector & y) { +// lp_assert(del.is_OK()); + // lp_assert(y.is_OK()); for (auto i : del.m_index) { y.add_value_at_index(i, del[i]); } } template template -void sparse_matrix::double_solve_U_y(indexed_vector& y, const lp_settings & settings){ - SASSERT(y.is_OK()); +void square_sparse_matrix::double_solve_U_y(indexed_vector& y, const lp_settings & settings){ + lp_assert(y.is_OK()); indexed_vector y_orig(y); // copy y aside vector active_rows; solve_U_y_indexed_only(y, settings, active_rows); - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); find_error_in_solution_U_y_indexed(y_orig, y, active_rows); // y_orig contains the error now if (y_orig.m_index.size() * ratio_of_index_size_to_all_size() < 32 * dimension()) { @@ -578,11 +616,11 @@ void sparse_matrix::double_solve_U_y(indexed_vector& y, const lp_settin add_delta_to_solution(y_orig.m_data, y.m_data); y.restore_index_and_clean_from_data(); } - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); } template template -void sparse_matrix::double_solve_U_y(vector& y){ +void square_sparse_matrix::double_solve_U_y(vector& y){ vector y_orig(y); // copy y aside solve_U_y(y); find_error_in_solution_U_y(y_orig, y); @@ -595,7 +633,7 @@ void sparse_matrix::double_solve_U_y(vector& y){ // the matrix here has to be upper triangular template template -void sparse_matrix::solve_U_y(vector & y) { // it is a column wise version +void square_sparse_matrix::solve_U_y(vector & y) { // it is a column wise version #ifdef Z3DEBUG // T * rs = clone_vector(y, dimension()); #endif @@ -614,12 +652,12 @@ void sparse_matrix::solve_U_y(vector & y) { // it is a column wise vers // dense_matrix deb(*this); // T * clone_y = clone_vector(y, dimension()); // deb.apply_from_left(clone_y); - // SASSERT(vectors_are_equal(rs, clone_y, dimension())); + // lp_assert(vectors_are_equal(rs, clone_y, dimension())); #endif } template -void sparse_matrix::process_index_recursively_for_y_U(unsigned j, vector & sorted_active_rows) { - SASSERT(m_processed[j] == false); +void square_sparse_matrix::process_index_recursively_for_y_U(unsigned j, vector & sorted_active_rows) { + lp_assert(m_processed[j] == false); m_processed[j]=true; auto & row = m_rows[adjust_row(j)]; for (auto & c : row) { @@ -633,8 +671,8 @@ void sparse_matrix::process_index_recursively_for_y_U(unsigned j, vector -void sparse_matrix::process_column_recursively(unsigned j, vector & sorted_active_rows) { - SASSERT(m_processed[j] == false); +void square_sparse_matrix::process_column_recursively(unsigned j, vector & sorted_active_rows) { + lp_assert(m_processed[j] == false); auto & mc = m_columns[adjust_column(j)].m_values; for (auto & iv : mc) { unsigned i = adjust_row_inverse(iv.m_index); @@ -649,7 +687,7 @@ void sparse_matrix::process_column_recursively(unsigned j, vector -void sparse_matrix::create_graph_G(const vector & index_or_right_side, vector & sorted_active_rows) { +void square_sparse_matrix::create_graph_G(const vector & index_or_right_side, vector & sorted_active_rows) { for (auto i : index_or_right_side) { if (m_processed[i]) continue; process_column_recursively(i, sorted_active_rows); @@ -662,7 +700,7 @@ void sparse_matrix::create_graph_G(const vector & index_or_right template -void sparse_matrix::extend_and_sort_active_rows(const vector & index_or_right_side, vector & sorted_active_rows) { +void square_sparse_matrix::extend_and_sort_active_rows(const vector & index_or_right_side, vector & sorted_active_rows) { for (auto i : index_or_right_side) { if (m_processed[i]) continue; process_index_recursively_for_y_U(i, sorted_active_rows); @@ -676,7 +714,7 @@ void sparse_matrix::extend_and_sort_active_rows(const vector & i template template -void sparse_matrix::solve_U_y_indexed_only(indexed_vector & y, const lp_settings & settings, vector & sorted_active_rows) { // it is a column wise version +void square_sparse_matrix::solve_U_y_indexed_only(indexed_vector & y, const lp_settings & settings, vector & sorted_active_rows) { // it is a column wise version create_graph_G(y.m_index, sorted_active_rows); for (auto k = sorted_active_rows.size(); k-- > 0;) { @@ -699,18 +737,18 @@ void sparse_matrix::solve_U_y_indexed_only(indexed_vector & y, const lp y[j] = zero_of_type(); } - SASSERT(y.is_OK()); + lp_assert(y.is_OK()); #ifdef Z3DEBUG // dense_matrix deb(this); // vector clone_y(y.m_data); // deb.apply_from_left(clone_y); - // SASSERT(vectors_are_equal(rs, clone_y)); + // lp_assert(vectors_are_equal(rs, clone_y)); #endif } template template -L sparse_matrix::dot_product_with_row (unsigned row, const vector & y) const { +L square_sparse_matrix::dot_product_with_row (unsigned row, const vector & y) const { L ret = zero_of_type(); auto & mc = get_row_values(adjust_row(row)); for (auto & c : mc) { @@ -722,7 +760,7 @@ L sparse_matrix::dot_product_with_row (unsigned row, const vector & y) template template -L sparse_matrix::dot_product_with_row (unsigned row, const indexed_vector & y) const { +L square_sparse_matrix::dot_product_with_row (unsigned row, const indexed_vector & y) const { L ret = zero_of_type(); auto & mc = get_row_values(adjust_row(row)); for (auto & c : mc) { @@ -734,7 +772,7 @@ L sparse_matrix::dot_product_with_row (unsigned row, const indexed_vector< template -unsigned sparse_matrix::get_number_of_nonzeroes() const { +unsigned square_sparse_matrix::get_number_of_nonzeroes() const { unsigned ret = 0; for (unsigned i = dimension(); i--; ) { ret += number_of_non_zeroes_in_row(i); @@ -743,7 +781,7 @@ unsigned sparse_matrix::get_number_of_nonzeroes() const { } template -bool sparse_matrix::get_non_zero_column_in_row(unsigned i, unsigned *j) const { +bool square_sparse_matrix::get_non_zero_column_in_row(unsigned i, unsigned *j) const { // go over the i-th row auto & mc = get_row_values(adjust_row(i)); if (mc.size() > 0) { @@ -754,7 +792,7 @@ bool sparse_matrix::get_non_zero_column_in_row(unsigned i, unsigned *j) co } template -void sparse_matrix::remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv) { +void square_sparse_matrix::remove_element_that_is_not_in_w(vector> & column_vals, indexed_value & col_el_iv) { auto & row_chunk = m_rows[col_el_iv.m_index]; indexed_value & row_el_iv = row_chunk[col_el_iv.m_other]; unsigned index_in_row = col_el_iv.m_other; @@ -767,7 +805,7 @@ void sparse_matrix::remove_element_that_is_not_in_w(vector -void sparse_matrix::remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w) { +void square_sparse_matrix::remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w) { // -------------------------------- // column_vals represents the old column auto & column_vals = m_columns[column_to_replace].m_values; @@ -796,7 +834,7 @@ void sparse_matrix::remove_elements_that_are_not_in_w_and_update_common_el } template -void sparse_matrix::add_new_element(unsigned row, unsigned col, const T& val) { +void square_sparse_matrix::add_new_element(unsigned row, unsigned col, const T& val) { auto & row_vals = m_rows[row]; auto & col_vals = m_columns[col].m_values; unsigned row_el_offs = static_cast(row_vals.size()); @@ -809,7 +847,7 @@ void sparse_matrix::add_new_element(unsigned row, unsigned col, const T& v // w contains the "rest" of the new column; all common elements of w and the old column has been zeroed // the old column inside of the matrix has not been changed yet template -void sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings) { +void square_sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings) { for (unsigned i : w.m_index) { T w_at_i = w[i]; if (numeric_traits::is_zero(w_at_i)) continue; // was dealt with already @@ -817,7 +855,7 @@ void sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_r unsigned ai = adjust_row(i); add_new_element(ai, column_to_replace, w_at_i); auto & row_chunk = m_rows[ai]; - SASSERT(row_chunk.size() > 0); + lp_assert(row_chunk.size() > 0); if (abs(w_at_i) > abs(row_chunk[0].m_value)) put_max_index_to_0(row_chunk, static_cast(row_chunk.size()) - 1); } @@ -827,14 +865,14 @@ void sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_r } template -void sparse_matrix::replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings) { +void square_sparse_matrix::replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings) { column_to_replace = adjust_column(column_to_replace); remove_elements_that_are_not_in_w_and_update_common_elements(column_to_replace, w); add_new_elements_of_w_and_clear_w(column_to_replace, w, settings); } template -unsigned sparse_matrix::pivot_score(unsigned i, unsigned j) { +unsigned square_sparse_matrix::pivot_score(unsigned i, unsigned j) { // It goes like this (rnz-1)(cnz-1) is the Markovitz number, that is the max number of // new non zeroes we can obtain after the pivoting. // In addition we will get another cnz - 1 elements in the eta matrix created for this pivot, @@ -847,8 +885,8 @@ unsigned sparse_matrix::pivot_score(unsigned i, unsigned j) { } template -void sparse_matrix::enqueue_domain_into_pivot_queue() { - SASSERT(m_pivot_queue.size() == 0); +void square_sparse_matrix::enqueue_domain_into_pivot_queue() { + lp_assert(m_pivot_queue.size() == 0); for (unsigned i = 0; i < dimension(); i++) { auto & rh = m_rows[i]; unsigned rnz = static_cast(rh.size()); @@ -860,7 +898,7 @@ void sparse_matrix::enqueue_domain_into_pivot_queue() { } template -void sparse_matrix::set_max_in_rows() { +void square_sparse_matrix::set_max_in_rows() { unsigned i = dimension(); while (i--) set_max_in_row(i); @@ -868,27 +906,27 @@ void sparse_matrix::set_max_in_rows() { template -void sparse_matrix::zero_shortened_markovitz_numbers() { +void square_sparse_matrix::zero_shortened_markovitz_numbers() { for (auto & ch : m_columns) ch.zero_shortened_markovitz(); } template -void sparse_matrix::prepare_for_factorization() { +void square_sparse_matrix::prepare_for_factorization() { zero_shortened_markovitz_numbers(); set_max_in_rows(); enqueue_domain_into_pivot_queue(); } template -void sparse_matrix::recover_pivot_queue(vector & rejected_pivots) { +void square_sparse_matrix::recover_pivot_queue(vector & rejected_pivots) { for (auto p : rejected_pivots) { m_pivot_queue.enqueue(p.first, p.second, pivot_score(p.first, p.second)); } } template -int sparse_matrix::elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting) { +int square_sparse_matrix::elem_is_too_small(unsigned i, unsigned j, int c_partial_pivoting) { auto & row_chunk = m_rows[i]; if (j == row_chunk[0].m_index) { @@ -904,7 +942,7 @@ int sparse_matrix::elem_is_too_small(unsigned i, unsigned j, int c_partial } template -bool sparse_matrix::remove_row_from_active_pivots_and_shorten_columns(unsigned row) { +bool square_sparse_matrix::remove_row_from_active_pivots_and_shorten_columns(unsigned row) { unsigned arow = adjust_row(row); for (auto & iv : m_rows[arow]) { m_pivot_queue.remove(arow, iv.m_index); @@ -921,7 +959,7 @@ bool sparse_matrix::remove_row_from_active_pivots_and_shorten_columns(unsi } template -void sparse_matrix::remove_pivot_column(unsigned row) { +void square_sparse_matrix::remove_pivot_column(unsigned row) { unsigned acol = adjust_column(row); for (const auto & iv : m_columns[acol].m_values) if (adjust_row_inverse(iv.m_index) >= row) @@ -929,12 +967,12 @@ void sparse_matrix::remove_pivot_column(unsigned row) { } template -void sparse_matrix::update_active_pivots(unsigned row) { +void square_sparse_matrix::update_active_pivots(unsigned row) { unsigned arow = adjust_row(row); for (const auto & iv : m_rows[arow]) { col_header & ch = m_columns[iv.m_index]; int cols = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; - SASSERT(cols >= 0); + lp_assert(cols >= 0); for (const auto &ivc : ch.m_values) { unsigned i = ivc.m_index; if (adjust_row_inverse(i) <= row) continue; // the i is not an active row @@ -944,7 +982,7 @@ void sparse_matrix::update_active_pivots(unsigned row) { } template -bool sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix *eta_matrix) { +bool square_sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix *eta_matrix) { if (!remove_row_from_active_pivots_and_shorten_columns(row)) return false; remove_pivot_column(row); @@ -960,7 +998,7 @@ bool sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix * for (auto & iv : row_values) { const col_header& ch = m_columns[iv.m_index]; int cnz = static_cast(ch.m_values.size()) - ch.m_shortened_markovitz - 1; - SASSERT(cnz >= 0); + lp_assert(cnz >= 0); m_pivot_queue.enqueue(row, iv.m_index, rnz * cnz); } } @@ -969,32 +1007,32 @@ bool sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix * } template -unsigned sparse_matrix::pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k) { +unsigned square_sparse_matrix::pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k) { auto &cols = m_columns[j].m_values; unsigned cnz = cols.size(); for (auto & iv : cols) { if (adjust_row_inverse(iv.m_index) < k) cnz--; } - SASSERT(cnz > 0); + lp_assert(cnz > 0); return m_rows[i].m_values.size() * (cnz - 1); } #ifdef Z3DEBUG template -bool sparse_matrix::can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k) { +bool square_sparse_matrix::can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k) { unsigned arow = adjust_row(row); auto & row_vals = m_rows[arow].m_values; auto & begin_iv = row_vals[0]; T row_max = abs(begin_iv.m_value); - SASSERT(adjust_column_inverse(begin_iv.m_index) >= k); + lp_assert(adjust_column_inverse(begin_iv.m_index) >= k); if (pivot_score_without_shortened_counters(arow, begin_iv.m_index, k) < score) { print_active_matrix(k); return true; } for (unsigned jj = 1; jj < row_vals.size(); jj++) { auto & iv = row_vals[jj]; - SASSERT(adjust_column_inverse(iv.m_index) >= k); - SASSERT(abs(iv.m_value) <= row_max); + lp_assert(adjust_column_inverse(iv.m_index) >= k); + lp_assert(abs(iv.m_value) <= row_max); if (c_partial_pivoting * abs(iv.m_value) < row_max) continue; if (pivot_score_without_shortened_counters(arow, iv.m_index, k) < score) { print_active_matrix(k); @@ -1005,15 +1043,15 @@ bool sparse_matrix::can_improve_score_for_row(unsigned row, unsigned score } template -bool sparse_matrix::really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k) { +bool square_sparse_matrix::really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k) { unsigned queue_pivot_score = pivot_score_without_shortened_counters(i, j, k); for (unsigned ii = k; ii < dimension(); ii++) { - SASSERT(!can_improve_score_for_row(ii, queue_pivot_score, c_partial_pivoting, k)); + lp_assert(!can_improve_score_for_row(ii, queue_pivot_score, c_partial_pivoting, k)); } return true; } template -void sparse_matrix::print_active_matrix(unsigned k, std::ostream & out) { +void square_sparse_matrix::print_active_matrix(unsigned k, std::ostream & out) { out << "active matrix for k = " << k << std::endl; if (k >= dimension()) { out << "empty" << std::endl; @@ -1038,26 +1076,26 @@ void sparse_matrix::print_active_matrix(unsigned k, std::ostream & out) { } template -bool sparse_matrix::pivot_queue_is_correct_for_row(unsigned i, unsigned k) { +bool square_sparse_matrix::pivot_queue_is_correct_for_row(unsigned i, unsigned k) { unsigned arow = adjust_row(i); for (auto & iv : m_rows[arow].m_values) { - SASSERT(pivot_score_without_shortened_counters(arow, iv.m_index, k + 1) == + lp_assert(pivot_score_without_shortened_counters(arow, iv.m_index, k + 1) == m_pivot_queue.get_priority(arow, iv.m_index)); } return true; } template -bool sparse_matrix::pivot_queue_is_correct_after_pivoting(int k) { +bool square_sparse_matrix::pivot_queue_is_correct_after_pivoting(int k) { for (unsigned i = k + 1; i < dimension(); i++ ) - SASSERT(pivot_queue_is_correct_for_row(i, k)); - SASSERT(m_pivot_queue.is_correct()); + lp_assert(pivot_queue_is_correct_for_row(i, k)); + lp_assert(m_pivot_queue.is_correct()); return true; } #endif template -bool sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k) { +bool square_sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_partial_pivoting, unsigned k) { vector pivots_candidates_that_are_too_small; while (!m_pivot_queue.is_empty()) { m_pivot_queue.dequeue(i, j); @@ -1070,7 +1108,7 @@ bool sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_p #ifdef Z3DEBUG // if (!really_best_pivot(i, j, c_partial_pivoting, k)) { // print_active_matrix(k); - // SASSERT(false); + // lp_assert(false); // } #endif recover_pivot_queue(pivots_candidates_that_are_too_small); @@ -1087,7 +1125,7 @@ bool sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, int c_p } template -bool sparse_matrix::elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting) { +bool square_sparse_matrix::elem_is_too_small(vector> & row_chunk, indexed_value & iv, int c_partial_pivoting) { if (&iv == &row_chunk[0]) { return false; // the max element is at the head } @@ -1097,13 +1135,13 @@ bool sparse_matrix::elem_is_too_small(vector> & row_chunk } template -bool sparse_matrix::shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column) { +bool square_sparse_matrix::shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column) { vector> & row_chunk = get_row_values(i); for (indexed_value & iv : row_chunk) { unsigned j = iv.m_index; if (j == pivot_column) { - SASSERT(!col_is_active(j)); + lp_assert(!col_is_active(j)); continue; } m_columns[j].shorten_markovich_by_one(); @@ -1116,7 +1154,7 @@ bool sparse_matrix::shorten_columns_by_pivot_row(unsigned i, unsigned pivo } template -bool sparse_matrix::fill_eta_matrix(unsigned j, eta_matrix ** eta) { +bool square_sparse_matrix::fill_eta_matrix(unsigned j, eta_matrix ** eta) { const vector> & col_chunk = get_column_values(adjust_column(j)); bool is_unit = true; for (const auto & iv : col_chunk) { @@ -1163,20 +1201,19 @@ bool sparse_matrix::fill_eta_matrix(unsigned j, eta_matrix ** eta) { } #ifdef Z3DEBUG template -bool sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const { +bool square_sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const { for (unsigned i = 0; i < dimension(); i++) { vector> const & row_chunk = get_row_values(i); - SASSERT(row_chunk.size()); + lp_assert(row_chunk.size()); T const & max = abs(row_chunk[0].m_value); unsigned ai = adjust_row_inverse(i); for (auto & iv : row_chunk) { - SASSERT(abs(iv.m_value) <= max); + lp_assert(abs(iv.m_value) <= max); unsigned aj = adjust_column_inverse(iv.m_index); if (!(ai <= aj || numeric_traits::is_zero(iv.m_value))) return false; if (aj == ai) { if (iv.m_value != 1) { - // std::cout << "value at diagonal = " << iv.m_value << std::endl; return false; } } @@ -1188,7 +1225,7 @@ bool sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_ } template -bool sparse_matrix::is_upper_triangular_until(unsigned k) const { +bool square_sparse_matrix::is_upper_triangular_until(unsigned k) const { for (unsigned j = 0; j < dimension() && j < k; j++) { unsigned aj = adjust_column(j); auto & col = get_column_values(aj); @@ -1202,67 +1239,59 @@ bool sparse_matrix::is_upper_triangular_until(unsigned k) const { } template -void sparse_matrix::check_column_vs_rows(unsigned col) { +void square_sparse_matrix::check_column_vs_rows(unsigned col) { auto & mc = get_column_values(col); for (indexed_value & column_iv : mc) { indexed_value & row_iv = column_iv_other(column_iv); if (row_iv.m_index != col) { - // std::cout << "m_other in row does not belong to column " << col << ", but to column " << row_iv.m_index << std::endl; - SASSERT(false); + lp_assert(false); } if (& row_iv_other(row_iv) != &column_iv) { - // std::cout << "row and col do not point to each other" << std::endl; - SASSERT(false); + lp_assert(false); } if (row_iv.m_value != column_iv.m_value) { - // std::cout << "the data from col " << col << " for row " << column_iv.m_index << " is different in the column " << std::endl; - // std::cout << "in the col it is " << column_iv.m_value << ", but in the row it is " << row_iv.m_value << std::endl; - SASSERT(false); + lp_assert(false); } } } template -void sparse_matrix::check_row_vs_columns(unsigned row) { +void square_sparse_matrix::check_row_vs_columns(unsigned row) { auto & mc = get_row_values(row); for (indexed_value & row_iv : mc) { indexed_value & column_iv = row_iv_other(row_iv); if (column_iv.m_index != row) { - // std::cout << "col_iv does not point to correct row " << row << " but to " << column_iv.m_index << std::endl; - SASSERT(false); + lp_assert(false); } if (& row_iv != & column_iv_other(column_iv)) { - // std::cout << "row and col do not point to each other" << std::endl; - SASSERT(false); + lp_assert(false); } if (row_iv.m_value != column_iv.m_value) { - // std::cout << "the data from col " << column_iv.m_index << " for row " << row << " is different in the column " << std::endl; - // std::cout << "in the col it is " << column_iv.m_value << ", but in the row it is " << row_iv.m_value << std::endl; - SASSERT(false); + lp_assert(false); } } } template -void sparse_matrix::check_rows_vs_columns() { +void square_sparse_matrix::check_rows_vs_columns() { for (unsigned i = 0; i < dimension(); i++) { check_row_vs_columns(i); } } template -void sparse_matrix::check_columns_vs_rows() { +void square_sparse_matrix::check_columns_vs_rows() { for (unsigned i = 0; i < dimension(); i++) { check_column_vs_rows(i); } } template -void sparse_matrix::check_matrix() { +void square_sparse_matrix::check_matrix() { check_rows_vs_columns(); check_columns_vs_rows(); } diff --git a/src/util/lp/stacked_map.h b/src/util/lp/stacked_map.h deleted file mode 100644 index 1bcad5649..000000000 --- a/src/util/lp/stacked_map.h +++ /dev/null @@ -1,194 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -// this class implements a map with some stack functionality -#include -#include -#include -namespace lp { - - -template , - typename KeyEqual = std::equal_to, - typename Allocator = std::allocator< std::pair > > -class stacked_map { - struct delta { - std::unordered_set m_new; - std::unordered_map m_original_changed; - // std::unordered_map m_deb_copy; - }; - std::unordered_map m_map; - std::stack m_stack; -public: - class ref { - stacked_map & m_map; - const A & m_key; - public: - ref(stacked_map & m, const A & key) :m_map(m), m_key(key) {} - ref & operator=(const B & b) { - m_map.emplace_replace(m_key, b); - return *this; - } - ref & operator=(const ref & b) { SASSERT(false); return *this; } - operator const B&() const { - auto it = m_map.m_map.find(m_key); - SASSERT(it != m_map.m_map.end()); - return it->second; - } - }; -private: - void emplace_replace(const A & a, const B & b) { - if (!m_stack.empty()) { - delta & d = m_stack.top(); - auto it = m_map.find(a); - if (it == m_map.end()) { - d.m_new.insert(a); - m_map.emplace(a, b); - } else if (it->second != b) { - auto nit = d.m_new.find(a); - if (nit == d.m_new.end()) { // we do not have the old key - auto & orig_changed= d.m_original_changed; - auto itt = orig_changed.find(a); - if (itt == orig_changed.end()) { - orig_changed.emplace(a, it->second); - } else if (itt->second == b) { - orig_changed.erase(itt); - } - } - it->second = b; - } - } else { // there is no stack: just emplace or replace - m_map[a] = b; - } - } -public: - ref operator[] (const A & a) { - return ref(*this, a); - } - - const B & operator[]( const A & a) const { - auto it = m_map.find(a); - if (it == m_map.end()) { - SASSERT(false); - } - - return it->second; - } - - bool try_get_value(const A& key, B& val) const { - auto it = m_map.find(key); - if (it == m_map.end()) - return false; - - val = it->second; - return true; - } - bool try_get_value(const A&& key, B& val) const { - auto it = m_map.find(std::move(key)); - if (it == m_map.end()) - return false; - - val = it->second; - return true; - } - - unsigned size() const { - return m_map.size(); - } - - bool contains(const A & key) const { - return m_map.find(key) != m_map.end(); - } - - bool contains(const A && key) const { - return m_map.find(std::move(key)) != m_map.end(); - } - - void push() { - delta d; - // d.m_deb_copy = m_map; - m_stack.push(d); - } - - void pop() { - pop(1); - } - void pop(unsigned k) { - while (k-- > 0) { - if (m_stack.empty()) - return; - delta & d = m_stack.top(); - for (auto & t : d.m_new) { - m_map.erase(t); - } - for (auto & t: d.m_original_changed) { - m_map[t.first] = t.second; - } - // SASSERT(d.m_deb_copy == m_map); - m_stack.pop(); - } - } - - void erase(const A & key) { - if (m_stack.empty()) { - m_map.erase(key); - return; - } - - delta & d = m_stack.top(); - auto it = m_map.find(key); - if (it == m_map.end()) { - SASSERT(d.m_new.find(key) == d.m_new.end()); - return; - } - auto &orig_changed = d.m_original_changed; - auto nit = d.m_new.find(key); - if (nit == d.m_new.end()) { // key is old - if (orig_changed.find(key) == orig_changed.end()) - orig_changed.emplace(it->first, it->second); // need to restore - } else { // k is new - SASSERT(orig_changed.find(key) == orig_changed.end()); - d.m_new.erase(nit); - } - - m_map.erase(it); - } - - void clear() { - if (m_stack.empty()) { - m_map.clear(); - return; - } - - delta & d = m_stack.top(); - auto & oc = d.m_original_changed; - for (auto & p : m_map) { - const auto & it = oc.find(p.first); - if (it == oc.end() && d.m_new.find(p.first) == d.m_new.end()) - oc.emplace(p.first, p.second); - } - m_map.clear(); - } - - const std::unordered_map& operator()() const { return m_map;} -}; -} diff --git a/src/util/lp/stacked_unordered_set.h b/src/util/lp/stacked_unordered_set.h deleted file mode 100644 index 6e313e6c0..000000000 --- a/src/util/lp/stacked_unordered_set.h +++ /dev/null @@ -1,107 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - - Lev Nachmanson (levnach) - -Revision History: - - ---*/ - -#pragma once -// this class implements an unordered_set with some stack functionality -#include -#include -#include -namespace lp { - -template , - typename KeyEqual = std::equal_to, - typename Allocator = std::allocator - > class stacked_unordered_set { - struct delta { - std::unordered_set m_inserted; - std::unordered_set m_erased; - std::unordered_set m_deb_copy; - }; - std::unordered_set m_set; - std::stack m_stack; -public: - void insert(const A & a) { - if (m_stack.empty()) { - m_set.insert(a); - } else if (m_set.find(a) == m_set.end()) { - m_set.insert(a); - size_t in_erased = m_stack.top().m_erased.erase(a); - if (in_erased == 0) { - m_stack.top().m_inserted.insert(a); - } - } - } - - void erase(const A &a) { - if (m_stack.empty()) { - m_set.erase(a); - return; - } - auto erased = m_set.erase(a); - if (erased == 1) { - auto was_new = m_stack.top().m_inserted.erase(a); - if (was_new == 0) { - m_stack.top().m_erased.insert(a); - } - } - } - - unsigned size() const { - return m_set.size(); - } - - bool contains(A & key) const { - return m_set.find(key) != m_set.end(); - } - - bool contains(A && key) const { - return m_set.find(std::move(key)) != m_set.end(); - } - - void push() { - delta d; - d.m_deb_copy = m_set; - m_stack.push(d); - } - - void pop() { - pop(1); - } - void pop(unsigned k) { - while (k-- > 0) { - if (m_stack.empty()) - return; - delta & d = m_stack.top(); - for (auto & t : d.m_inserted) { - m_set.erase(t); - } - for (auto & t : d.m_erased) { - m_set.insert(t); - } - SASSERT(d.m_deb_copy == m_set); - m_stack.pop(); - } - } - - const std::unordered_set& operator()() const { return m_set;} -}; -} - diff --git a/src/util/lp/stacked_value.h b/src/util/lp/stacked_value.h index 5ef7ea0c8..a80e0e7d7 100644 --- a/src/util/lp/stacked_value.h +++ b/src/util/lp/stacked_value.h @@ -30,6 +30,10 @@ public: m_stack.push(m_value); } + void clear() { + m_stack.clear(); + } + unsigned stack_size() const { return static_cast(m_stack.size()); } diff --git a/src/util/lp/stacked_vector.h b/src/util/lp/stacked_vector.h index e8234d1e4..539e932b2 100644 --- a/src/util/lp/stacked_vector.h +++ b/src/util/lp/stacked_vector.h @@ -35,7 +35,7 @@ public: unsigned m_i; public: ref(stacked_vector &m, unsigned key) :m_vec(m), m_i(key) { - SASSERT(key < m.size()); + lp_assert(key < m.size()); } ref & operator=(const B & b) { m_vec.emplace_replace(m_i, b); @@ -48,7 +48,18 @@ public: operator const B&() const { return m_vec.m_vector[m_i]; } - + + bool operator==(B const& other) const { + return m_vec.m_vector[m_i] == other; + } + B& operator+=(B const &delta) { + // not tracking the change here! + return m_vec.m_vector[m_i] += delta; + } + B& operator-=(B const &delta) { + // not tracking the change here! + return m_vec.m_vector[m_i] -= delta; + } }; class ref_const { @@ -56,7 +67,7 @@ public: unsigned m_i; public: ref_const(const stacked_vector &m, unsigned key) :m_vec(m), m_i(key) { - SASSERT(key < m.size()); + lp_assert(key < m.size()); } operator const B&() const { @@ -83,10 +94,10 @@ public: } /* - const B & operator[](unsigned a) const { - SASSERT(a < m_vector.size()); - return m_vector[a]; - } + const B & operator[](unsigned a) const { + lp_assert(a < m_vector.size()); + return m_vector[a]; + } */ unsigned size() const { return m_vector.size(); @@ -104,7 +115,7 @@ public: template void pop_tail(vector & v, unsigned k) { - SASSERT(v.size() >= k); + lp_assert(v.size() >= k); v.resize(v.size() - k); } @@ -112,10 +123,16 @@ public: void resize(vector & v, unsigned new_size) { v.resize(new_size); } + + void pop_back() { + unsigned last = m_vector.size() - 1; + m_changes.push_back(std::make_pair(last, m_vector[last])); + m_vector.pop_back(); + } void pop(unsigned k) { - SASSERT(m_stack_of_vector_sizes.size() >= k); - SASSERT(k > 0); + lp_assert(m_stack_of_vector_sizes.size() >= k); + lp_assert(k > 0); resize(m_vector, m_stack_of_vector_sizes[m_stack_of_vector_sizes.size() - k]); pop_tail(m_stack_of_vector_sizes, k); unsigned first_change = m_stack_of_change_sizes[m_stack_of_change_sizes.size() - k]; @@ -129,22 +146,22 @@ public: resize(m_changes, first_change); /* - while (k-- > 0) { + while (k-- > 0) { - if (m_stack.empty()) - return; + if (m_stack.empty()) + return; - delta & d = m_stack.back(); - SASSERT(m_vector.size() >= d.m_size); - while (m_vector.size() > d.m_size) - m_vector.pop_back(); + delta & d = m_stack.back(); + lp_assert(m_vector.size() >= d.m_size); + while (m_vector.size() > d.m_size) + m_vector.pop_back(); - for (auto & t : d.m_original_changed) { - SASSERT(t.first < m_vector.size()); - m_vector[t.first] = t.second; - } - // SASSERT(d.m_deb_copy == m_vector); - m_stack.pop_back();*/ + for (auto & t : d.m_original_changed) { + lp_assert(t.first < m_vector.size()); + m_vector[t.first] = t.second; + } + // lp_assert(d.m_deb_copy == m_vector); + m_stack.pop_back();*/ } @@ -173,7 +190,7 @@ public: } unsigned peek_size(unsigned k) const { - SASSERT(k > 0 && k <= m_stack_of_vector_sizes.size()); + lp_assert(k > 0 && k <= m_stack_of_vector_sizes.size()); return m_stack_of_vector_sizes[m_stack_of_vector_sizes.size() - k]; } diff --git a/src/util/lp/static_matrix_instances.cpp b/src/util/lp/static_matrix.cpp similarity index 99% rename from src/util/lp/static_matrix_instances.cpp rename to src/util/lp/static_matrix.cpp index c57f31177..9d12087bb 100644 --- a/src/util/lp/static_matrix_instances.cpp +++ b/src/util/lp/static_matrix.cpp @@ -18,10 +18,10 @@ Revision History: --*/ #include +#include "util/vector.h" #include #include -#include "util/vector.h" -#include "util/lp/static_matrix.hpp" +#include "util/lp/static_matrix_def.h" #include "util/lp/lp_core_solver_base.h" #include "util/lp/lp_dual_core_solver.h" #include "util/lp/lp_dual_simplex.h" diff --git a/src/util/lp/static_matrix.h b/src/util/lp/static_matrix.h index 29b7ed646..e4bf257f4 100644 --- a/src/util/lp/static_matrix.h +++ b/src/util/lp/static_matrix.h @@ -26,7 +26,6 @@ Revision History: #include "util/lp/sparse_vector.h" #include "util/lp/indexed_vector.h" #include "util/lp/permutation_matrix.h" -#include "util/lp/linear_combination_iterator.h" #include namespace lp { @@ -35,20 +34,26 @@ struct column_cell { unsigned m_offset; // the offset of the element in the matrix row column_cell(unsigned i, unsigned offset) : m_i(i), m_offset(offset) { } + }; template struct row_cell { unsigned m_j; // points to the column unsigned m_offset; // offset in column + T m_value; row_cell(unsigned j, unsigned offset, T const & val) : m_j(j), m_offset(offset), m_value(val) { } const T & get_val() const { return m_value;} T & get_val() { return m_value;} void set_val(const T& v) { m_value = v; } - T m_value; + unsigned var() const { return m_j;} + const T & coeff() const { return m_value;} }; +template +using row_strip = vector>; + // each assignment for this matrix should be issued only once!!! template class static_matrix @@ -62,13 +67,11 @@ class static_matrix dim(unsigned m, unsigned n) :m_m(m), m_n(n) {} }; std::stack m_stack; - vector m_became_zeros; // the row indices that became zeroes during the pivoting public: - typedef vector> row_strip; typedef vector column_strip; vector m_vector_of_row_offsets; indexed_vector m_work_vector; - vector m_rows; + vector> m_rows; vector m_columns; // starting inner classes class ref { @@ -79,17 +82,17 @@ public: ref(static_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {} ref & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; } - ref & operator=(ref const & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } + ref operator=(ref & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; } operator T () const { return m_matrix.get_elem(m_row, m_col); } }; class ref_row { - static_matrix & m_matrix; + const static_matrix & m_matrix; unsigned m_row; public: - ref_row(static_matrix & m, unsigned row):m_matrix(m), m_row(row) {} - ref operator[](unsigned col) const { return ref(m_matrix, m_row, col); } + ref_row(const static_matrix & m, unsigned row): m_matrix(m), m_row(row) {} + const T operator[](unsigned col) const { return m_matrix.get_elem(m_row, col); } }; public: @@ -97,6 +100,10 @@ public: const T & get_val(const column_cell & c) const { return m_rows[c.m_i][c.m_offset].get_val(); } + + column_cell & get_column_cell(const row_cell &rc) { + return m_columns[rc.m_j][rc.m_offset]; + } void init_row_columns(unsigned m, unsigned n); @@ -125,7 +132,7 @@ public: void add_columns_at_the_end(unsigned delta); void add_new_element(unsigned i, unsigned j, const T & v); - void add_row() {m_rows.push_back(row_strip());} + void add_row() {m_rows.push_back(row_strip());} void add_column() { m_columns.push_back(column_strip()); m_vector_of_row_offsets.push_back(-1); @@ -188,7 +195,7 @@ public: // void fix_row_indices_in_each_column_for_crossed_row(unsigned k); - void cross_out_row_from_columns(unsigned k, row_strip & row); + void cross_out_row_from_columns(unsigned k, row_strip & row); void cross_out_row_from_column(unsigned col, unsigned k); @@ -218,7 +225,7 @@ public: virtual void set_number_of_columns(unsigned /*n*/) { } #endif - T get_max_val_in_row(unsigned /* i */) const { SASSERT(false); } + T get_max_val_in_row(unsigned /* i */) const { lp_unreachable(); } T get_balance() const; @@ -234,7 +241,7 @@ public: for (auto & c : row) { unsigned j = c.m_j; auto & col = m_columns[j]; - SASSERT(col[col.size() - 1].m_i == m_rows.size() -1 ); // todo : start here!!!! + lp_assert(col[col.size() - 1].m_i == m_rows.size() -1 ); // todo : start here!!!! col.pop_back(); } } @@ -261,7 +268,7 @@ public: m_columns.pop_back(); // delete the last column m_stack.pop(); } - SASSERT(is_correct()); + lp_assert(is_correct()); } void multiply_row(unsigned row, T const & alpha) { @@ -277,7 +284,7 @@ public: } T dot_product_with_column(const vector & y, unsigned j) const { - SASSERT(j < column_count()); + lp_assert(j < column_count()); T ret = numeric_traits::zero(); for (auto & it : m_columns[j]) { ret += y[it.m_i] * get_val(it); // get_value_of_column_cell(it); @@ -287,7 +294,7 @@ public: // pivot row i to row ii bool pivot_row_to_row_given_cell(unsigned i, column_cell& c, unsigned); - void scan_row_ii_to_offset_vector(unsigned ii); + void scan_row_ii_to_offset_vector(const row_strip & rvals); void transpose_rows(unsigned i, unsigned ii) { auto t = m_rows[i]; @@ -296,64 +303,72 @@ public: // now fix the columns for (auto & rc : m_rows[i]) { column_cell & cc = m_columns[rc.m_j][rc.m_offset]; - SASSERT(cc.m_i == ii); + lp_assert(cc.m_i == ii); cc.m_i = i; } for (auto & rc : m_rows[ii]) { column_cell & cc = m_columns[rc.m_j][rc.m_offset]; - SASSERT(cc.m_i == i); + lp_assert(cc.m_i == i); cc.m_i = ii; } } + void fill_last_row_with_pivoting_loop_block(unsigned j, const vector & basis_heading) { + int row_index = basis_heading[j]; + if (row_index < 0) + return; + T & alpha = m_work_vector[j]; // the pivot alpha + if (is_zero(alpha)) + return; + + for (const auto & c : m_rows[row_index]) { + if (c.m_j == j) { + continue; + } + T & wv = m_work_vector.m_data[c.m_j]; + bool was_zero = is_zero(wv); + wv -= alpha * c.m_value; + if (was_zero) + m_work_vector.m_index.push_back(c.m_j); + else { + if (is_zero(wv)) { + m_work_vector.erase_from_index(c.m_j); + } + } + } + alpha = zero_of_type(); + m_work_vector.erase_from_index(j); + } - void fill_last_row_with_pivoting(linear_combination_iterator & it, const vector & basis_heading) { - SASSERT(numeric_traits::precise()); - SASSERT(row_count() > 0); + + + template + void fill_last_row_with_pivoting(const term& row, + unsigned bj, // the index of the basis column + const vector & basis_heading) { + lp_assert(numeric_traits::precise()); + lp_assert(row_count() > 0); m_work_vector.resize(column_count()); T a; - unsigned j; - while (it.next(a, j)) { - m_work_vector.set_value(-a, j); // we use the form -it + 1 = 0 + // we use the form -it + 1 = 0 + m_work_vector.set_value(one_of_type(), bj); + for (auto p : row) { + m_work_vector.set_value(-p.coeff(), p.var()); // but take care of the basis 1 later } - it.reset(); - // not iterate with pivoting - while (it.next(j)) { - int row_index = basis_heading[j]; - if (row_index < 0) - continue; - - T & alpha = m_work_vector[j]; // the pivot alpha - if (is_zero(alpha)) - continue; - - for (const auto & c : m_rows[row_index]) { - if (c.m_j == j) { - continue; - } - T & wv = m_work_vector.m_data[c.m_j]; - bool was_zero = is_zero(wv); - wv -= alpha * c.m_value; - if (was_zero) - m_work_vector.m_index.push_back(c.m_j); - else { - if (is_zero(wv)) { - m_work_vector.erase_from_index(c.m_j); - } - } - } - alpha = zero_of_type(); - m_work_vector.erase_from_index(j); + // now iterate with pivoting + fill_last_row_with_pivoting_loop_block(bj, basis_heading); + for (auto p : row) { + fill_last_row_with_pivoting_loop_block(p.var(), basis_heading); } - SASSERT(m_work_vector.is_OK()); + lp_assert(m_work_vector.is_OK()); unsigned last_row = row_count() - 1; for (unsigned j : m_work_vector.m_index) { set (last_row, j, m_work_vector.m_data[j]); } - SASSERT(column_count() > 0); + lp_assert(column_count() > 0); set(last_row, column_count() - 1, one_of_type()); } @@ -369,11 +384,76 @@ public: template L dot_product_with_row(unsigned row, const vector & w) const { L ret = zero_of_type(); - SASSERT(row < m_rows.size()); + lp_assert(row < m_rows.size()); for (auto & it : m_rows[row]) { ret += w[it.m_j] * it.get_val(); } return ret; } + + struct column_cell_plus { + const column_cell & m_c; + const static_matrix& m_A; + // constructor + column_cell_plus(const column_cell & c, const static_matrix& A) : + m_c(c), m_A(A) {} + unsigned var() const { return m_c.m_i; } + const T & coeff() const { return m_A.m_rows[var()][m_c.m_offset].get_val(); } + + }; + + struct column_container { + unsigned m_j; // the column index + const static_matrix & m_A; + column_container(unsigned j, const static_matrix& A) : m_j(j), m_A(A) { + } + struct const_iterator { + // fields + const column_cell *m_c; + const static_matrix& m_A; + + //typedefs + + + typedef const_iterator self_type; + typedef column_cell_plus value_type; + typedef const column_cell_plus reference; + // typedef const column_cell* pointer; + typedef int difference_type; + typedef std::forward_iterator_tag iterator_category; + + reference operator*() const { + return column_cell_plus(*m_c, m_A); + } + self_type operator++() { self_type i = *this; m_c++; return i; } + self_type operator++(int) { m_c++; return *this; } + + const_iterator(const column_cell* it, const static_matrix& A) : + m_c(it), + m_A(A) + {} + bool operator==(const self_type &other) const { + return m_c == other.m_c; + } + bool operator!=(const self_type &other) const { return !(*this == other); } + }; + + const_iterator begin() const { + return const_iterator(m_A.m_columns[m_j].begin(), m_A); + } + + const_iterator end() const { + return const_iterator(m_A.m_columns[m_j].end(), m_A); + } + }; + + column_container column(unsigned j) const { + return column_container(j, *this); + } + + ref_row operator[](unsigned i) const { return ref_row(*this, i);} + typedef T coefftype; + typedef X argtype; }; + } diff --git a/src/util/lp/static_matrix.hpp b/src/util/lp/static_matrix_def.h similarity index 84% rename from src/util/lp/static_matrix.hpp rename to src/util/lp/static_matrix_def.h index 846c2a19f..42249b4d8 100644 --- a/src/util/lp/static_matrix.hpp +++ b/src/util/lp/static_matrix_def.h @@ -25,9 +25,9 @@ namespace lp { // each assignment for this matrix should be issued only once!!! template void static_matrix::init_row_columns(unsigned m, unsigned n) { - SASSERT(m_rows.size() == 0 && m_columns.size() == 0); + lp_assert(m_rows.size() == 0 && m_columns.size() == 0); for (unsigned i = 0; i < m; i++){ - m_rows.push_back(row_strip()); + m_rows.push_back(row_strip()); } for (unsigned j = 0; j < n; j++){ m_columns.push_back(column_strip()); @@ -35,57 +35,46 @@ void static_matrix::init_row_columns(unsigned m, unsigned n) { } -template void static_matrix::scan_row_ii_to_offset_vector(unsigned ii) { - auto & rvals = m_rows[ii]; - unsigned size = rvals.size(); - for (unsigned j = 0; j < size; j++) +template void static_matrix::scan_row_ii_to_offset_vector(const row_strip & rvals) { + for (unsigned j = 0; j < rvals.size(); j++) m_vector_of_row_offsets[rvals[j].m_j] = j; } template bool static_matrix::pivot_row_to_row_given_cell(unsigned i, column_cell & c, unsigned pivot_col) { unsigned ii = c.m_i; - SASSERT(i < row_count() && ii < column_count()); - SASSERT(i != ii); - - m_became_zeros.reset(); + lp_assert(i < row_count() && ii < column_count() && i != ii); T alpha = -get_val(c); - SASSERT(!is_zero(alpha)); - auto & ii_row_vals = m_rows[ii]; - remove_element(ii_row_vals, ii_row_vals[c.m_offset]); - scan_row_ii_to_offset_vector(ii); - SASSERT(!is_zero(alpha)); - unsigned prev_size_ii = ii_row_vals.size(); + lp_assert(!is_zero(alpha)); + auto & rowii = m_rows[ii]; + remove_element(rowii, rowii[c.m_offset]); + scan_row_ii_to_offset_vector(rowii); + unsigned prev_size_ii = rowii.size(); // run over the pivot row and update row ii for (const auto & iv : m_rows[i]) { unsigned j = iv.m_j; if (j == pivot_col) continue; T alv = alpha * iv.m_value; - SASSERT(!is_zero(iv.m_value)); + lp_assert(!is_zero(iv.m_value)); int j_offs = m_vector_of_row_offsets[j]; if (j_offs == -1) { // it is a new element add_new_element(ii, j, alv); } else { - auto & row_el_iv = ii_row_vals[j_offs]; - row_el_iv.m_value += alv; - if (is_zero(row_el_iv.m_value)) { - m_became_zeros.push_back(j_offs); - ensure_increasing(m_became_zeros); - } + rowii[j_offs].m_value += alv; } } - // clean the work vector for (unsigned k = 0; k < prev_size_ii; k++) { - m_vector_of_row_offsets[ii_row_vals[k].m_j] = -1; + m_vector_of_row_offsets[rowii[k].m_j] = -1; } - for (unsigned k = m_became_zeros.size(); k-- > 0; ) { - unsigned j = m_became_zeros[k]; - remove_element(ii_row_vals, ii_row_vals[j]); + // remove zeroes + for (unsigned k = rowii.size(); k-- > 0; ) { + if (is_zero(rowii[k].m_value)) + remove_element(rowii, rowii[k]); } - return !ii_row_vals.empty(); + return !rowii.empty(); } @@ -119,9 +108,9 @@ template void static_matrix::init_empty_matrix } template unsigned static_matrix::lowest_row_in_column(unsigned col) { - SASSERT(col < column_count()); + lp_assert(col < column_count()); column_strip & colstrip = m_columns[col]; - SASSERT(colstrip.size() > 0); + lp_assert(colstrip.size() > 0); unsigned ret = 0; for (auto & t : colstrip) { if (t.m_i > ret) { @@ -137,7 +126,7 @@ template void static_matrix::add_columns_at_th } template void static_matrix::forget_last_columns(unsigned how_many_to_forget) { - SASSERT(m_columns.size() >= how_many_to_forget); + lp_assert(m_columns.size() >= how_many_to_forget); unsigned j = column_count() - 1; for (; how_many_to_forget > 0; how_many_to_forget--) { remove_last_column(j --); @@ -166,7 +155,7 @@ template void static_matrix::remove_last_column(u template void static_matrix::set(unsigned row, unsigned col, T const & val) { if (numeric_traits::is_zero(val)) return; - SASSERT(row < row_count() && col < column_count()); + lp_assert(row < row_count() && col < column_count()); auto & r = m_rows[row]; unsigned offs_in_cols = static_cast(m_columns[col].size()); m_columns[col].push_back(make_column_cell(row, static_cast(r.size()))); @@ -177,7 +166,7 @@ template std::set> static_matrix::get_domain() { std::set> ret; for (unsigned i = 0; i < m_rows.size(); i++) { - for (auto it : m_rows[i]) { + for (auto &it : m_rows[i]) { ret.insert(std::make_pair(i, it.m_j)); } } @@ -186,7 +175,7 @@ std::set> static_matrix::get_domain() { template void static_matrix::copy_column_to_indexed_vector (unsigned j, indexed_vector & v) const { - SASSERT(j < m_columns.size()); + lp_assert(j < m_columns.size()); for (auto & it : m_columns[j]) { const T& val = get_val(it); if (!is_zero(val)) @@ -255,7 +244,7 @@ template void static_matrix::check_consistency for (int i = 0; i < m_rows.size(); i++){ for (auto & t : m_rows[i]) { std::pair p(i, t.m_j); - SASSERT(by_rows.find(p) == by_rows.end()); + lp_assert(by_rows.find(p) == by_rows.end()); by_rows[p] = t.get_val(); } } @@ -263,20 +252,16 @@ template void static_matrix::check_consistency for (int i = 0; i < m_columns.size(); i++){ for (auto & t : m_columns[i]) { std::pair p(t.m_i, i); - SASSERT(by_cols.find(p) == by_cols.end()); + lp_assert(by_cols.find(p) == by_cols.end()); by_cols[p] = get_val(t); } } - SASSERT(by_rows.size() == by_cols.size()); + lp_assert(by_rows.size() == by_cols.size()); for (auto & t : by_rows) { auto ic = by_cols.find(t.first); - if (ic == by_cols.end()){ - //std::cout << "rows have pair (" << t.first.first <<"," << t.first.second - // << "), but columns don't " << std::endl; - } - SASSERT(ic != by_cols.end()); - SASSERT(t.second == ic->second); + lp_assert(ic != by_cols.end()); + lp_assert(t.second == ic->second); } } #endif @@ -307,7 +292,7 @@ template void static_matrix::fix_row_indices_i } } -template void static_matrix::cross_out_row_from_columns(unsigned k, row_strip & row) { +template void static_matrix::cross_out_row_from_columns(unsigned k, row_strip & row) { for (auto & t : row) { cross_out_row_from_column(t.m_j, k); } @@ -353,11 +338,11 @@ template T static_matrix::get_row_balance(unsi } template bool static_matrix::is_correct() const { - for (auto & r : m_rows) { + for (unsigned i = 0; i < m_rows.size(); i++) { + auto &r = m_rows[i]; std::unordered_set s; for (auto & rc : r) { if (s.find(rc.m_j) != s.end()) { - std::cout << "found column " << rc.m_j << " twice in a row" << std::endl; return false; } s.insert(rc.m_j); @@ -367,13 +352,18 @@ template bool static_matrix::is_correct() const { return false; if (rc.get_val() != get_val(m_columns[rc.m_j][rc.m_offset])) return false; + if (is_zero(rc.get_val())) { + return false; + } + } } - for (auto & c : m_columns) { + + for (unsigned j = 0; j < m_columns.size(); j++) { + auto & c = m_columns[j]; std::unordered_set s; for (auto & cc : c) { if (s.find(cc.m_i) != s.end()) { - std::cout << "found row " << cc.m_i << " twice in a column" << std::endl; return false; } s.insert(cc.m_i); diff --git a/src/util/lp/tail_matrix.h b/src/util/lp/tail_matrix.h index 37b217205..2a851bc16 100644 --- a/src/util/lp/tail_matrix.h +++ b/src/util/lp/tail_matrix.h @@ -39,5 +39,12 @@ public: virtual void apply_from_right(indexed_vector & w) = 0; virtual ~tail_matrix() {} virtual bool is_dense() const = 0; + struct ref_row { + const tail_matrix & m_A; + unsigned m_row; + ref_row(const tail_matrix& m, unsigned row): m_A(m), m_row(row) {} + T operator[](unsigned j) const { return m_A.get_elem(m_row, j);} + }; + ref_row operator[](unsigned i) const { return ref_row(*this, i);} }; } diff --git a/src/util/lp/test_bound_analyzer.h b/src/util/lp/test_bound_analyzer.h index 30f2dd16a..7cee0cf56 100644 --- a/src/util/lp/test_bound_analyzer.h +++ b/src/util/lp/test_bound_analyzer.h @@ -17,9 +17,9 @@ Revision History: --*/ +#if 0 #pragma once #include "util/vector.h" -#include "util/lp/linear_combination_iterator.h" #include "util/lp/implied_bound.h" #include "util/lp/lp_settings.h" #include @@ -35,7 +35,7 @@ namespace lp { class test_bound_analyzer { linear_combination_iterator & m_it; - std::function& m_low_bounds; + std::function& m_lower_bounds; std::function& m_upper_bounds; std::function m_column_types; vector & m_implied_bounds; @@ -47,14 +47,14 @@ class test_bound_analyzer { public : // constructor test_bound_analyzer(linear_combination_iterator &it, - std::function & low_bounds, + std::function & lower_bounds, std::function & upper_bounds, std::function column_types, vector & evidence_vector, unsigned row_or_term_index, std::function & try_get_found_bound) : m_it(it), - m_low_bounds(low_bounds), + m_lower_bounds(lower_bounds), m_upper_bounds(upper_bounds), m_column_types(column_types), m_implied_bounds(evidence_vector), @@ -89,7 +89,7 @@ public : void analyze_i_for_upper(unsigned i) { mpq l; bool strict = false; - SASSERT(is_zero(l)); + lp_assert(is_zero(l)); for (unsigned k = 0; k < m_index.size(); k++) { if (k == i) continue; @@ -115,22 +115,21 @@ public : } } default: - // std::cout << " got an upper bound with " << T_to_string(l) << "\n"; m_implied_bounds.push_back(implied_bound(l, j, false, is_pos(m_coeffs[i]), m_row_or_term_index, strict)); } } - bool low_bound_of_monoid(unsigned k, mpq & lb, bool &strict) const { + bool lower_bound_of_monoid(unsigned k, mpq & lb, bool &strict) const { int s = - m_coeff_sign * sign(m_coeffs[k]); unsigned j = m_index[k]; if (s > 0) { switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: - lb = -m_coeffs[k] * m_low_bounds(j).x; - strict = !is_zero(m_low_bounds(j).y); + case column_type::lower_bound: + lb = -m_coeffs[k] * m_lower_bounds(j).x; + strict = !is_zero(m_lower_bounds(j).y); return true; default: return false; @@ -169,9 +168,9 @@ public : switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: - lb = -m_coeffs[k] * m_low_bounds(j).x; - strict = !is_zero(m_low_bounds(j).y); + case column_type::lower_bound: + lb = -m_coeffs[k] * m_lower_bounds(j).x; + strict = !is_zero(m_lower_bounds(j).y); return true; default: return false; @@ -180,14 +179,14 @@ public : void analyze_i_for_lower(unsigned i) { mpq l; - SASSERT(is_zero(l)); + lp_assert(is_zero(l)); bool strict = false; for (unsigned k = 0; k < m_index.size(); k++) { if (k == i) continue; mpq lb; bool str; - if (!low_bound_of_monoid(k, lb, str)) { + if (!lower_bound_of_monoid(k, lb, str)) { return; } if (str) @@ -199,24 +198,23 @@ public : switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: + case column_type::lower_bound: { - const auto & lb = m_low_bounds(j); + const auto & lb = m_lower_bounds(j); if (l < lb.x || (l == lb.x && !(is_zero(lb.y) && strict))) { break; // no improvement on the existing upper bound } } default: - // std::cout << " got a lower bound with " << T_to_string(l) << "\n"; m_implied_bounds.push_back(implied_bound(l, j, true, is_pos(m_coeffs[i]), m_row_or_term_index, strict)); } } - bool low_bound_is_available(unsigned j) const { + bool lower_bound_is_available(unsigned j) const { switch(m_column_types(j)) { case column_type::fixed: case column_type::boxed: - case column_type::low_bound: + case column_type::lower_bound: return true; default: return false; @@ -239,15 +237,15 @@ public : return true; } if (is_lower_bound) { - if (low_bound_is_available(j)) { - best_bound = m_low_bounds(j).x; - strict_of_best_bound = !is_zero(m_low_bounds(j).y); + if (lower_bound_is_available(j)) { + best_bound = m_lower_bounds(j).x; + strict_of_best_bound = !is_zero(m_lower_bounds(j).y); return true; } } else { if (upper_bound_is_available(j)) { best_bound = m_upper_bounds(j).x; - strict_of_best_bound = !is_zero(m_low_bounds(j).y); + strict_of_best_bound = !is_zero(m_lower_bounds(j).y); return true; } } @@ -273,3 +271,4 @@ public : }; } +#endif diff --git a/src/util/lp/ul_pair.h b/src/util/lp/ul_pair.h index cbf511d90..7fac6b3ae 100644 --- a/src/util/lp/ul_pair.h +++ b/src/util/lp/ul_pair.h @@ -27,31 +27,34 @@ Revision History: namespace lp { - enum lconstraint_kind { - LE = -2, LT = -1 , GE = 2, GT = 1, EQ = 0 - }; - - inline std::ostream& operator<<(std::ostream& out, lconstraint_kind k) { - switch (k) { - case LE: return out << "<="; - case LT: return out << "<"; - case GE: return out << ">="; - case GT: return out << ">"; - case EQ: return out << "="; - } - return out << "??"; +enum lconstraint_kind { + LE = -2, LT = -1 , GE = 2, GT = 1, EQ = 0 +}; + + +inline bool kind_is_strict(lconstraint_kind kind) { return kind == LT || kind == GT;} + +inline std::ostream& operator<<(std::ostream& out, lconstraint_kind k) { + switch (k) { + case LE: return out << "<="; + case LT: return out << "<"; + case GE: return out << ">="; + case GT: return out << ">"; + case EQ: return out << "="; } + return out << "??"; +} inline bool compare(const std::pair & a, const std::pair & b) { return a.second < b.second; } class ul_pair { - constraint_index m_low_bound_witness; + constraint_index m_lower_bound_witness; constraint_index m_upper_bound_witness; public: - constraint_index& low_bound_witness() {return m_low_bound_witness;} - constraint_index low_bound_witness() const {return m_low_bound_witness;} + constraint_index& lower_bound_witness() {return m_lower_bound_witness;} + constraint_index lower_bound_witness() const {return m_lower_bound_witness;} constraint_index& upper_bound_witness() { return m_upper_bound_witness;} constraint_index upper_bound_witness() const {return m_upper_bound_witness;} row_index m_i; @@ -60,21 +63,21 @@ public: } bool operator==(const ul_pair & p) const { - return m_low_bound_witness == p.m_low_bound_witness + return m_lower_bound_witness == p.m_lower_bound_witness && m_upper_bound_witness == p.m_upper_bound_witness && m_i == p.m_i; } // empty constructor ul_pair() : - m_low_bound_witness(static_cast(-1)), + m_lower_bound_witness(static_cast(-1)), m_upper_bound_witness(static_cast(-1)), m_i(static_cast(-1)) -{} + {} ul_pair(row_index ri) : - m_low_bound_witness(static_cast(-1)), + m_lower_bound_witness(static_cast(-1)), m_upper_bound_witness(static_cast(-1)), m_i(ri) {} - ul_pair(const ul_pair & o): m_low_bound_witness(o.m_low_bound_witness), m_upper_bound_witness(o.m_upper_bound_witness), m_i(o.m_i) {} + ul_pair(const ul_pair & o): m_lower_bound_witness(o.m_lower_bound_witness), m_upper_bound_witness(o.m_upper_bound_witness), m_i(o.m_i) {} }; } diff --git a/src/util/lp/var_register.h b/src/util/lp/var_register.h new file mode 100644 index 000000000..93404bf91 --- /dev/null +++ b/src/util/lp/var_register.h @@ -0,0 +1,109 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +namespace lp { +class ext_var_info { + unsigned m_external_j; // the internal index + bool m_is_integer; +public: + ext_var_info() {} + ext_var_info(unsigned j): ext_var_info(j, true) {} + ext_var_info(unsigned j , bool is_int) : m_external_j(j), m_is_integer(is_int) {} + unsigned external_j() const { return m_external_j;} + bool is_integer() const {return m_is_integer;} +}; + +class var_register { + svector m_local_to_external; + std::unordered_map m_external_to_local; +public: + unsigned add_var(unsigned user_var) { + return add_var(user_var, true); + } + unsigned add_var(unsigned user_var, bool is_int) { + auto t = m_external_to_local.find(user_var); + if (t != m_external_to_local.end()) { + return t->second; + } + + m_local_to_external.push_back(ext_var_info(user_var, is_int)); + return m_external_to_local[user_var] = size() - 1; + } + + svector vars() const { + svector ret; + for (const auto& p : m_local_to_external) { + ret.push_back(p.external_j()); + } + return ret; + } + + unsigned local_to_external(unsigned local_var) const { + return m_local_to_external[local_var].external_j(); + } + unsigned size() const { + return m_local_to_external.size(); + } + void clear() { + m_local_to_external.clear(); + m_external_to_local.clear(); + } + + unsigned external_to_local(unsigned j) const { + auto it = m_external_to_local.find(j); + lp_assert(it != m_external_to_local.end()); + return it->second; + } + + bool external_is_used(unsigned ext_j) const { + auto it = m_external_to_local.find(ext_j); + return it != m_external_to_local.end(); + } + + bool external_is_used(unsigned ext_j, unsigned & local_j ) const { + auto it = m_external_to_local.find(ext_j); + if ( it == m_external_to_local.end()) + return false; + local_j = it->second; + return true; + } + + bool external_is_used(unsigned ext_j, unsigned & local_j, bool & is_int ) const { + auto it = m_external_to_local.find(ext_j); + if ( it == m_external_to_local.end()) + return false; + local_j = it->second; + is_int = m_local_to_external[local_j].is_integer(); + return true; + } + + + bool local_is_int(unsigned j) const { + return m_local_to_external[j].is_integer(); + } + + void shrink(unsigned shrunk_size) { + for (unsigned j = size(); j-- > shrunk_size;) { + m_external_to_local.erase(m_local_to_external[j].external_j()); + } + m_local_to_external.resize(shrunk_size); + } + +}; +} diff --git a/src/util/mpff.cpp b/src/util/mpff.cpp index 64e07f9f0..e00a25b1b 100644 --- a/src/util/mpff.cpp +++ b/src/util/mpff.cpp @@ -1070,7 +1070,7 @@ bool mpff_manager::is_power_of_two(mpff const & a) const { template void mpff_manager::significand_core(mpff const & n, mpz_manager & m, mpz & t) { - m.set(t, m_precision, sig(n)); + m.set_digits(t, m_precision, sig(n)); } void mpff_manager::significand(mpff const & n, unsynch_mpz_manager & m, mpz & t) { @@ -1090,10 +1090,10 @@ void mpff_manager::to_mpz_core(mpff const & n, mpz_manager & m, mpz & t) to_buffer(0, n); unsigned * b = m_buffers[0].c_ptr(); shr(m_precision, b, -exp, m_precision, b); - m.set(t, m_precision, b); + m.set_digits(t, m_precision, b); } else { - m.set(t, m_precision, sig(n)); + m.set_digits(t, m_precision, sig(n)); if (exp > 0) { _scoped_numeral > p(m); m.set(p, 2); diff --git a/src/util/mpfx.cpp b/src/util/mpfx.cpp index e17a5e766..2ebc54840 100644 --- a/src/util/mpfx.cpp +++ b/src/util/mpfx.cpp @@ -705,7 +705,7 @@ template void mpfx_manager::to_mpz_core(mpfx const & n, mpz_manager & m, mpz & t) { SASSERT(is_int(n)); unsigned * w = words(n); - m.set(t, m_int_part_sz, w+m_frac_part_sz); + m.set_digits(t, m_int_part_sz, w+m_frac_part_sz); if (is_neg(n)) m.neg(t); } diff --git a/src/util/mpq.cpp b/src/util/mpq.cpp index bc6f99213..4c636f1af 100644 --- a/src/util/mpq.cpp +++ b/src/util/mpq.cpp @@ -315,6 +315,112 @@ unsigned mpq_manager::prev_power_of_two(mpq const & a) { return prev_power_of_two(_tmp); } + +template +template +void mpq_manager::lin_arith_op(mpq const& a, mpq const& b, mpq& c, mpz& g, mpz& tmp1, mpz& tmp2, mpz& tmp3) { + gcd(a.m_den, b.m_den, g); + if (is_one(g)) { + mul(a.m_num, b.m_den, tmp1); + mul(b.m_num, a.m_den, tmp2); + if (SUB) sub(tmp1, tmp2, c.m_num); else add(tmp1, tmp2, c.m_num); + mul(a.m_den, b.m_den, c.m_den); + } + else { + div(a.m_den, g, tmp3); + mul(tmp3, b.m_den, c.m_den); + mul(tmp3, b.m_num, tmp2); + div(b.m_den, g, tmp3); + mul(tmp3, a.m_num, tmp1); + if (SUB) sub(tmp1, tmp2, tmp3); else add(tmp1, tmp2, tmp3); + gcd(tmp3, g, tmp1); + if (is_one(tmp1)) { + set(c.m_num, tmp3); + } + else { + div(tmp3, tmp1, c.m_num); + div(c.m_den, tmp1, c.m_den); + } + } +} + +template +void mpq_manager::rat_mul(mpq const & a, mpq const & b, mpq & c, mpz& g1, mpz& g2, mpz& tmp1, mpz& tmp2) { + gcd(a.m_den, b.m_num, g1); + gcd(a.m_num, b.m_den, g2); + div(a.m_num, g2, tmp1); + div(b.m_num, g1, tmp2); + mul(tmp1, tmp2, c.m_num); + div(b.m_den, g2, tmp1); + div(a.m_den, g1, tmp2); + mul(tmp1, tmp2, c.m_den); +} + +template +void mpq_manager::rat_mul(mpz const & a, mpq const & b, mpq & c) { + STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); + mul(a, b.m_num, c.m_num); + set(c.m_den, b.m_den); + normalize(c); + STRACE("rat_mpq", tout << to_string(c) << "\n";); +} + + +template +void mpq_manager::rat_mul(mpq const & a, mpq const & b, mpq & c) { + STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); + if (SYNCH) { + mpz g1, g2, tmp1, tmp2; + rat_mul(a, b, c, g1, g2, tmp1, tmp2); + del(g1); + del(g2); + del(tmp1); + del(tmp2); + } + else { + mpz& g1 = m_n_tmp, &g2 = m_addmul_tmp.m_num, &tmp1 = m_add_tmp1, &tmp2 = m_add_tmp2; + rat_mul(a, b, c, g1, g2, tmp1, tmp2); + } + STRACE("rat_mpq", tout << to_string(c) << "\n";); +} + +template +void mpq_manager::rat_add(mpq const & a, mpq const & b, mpq & c) { + STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); + if (SYNCH) { + mpz_stack tmp1, tmp2, tmp3, g; + lin_arith_op(a, b, c, g, tmp1, tmp2, tmp3); + del(tmp1); + del(tmp2); + del(tmp3); + del(g); + } + else { + mpz& g = m_n_tmp, &tmp1 = m_add_tmp1, &tmp2 = m_add_tmp2, &tmp3 = m_addmul_tmp.m_num; + lin_arith_op(a, b, c, g, tmp1, tmp2, tmp3); + } + STRACE("rat_mpq", tout << to_string(c) << "\n";); +} + +template +void mpq_manager::rat_sub(mpq const & a, mpq const & b, mpq & c) { + STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " - " << to_string(b) << " == ";); + if (SYNCH) { + mpz tmp1, tmp2, tmp3, g; + lin_arith_op(a, b, c, g, tmp1, tmp2, tmp3); + del(tmp1); + del(tmp2); + del(tmp3); + del(g); + } + else { + mpz& g = m_n_tmp, &tmp1 = m_add_tmp1, &tmp2 = m_add_tmp2, &tmp3 = m_addmul_tmp.m_num; + lin_arith_op(a, b, c, g, tmp1, tmp2, tmp3); + } + STRACE("rat_mpq", tout << to_string(c) << "\n";); +} + + template class mpq_manager; template class mpq_manager; diff --git a/src/util/mpq.h b/src/util/mpq.h index 010bb2c8a..1bccabc74 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -74,27 +74,7 @@ class mpq_manager : public mpz_manager { } } - void rat_add(mpq const & a, mpq const & b, mpq & c) { - STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); - if (SYNCH) { - mpz tmp1, tmp2; - mul(a.m_num, b.m_den, tmp1); - mul(b.m_num, a.m_den, tmp2); - mul(a.m_den, b.m_den, c.m_den); - add(tmp1, tmp2, c.m_num); - normalize(c); - del(tmp1); - del(tmp2); - } - else { - mul(a.m_num, b.m_den, m_add_tmp1); - mul(b.m_num, a.m_den, m_add_tmp2); - mul(a.m_den, b.m_den, c.m_den); - add(m_add_tmp1, m_add_tmp2, c.m_num); - normalize(c); - } - STRACE("rat_mpq", tout << to_string(c) << "\n";); - } + void rat_add(mpq const & a, mpq const & b, mpq & c); void rat_add(mpq const & a, mpz const & b, mpq & c) { STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " + " << to_string(b) << " == ";); @@ -115,46 +95,19 @@ class mpq_manager : public mpz_manager { STRACE("rat_mpq", tout << to_string(c) << "\n";); } - void rat_sub(mpq const & a, mpq const & b, mpq & c) { - STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " - " << to_string(b) << " == ";); - if (SYNCH) { - mpz tmp1, tmp2; - mul(a.m_num, b.m_den, tmp1); - mul(b.m_num, a.m_den, tmp2); - mul(a.m_den, b.m_den, c.m_den); - sub(tmp1, tmp2, c.m_num); - normalize(c); - del(tmp1); - del(tmp2); - } - else { - mul(a.m_num, b.m_den, m_add_tmp1); - mul(b.m_num, a.m_den, m_add_tmp2); - mul(a.m_den, b.m_den, c.m_den); - sub(m_add_tmp1, m_add_tmp2, c.m_num); - normalize(c); - } - STRACE("rat_mpq", tout << to_string(c) << "\n";); - } + void rat_sub(mpq const & a, mpq const & b, mpq & c); - void rat_mul(mpq const & a, mpq const & b, mpq & c) { - STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); - mul(a.m_num, b.m_num, c.m_num); - mul(a.m_den, b.m_den, c.m_den); - normalize(c); - STRACE("rat_mpq", tout << to_string(c) << "\n";); - } + void rat_mul(mpq const & a, mpq const & b, mpq & c); - void rat_mul(mpz const & a, mpq const & b, mpq & c) { - STRACE("rat_mpq", tout << "[mpq] " << to_string(a) << " * " << to_string(b) << " == ";); - mul(a, b.m_num, c.m_num); - set(c.m_den, b.m_den); - normalize(c); - STRACE("rat_mpq", tout << to_string(c) << "\n";); - } + void rat_mul(mpz const & a, mpq const & b, mpq & c); bool rat_lt(mpq const & a, mpq const & b); + template + void lin_arith_op(mpq const& a, mpq const& b, mpq& c, mpz& g, mpz& tmp1, mpz& tmp2, mpz& tmp3); + + void rat_mul(mpq const & a, mpq const & b, mpq & c, mpz& g1, mpz& g2, mpz& tmp1, mpz& tmp2); + public: typedef mpq numeral; typedef mpq rational; @@ -501,7 +454,7 @@ 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 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); } @@ -746,10 +699,12 @@ public: reset_denominator(a); } - void set(mpz & a, unsigned sz, digit_t const * digits) { mpz_manager::set(a, sz, digits); } + void set(mpz & a, unsigned sz, digit_t const * digits) { + mpz_manager::set_digits(a, sz, digits); + } void set(mpq & a, unsigned sz, digit_t const * digits) { - mpz_manager::set(a.m_num, sz, digits); + mpz_manager::set_digits(a.m_num, sz, digits); reset_denominator(a); } diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 861a31cfb..7ad4797b7 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -30,6 +30,7 @@ Revision History: #else #error No multi-precision library selected. #endif +#include // Available GCD algorithms // #define EUCLID_GCD @@ -45,9 +46,6 @@ Revision History: #define LEHMER_GCD #endif - -#include - #if defined(__GNUC__) #define _trailing_zeros32(X) __builtin_ctz(X) #else @@ -146,11 +144,6 @@ mpz_manager::mpz_manager(): else { m_init_cell_capacity = 6; } - for (unsigned i = 0; i < 2; i++) { - m_tmp[i] = allocate(m_init_cell_capacity); - m_arg[i] = allocate(m_init_cell_capacity); - m_arg[i]->m_size = 1; - } set(m_int_min, -static_cast(INT_MIN)); #else // GMP @@ -160,8 +153,6 @@ mpz_manager::mpz_manager(): mpz_set_ui(m_two32, UINT_MAX); mpz_set_ui(m_tmp, 1); mpz_add(m_two32, m_two32, m_tmp); - m_arg[0] = allocate(); - m_arg[1] = allocate(); mpz_init(m_uint64_max); unsigned max_l = static_cast(UINT64_MAX); unsigned max_h = static_cast(UINT64_MAX >> 32); @@ -193,16 +184,10 @@ mpz_manager::~mpz_manager() { del(m_two64); #ifndef _MP_GMP del(m_int_min); - for (unsigned i = 0; i < 2; i++) { - deallocate(m_tmp[i]); - deallocate(m_arg[i]); - } #else mpz_clear(m_tmp); mpz_clear(m_tmp2); mpz_clear(m_two32); - deallocate(m_arg[0]); - deallocate(m_arg[1]); mpz_clear(m_uint64_max); mpz_clear(m_int64_max); mpz_clear(m_int64_min); @@ -211,12 +196,79 @@ mpz_manager::~mpz_manager() { omp_destroy_nest_lock(&m_lock); } +#ifndef _MP_GMP +template +mpz_cell * mpz_manager::allocate(unsigned capacity) { + SASSERT(capacity >= m_init_cell_capacity); + MPZ_BEGIN_CRITICAL(); + mpz_cell * cell = reinterpret_cast(m_allocator.allocate(cell_size(capacity))); + MPZ_END_CRITICAL(); + cell->m_capacity = capacity; + return cell; +} + +template +void mpz_manager::deallocate(bool is_heap, mpz_cell * ptr) { + if (is_heap) { + MPZ_BEGIN_CRITICAL(); + m_allocator.deallocate(cell_size(ptr->m_capacity), ptr); + MPZ_END_CRITICAL(); + } +} + +template +mpz_manager::sign_cell::sign_cell(mpz_manager& m, mpz const& a): + m_local(reinterpret_cast(m_bytes)), m_a(a) { + m_local.m_ptr->m_capacity = capacity; + m.get_sign_cell(a, m_sign, m_cell, m_local.m_ptr); +} + + +#endif + + +template +void mpz_manager::del(mpz & a) { + if (a.m_ptr) { + deallocate(a.m_owner == mpz_self, a.m_ptr); + a.m_ptr = nullptr; + a.m_kind = mpz_small; + a.m_owner = mpz_self; + } +} + +template +void mpz_manager::add(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz] " << to_string(a) << " + " << to_string(b) << " == ";); + if (is_small(a) && is_small(b)) { + set_i64(c, i64(a) + i64(b)); + } + else { + big_add(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +template +void mpz_manager::sub(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz] " << to_string(a) << " - " << to_string(b) << " == ";); + if (is_small(a) && is_small(b)) { + set_i64(c, i64(a) - i64(b)); + } + else { + big_sub(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + template void mpz_manager::set_big_i64(mpz & c, int64_t v) { #ifndef _MP_GMP - if (is_small(c)) { + if (c.m_ptr == nullptr) { c.m_ptr = allocate(m_init_cell_capacity); + c.m_owner = mpz_self; } + c.m_kind = mpz_large; SASSERT(capacity(c) >= m_init_cell_capacity); uint64_t _v; if (v < 0) { @@ -239,9 +291,11 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2; } #else - if (is_small(c)) { + if (c.m_ptr == nullptr) { c.m_ptr = allocate(); + c.m_owner = mpz_self; } + c.m_kind = mpz_large; uint64_t _v; bool sign; if (v < 0) { @@ -253,9 +307,11 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { sign = false; } mpz_set_ui(*c.m_ptr, static_cast(_v)); + MPZ_BEGIN_CRITICAL(); mpz_set_ui(m_tmp, static_cast(_v >> 32)); mpz_mul(m_tmp, m_tmp, m_two32); mpz_add(*c.m_ptr, *c.m_ptr, m_tmp); + MPZ_END_CRITICAL(); if (sign) mpz_neg(*c.m_ptr, *c.m_ptr); #endif @@ -264,9 +320,11 @@ void mpz_manager::set_big_i64(mpz & c, int64_t v) { template void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { #ifndef _MP_GMP - if (is_small(c)) { + if (c.m_ptr == nullptr) { c.m_ptr = allocate(m_init_cell_capacity); + c.m_owner = mpz_self; } + c.m_kind = mpz_large; SASSERT(capacity(c) >= m_init_cell_capacity); c.m_val = 1; if (sizeof(digit_t) == sizeof(uint64_t)) { @@ -281,57 +339,74 @@ void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { c.m_ptr->m_size = digits(c)[1] == 0 ? 1 : 2; } #else - if (is_small(c)) { + if (c.m_ptr == nullptr) { c.m_ptr = allocate(); + c.m_owner = mpz_self; } + c.m_kind = mpz_large; mpz_set_ui(*c.m_ptr, static_cast(v)); + MPZ_BEGIN_CRITICAL(); mpz_set_ui(m_tmp, static_cast(v >> 32)); mpz_mul(m_tmp, m_tmp, m_two32); mpz_add(*c.m_ptr, *c.m_ptr, m_tmp); + MPZ_END_CRITICAL(); #endif } +#ifdef _MP_GMP + +template +mpz_manager::ensure_mpz_t::ensure_mpz_t(mpz const& a) { + if (is_small(a)) { + m_result = &m_local; + mpz_init(m_local); + mpz_set_si(m_local, a.m_val); + } + else { + m_result = a.m_ptr; + } +} + +template +mpz_manager::ensure_mpz_t::~ensure_mpz_t() { + if (m_result == &m_local) { + mpz_clear(m_local); + } +} + +#endif + #ifndef _MP_GMP template -template -void mpz_manager::set(mpz & a, int sign, unsigned sz) { -#if 0 - static unsigned max_sz = 0; - if (sz > max_sz) { - max_sz = sz; - verbose_stream() << "max_sz: " << max_sz << "\n"; - } -#endif +void mpz_manager::set(mpz_cell& src, mpz & a, int sign, unsigned sz) { unsigned i = sz; - for (; i > 0; --i) { - if (m_tmp[IDX]->m_digits[i-1] != 0) - break; - } + for (; i > 0 && src.m_digits[i-1] == 0; --i) ; if (i == 0) { - // m_tmp[IDX] is zero - reset(a); + // src is zero + set(a, 0); return; } - if (i == 1 && m_tmp[IDX]->m_digits[0] <= INT_MAX) { - // m_tmp[IDX] fits is a fixnum - del(a); - a.m_val = sign < 0 ? -static_cast(m_tmp[IDX]->m_digits[0]) : static_cast(m_tmp[IDX]->m_digits[0]); + unsigned d = src.m_digits[0]; + if (i == 1 && d <= INT_MAX) { + // src fits is a fixnum + a.m_val = sign < 0 ? -static_cast(d) : static_cast(d); + a.m_kind = mpz_small; return; } + set_digits(a, i, src.m_digits); a.m_val = sign; - std::swap(a.m_ptr, m_tmp[IDX]); - a.m_ptr->m_size = i; - if (!m_tmp[IDX]) // 'a' was a small number - m_tmp[IDX] = allocate(m_init_cell_capacity); + + SASSERT(a.m_kind == mpz_large); } #endif + template void mpz_manager::set(mpz & a, char const * val) { - reset(a); + set(a, 0); mpz ten(10); mpz tmp; char const * str = val; @@ -353,125 +428,316 @@ void mpz_manager::set(mpz & a, char const * val) { } template -void mpz_manager::set(mpz & target, unsigned sz, digit_t const * digits) { +void mpz_manager::set_digits(mpz & target, unsigned sz, digit_t const * digits) { // remove zero digits while (sz > 0 && digits[sz - 1] == 0) sz--; if (sz == 0) - reset(target); + set(target, 0); else if (sz == 1) set(target, digits[0]); else { #ifndef _MP_GMP target.m_val = 1; // number is positive. - if (is_small(target)) { + if (target.m_ptr == nullptr) { unsigned c = sz < m_init_cell_capacity ? m_init_cell_capacity : sz; target.m_ptr = allocate(c); target.m_ptr->m_size = sz; target.m_ptr->m_capacity = c; + target.m_kind = mpz_large; + target.m_owner = mpz_self; memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); } + else if (capacity(target) < sz) { + SASSERT(sz > m_init_cell_capacity); + mpz_cell* ptr = allocate(sz); + memcpy(ptr->m_digits, digits, sizeof(digit_t) * sz); + ptr->m_size = sz; + ptr->m_capacity = sz; + deallocate(target); + target.m_val = 1; + target.m_ptr = ptr; + target.m_kind = mpz_large; + target.m_owner = mpz_self; + } else { - if (capacity(target) < sz) { - SASSERT(sz > m_init_cell_capacity); - deallocate(target.m_ptr); - target.m_ptr = allocate(sz); - target.m_ptr->m_size = sz; - target.m_ptr->m_capacity = sz; + target.m_ptr->m_size = sz; + if (target.m_ptr->m_digits != digits) memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); - } - else { - target.m_ptr->m_size = sz; - memcpy(target.m_ptr->m_digits, digits, sizeof(digit_t) * sz); - } + target.m_kind = mpz_large; } #else - mk_big(target); - // reset - mpz_set_ui(*target.m_ptr, digits[sz - 1]); - SASSERT(sz > 0); - unsigned i = sz - 1; - while (i > 0) { - --i; - mpz_mul_2exp(*target.m_ptr, *target.m_ptr, 32); - mpz_set_ui(m_tmp, digits[i]); - mpz_add(*target.m_ptr, *target.m_ptr, m_tmp); - } + mk_big(target); + // reset + mpz_set_ui(*target.m_ptr, digits[sz - 1]); + SASSERT(sz > 0); + unsigned i = sz - 1; + MPZ_BEGIN_CRITICAL(); + while (i > 0) { + --i; + mpz_mul_2exp(*target.m_ptr, *target.m_ptr, 32); + mpz_set_ui(m_tmp, digits[i]); + mpz_add(*target.m_ptr, *target.m_ptr, m_tmp); + } + MPZ_END_CRITICAL(); #endif } } +template +void mpz_manager::mul(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz] " << to_string(a) << " * " << to_string(b) << " == ";); + if (is_small(a) && is_small(b)) { + set_i64(c, i64(a) * i64(b)); + } + else { + big_mul(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +// d <- a + b*c +template +void mpz_manager::addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { + if (is_one(b)) { + add(a, c, d); + } + else if (is_minus_one(b)) { + sub(a, c, d); + } + else { + mpz tmp; + mul(b,c,tmp); + add(a,tmp,d); + del(tmp); + } +} + + +// d <- a - b*c +template +void mpz_manager::submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { + if (is_one(b)) { + sub(a, c, d); + } + else if (is_minus_one(b)) { + add(a, c, d); + } + else { + mpz tmp; + mul(b,c,tmp); + sub(a,tmp,d); + del(tmp); + } +} + + +template +void mpz_manager::machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { + STRACE("mpz", tout << "[mpz-ext] divrem(" << to_string(a) << ", " << to_string(b) << ") == ";); + if (is_small(a) && is_small(b)) { + int64_t _a = i64(a); + int64_t _b = i64(b); + set_i64(q, _a / _b); + set_i64(r, _a % _b); + } + else { + big_div_rem(a, b, q, r); + } + STRACE("mpz", tout << "(" << to_string(q) << ", " << to_string(r) << ")\n";); +} + +template +void mpz_manager::machine_div(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz-ext] machine-div(" << to_string(a) << ", " << to_string(b) << ") == ";); + if (is_small(a) && is_small(b)) { + set_i64(c, i64(a) / i64(b)); + } + else { + big_div(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +template +void mpz_manager::reset(mpz & a) { + deallocate(a); + set(a, 0); +} + +template +void mpz_manager::rem(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz-ext] rem(" << to_string(a) << ", " << to_string(b) << ") == ";); + if (is_small(a) && is_small(b)) { + set_i64(c, i64(a) % i64(b)); + } + else { + big_rem(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + + +template +void mpz_manager::div_gcd(mpz const& a, mpz const& b, mpz & c) { + STRACE("mpz", tout << "[mpz-ext] div(" << to_string(a) << ", " << to_string(b) << ") == ";); + if (is_one(b)) { + set(c, a); + } + else { + machine_div(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +template +void mpz_manager::div(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz-ext] div(" << to_string(a) << ", " << to_string(b) << ") == ";); + if (is_one(b)) { + set(c, a); + } + else if (is_neg(a)) { + mpz tmp; + machine_div_rem(a, b, c, tmp); + if (!is_zero(tmp)) { + if (is_neg(b)) + add(c, mk_z(1), c); + else + sub(c, mk_z(1), c); + } + del(tmp); + } + else { + machine_div(a, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +template +void mpz_manager::mod(mpz const & a, mpz const & b, mpz & c) { + STRACE("mpz", tout << "[mpz-ext] mod(" << to_string(a) << ", " << to_string(b) << ") == ";); + rem(a, b, c); + if (is_neg(c)) { + if (is_pos(b)) + add(c, b, c); + else + sub(c, b, c); + } + STRACE("mpz", tout << to_string(c) << "\n";); +} + +template +void mpz_manager::neg(mpz & a) { + STRACE("mpz", tout << "[mpz] 0 - " << to_string(a) << " == ";); + if (is_small(a) && a.m_val == INT_MIN) { + // neg(INT_MIN) is not a small int + set_big_i64(a, - static_cast(INT_MIN)); + return; + } +#ifndef _MP_GMP + a.m_val = -a.m_val; +#else + if (is_small(a)) { + a.m_val = -a.m_val; + } + else { + mpz_neg(*a.m_ptr, *a.m_ptr); + } +#endif + STRACE("mpz", tout << to_string(a) << "\n";); +} + +template +void mpz_manager::abs(mpz & a) { + if (is_small(a)) { + if (a.m_val < 0) { + if (a.m_val == INT_MIN) { + // abs(INT_MIN) is not a small int + set_big_i64(a, - static_cast(INT_MIN)); + } + else + a.m_val = -a.m_val; + } + } + else { +#ifndef _MP_GMP + a.m_val = 1; +#else + mpz_abs(*a.m_ptr, *a.m_ptr); +#endif + } +} + + +// TBD: replace use of 'tmp' by 'c'. #ifndef _MP_GMP template template void mpz_manager::big_add_sub(mpz const & a, mpz const & b, mpz & c) { - int sign_a; - int sign_b; - mpz_cell * cell_a; - mpz_cell * cell_b; - get_sign_cell<0>(a, sign_a, cell_a); - get_sign_cell<1>(b, sign_b, cell_b); + sign_cell ca(*this, a), cb(*this, b); + int sign_b = cb.sign(); + mpz_stack tmp; if (SUB) sign_b = -sign_b; size_t real_sz; - if (sign_a == sign_b) { - unsigned sz = std::max(cell_a->m_size, cell_b->m_size)+1; - ensure_tmp_capacity<0>(sz); - m_mpn_manager.add(cell_a->m_digits, cell_a->m_size, - cell_b->m_digits, cell_b->m_size, - m_tmp[0]->m_digits, sz, &real_sz); + if (ca.sign() == sign_b) { + unsigned sz = std::max(ca.cell()->m_size, cb.cell()->m_size)+1; + allocate_if_needed(tmp, sz); + m_mpn_manager.add(ca.cell()->m_digits, ca.cell()->m_size, + cb.cell()->m_digits, cb.cell()->m_size, + tmp.m_ptr->m_digits, sz, &real_sz); SASSERT(real_sz <= sz); - set<0>(c, sign_a, static_cast(real_sz)); + set(*tmp.m_ptr, c, ca.sign(), static_cast(real_sz)); } else { digit_t borrow; - int r = m_mpn_manager.compare(cell_a->m_digits, cell_a->m_size, - cell_b->m_digits, cell_b->m_size); + int r = m_mpn_manager.compare(ca.cell()->m_digits, ca.cell()->m_size, + cb.cell()->m_digits, cb.cell()->m_size); if (r == 0) { - reset(c); + set(c, 0); } else if (r < 0) { // a < b - unsigned sz = cell_b->m_size; - ensure_tmp_capacity<0>(sz); - m_mpn_manager.sub(cell_b->m_digits, - cell_b->m_size, - cell_a->m_digits, - cell_a->m_size, - m_tmp[0]->m_digits, + unsigned sz = cb.cell()->m_size; + allocate_if_needed(tmp, sz); + m_mpn_manager.sub(cb.cell()->m_digits, + cb.cell()->m_size, + ca.cell()->m_digits, + ca.cell()->m_size, + tmp.m_ptr->m_digits, &borrow); SASSERT(borrow == 0); - set<0>(c, sign_b, sz); + set(*tmp.m_ptr, c, sign_b, sz); } else { // a > b - unsigned sz = cell_a->m_size; - ensure_tmp_capacity<0>(sz); - m_mpn_manager.sub(cell_a->m_digits, - cell_a->m_size, - cell_b->m_digits, - cell_b->m_size, - m_tmp[0]->m_digits, + unsigned sz = ca.cell()->m_size; + allocate_if_needed(tmp, sz); + m_mpn_manager.sub(ca.cell()->m_digits, + ca.cell()->m_size, + cb.cell()->m_digits, + cb.cell()->m_size, + tmp.m_ptr->m_digits, &borrow); SASSERT(borrow == 0); - set<0>(c, sign_a, sz); + set(*tmp.m_ptr, c, ca.sign(), sz); } } + del(tmp); } #endif + + template void mpz_manager::big_add(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP big_add_sub(a, b, c); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_add(*c.m_ptr, *arg0, *arg1); + mpz_add(*c.m_ptr, a1(), b1()); #endif } @@ -481,80 +747,35 @@ void mpz_manager::big_sub(mpz const & a, mpz const & b, mpz & c) { big_add_sub(a, b, c); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_sub(*c.m_ptr, *arg0, *arg1); + mpz_sub(*c.m_ptr, a1(), b1()); #endif } template void mpz_manager::big_mul(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP - int sign_a; - int sign_b; - mpz_cell * cell_a; - mpz_cell * cell_b; - get_sign_cell<0>(a, sign_a, cell_a); - get_sign_cell<1>(b, sign_b, cell_b); - unsigned sz = cell_a->m_size + cell_b->m_size; - ensure_tmp_capacity<0>(sz); - m_mpn_manager.mul(cell_a->m_digits, - cell_a->m_size, - cell_b->m_digits, - cell_b->m_size, - m_tmp[0]->m_digits); - set<0>(c, sign_a == sign_b ? 1 : -1, sz); + // TBD replace tmp by c. + mpz_stack tmp; + sign_cell ca(*this, a), cb(*this, b); + unsigned sz = ca.cell()->m_size + cb.cell()->m_size; + allocate_if_needed(tmp, sz); + m_mpn_manager.mul(ca.cell()->m_digits, + ca.cell()->m_size, + cb.cell()->m_digits, + cb.cell()->m_size, + tmp.m_ptr->m_digits); + set(*tmp.m_ptr, c, ca.sign() == cb.sign() ? 1 : -1, sz); + del(tmp); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_mul(*c.m_ptr, *arg0, *arg1); + mpz_mul(*c.m_ptr, a1(), b1()); #endif } -#ifndef _MP_GMP -template -template -void mpz_manager::quot_rem_core(mpz const & a, mpz const & b, mpz & q, mpz & r) { - /* - +26 / +7 = +3, remainder is +5 - -26 / +7 = -3, remainder is -5 - +26 / -7 = -3, remainder is +5 - -26 / -7 = +3, remainder is -5 - */ - int sign_a; - int sign_b; - mpz_cell * cell_a; - mpz_cell * cell_b; - get_sign_cell<0>(a, sign_a, cell_a); - get_sign_cell<1>(b, sign_b, cell_b); - if (cell_b->m_size > cell_a->m_size) { - if (MODE == REM_ONLY || MODE == QUOT_AND_REM) - set(r, a); - if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) - reset(q); - return; - } - unsigned q_sz = cell_a->m_size - cell_b->m_size + 1; - unsigned r_sz = cell_b->m_size; - ensure_tmp_capacity<0>(q_sz); - ensure_tmp_capacity<1>(r_sz); - m_mpn_manager.div(cell_a->m_digits, cell_a->m_size, - cell_b->m_digits, cell_b->m_size, - m_tmp[0]->m_digits, - m_tmp[1]->m_digits); - if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) - set<0>(q, sign_a == sign_b ? 1 : -1, q_sz); - if (MODE == REM_ONLY || MODE == QUOT_AND_REM) - set<1>(r, sign_a, r_sz); -} -#endif template void mpz_manager::big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { @@ -562,30 +783,63 @@ void mpz_manager::big_div_rem(mpz const & a, mpz const & b, mpz & q, mpz quot_rem_core(a, b, q, r); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz_t a1(a), b1(b); mk_big(q); mk_big(r); - mpz_tdiv_qr(*q.m_ptr, *r.m_ptr, *arg0, *arg1); + mpz_tdiv_qr(*q.m_ptr, *r.m_ptr, a1(), b1()); #endif } +#ifndef _MP_GMP +template +template +void mpz_manager::quot_rem_core(mpz const & a, mpz const & b, mpz & q, mpz & r) +{ + /* + +26 / +7 = +3, remainder is +5 + -26 / +7 = -3, remainder is -5 + +26 / -7 = -3, remainder is +5 + -26 / -7 = +3, remainder is -5 + */ + + mpz_stack q1, r1; + sign_cell ca(*this, a), cb(*this, b); + if (cb.cell()->m_size > ca.cell()->m_size) { + if (MODE == REM_ONLY || MODE == QUOT_AND_REM) + set(r, a); + if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) + set(q, 0); + return; + } + unsigned q_sz = ca.cell()->m_size - cb.cell()->m_size + 1; + unsigned r_sz = cb.cell()->m_size; + allocate_if_needed(q1, q_sz); + allocate_if_needed(r1, r_sz); + m_mpn_manager.div(ca.cell()->m_digits, ca.cell()->m_size, + cb.cell()->m_digits, cb.cell()->m_size, + q1.m_ptr->m_digits, + r1.m_ptr->m_digits); + if (MODE == QUOT_ONLY || MODE == QUOT_AND_REM) + set(*q1.m_ptr, q, ca.sign() == cb.sign() ? 1 : -1, q_sz); + if (MODE == REM_ONLY || MODE == QUOT_AND_REM) + set(*r1.m_ptr, r, ca.sign(), r_sz); + del(q1); + del(r1); +} +#endif + template void mpz_manager::big_div(mpz const & a, mpz const & b, mpz & c) { #ifndef _MP_GMP mpz dummy; quot_rem_core(a, b, c, dummy); SASSERT(is_zero(dummy)); + del(dummy); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_tdiv_q(*c.m_ptr, *arg0, *arg1); + mpz_tdiv_q(*c.m_ptr, a1(), b1()); #endif } @@ -595,20 +849,19 @@ void mpz_manager::big_rem(mpz const & a, mpz const & b, mpz & c) { mpz dummy; quot_rem_core(a, b, dummy, c); SASSERT(is_zero(dummy)); + del(dummy); #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_tdiv_r(*c.m_ptr, *arg0, *arg1); + mpz_tdiv_r(*c.m_ptr, a1(), b1()); #endif } template void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { static_assert(sizeof(a.m_val) == sizeof(int), "size mismatch"); + static_assert(sizeof(mpz) <= 16, "mpz size overflow"); if (is_small(a) && is_small(b) && a.m_val != INT_MIN && b.m_val != INT_MIN) { int _a = a.m_val; int _b = b.m_val; @@ -619,12 +872,9 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { } else { #ifdef _MP_GMP - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_gcd(*c.m_ptr, *arg0, *arg1); + mpz_gcd(*c.m_ptr, a1(), b1()); return; #endif if (is_zero(a)) { @@ -879,6 +1129,8 @@ unsigned mpz_manager::size_info(mpz const & a) { #endif } + + template struct mpz_manager::sz_lt { mpz_manager & m; @@ -891,6 +1143,7 @@ struct mpz_manager::sz_lt { } }; + template void mpz_manager::gcd(unsigned sz, mpz const * as, mpz & g) { #if 0 @@ -898,7 +1151,7 @@ void mpz_manager::gcd(unsigned sz, mpz const * as, mpz & g) { // The optimization did not really help. switch (sz) { case 0: - reset(g); + set(g, 0); return; case 1: set(g, as[0]); @@ -943,7 +1196,7 @@ void mpz_manager::gcd(unsigned sz, mpz const * as, mpz & g) { // Vanilla implementation switch (sz) { case 0: - reset(g); + set(g, 0); return; case 1: set(g, as[0]); @@ -1070,8 +1323,8 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(b)); TRACE("mpz", tout << "is_small(a): " << is_small(a) << ", is_small(b): " << is_small(b) << "\n";); if (is_small(a) && is_small(b)) { - del(c); c.m_val = a.m_val | b.m_val; + c.m_kind = mpz_small; } else { #ifndef _MP_GMP @@ -1079,7 +1332,7 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { set(a1, a); set(b1, b); set(m, 1); - reset(c); + set(c, 0); while (!is_zero(a1) && !is_zero(b1)) { TRACE("mpz", tout << "a1: " << to_string(a1) << ", b1: " << to_string(b1) << "\n";); mod(a1, m_two64, a2); @@ -1104,12 +1357,9 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_ior(*c.m_ptr, *arg0, *arg1); + mpz_ior(*c.m_ptr, a1(), b1()); #endif } } @@ -1119,8 +1369,8 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { SASSERT(is_nonneg(a)); SASSERT(is_nonneg(b)); if (is_small(a) && is_small(b)) { - del(c); c.m_val = a.m_val & b.m_val; + c.m_kind = mpz_small; } else { #ifndef _MP_GMP @@ -1128,7 +1378,7 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { set(a1, a); set(b1, b); set(m, 1); - reset(c); + set(c, 0); while (!is_zero(a1) && !is_zero(b1)) { mod(a1, m_two64, a2); mod(b1, m_two64, b2); @@ -1142,12 +1392,9 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_and(*c.m_ptr, *arg0, *arg1); + mpz_and(*c.m_ptr, a1(), b1()); #endif } } @@ -1165,7 +1412,7 @@ void mpz_manager::bitwise_xor(mpz const & a, mpz const & b, mpz & c) { set(a1, a); set(b1, b); set(m, 1); - reset(c); + set(c, 0); while (!is_zero(a1) && !is_zero(b1)) { mod(a1, m_two64, a2); mod(b1, m_two64, b2); @@ -1187,12 +1434,9 @@ void mpz_manager::bitwise_xor(mpz const & a, mpz const & b, mpz & c) { } del(a1); del(b1); del(a2); del(b2); del(m); del(tmp); #else - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); + ensure_mpz_t a1(a), b1(b); mk_big(c); - mpz_xor(*c.m_ptr, *arg0, *arg1); + mpz_xor(*c.m_ptr, a1(), b1()); #endif } } @@ -1238,24 +1482,27 @@ void mpz_manager::big_set(mpz & target, mpz const & source) { if (&target == &source) return; target.m_val = source.m_val; - if (is_small(target)) { + if (target.m_ptr == nullptr) { target.m_ptr = allocate(capacity(source)); target.m_ptr->m_size = size(source); target.m_ptr->m_capacity = capacity(source); + target.m_kind = mpz_large; + target.m_owner = mpz_self; + memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); + } + else if (capacity(target) < size(source)) { + deallocate(target); + target.m_ptr = allocate(capacity(source)); + target.m_ptr->m_size = size(source); + target.m_ptr->m_capacity = capacity(source); + target.m_kind = mpz_large; + target.m_owner = mpz_self; memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); } else { - if (capacity(target) < size(source)) { - deallocate(target.m_ptr); - target.m_ptr = allocate(capacity(source)); - target.m_ptr->m_size = size(source); - target.m_ptr->m_capacity = capacity(source); - memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); - } - else { - target.m_ptr->m_size = size(source); - memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); - } + target.m_ptr->m_size = size(source); + memcpy(target.m_ptr->m_digits, source.m_ptr->m_digits, sizeof(digit_t) * size(source)); + target.m_kind = mpz_large; } #else // GMP version @@ -1267,19 +1514,14 @@ void mpz_manager::big_set(mpz & target, mpz const & source) { template int mpz_manager::big_compare(mpz const & a, mpz const & b) { #ifndef _MP_GMP - int sign_a; - int sign_b; - mpz_cell * cell_a; - mpz_cell * cell_b; - get_sign_cell<0>(a, sign_a, cell_a); - get_sign_cell<1>(b, sign_b, cell_b); + sign_cell ca(*this, a), cb(*this, b); - if (sign_a > 0) { + if (ca.sign() > 0) { // a is positive - if (sign_b > 0) { + if (cb.sign() > 0) { // a & b are positive - return m_mpn_manager.compare(cell_a->m_digits, cell_a->m_size, - cell_b->m_digits, cell_b->m_size); + return m_mpn_manager.compare(ca.cell()->m_digits, ca.cell()->m_size, + cb.cell()->m_digits, cb.cell()->m_size); } else { // b is negative @@ -1288,23 +1530,20 @@ int mpz_manager::big_compare(mpz const & a, mpz const & b) { } else { // a is negative - if (sign_b > 0) { + if (cb.sign() > 0) { // b is positive return -1; // a < b } else { // a & b are negative - return m_mpn_manager.compare(cell_b->m_digits, cell_b->m_size, - cell_a->m_digits, cell_a->m_size); + return m_mpn_manager.compare(cb.cell()->m_digits, cb.cell()->m_size, + ca.cell()->m_digits, ca.cell()->m_size); } } #else // GMP version - mpz_t * arg0; - mpz_t * arg1; - get_arg<0>(a, arg0); - get_arg<1>(b, arg1); - return mpz_cmp(*arg0, *arg1); + ensure_mpz_t a1(a), b1(b); + return mpz_cmp(a1(), b1()); #endif } @@ -1369,6 +1608,7 @@ uint64_t mpz_manager::get_uint64(mpz const & a) const { return mpz_get_ui(*a.m_ptr); } else { + MPZ_BEGIN_CRITICAL(); mpz_manager * _this = const_cast(this); mpz_set(_this->m_tmp, *a.m_ptr); mpz_mod(_this->m_tmp, m_tmp, m_two32); @@ -1376,6 +1616,7 @@ uint64_t mpz_manager::get_uint64(mpz const & a) const { mpz_set(_this->m_tmp, *a.m_ptr); mpz_div(_this->m_tmp, m_tmp, m_two32); r += static_cast(mpz_get_ui(m_tmp)) << static_cast(32); + MPZ_END_CRITICAL(); return r; } #endif @@ -1400,11 +1641,13 @@ int64_t mpz_manager::get_int64(mpz const & a) const { return mpz_get_si(*a.m_ptr); } else { + MPZ_BEGIN_CRITICAL(); mpz_manager * _this = const_cast(this); mpz_mod(_this->m_tmp, *a.m_ptr, m_two32); int64_t r = static_cast(mpz_get_ui(m_tmp)); mpz_div(_this->m_tmp, *a.m_ptr, m_two32); r += static_cast(mpz_get_si(m_tmp)) << static_cast(32); + MPZ_END_CRITICAL(); return r; } #endif @@ -1512,8 +1755,8 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { if (is_small(a)) { if (a.m_val == 2) { if (p < 8 * sizeof(int) - 1) { - del(b); b.m_val = 1 << p; + b.m_kind = mpz_small; } else { unsigned sz = p/(8 * sizeof(digit_t)) + 1; @@ -1526,12 +1769,13 @@ void mpz_manager::power(mpz const & a, unsigned p, mpz & b) { b.m_ptr->m_digits[i] = 0; b.m_ptr->m_digits[sz-1] = 1 << shift; b.m_val = 1; + b.m_kind = mpz_large; } return; } if (a.m_val == 0) { SASSERT(p != 0); - reset(b); + set(b, 0); return; } if (a.m_val == 1) { @@ -1608,38 +1852,41 @@ void mpz_manager::ensure_capacity(mpz & a, unsigned capacity) { return; if (capacity < m_init_cell_capacity) capacity = m_init_cell_capacity; + if (is_small(a)) { - a.m_ptr = allocate(capacity); - SASSERT(a.m_ptr->m_capacity == capacity); - if (a.m_val == INT_MIN) { + int val = a.m_val; + allocate_if_needed(a, capacity); + a.m_kind = mpz_large; + SASSERT(a.m_ptr->m_capacity >= capacity); + if (val == INT_MIN) { unsigned intmin_sz = m_int_min.m_ptr->m_size; for (unsigned i = 0; i < intmin_sz; i++) a.m_ptr->m_digits[i] = m_int_min.m_ptr->m_digits[i]; a.m_val = -1; a.m_ptr->m_size = m_int_min.m_ptr->m_size; } - else if (a.m_val < 0) { - a.m_ptr->m_digits[0] = -a.m_val; + else if (val < 0) { + a.m_ptr->m_digits[0] = -val; a.m_val = -1; a.m_ptr->m_size = 1; } else { - a.m_ptr->m_digits[0] = a.m_val; + a.m_ptr->m_digits[0] = val; a.m_val = 1; a.m_ptr->m_size = 1; } } - else { - if (a.m_ptr->m_capacity >= capacity) - return; + else if (a.m_ptr->m_capacity < capacity) { mpz_cell * new_cell = allocate(capacity); SASSERT(new_cell->m_capacity == capacity); unsigned old_sz = a.m_ptr->m_size; new_cell->m_size = old_sz; for (unsigned i = 0; i < old_sz; i++) new_cell->m_digits[i] = a.m_ptr->m_digits[i]; - deallocate(a.m_ptr); + deallocate(a); a.m_ptr = new_cell; + a.m_owner = mpz_self; + a.m_kind = mpz_large; } } @@ -1655,15 +1902,15 @@ void mpz_manager::normalize(mpz & a) { if (i == 0) { // a is zero... - reset(a); + set(a, 0); return; } if (i == 1 && ds[0] <= INT_MAX) { // a is small int val = a.m_val < 0 ? -static_cast(ds[0]) : static_cast(ds[0]); - del(a); a.m_val = val; + a.m_kind = mpz_small; return; } // adjust size @@ -1690,7 +1937,7 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { mpz_cell * c = a.m_ptr; unsigned sz = c->m_size; if (digit_shift >= sz) { - reset(a); + set(a, 0); return; } unsigned bit_shift = k % (8 * sizeof(digit_t)); @@ -1731,10 +1978,9 @@ void mpz_manager::machine_div2k(mpz & a, unsigned k) { c->m_size = new_sz; normalize(a); #else + ensure_mpz_t a1(a); MPZ_BEGIN_CRITICAL(); - mpz_t * arg0; - get_arg<0>(a, arg0); - mpz_tdiv_q_2exp(m_tmp, *arg0, k); + mpz_tdiv_q_2exp(m_tmp, a1(), k); mk_big(a); mpz_swap(*a.m_ptr, m_tmp); MPZ_END_CRITICAL(); @@ -1757,7 +2003,8 @@ void mpz_manager::mul2k(mpz & a, unsigned k) { unsigned new_sz = old_sz + word_shift + 1; ensure_capacity(a, new_sz); TRACE("mpz_mul2k", tout << "word_shift: " << word_shift << "\nbit_shift: " << bit_shift << "\nold_sz: " << old_sz << "\nnew_sz: " << new_sz - << "\na after ensure capacity:\n" << to_string(a) << "\n";); + << "\na after ensure capacity:\n" << to_string(a) << "\n"; + tout << a.m_kind << "\n";); SASSERT(!is_small(a)); mpz_cell * cell_a = a.m_ptr; old_sz = cell_a->m_size; @@ -1796,10 +2043,9 @@ void mpz_manager::mul2k(mpz & a, unsigned k) { normalize(a); TRACE("mpz_mul2k", tout << "mul2k result:\n" << to_string(a) << "\n";); #else - mpz_t * arg0; - get_arg<0>(a, arg0); + ensure_mpz_t a1(a); mk_big(a); - mpz_mul_2exp(*a.m_ptr, *arg0, k); + mpz_mul_2exp(*a.m_ptr, a1(), k); #endif } @@ -1902,8 +2148,10 @@ unsigned mpz_manager::mlog2(mpz const & a) { else return (sz - 1)*32 + ::log2(static_cast(ds[sz-1])); #else + MPZ_BEGIN_CRITICAL(); mpz_neg(m_tmp, *a.m_ptr); unsigned r = mpz_sizeinbase(m_tmp, 2); + MPZ_END_CRITICAL(); SASSERT(r > 0); return r - 1; #endif @@ -1921,7 +2169,7 @@ template bool mpz_manager::is_perfect_square(mpz const & a, mpz & root) { if (is_neg(a)) return false; - reset(root); + set(root, 0); if (is_zero(a)) { return true; } @@ -2087,6 +2335,7 @@ bool mpz_manager::decompose(mpz const & a, svector & digits) { return a.m_val < 0; #else bool r = is_neg(a); + MPZ_BEGIN_CRITICAL(); mpz_set(m_tmp, *a.m_ptr); mpz_abs(m_tmp, m_tmp); while (mpz_sgn(m_tmp) != 0) { @@ -2095,6 +2344,7 @@ bool mpz_manager::decompose(mpz const & a, svector & digits) { digits.push_back(v); mpz_tdiv_q_2exp(m_tmp, m_tmp, 32); } + MPZ_END_CRITICAL(); return r; #endif } diff --git a/src/util/mpz.h b/src/util/mpz.h index 92c6d0d10..f480b097e 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -64,6 +64,7 @@ class mpz_cell { digit_t m_digits[0]; friend class mpz_manager; friend class mpz_manager; + friend class mpz_stack; }; #else #include @@ -72,17 +73,25 @@ class mpz_cell { /** \brief Multi-precision integer. - If m_ptr == 0, the it is a small number and the value is stored at m_val. - Otherwise, m_val contains the sign (-1 negative, 1 positive), and m_ptr points to a mpz_cell that - store the value. <<< This last statement is true only in Windows. + If m_kind == mpz_small, it is a small number and the value is stored in m_val. + If m_kind == mpz_large, the value is stored in m_ptr and m_ptr != nullptr. + m_val contains the sign (-1 negative, 1 positive) + under winodws, m_ptr points to a mpz_cell that store the value. */ + +enum mpz_kind { mpz_small = 0, mpz_large = 1}; +enum mpz_owner { mpz_self = 0, mpz_ext = 1}; + class mpz { - int m_val; #ifndef _MP_GMP - mpz_cell * m_ptr; + typedef mpz_cell mpz_type; #else - mpz_t * m_ptr; + typedef mpz_t mpz_type; #endif + int m_val; + unsigned m_kind:1; + unsigned m_owner:1; + mpz_type * m_ptr; friend class mpz_manager; friend class mpz_manager; friend class mpq_manager; @@ -90,107 +99,128 @@ class mpz { friend class mpq; friend class mpbq; friend class mpbq_manager; + friend class mpz_stack; mpz & operator=(mpz const & other) { UNREACHABLE(); return *this; } public: - mpz(int v):m_val(v), m_ptr(nullptr) {} - mpz():m_val(0), m_ptr(nullptr) {} - mpz(mpz && other) : m_val(other.m_val), m_ptr(nullptr) { + mpz(int v):m_val(v), m_kind(mpz_small), m_owner(mpz_self), m_ptr(nullptr) {} + mpz():m_val(0), m_kind(mpz_small), m_owner(mpz_self), m_ptr(nullptr) {} + mpz(mpz_type* ptr): m_val(0), m_kind(mpz_small), m_owner(mpz_ext), m_ptr(ptr) { SASSERT(ptr);} + mpz(mpz && other) : m_val(other.m_val), m_kind(mpz_small), m_owner(mpz_self), m_ptr(nullptr) { std::swap(m_ptr, other.m_ptr); + unsigned o = m_owner; m_owner = other.m_owner; other.m_owner = o; + unsigned k = m_kind; m_kind = other.m_kind; other.m_kind = k; } void swap(mpz & other) { std::swap(m_val, other.m_val); std::swap(m_ptr, other.m_ptr); + unsigned o = m_owner; m_owner = other.m_owner; other.m_owner = o; + unsigned k = m_kind; m_kind = other.m_kind; other.m_kind = k; } }; +#ifndef _MP_GMP +class mpz_stack : public mpz { + static const unsigned capacity = 8; + unsigned char m_bytes[sizeof(mpz_cell) + sizeof(digit_t) * capacity]; +public: + mpz_stack():mpz(reinterpret_cast(m_bytes)) { + m_ptr->m_capacity = capacity; + } +}; +#else +class mpz_stack : public mpz {}; +#endif + inline void swap(mpz & m1, mpz & m2) { m1.swap(m2); } template class mpz_manager { - small_object_allocator m_allocator; - omp_nest_lock_t m_lock; + mutable small_object_allocator m_allocator; + mutable omp_nest_lock_t m_lock; #define MPZ_BEGIN_CRITICAL() if (SYNCH) omp_set_nest_lock(&m_lock); #define MPZ_END_CRITICAL() if (SYNCH) omp_unset_nest_lock(&m_lock); - mpn_manager m_mpn_manager; + mutable mpn_manager m_mpn_manager; #ifndef _MP_GMP unsigned m_init_cell_capacity; - mpz_cell * m_tmp[2]; - mpz_cell * m_arg[2]; mpz m_int_min; - static unsigned cell_size(unsigned capacity) { return sizeof(mpz_cell) + sizeof(digit_t) * capacity; } - - mpz_cell * allocate(unsigned capacity) { - SASSERT(capacity >= m_init_cell_capacity); - mpz_cell * cell = reinterpret_cast(m_allocator.allocate(cell_size(capacity))); - cell->m_capacity = capacity; - return cell; + static unsigned cell_size(unsigned capacity) { + return sizeof(mpz_cell) + sizeof(digit_t) * capacity; } + + mpz_cell * allocate(unsigned capacity); // make sure that n is a big number and has capacity equal to at least c. void allocate_if_needed(mpz & n, unsigned c) { - if (c < m_init_cell_capacity) - c = m_init_cell_capacity; - if (is_small(n)) { + c = std::max(c, m_init_cell_capacity); + if (n.m_ptr == nullptr || capacity(n) < c) { + deallocate(n); n.m_val = 1; + n.m_kind = mpz_large; + n.m_owner = mpz_self; n.m_ptr = allocate(c); - n.m_ptr->m_capacity = c; } - else if (capacity(n) < c) { - deallocate(n.m_ptr); - n.m_val = 1; - n.m_ptr = allocate(c); - n.m_ptr->m_capacity = c; + else { + n.m_kind = mpz_large; } } - void deallocate(mpz_cell * ptr) { - m_allocator.deallocate(cell_size(ptr->m_capacity), ptr); - } - - /** - \brief Make sure that m_tmp[IDX] can hold the given number of digits - */ - template - void ensure_tmp_capacity(unsigned capacity) { - if (m_tmp[IDX]->m_capacity >= capacity) - return; - deallocate(m_tmp[IDX]); - unsigned new_capacity = (3 * capacity + 1) >> 1; - m_tmp[IDX] = allocate(new_capacity); - SASSERT(m_tmp[IDX]->m_capacity >= capacity); - } + void deallocate(bool is_heap, mpz_cell * ptr); // Expand capacity of a while preserving its content. void ensure_capacity(mpz & a, unsigned sz); void normalize(mpz & a); + + void clear(mpz& n) { reset(n); } + + /** + \brief Set \c a with the value stored at src, and the given sign. + \c sz is an overapproximation of the size of the number stored at \c src. + */ + void set(mpz_cell& src, mpz & a, int sign, unsigned sz); + + #else // GMP code - mpz_t m_tmp, m_tmp2; - mpz_t m_two32; + mutable mpz_t m_tmp, m_tmp2; + mutable mpz_t m_two32; mpz_t * m_arg[2]; - mpz_t m_uint64_max; - mpz_t m_int64_max; - mpz_t m_int64_min; + mutable mpz_t m_uint64_max; + mutable mpz_t m_int64_max; + mutable mpz_t m_int64_min; - mpz_t * allocate() { + mpz_t * allocate() { + MPZ_BEGIN_CRITICAL(); mpz_t * cell = reinterpret_cast(m_allocator.allocate(sizeof(mpz_t))); + MPZ_END_CRITICAL(); mpz_init(*cell); return cell; } - void deallocate(mpz_t * ptr) { mpz_clear(*ptr); m_allocator.deallocate(sizeof(mpz_t), ptr); } + void deallocate(bool is_heap, mpz_t * ptr) { + mpz_clear(*ptr); + if (is_heap) { + MPZ_BEGIN_CRITICAL(); + m_allocator.deallocate(sizeof(mpz_t), ptr); + MPZ_END_CRITICAL(); + } + } + + void clear(mpz& n) { if (n.m_ptr) { mpz_clear(*n.m_ptr); }} #endif + + void deallocate(mpz& n) { + if (n.m_ptr) { + deallocate(n.m_owner == mpz_self, n.m_ptr); + n.m_ptr = nullptr; + n.m_kind = mpz_small; + } + } + mpz m_two64; - /** - \brief Set \c a with the value stored at m_tmp[IDX], and the given sign. - \c sz is an overapproximation of the size of the number stored at \c tmp. - */ - template - void set(mpz & a, int sign, unsigned sz); static int64_t i64(mpz const & a) { return static_cast(a.m_val); } @@ -198,19 +228,19 @@ class mpz_manager { void set_i64(mpz & c, int64_t v) { if (v >= INT_MIN && v <= INT_MAX) { - del(c); c.m_val = static_cast(v); + c.m_kind = mpz_small; } else { - MPZ_BEGIN_CRITICAL(); set_big_i64(c, v); - MPZ_END_CRITICAL(); } } void set_big_ui64(mpz & c, uint64_t v); + #ifndef _MP_GMP + static unsigned capacity(mpz const & c) { return c.m_ptr->m_capacity; } static unsigned size(mpz const & c) { return c.m_ptr->m_size; } @@ -241,16 +271,28 @@ class mpz_manager { return ((static_cast(digits(a)[1]) << 32) | (static_cast(digits(a)[0]))); } - template - void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell) { + class sign_cell { + static const unsigned capacity = 2; + unsigned char m_bytes[sizeof(mpz_cell) + sizeof(digit_t) * capacity]; + mpz m_local; + mpz const& m_a; + int m_sign; + mpz_cell* m_cell; + public: + sign_cell(mpz_manager& m, mpz const& a); + int sign() { return m_sign; } + mpz_cell const* cell() { return m_cell; } + }; + + void get_sign_cell(mpz const & a, int & sign, mpz_cell * & cell, mpz_cell* reserve) { if (is_small(a)) { if (a.m_val == INT_MIN) { sign = -1; cell = m_int_min.m_ptr; } else { - cell = m_arg[IDX]; - SASSERT(cell->m_size == 1); + cell = reserve; + cell->m_size = 1; if (a.m_val < 0) { sign = -1; cell->m_digits[0] = -a.m_val; @@ -266,26 +308,29 @@ class mpz_manager { cell = a.m_ptr; } } + #else // GMP code - template - void get_arg(mpz const & a, mpz_t * & result) { - if (is_small(a)) { - result = m_arg[IDX]; - mpz_set_si(*result, a.m_val); - } - else { - result = a.m_ptr; - } - } + class ensure_mpz_t { + mpz_t m_local; + mpz_t* m_result; + public: + ensure_mpz_t(mpz const& a); + ~ensure_mpz_t(); + mpz_t& operator()() { return *m_result; } + }; void mk_big(mpz & a) { - if (a.m_ptr == 0) { + if (a.m_ptr == nullptr) { a.m_val = 0; a.m_ptr = allocate(); + a.m_owner = mpz_self; } + a.m_kind = mpz_large; } + + #endif #ifndef _MP_GMP @@ -333,245 +378,49 @@ public: ~mpz_manager(); - static bool is_small(mpz const & a) { return a.m_ptr == nullptr; } + static bool is_small(mpz const & a) { return a.m_kind == mpz_small; } static mpz mk_z(int val) { return mpz(val); } - void del(mpz & a) { - if (a.m_ptr != nullptr) { - MPZ_BEGIN_CRITICAL(); - deallocate(a.m_ptr); - MPZ_END_CRITICAL(); - a.m_ptr = nullptr; - } - } + void del(mpz & a); - void add(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz] " << to_string(a) << " + " << to_string(b) << " == ";); - if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) + i64(b)); - } - else { - MPZ_BEGIN_CRITICAL(); - big_add(a, b, c); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void add(mpz const & a, mpz const & b, mpz & c); - void sub(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz] " << to_string(a) << " - " << to_string(b) << " == ";); - if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) - i64(b)); - } - else { - MPZ_BEGIN_CRITICAL(); - big_sub(a, b, c); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void sub(mpz const & a, mpz const & b, mpz & c); void inc(mpz & a) { add(a, mpz(1), a); } void dec(mpz & a) { add(a, mpz(-1), a); } - void mul(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz] " << to_string(a) << " * " << to_string(b) << " == ";); - if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) * i64(b)); - } - else { - MPZ_BEGIN_CRITICAL(); - big_mul(a, b, c); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void mul(mpz const & a, mpz const & b, mpz & c); // d <- a + b*c - void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { - if (is_one(b)) { - add(a, c, d); - } - else if (is_minus_one(b)) { - sub(a, c, d); - } - else { - mpz tmp; - mul(b,c,tmp); - add(a,tmp,d); - del(tmp); - } - } - + void addmul(mpz const & a, mpz const & b, mpz const & c, mpz & d); // d <- a - b*c - void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d) { - if (is_one(b)) { - sub(a, c, d); - } - else if (is_minus_one(b)) { - add(a, c, d); - } - else { - mpz tmp; - mul(b,c,tmp); - sub(a,tmp,d); - del(tmp); - } - } + void submul(mpz const & a, mpz const & b, mpz const & c, mpz & d); + void machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r); - void machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { - STRACE("mpz", tout << "[mpz-ext] divrem(" << to_string(a) << ", " << to_string(b) << ") == ";); - if (is_small(a) && is_small(b)) { - int64_t _a = i64(a); - int64_t _b = i64(b); - set_i64(q, _a / _b); - set_i64(r, _a % _b); - } - else { - MPZ_BEGIN_CRITICAL(); - big_div_rem(a, b, q, r); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << "(" << to_string(q) << ", " << to_string(r) << ")\n";); - } + void machine_div(mpz const & a, mpz const & b, mpz & c); - void machine_div(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz-ext] machine-div(" << to_string(a) << ", " << to_string(b) << ") == ";); - if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) / i64(b)); - } - else { - MPZ_BEGIN_CRITICAL(); - big_div(a, b, c); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void rem(mpz const & a, mpz const & b, mpz & c); - void rem(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz-ext] rem(" << to_string(a) << ", " << to_string(b) << ") == ";); - if (is_small(a) && is_small(b)) { - set_i64(c, i64(a) % i64(b)); - } - else { - MPZ_BEGIN_CRITICAL(); - big_rem(a, b, c); - MPZ_END_CRITICAL(); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void div_gcd(mpz const & a, mpz const & b, mpz & c); - void div(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz-ext] div(" << to_string(a) << ", " << to_string(b) << ") == ";); - if (is_neg(a)) { - mpz tmp; - machine_div_rem(a, b, c, tmp); - if (!is_zero(tmp)) { - if (is_neg(b)) - add(c, mk_z(1), c); - else - sub(c, mk_z(1), c); - } - del(tmp); - } - else { - machine_div(a, b, c); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void div(mpz const & a, mpz const & b, mpz & c); - void mod(mpz const & a, mpz const & b, mpz & c) { - STRACE("mpz", tout << "[mpz-ext] mod(" << to_string(a) << ", " << to_string(b) << ") == ";); - rem(a, b, c); - if (is_neg(c)) { - if (is_pos(b)) - add(c, b, c); - else - sub(c, b, c); - } - STRACE("mpz", tout << to_string(c) << "\n";); - } + void mod(mpz const & a, mpz const & b, mpz & c); - void neg(mpz & a) { - STRACE("mpz", tout << "[mpz] 0 - " << to_string(a) << " == ";); - if (is_small(a) && a.m_val == INT_MIN) { - // neg(INT_MIN) is not a small int - MPZ_BEGIN_CRITICAL(); - set_big_i64(a, - static_cast(INT_MIN)); - MPZ_END_CRITICAL(); - return; - } -#ifndef _MP_GMP - a.m_val = -a.m_val; -#else - if (is_small(a)) { - a.m_val = -a.m_val; - } - else { - mpz_neg(*a.m_ptr, *a.m_ptr); - } -#endif - STRACE("mpz", tout << to_string(a) << "\n";); - } + void neg(mpz & a); - void abs(mpz & a) { - if (is_small(a)) { - if (a.m_val < 0) { - if (a.m_val == INT_MIN) { - // abs(INT_MIN) is not a small int - MPZ_BEGIN_CRITICAL(); - set_big_i64(a, - static_cast(INT_MIN)); - MPZ_END_CRITICAL(); - } - else - a.m_val = -a.m_val; - } - } - else { -#ifndef _MP_GMP - a.m_val = 1; -#else - mpz_abs(*a.m_ptr, *a.m_ptr); -#endif - } - } + void abs(mpz & a); - static bool is_pos(mpz const & a) { -#ifndef _MP_GMP - return a.m_val > 0; -#else - if (is_small(a)) - return a.m_val > 0; - else - return mpz_sgn(*a.m_ptr) > 0; -#endif - } + static bool is_pos(mpz const & a) { return sign(a) > 0; } - static bool is_neg(mpz const & a) { -#ifndef _MP_GMP - return a.m_val < 0; -#else - if (is_small(a)) - return a.m_val < 0; - else - return mpz_sgn(*a.m_ptr) < 0; -#endif - } + static bool is_neg(mpz const & a) { return sign(a) < 0; } - static bool is_zero(mpz const & a) { -#ifndef _MP_GMP - return a.m_val == 0; -#else - if (is_small(a)) - return a.m_val == 0; - else - return mpz_sgn(*a.m_ptr) == 0; -#endif - } + static bool is_zero(mpz const & a) { return sign(a) == 0; } static int sign(mpz const & a) { #ifndef _MP_GMP @@ -593,10 +442,7 @@ public: return a.m_val == b.m_val; } else { - MPZ_BEGIN_CRITICAL(); - bool res = big_compare(a, b) == 0; - MPZ_END_CRITICAL(); - return res; + return big_compare(a, b) == 0; } } @@ -614,10 +460,7 @@ public: return a.m_val < b.m_val; } else { - MPZ_BEGIN_CRITICAL(); - bool res = big_compare(a, b) < 0; - MPZ_END_CRITICAL(); - return res; + return big_compare(a, b) < 0; } } @@ -661,25 +504,24 @@ public: void set(mpz & target, mpz const & source) { if (is_small(source)) { - del(target); target.m_val = source.m_val; + target.m_kind = mpz_small; } else { - MPZ_BEGIN_CRITICAL(); big_set(target, source); - MPZ_END_CRITICAL(); } } void set(mpz & target, mpz && source) { - del(target); target.m_val = source.m_val; std::swap(target.m_ptr, source.m_ptr); + auto o = target.m_owner; target.m_owner = source.m_owner; source.m_owner = o; + auto k = target.m_kind; target.m_kind = source.m_kind; source.m_kind = k; } void set(mpz & a, int val) { - del(a); a.m_val = val; + a.m_kind = mpz_small; } void set(mpz & a, unsigned val) { @@ -697,17 +539,15 @@ public: void set(mpz & a, uint64_t val) { if (val < INT_MAX) { - del(a); a.m_val = static_cast(val); + a.m_kind = mpz_small; } else { - MPZ_BEGIN_CRITICAL(); set_big_ui64(a, val); - MPZ_END_CRITICAL(); } } - void set(mpz & target, unsigned sz, digit_t const * digits); + void set_digits(mpz & target, unsigned sz, digit_t const * digits); mpz dup(const mpz & source) { mpz temp; @@ -715,14 +555,14 @@ public: return temp; } - void reset(mpz & a) { - del(a); - a.m_val = 0; - } + // deallocates any memory. + void reset(mpz & a); void swap(mpz & a, mpz & b) { std::swap(a.m_val, b.m_val); std::swap(a.m_ptr, b.m_ptr); + auto o = a.m_owner; a.m_owner = b.m_owner; b.m_owner = o; + auto k = a.m_kind; a.m_kind = b.m_kind; b.m_kind = k; } bool is_uint64(mpz const & a) const; diff --git a/src/util/obj_ref.h b/src/util/obj_ref.h index 409a77fc0..885601375 100644 --- a/src/util/obj_ref.h +++ b/src/util/obj_ref.h @@ -92,6 +92,15 @@ public: return *this; } + obj_ref & operator=(obj_ref && n) { + SASSERT(&m_manager == &n.m_manager); + if (this != &n) { + std::swap(m_obj, n.m_obj); + n.reset(); + } + return *this; + } + void reset() { dec_ref(); m_obj = nullptr; diff --git a/src/util/obj_ref_hashtable.h b/src/util/obj_ref_hashtable.h index 3d6bbf883..23a2a1867 100644 --- a/src/util/obj_ref_hashtable.h +++ b/src/util/obj_ref_hashtable.h @@ -26,7 +26,7 @@ class obj_ref_map { M& m; obj_map m_table; public: - typedef typename obj_map iterator; + typedef typename obj_map::iterator iterator; typedef Key key; typedef Value value; typedef typename obj_map::key_data key_data; diff --git a/src/util/plugin_manager.h b/src/util/plugin_manager.h index 2c4223eeb..4bf04da34 100644 --- a/src/util/plugin_manager.h +++ b/src/util/plugin_manager.h @@ -58,6 +58,8 @@ public: return m_fid2plugins.get(fid, 0); } + ptr_vector const& plugins() const { return m_plugins; } + typename ptr_vector::const_iterator begin() const { return m_plugins.begin(); } diff --git a/src/util/rational.h b/src/util/rational.h index dc7b79419..3403848c4 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -54,7 +54,6 @@ public: rational(mpz const & z) { m().set(m_val, z); } rational(double z) { UNREACHABLE(); } - explicit rational(char const * v) { m().set(m_val, v); } @@ -189,6 +188,12 @@ public: return r; } + friend inline rational machine_div_rem(rational const & r1, rational const & r2, rational & rem) { + rational r; + rational::m().machine_idiv_rem(r1.m_val, r2.m_val, r.m_val, rem.m_val); + return r; + } + friend inline rational mod(rational const & r1, rational const & r2) { rational r; rational::m().mod(r1.m_val, r2.m_val, r.m_val); @@ -410,8 +415,6 @@ public: } return num_bits; } - - }; inline bool operator!=(rational const & r1, rational const & r2) { @@ -422,6 +425,10 @@ inline bool operator>(rational const & r1, rational const & r2) { return operator<(r2, r1); } +inline bool operator<(int r1, rational const & r2) { + return rational(r1) < r2; +} + inline bool operator<(rational const & r1, int r2) { return r1 < rational(r2); } @@ -430,6 +437,11 @@ inline bool operator<=(rational const & r1, rational const & r2) { return !operator>(r1, r2); } +inline bool operator<=(rational const & r1, int r2) { + return r1 <= rational(r2); +} + + inline bool operator>=(rational const & r1, rational const & r2) { return !operator<(r1, r2); } @@ -438,6 +450,11 @@ inline bool operator>(rational const & a, int b) { return a > rational(b); } +inline bool operator>(int a, rational const & b) { + return rational(a) > b; +} + + inline bool operator!=(rational const & a, int b) { return !(a == rational(b)); } diff --git a/src/util/ref_vector.h b/src/util/ref_vector.h index f340d8886..1e6525a06 100644 --- a/src/util/ref_vector.h +++ b/src/util/ref_vector.h @@ -99,6 +99,13 @@ public: return *this; } + template + ref_vector_core& push_back(obj_ref && n) { + m_nodes.push_back(n.get()); + n.steal(); + return *this; + } + void pop_back() { SASSERT(!m_nodes.empty()); T * n = m_nodes.back(); @@ -253,6 +260,13 @@ public: return *this; } + template + element_ref & operator=(obj_ref && n) { + m_manager.dec_ref(m_ref); + m_ref = n.steal(); + return *this; + } + T * get() const { return m_ref; } @@ -288,9 +302,8 @@ public: return *this; } -private: // prevent abuse: - ref_vector & operator=(ref_vector const & other); + ref_vector & operator=(ref_vector const & other) = delete; }; template diff --git a/src/util/sorting_network.h b/src/util/sorting_network.h index f2906b00c..2a0d929dd 100644 --- a/src/util/sorting_network.h +++ b/src/util/sorting_network.h @@ -177,8 +177,8 @@ Notes: // for testing static const bool m_disable_dcard = false; - static const bool m_disable_dsorting = false; - static const bool m_disable_dsmerge = false; + static const bool m_disable_dsorting = true; // false; + static const bool m_disable_dsmerge = true; // false; static const bool m_force_dcard = false; static const bool m_force_dsorting = false; static const bool m_force_dsmerge = false; @@ -611,7 +611,7 @@ Notes: in.push_back(ctx.mk_not(xs[i])); } TRACE("pb_verbose", - pp(tout << N << ": ", in); + //pp(tout << N << ": ", in); tout << " ~ " << k << "\n";); return true; } @@ -696,18 +696,21 @@ Notes: psort_nw::sorting(n, xs, out); } else if (use_dcard(k, n)) { + TRACE("pb_verbose", tout << "use dcard\n";); dsorting(k, n, xs, out); } else { + TRACE("pb_verbose", tout << "use merge\n";); literal_vector out1, out2; - unsigned l = n/2; // TBD - card(k, l, xs, out1); - card(k, n-l, xs + l, out2); + unsigned half = n/2; // TBD + card(k, half, xs, out1); + card(k, n-half, xs + half, out2); smerge(k, out1.size(), out1.c_ptr(), out2.size(), out2.c_ptr(), out); } TRACE("pb_verbose", tout << "card k: " << k << " n: " << n << "\n"; - pp(tout << "in:", n, xs) << "\n"; - pp(tout << "out:", out) << "\n";); + //pp(tout << "in:", n, xs) << "\n"; + //pp(tout << "out:", out) << "\n"; + ); } vc vc_card(unsigned k, unsigned n) { @@ -733,7 +736,7 @@ Notes: void merge(unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { - TRACE("pb_verbose", tout << "merge a: " << a << " b: " << b << "\n";); + unsigned nc = m_stats.m_num_compiled_clauses; if (a == 1 && b == 1) { literal y1 = mk_max(as[0], bs[0]); literal y2 = mk_min(as[0], bs[0]); @@ -768,10 +771,12 @@ Notes: odd_b.size(), odd_b.c_ptr(), out2); interleave(out1, out2, out); } - 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";); + TRACE("pb_verbose", tout << "merge a: " << a << " b: " << b << " "; + tout << "num clauses " << m_stats.m_num_compiled_clauses - nc << "\n"; + //pp(tout << "a:", a, as) << "\n"; + //pp(tout << "b:", b, bs) << "\n"; + //pp(tout << "out:", out) << "\n"; + ); } vc vc_merge(unsigned a, unsigned b) { if (a == 1 && b == 1) { @@ -805,7 +810,7 @@ Notes: void interleave(literal_vector const& as, literal_vector const& bs, literal_vector& out) { - TRACE("pb_verbose", tout << "interleave: " << as.size() << " " << bs.size() << "\n";); + unsigned nc = m_stats.m_num_compiled_clauses; SASSERT(as.size() >= bs.size()); SASSERT(as.size() <= bs.size() + 2); SASSERT(!as.empty()); @@ -825,10 +830,12 @@ Notes: out.push_back(as[sz+1]); } SASSERT(out.size() == as.size() + bs.size()); - 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";); + TRACE("pb_verbose", tout << "interleave: " << as.size() << " " << bs.size() << " "; + tout << "num clauses " << m_stats.m_num_compiled_clauses - nc << "\n"; + //pp(tout << "a: ", as) << "\n"; + //pp(tout << "b: ", bs) << "\n"; + //pp(tout << "out: ", out) << "\n"; + ); } vc vc_interleave(unsigned a, unsigned b) { @@ -849,13 +856,15 @@ Notes: break; default: if (use_dsorting(n)) { + TRACE("pb_verbose", tout << "use dsorting: " << n << "\n";); dsorting(n, n, xs, out); } else { + TRACE("pb_verbose", tout << "use merge: " << n << "\n";); literal_vector out1, out2; - unsigned l = n/2; // TBD - sorting(l, xs, out1); - sorting(n-l, xs+l, out2); + unsigned half = n/2; // TBD + sorting(half, xs, out1); + sorting(n-half, xs+half, out2); merge(out1.size(), out1.c_ptr(), out2.size(), out2.c_ptr(), out); @@ -863,8 +872,9 @@ Notes: break; } TRACE("pb_verbose", tout << "sorting: " << n << "\n"; - pp(tout << "in:", n, xs) << "\n"; - pp(tout << "out:", out) << "\n";); + //pp(tout << "in:", n, xs) << "\n"; + //pp(tout << "out:", out) << "\n"; + ); } private: @@ -898,7 +908,7 @@ Notes: unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { - TRACE("pb_verbose", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n";); + unsigned nc = m_stats.m_num_compiled_clauses; if (a == 1 && b == 1 && c == 1) { literal y = mk_max(as[0], bs[0]); if (m_t != GE) { @@ -972,10 +982,11 @@ Notes: out.push_back(y); } } - 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"; + TRACE("pb_verbose", tout << "smerge: c:" << c << " a:" << a << " b:" << b << " "; + tout << "num clauses " << m_stats.m_num_compiled_clauses - nc << "\n"; + //pp(tout << "a:", a, as) << "\n"; + //pp(tout << "b:", b, bs) << "\n"; + //pp(tout << "out:", out) << "\n"; ); SASSERT(out.size() == std::min(a + b, c)); } @@ -1007,7 +1018,7 @@ Notes: return m_force_dsmerge || (!m_disable_dsmerge && - a < (1 << 15) && b < (1 << 15) && + a < (1 << 7) && b < (1 << 7) && vc_dsmerge(a, b, a + b) < vc_smerge_rec(a, b, c)); } @@ -1075,9 +1086,9 @@ Notes: void dsorting(unsigned m, unsigned n, literal const* xs, literal_vector& out) { - TRACE("pb_verbose", tout << "dsorting m: " << m << " n: " << n << "\n";); SASSERT(m <= n); literal_vector lits; + unsigned nc = m_stats.m_num_compiled_clauses; for (unsigned i = 0; i < m; ++i) { out.push_back(fresh("dsort")); } @@ -1095,7 +1106,11 @@ Notes: lits.pop_back(); } } + TRACE("pb_verbose", + tout << "dsorting m: " << m << " n: " << n << " "; + tout << "num clauses: " << m_stats.m_num_compiled_clauses - nc << "\n";); } + vc vc_dsorting(unsigned m, unsigned n) { SASSERT(m <= n && n < 10); vc v(m, 0); @@ -1111,7 +1126,8 @@ Notes: void add_subset(bool polarity, unsigned k, unsigned offset, literal_vector& lits, unsigned n, literal const* xs) { TRACE("pb_verbose", tout << "k:" << k << " offset: " << offset << " n: " << n << " "; - pp(tout, lits) << "\n";); + //pp(tout, lits) << "\n"; + ); SASSERT(k + offset <= n); if (k == 0) { add_clause(lits); diff --git a/src/util/symbol.cpp b/src/util/symbol.cpp index 220711003..90b245e6c 100644 --- a/src/util/symbol.cpp +++ b/src/util/symbol.cpp @@ -21,6 +21,7 @@ Revision History: #include "util/region.h" #include "util/string_buffer.h" #include "util/z3_omp.h" +#include symbol symbol::m_dummy(TAG(void*, nullptr, 2)); const symbol symbol::null; @@ -60,7 +61,7 @@ public: } }; -internal_symbol_table* g_symbol_table = nullptr; +static internal_symbol_table* g_symbol_table = nullptr; void initialize_symbols() { if (!g_symbol_table) { @@ -140,27 +141,7 @@ bool lt(symbol const & s1, symbol const & s2) { return false; } SASSERT(!s1.is_numerical() && !s2.is_numerical()); - char const * str1 = s1.bare_str(); - char const * str2 = s2.bare_str(); - while (true) { - if (*str1 < *str2) { - return true; - } - else if (*str1 == *str2) { - str1++; - str2++; - if (!*str1) { - SASSERT(*str2); // the strings can't be equal. - return true; - } - if (!*str2) { - SASSERT(*str1); // the strings can't be equal. - return false; - } - } - else { - SASSERT(*str1 > *str2); - return false; - } - } + auto cmp = strcmp(s1.bare_str(), s2.bare_str()); + SASSERT(cmp != 0); + return cmp < 0; } diff --git a/src/util/symbol.h b/src/util/symbol.h index 6a6e93747..88e825551 100644 --- a/src/util/symbol.h +++ b/src/util/symbol.h @@ -83,8 +83,7 @@ public: // It is the inverse of c_ptr(). // It was made public to simplify the implementation of the C API. static symbol mk_symbol_from_c_ptr(void const * ptr) { - symbol s(ptr); - return s; + return symbol(ptr); } unsigned hash() const { if (m_data == nullptr) return 0x9e3779d9; @@ -93,7 +92,7 @@ public: } bool contains(char c) const; unsigned size() const; - char const * bare_str() const { SASSERT(!is_numerical()); return is_numerical() ? "" : m_data; } + char const * bare_str() const { SASSERT(!is_numerical()); return m_data; } friend std::ostream & operator<<(std::ostream & target, symbol s) { SASSERT(!s.is_marked()); if (GET_TAG(s.m_data) == 0) { diff --git a/src/util/timeout.cpp b/src/util/timeout.cpp index 283b4c206..ad02a747d 100644 --- a/src/util/timeout.cpp +++ b/src/util/timeout.cpp @@ -27,9 +27,10 @@ Revision History: #include "util/event_handler.h" #include "util/scoped_timer.h" -scoped_timer * g_timeout = nullptr; -void (* g_on_timeout)() = nullptr; +static scoped_timer * g_timeout = nullptr; +static void (* g_on_timeout)() = nullptr; +namespace { class g_timeout_eh : public event_handler { public: void operator()(event_handler_caller_t caller_id) override { @@ -46,6 +47,7 @@ public: } } }; +} void set_timeout(long ms) { if (g_timeout) diff --git a/src/util/top_sort.h b/src/util/top_sort.h index 5f7db9c3e..f529d8a44 100644 --- a/src/util/top_sort.h +++ b/src/util/top_sort.h @@ -75,7 +75,7 @@ class top_sort { public: - ~top_sort() { + virtual ~top_sort() { for (auto & kv : m_deps) dealloc(kv.m_value); } @@ -96,6 +96,20 @@ public: } ptr_vector const& top_sorted() { return m_top_sorted; } + + obj_map const& partition_ids() const { return m_partition_id; } + + unsigned partition_id(T* t) const { return m_partition_id[t]; } + + bool is_singleton_partition(T* f) const { + unsigned pid = m_partition_id[f]; + return f == m_top_sorted[pid] && + (pid == 0 || m_partition_id[m_top_sorted[pid-1]] != pid) && + (pid + 1 == m_top_sorted.size() || m_partition_id[m_top_sorted[pid+1]] != pid); + } + + obj_map const& deps() const { return m_deps; } + }; #endif /* TOP_SORT_H_ */ diff --git a/src/util/vector.h b/src/util/vector.h index c4443255a..edcee7449 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -188,6 +188,24 @@ public: m_data = nullptr; } + bool operator==(vector const & other) const { + if (this == &other) { + return true; + } + if (size() != other.size()) + return false; + for (unsigned i = 0; i < size(); i++) { + if ((*this)[i] != other[i]) + return false; + } + return true; + } + + bool operator!=(vector const & other) const { + return !(*this == other); + } + + vector & operator=(vector const & source) { if (this == &source) { return *this; diff --git a/src/util/z3_exception.cpp b/src/util/z3_exception.cpp index d028a688a..585313024 100644 --- a/src/util/z3_exception.cpp +++ b/src/util/z3_exception.cpp @@ -67,9 +67,6 @@ default_exception::default_exception(fmt, char const* msg, ...) { m_msg = out.str(); } -default_exception::default_exception(std::string const & msg): m_msg(msg) { -} - char const * default_exception::msg() const { return m_msg.c_str(); } diff --git a/src/util/z3_exception.h b/src/util/z3_exception.h index e3260e89d..b5c1bf55d 100644 --- a/src/util/z3_exception.h +++ b/src/util/z3_exception.h @@ -41,7 +41,7 @@ class default_exception : public z3_exception { std::string m_msg; public: struct fmt {}; - default_exception(std::string const& msg); + default_exception(std::string && msg) : m_msg(std::move(msg)) {} default_exception(fmt, char const* msg, ...); ~default_exception() override {} char const * msg() const override;