From 461e88e34cfd6819343297e1ee87543a1ea3817e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner <nbjorner@microsoft.com> Date: Tue, 25 Oct 2016 20:32:13 -0700 Subject: [PATCH] additional robustness check for incremental sat solver core when it recieves interpreted constants, added PB equality to interface and special handling of equalities to adddress performance gap documented in #755 Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> --- contrib/cmake/src/solver/CMakeLists.txt | 1 + src/api/api_pb.cpp | 18 ++ src/api/dotnet/Context.cs | 15 ++ src/api/dotnet/Optimize.cs | 9 +- src/api/python/z3/z3.py | 20 ++ src/api/z3_api.h | 16 ++ src/sat/sat_solver/inc_sat_solver.cpp | 10 + src/sat/tactic/goal2sat.cpp | 17 +- src/sat/tactic/goal2sat.h | 6 +- src/smt/tactic/smt_tactic.cpp | 55 +----- src/smt/tactic/smt_tactic.h | 2 - src/solver/combined_solver.cpp | 1 + src/solver/solver2tactic.cpp | 179 ++++++++++++++++++ src/solver/solver2tactic.h | 30 +++ src/solver/tactic2solver.cpp | 1 + src/tactic/arith/bound_manager.cpp | 39 +++- src/tactic/arith/bound_manager.h | 2 + src/tactic/nlsat_smt/nl_purify_tactic.cpp | 1 + .../portfolio/bounded_int2bv_solver.cpp | 10 + src/tactic/portfolio/smt_strategic_solver.cpp | 3 + src/util/sorting_network.h | 79 ++++++-- 21 files changed, 430 insertions(+), 84 deletions(-) create mode 100644 src/solver/solver2tactic.cpp create mode 100644 src/solver/solver2tactic.h diff --git a/contrib/cmake/src/solver/CMakeLists.txt b/contrib/cmake/src/solver/CMakeLists.txt index 8142ca872..7f54e7745 100644 --- a/contrib/cmake/src/solver/CMakeLists.txt +++ b/contrib/cmake/src/solver/CMakeLists.txt @@ -5,6 +5,7 @@ z3_add_component(solver mus.cpp solver.cpp solver_na2as.cpp + solver2tactic.cpp tactic2solver.cpp COMPONENT_DEPENDENCIES model diff --git a/src/api/api_pb.cpp b/src/api/api_pb.cpp index 6d5a56d2c..b7c28c34f 100644 --- a/src/api/api_pb.cpp +++ b/src/api/api_pb.cpp @@ -57,5 +57,23 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_ast Z3_API Z3_mk_pbeq(Z3_context c, unsigned num_args, + Z3_ast const args[], int _coeffs[], + int k) { + Z3_TRY; + LOG_Z3_mk_pble(c, num_args, args, _coeffs, k); + RESET_ERROR_CODE(); + pb_util util(mk_c(c)->m()); + vector<rational> coeffs; + for (unsigned i = 0; i < num_args; ++i) { + coeffs.push_back(rational(_coeffs[i])); + } + ast* a = util.mk_eq(num_args, coeffs.c_ptr(), to_exprs(args), rational(k)); + mk_c(c)->save_ast_trail(a); + check_sorts(c, a); + RETURN_Z3(of_ast(a)); + Z3_CATCH_RETURN(0); + } + }; diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index 6ca48fcb0..c3c33a41e 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -2640,6 +2640,21 @@ namespace Microsoft.Z3 AST.ArrayToNative(args), coeffs, k)); } + + /// <summary> + /// Create a pseudo-Boolean equal constraint. + /// </summary> + public BoolExpr MkPBEq(int[] coeffs, BoolExpr[] args, int k) + { + Contract.Requires(args != null); + Contract.Requires(coeffs != null); + Contract.Requires(args.Length == coeffs.Length); + Contract.Requires(Contract.Result<BoolExpr[]>() != null); + CheckContextMatch<BoolExpr>(args); + return new BoolExpr(this, Native.Z3_mk_pbeq(nCtx, (uint) args.Length, + AST.ArrayToNative(args), + coeffs, k)); + } #endregion #region Numerals diff --git a/src/api/dotnet/Optimize.cs b/src/api/dotnet/Optimize.cs index 036aaf2f2..c8954a473 100644 --- a/src/api/dotnet/Optimize.cs +++ b/src/api/dotnet/Optimize.cs @@ -258,10 +258,13 @@ namespace Microsoft.Z3 /// <summary> /// Return a string the describes why the last to check returned unknown /// </summary> - public String getReasonUnknown() + public String ReasonUnknown { - Contract.Ensures(Contract.Result<string>() != null); - return Native.Z3_optimize_get_reason_unknown(Context.nCtx, NativeObject); + get + { + Contract.Ensures(Contract.Result<string>() != null); + return Native.Z3_optimize_get_reason_unknown(Context.nCtx, NativeObject); + } } diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index a037bbc5d..514a45fc6 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -7654,6 +7654,26 @@ def PbLe(args, k): _coeffs[i] = coeffs[i] return BoolRef(Z3_mk_pble(ctx.ref(), sz, _args, _coeffs, k), ctx) +def PbEq(args, k): + """Create a Pseudo-Boolean inequality k constraint. + + >>> a, b, c = Bools('a b c') + >>> f = PbEq(((a,1),(b,3),(c,2)), 3) + """ + args = _get_args(args) + args, coeffs = zip(*args) + if __debug__: + _z3_assert(len(args) > 0, "Non empty list of arguments expected") + ctx = _ctx_from_ast_arg_list(args) + if __debug__: + _z3_assert(ctx is not None, "At least one of the arguments must be a Z3 expression") + args = _coerce_expr_list(args, ctx) + _args, sz = _to_ast_array(args) + _coeffs = (ctypes.c_int * len(coeffs))() + for i in range(len(coeffs)): + _coeffs[i] = coeffs[i] + return BoolRef(Z3_mk_pbeq(ctx.ref(), sz, _args, _coeffs, k), ctx) + def solve(*args, **keywords): """Solve the constraints `*args`. diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 9e9771884..069f83340 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -861,6 +861,9 @@ typedef enum - Z3_OP_PB_GE: Generalized Pseudo-Boolean cardinality constraint. Example 2*x + 3*y + 2*z >= 4 + - Z3_OP_PB_EQ: Generalized Pseudo-Boolean equality constraint. + Example 2*x + 1*y + 2*z + 1*u = 4 + - Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN: Floating-point rounding mode RNE - Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY: Floating-point rounding mode RNA @@ -1166,6 +1169,7 @@ typedef enum { Z3_OP_PB_AT_MOST=0x900, Z3_OP_PB_LE, Z3_OP_PB_GE, + Z3_OP_PB_EQ, // Floating-Point Arithmetic Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN, @@ -3913,6 +3917,18 @@ extern "C" { Z3_ast const args[], int coeffs[], int k); + /** + \brief Pseudo-Boolean relations. + + Encode k1*p1 + k2*p2 + ... + kn*pn = k + + def_API('Z3_mk_pbeq', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in_array(1,INT), _in(INT))) + */ + + Z3_ast Z3_API Z3_mk_pbeq(Z3_context c, unsigned num_args, + Z3_ast const args[], int coeffs[], + int k); + /** \brief Convert a \c Z3_func_decl into \c Z3_ast. This is just type casting. diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 349b60f55..d091339bd 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -371,8 +371,18 @@ private: return l_undef; } g = m_subgoals[0]; + expr_ref_vector atoms(m); TRACE("sat", g->display_with_dependencies(tout);); m_goal2sat(*g, m_params, m_solver, m_map, dep2asm, true); + m_goal2sat.get_interpreted_atoms(atoms); + if (!atoms.empty()) { + std::stringstream strm; + strm << "interpreted atoms sent to SAT solver " << atoms; + TRACE("sat", std::cout << strm.str() << "\n";); + IF_VERBOSE(1, verbose_stream() << strm.str() << "\n";); + set_reason_unknown(strm.str().c_str()); + return l_undef; + } return l_true; } diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 4d5542866..136921914 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -59,6 +59,7 @@ struct goal2sat::imp { bool m_ite_extra; unsigned long long m_max_memory; expr_ref_vector m_trail; + expr_ref_vector m_interpreted_atoms; bool m_default_external; imp(ast_manager & _m, params_ref const & p, sat::solver & s, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external): @@ -67,6 +68,7 @@ struct goal2sat::imp { m_map(map), m_dep2asm(dep2asm), m_trail(m), + m_interpreted_atoms(m), m_default_external(default_external) { updt_params(p); m_true = sat::null_bool_var; @@ -128,6 +130,9 @@ struct goal2sat::imp { m_map.insert(t, v); l = sat::literal(v, sign); TRACE("goal2sat", tout << "new_var: " << v << "\n" << mk_ismt2_pp(t, m) << "\n";); + if (ext && !is_uninterp_const(t)) { + m_interpreted_atoms.push_back(t); + } } } else { @@ -474,7 +479,7 @@ bool goal2sat::has_unsupported_bool(goal const & g) { return test<unsupported_bool_proc>(g); } -goal2sat::goal2sat():m_imp(0) { +goal2sat::goal2sat():m_imp(0), m_interpreted_atoms(0) { } void goal2sat::collect_param_descrs(param_descrs & r) { @@ -492,10 +497,20 @@ struct goal2sat::scoped_set_imp { } }; + void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external) { imp proc(g.m(), p, t, m, dep2asm, default_external); scoped_set_imp set(this, &proc); proc(g); + dealloc(m_interpreted_atoms); + m_interpreted_atoms = alloc(expr_ref_vector, g.m()); + m_interpreted_atoms->append(proc.m_interpreted_atoms); +} + +void goal2sat::get_interpreted_atoms(expr_ref_vector& atoms) { + if (m_interpreted_atoms) { + atoms.append(*m_interpreted_atoms); + } } diff --git a/src/sat/tactic/goal2sat.h b/src/sat/tactic/goal2sat.h index c6776f2f7..cd63cd497 100644 --- a/src/sat/tactic/goal2sat.h +++ b/src/sat/tactic/goal2sat.h @@ -38,8 +38,11 @@ class goal2sat { struct imp; imp * m_imp; struct scoped_set_imp; + expr_ref_vector* m_interpreted_atoms; + public: goal2sat(); + ~goal2sat() { dealloc(m_interpreted_atoms); } typedef obj_map<expr, sat::literal> dep2asm_map; @@ -53,12 +56,13 @@ public: \remark m doesn't need to be empty. the definitions there are reused. - + \warning conversion throws a tactic_exception, if it is interrupted (by set_cancel), an unsupported operator is found, or memory consumption limit is reached (set with param :max-memory). */ void operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false); + void get_interpreted_atoms(expr_ref_vector& atoms); }; diff --git a/src/smt/tactic/smt_tactic.cpp b/src/smt/tactic/smt_tactic.cpp index f2fbcf6d9..2909d8848 100644 --- a/src/smt/tactic/smt_tactic.cpp +++ b/src/smt/tactic/smt_tactic.cpp @@ -24,63 +24,10 @@ Notes: #include"rewriter_types.h" #include"filter_model_converter.h" #include"ast_util.h" +#include"solver2tactic.h" typedef obj_map<expr, expr *> expr2expr_map; -void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector<expr>& assumptions, expr2expr_map& bool2dep, ref<filter_model_converter>& fmc) { - expr2expr_map dep2bool; - ptr_vector<expr> deps; - ast_manager& m = g->m(); - expr_ref_vector clause(m); - unsigned sz = g->size(); - for (unsigned i = 0; i < sz; i++) { - expr * f = g->form(i); - expr_dependency * d = g->dep(i); - if (d == 0 || !g->unsat_core_enabled()) { - clauses.push_back(f); - } - else { - // create clause (not d1 \/ ... \/ not dn \/ f) when the d's are the assumptions/dependencies of f. - clause.reset(); - clause.push_back(f); - deps.reset(); - m.linearize(d, deps); - SASSERT(!deps.empty()); // d != 0, then deps must not be empty - ptr_vector<expr>::iterator it = deps.begin(); - ptr_vector<expr>::iterator end = deps.end(); - for (; it != end; ++it) { - expr * d = *it; - if (is_uninterp_const(d) && m.is_bool(d)) { - // no need to create a fresh boolean variable for d - if (!bool2dep.contains(d)) { - assumptions.push_back(d); - bool2dep.insert(d, d); - } - clause.push_back(m.mk_not(d)); - } - else { - // must normalize assumption - expr * b = 0; - if (!dep2bool.find(d, b)) { - b = m.mk_fresh_const(0, m.mk_bool_sort()); - dep2bool.insert(d, b); - bool2dep.insert(b, d); - assumptions.push_back(b); - if (!fmc) { - fmc = alloc(filter_model_converter, m); - } - fmc->insert(to_app(b)->get_decl()); - } - clause.push_back(m.mk_not(b)); - } - } - SASSERT(clause.size() > 1); - expr_ref cls(m); - cls = mk_or(m, clause.size(), clause.c_ptr()); - clauses.push_back(cls); - } - } -} class smt_tactic : public tactic { smt_params m_params; diff --git a/src/smt/tactic/smt_tactic.h b/src/smt/tactic/smt_tactic.h index dd113634b..a23695fd1 100644 --- a/src/smt/tactic/smt_tactic.h +++ b/src/smt/tactic/smt_tactic.h @@ -32,8 +32,6 @@ tactic * mk_smt_tactic(params_ref const & p = params_ref()); tactic * mk_smt_tactic_using(bool auto_config = true, params_ref const & p = params_ref()); -void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector<expr>& assumptions, obj_map<expr, expr*>& bool2dep, ref<filter_model_converter>& fmc); - /* ADD_TACTIC("smt", "apply a SAT based SMT solver.", "mk_smt_tactic(p)") */ diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index fc76f85c1..2856315bd 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -196,6 +196,7 @@ public: virtual lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { switch_inc_mode(); + m_use_solver1_results = false; return m_solver2->get_consequences(asms, vars, consequences); } diff --git a/src/solver/solver2tactic.cpp b/src/solver/solver2tactic.cpp new file mode 100644 index 000000000..53b120fea --- /dev/null +++ b/src/solver/solver2tactic.cpp @@ -0,0 +1,179 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + solver2tactic.cpp + +Abstract: + + Convert solver to a tactic. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-10-17 + +Notes: + +--*/ + +#include "solver.h" +#include "tactic.h" +#include"filter_model_converter.h" +#include "solver2tactic.h" +#include "ast_util.h" + +typedef obj_map<expr, expr *> expr2expr_map; + +void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector<expr>& assumptions, expr2expr_map& bool2dep, ref<filter_model_converter>& fmc) { + expr2expr_map dep2bool; + ptr_vector<expr> deps; + ast_manager& m = g->m(); + expr_ref_vector clause(m); + unsigned sz = g->size(); + for (unsigned i = 0; i < sz; i++) { + expr * f = g->form(i); + expr_dependency * d = g->dep(i); + if (d == 0 || !g->unsat_core_enabled()) { + clauses.push_back(f); + } + else { + // create clause (not d1 \/ ... \/ not dn \/ f) when the d's are the assumptions/dependencies of f. + clause.reset(); + clause.push_back(f); + deps.reset(); + m.linearize(d, deps); + SASSERT(!deps.empty()); // d != 0, then deps must not be empty + ptr_vector<expr>::iterator it = deps.begin(); + ptr_vector<expr>::iterator end = deps.end(); + for (; it != end; ++it) { + expr * d = *it; + if (is_uninterp_const(d) && m.is_bool(d)) { + // no need to create a fresh boolean variable for d + if (!bool2dep.contains(d)) { + assumptions.push_back(d); + bool2dep.insert(d, d); + } + clause.push_back(m.mk_not(d)); + } + else { + // must normalize assumption + expr * b = 0; + if (!dep2bool.find(d, b)) { + b = m.mk_fresh_const(0, m.mk_bool_sort()); + dep2bool.insert(d, b); + bool2dep.insert(b, d); + assumptions.push_back(b); + if (!fmc) { + fmc = alloc(filter_model_converter, m); + } + fmc->insert(to_app(b)->get_decl()); + } + clause.push_back(m.mk_not(b)); + } + } + SASSERT(clause.size() > 1); + expr_ref cls(m); + cls = mk_or(m, clause.size(), clause.c_ptr()); + clauses.push_back(cls); + } + } +} + +class solver2tactic : public tactic { + ast_manager& m; + ref<solver> m_solver; + params_ref m_params; + +public: + solver2tactic(solver* s): + m(s->get_manager()), + m_solver(s) + {} + + virtual void updt_params(params_ref const & p) { + m_solver->updt_params(p); + } + + virtual void collect_param_descrs(param_descrs & r) { + m_solver->collect_param_descrs(r); + } + + virtual void operator()(/* in */ goal_ref const & in, + /* out */ goal_ref_buffer & result, + /* out */ model_converter_ref & mc, + /* out */ proof_converter_ref & pc, + /* out */ expr_dependency_ref & core) { + expr_ref_vector clauses(m); + expr2expr_map bool2dep; + ptr_vector<expr> assumptions; + ref<filter_model_converter> fmc; + extract_clauses_and_dependencies(in, clauses, assumptions, bool2dep, fmc); + m_solver->push(); + m_solver->assert_expr(clauses); + lbool r = m_solver->check_sat(assumptions.size(), assumptions.c_ptr()); + switch (r) { + case l_true: + if (in->models_enabled()) { + model_ref mdl; + m_solver->get_model(mdl); + mc = model2model_converter(mdl.get()); + mc = concat(fmc.get(), mc.get()); + } + in->reset(); + result.push_back(in.get()); + pc = 0; + core = 0; + break; + case l_false: { + in->reset(); + proof* pr = 0; + expr_dependency* lcore = 0; + if (in->proofs_enabled()) { + pr = m_solver->get_proof(); + } + if (in->unsat_core_enabled()) { + ptr_vector<expr> core; + m_solver->get_unsat_core(core); + for (unsigned i = 0; i < core.size(); ++i) { + lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(core[i]))); + } + } + in->assert_expr(m.mk_false(), pr, lcore); + result.push_back(in.get()); + mc = 0; + pc = 0; + core = 0; + break; + } + case l_undef: + if (m.canceled()) { + throw tactic_exception(Z3_CANCELED_MSG); + } + throw tactic_exception(m_solver->reason_unknown().c_str()); + } + m_solver->pop(1); + } + + virtual void collect_statistics(statistics & st) const { + m_solver->collect_statistics(st); + } + virtual void reset_statistics() {} + + virtual void cleanup() { + m_solver = m_solver->translate(m, m_params); + } + virtual void reset() { cleanup(); } + + virtual void set_logic(symbol const & l) {} + + virtual void set_progress_callback(progress_callback * callback) { + m_solver->set_progress_callback(callback); + } + + virtual tactic * translate(ast_manager & m) { + return alloc(solver2tactic, m_solver->translate(m, m_params)); + } +}; + +tactic* mk_solver2tactic(solver* s) { return alloc(solver2tactic, s); } diff --git a/src/solver/solver2tactic.h b/src/solver/solver2tactic.h new file mode 100644 index 000000000..65fbd37b1 --- /dev/null +++ b/src/solver/solver2tactic.h @@ -0,0 +1,30 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + solver2tactic.h + +Abstract: + + Convert solver to a tactic. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-10-17 + +Notes: + +--*/ +#ifndef SOLVER2TACTIC_H_ +#define SOLVER2TACTIC_H_ + +#include "tactic.h" +#include "filter_model_converter.h" +class solver; + +tactic * mk_solver2tactic(solver* s); + +void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector<expr>& assumptions, obj_map<expr, expr*>& bool2dep, ref<filter_model_converter>& fmc); + +#endif diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index f53301948..213ba5c1c 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -160,6 +160,7 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass } } catch (z3_error & ex) { + TRACE("tactic2solver", tout << "exception: " << ex.msg() << "\n";); throw ex; } catch (z3_exception & ex) { diff --git a/src/tactic/arith/bound_manager.cpp b/src/tactic/arith/bound_manager.cpp index ed77467c3..97d93658c 100644 --- a/src/tactic/arith/bound_manager.cpp +++ b/src/tactic/arith/bound_manager.cpp @@ -79,12 +79,23 @@ static bool is_strict(decl_kind k) { return k == OP_LT || k == OP_GT; } +bool bound_manager::is_numeral(expr* v, numeral& n, bool& is_int) { + expr* w; + if (m_util.is_uminus(v, w) && is_numeral(w, n, is_int)) { + n.neg(); + return true; + } + return m_util.is_numeral(v, n, is_int); +} + void bound_manager::operator()(expr * f, expr_dependency * d) { TRACE("bound_manager", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";); expr * v; numeral n; if (is_disjunctive_bound(f, d)) return; + if (is_equality_bound(f, d)) + return; bool pos = true; while (m().is_not(f, f)) pos = !pos; @@ -99,10 +110,10 @@ void bound_manager::operator()(expr * f, expr_dependency * d) { expr * lhs = t->get_arg(0); expr * rhs = t->get_arg(1); bool is_int; - if (is_uninterp_const(lhs) && m_util.is_numeral(rhs, n, is_int)) { + if (is_uninterp_const(lhs) && is_numeral(rhs, n, is_int)) { v = lhs; } - else if (is_uninterp_const(rhs) && m_util.is_numeral(lhs, n, is_int)) { + else if (is_uninterp_const(rhs) && is_numeral(lhs, n, is_int)) { v = rhs; k = swap_decl(k); } @@ -165,6 +176,26 @@ void bound_manager::insert_lower(expr * v, bool strict, numeral const & n, expr_ } } +bool bound_manager::is_equality_bound(expr * f, expr_dependency * d) { + expr* x, *y; + if (!m().is_eq(f, x, y)) { + return false; + } + if (!is_uninterp_const(x)) { + std::swap(x, y); + } + numeral n; + bool is_int; + if (is_uninterp_const(x) && is_numeral(y, n, is_int)) { + insert_lower(x, false, n, d); + insert_upper(x, false, n, d); + return true; + } + else { + return false; + } +} + bool bound_manager::is_disjunctive_bound(expr * f, expr_dependency * d) { numeral lo, hi, n; if (!m().is_or(f)) return false; @@ -176,14 +207,14 @@ bool bound_manager::is_disjunctive_bound(expr * f, expr_dependency * d) { expr * e = to_app(f)->get_arg(i); if (!m().is_eq(e, x, y)) return false; if (is_uninterp_const(x) && - m_util.is_numeral(y, n, is_int) && is_int && + is_numeral(y, n, is_int) && is_int && (x == v || v == 0)) { if (v == 0) { v = x; lo = hi = n; } if (n < lo) lo = n; if (n > hi) hi = n; } else if (is_uninterp_const(y) && - m_util.is_numeral(x, n, is_int) && is_int && + is_numeral(x, n, is_int) && is_int && (y == v || v == 0)) { if (v == 0) { v = y; lo = hi = n; } if (n < lo) lo = n; diff --git a/src/tactic/arith/bound_manager.h b/src/tactic/arith/bound_manager.h index cc0d693e9..6a4dc2c96 100644 --- a/src/tactic/arith/bound_manager.h +++ b/src/tactic/arith/bound_manager.h @@ -36,6 +36,8 @@ private: obj_map<expr, expr_dependency*> m_upper_deps; ptr_vector<expr> m_bounded_vars; bool is_disjunctive_bound(expr * f, expr_dependency * d); + bool is_equality_bound(expr * f, expr_dependency * d); + bool is_numeral(expr* v, rational& n, bool& is_int); void insert_lower(expr * v, bool strict, numeral const & n, expr_dependency * d); void insert_upper(expr * v, bool strict, numeral const & n, expr_dependency * d); public: diff --git a/src/tactic/nlsat_smt/nl_purify_tactic.cpp b/src/tactic/nlsat_smt/nl_purify_tactic.cpp index d8b016d7b..46b5a51b0 100644 --- a/src/tactic/nlsat_smt/nl_purify_tactic.cpp +++ b/src/tactic/nlsat_smt/nl_purify_tactic.cpp @@ -59,6 +59,7 @@ Revision History: #include "model_smt2_pp.h" #include "expr_safe_replace.h" #include "ast_util.h" +#include "solver2tactic.h" class nl_purify_tactic : public tactic { diff --git a/src/tactic/portfolio/bounded_int2bv_solver.cpp b/src/tactic/portfolio/bounded_int2bv_solver.cpp index f7236351c..688d9403a 100644 --- a/src/tactic/portfolio/bounded_int2bv_solver.cpp +++ b/src/tactic/portfolio/bounded_int2bv_solver.cpp @@ -251,6 +251,16 @@ private: } sub.insert(e, t); } + else { + IF_VERBOSE(1, + verbose_stream() << "unprocessed entry: " << mk_pp(e, m) << "\n"; + if (bm.has_lower(e, lo, s1)) { + verbose_stream() << "lower: " << lo << " " << s1 << "\n"; + } + if (bm.has_upper(e, hi, s2)) { + verbose_stream() << "upper: " << hi << " " << s2 << "\n"; + }); + } } } diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 81825221e..d2414ec72 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -40,6 +40,7 @@ Notes: #include"inc_sat_solver.h" #include"fd_solver.h" #include"bv_rewriter.h" +#include"solver2tactic.h" tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const & logic) { @@ -89,6 +90,8 @@ tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const return mk_qffpbv_tactic(m, p); else if (logic=="HORN") return mk_horn_tactic(m, p); + else if (logic == "QF_FD") + return mk_solver2tactic(mk_fd_solver(m, p)); //else if (logic=="QF_UFNRA") // return mk_qfufnra_tactic(m, p); else diff --git a/src/util/sorting_network.h b/src/util/sorting_network.h index 0f5d2838e..9e35d6e8b 100644 --- a/src/util/sorting_network.h +++ b/src/util/sorting_network.h @@ -202,7 +202,8 @@ Notes: return ge(full, k, n, in.c_ptr()); } else if (k == 1) { - return mk_at_most_1(full, n, xs); + literal_vector ors; + return mk_at_most_1(full, n, xs, ors); } else { SASSERT(2*k <= n); @@ -221,6 +222,9 @@ Notes: if (dualize(k, n, xs, in)) { return eq(k, n, in.c_ptr()); } + else if (k == 1) { + return mk_exactly_1(true, n, xs); + } else { SASSERT(2*k <= n); m_t = EQ; @@ -238,12 +242,56 @@ Notes: private: - literal mk_at_most_1(bool full, unsigned n, literal const* xs) { + + literal mk_and(literal l1, literal l2) { + literal result = fresh(); + add_clause(ctx.mk_not(result), l1); + add_clause(ctx.mk_not(result), l2); + add_clause(ctx.mk_not(l1), ctx.mk_not(l2), result); + return result; + } + + void mk_implies_or(literal l, unsigned n, literal const* xs) { + literal_vector lits(n, xs); + lits.push_back(ctx.mk_not(l)); + add_clause(lits); + } + + void mk_or_implies(literal l, unsigned n, literal const* xs) { + for (unsigned j = 0; j < n; ++j) { + add_clause(ctx.mk_not(xs[j]), l); + } + } + + literal mk_or(literal_vector const& ors) { + if (ors.size() == 1) { + return ors[0]; + } + literal result = fresh(); + mk_implies_or(result, ors.size(), ors.c_ptr()); + mk_or_implies(result, ors.size(), ors.c_ptr()); + return result; + } + + literal mk_exactly_1(bool full, unsigned n, literal const* xs) { + literal_vector ors; + literal r1 = mk_at_most_1(full, n, xs, ors); + + if (full) { + r1 = mk_and(r1, mk_or(ors)); + } + else { + mk_implies_or(r1, ors.size(), ors.c_ptr()); + } + return r1; + } + + literal mk_at_most_1(bool full, unsigned n, literal const* xs, literal_vector& ors) { TRACE("pb", tout << (full?"full":"partial") << " "; for (unsigned i = 0; i < n; ++i) tout << xs[i] << " "; tout << "\n";); - if (!full && n >= 4) { + if (false && !full && n >= 4) { return mk_at_most_1_bimander(n, xs); } literal_vector in(n, xs); @@ -252,9 +300,10 @@ Notes: literal_vector ands; ands.push_back(result); while (!in.empty()) { - literal_vector ors; + ors.reset(); unsigned i = 0; unsigned n = in.size(); + if (n + 1 == inc_size) ++inc_size; bool last = n <= inc_size; for (; i + inc_size < n; i += inc_size) { mk_at_most_1_small(full, last, inc_size, in.c_ptr() + i, result, ands, ors); @@ -267,7 +316,6 @@ Notes: } in.reset(); in.append(ors); - ors.reset(); } if (full) { add_clause(ands); @@ -278,23 +326,16 @@ Notes: void mk_at_most_1_small(bool full, bool last, unsigned n, literal const* xs, literal result, literal_vector& ands, literal_vector& ors) { SASSERT(n > 0); if (n == 1) { - if (!last) { - ors.push_back(xs[0]); - } + ors.push_back(xs[0]); return; } - if (!last) { - literal ex = fresh(); - for (unsigned j = 0; j < n; ++j) { - add_clause(ctx.mk_not(xs[j]), ex); - } - if (full) { - literal_vector lits(n, xs); - lits.push_back(ctx.mk_not(ex)); - add_clause(lits.size(), lits.c_ptr()); - } - ors.push_back(ex); + literal ex = fresh(); + mk_or_implies(ex, n, xs); + if (full) { + mk_implies_or(ex, n, xs); } + ors.push_back(ex); + // result => xs[0] + ... + xs[n-1] <= 1 for (unsigned i = 0; i < n; ++i) { for (unsigned j = i + 1; j < n; ++j) {