From e23509797a81c2730b066ea85330b1411e409324 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 19 Jan 2017 19:28:20 -0800 Subject: [PATCH 01/13] access parameters from Python API Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 01dacace7..a6f91a0a2 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -691,6 +691,30 @@ class FuncDeclRef(AstRef): """ return Z3_get_decl_kind(self.ctx_ref(), self.ast) + def params(self): + ctx = self.ctx + n = Z3_get_decl_num_parameters(self.ctx_ref(), self.ast) + result = [ None for i in range(n) ] + for i in range(n): + k = Z3_get_decl_parameter_kind(self.ctx_ref(), self.ast, i) + if k == Z3_PARAMETER_INT: + result[i] = Z3_get_decl_int_parameter(self.ctx_ref(), self.ast, i) + elif k == Z3_PARAMETER_DOUBLE: + result[i] = Z3_get_decl_double_parameter(self.ctx_ref(), self.ast, i) + elif k == Z3_PARAMETER_RATIONAL: + result[i] = Z3_get_decl_rational_parameter(self.ctx_ref(), self.ast, i) + elif k == Z3_PARAMETER_SYMBOL: + result[i] = Z3_get_decl_symbol_parameter(self.ctx_ref(), self.ast, i) + elif k == Z3_PARAMETER_SORT: + result[i] = SortRef(Z3_get_decl_sort_parameter(self.ctx_ref(), self.ast, i), ctx) + elif k == Z3_PARAMETER_AST: + result[i] = ExprRef(Z3_get_decl_ast_parameter(self.ctx_ref(), self.ast, i), ctx) + elif k == Z3_PARAMETER_FUNC_DECL: + result[i] = FuncDeclRef(Z3_get_decl_func_decl_parameter(self.ctx_ref(), self.ast, i), ctx) + else: + assert(False) + return result + def __call__(self, *args): """Create a Z3 application expression using the function `self`, and the given arguments. @@ -2637,6 +2661,13 @@ class RatNumRef(ArithRef): """ return self.denominator().as_long() + def is_int(self): + return self.denominator().is_int() and self.denominator_as_long() == 1 + + def as_long(self): + _z3_assert(self.is_int(), "Expected integer fraction") + return self.numerator_as_long() + def as_decimal(self, prec): """ Return a Z3 rational value as a string in decimal notation using at most `prec` decimal places. From adf8072eaacea187a60ff3690a6b0aaa12b586af Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Sat, 21 Jan 2017 12:28:37 +0000 Subject: [PATCH 02/13] Added option to limit the distance of unsat core extension through patterns. --- src/smt/params/smt_params_helper.pyg | 3 ++- src/smt/smt_solver.cpp | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 178b2117f..a3f163ed4 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -63,5 +63,6 @@ def_module_params(module_name='smt', ('dack.threshold', UINT, 10, ' number of times the congruence rule must be used before Leibniz\'s axiom is expanded'), ('core.validate', BOOL, False, 'validate unsat core produced by SMT context'), ('core.minimize', BOOL, False, 'minimize unsat core produced by SMT context'), - ('core.extend_patterns', BOOL, False, 'extend unsat core with literals that trigger (potential) quantifier instances') + ('core.extend_patterns', BOOL, False, 'extend unsat core with literals that trigger (potential) quantifier instances'), + ('core.extend_patterns.max_distance', UINT, UINT_MAX, 'limits the distance of a pattern-extended unsat core') )) diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index f80ff09f4..ab6be5d91 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -37,6 +37,7 @@ namespace smt { symbol m_logic; bool m_minimizing_core; bool m_core_extend_patterns; + unsigned m_core_extend_patterns_max_distance; obj_map m_name2assertion; public: @@ -46,12 +47,14 @@ namespace smt { m_params(p), m_context(m, m_smt_params), m_minimizing_core(false), - m_core_extend_patterns(false) { + m_core_extend_patterns(false), + m_core_extend_patterns_max_distance(UINT_MAX) { m_logic = l; if (m_logic != symbol::null) m_context.set_logic(m_logic); smt_params_helper smth(p); m_core_extend_patterns = smth.core_extend_patterns(); + m_core_extend_patterns_max_distance = smth.core_extend_patterns_max_distance(); } virtual solver * translate(ast_manager & m, params_ref const & p) { @@ -283,7 +286,7 @@ namespace smt { func_decl_set pattern_fds; vector assrtn_fds; - do { + for (unsigned d = 0; d < m_core_extend_patterns_max_distance; d++) { new_core_literals.reset(); unsigned sz = core.size(); @@ -308,8 +311,10 @@ namespace smt { } core.append(new_core_literals.size(), new_core_literals.c_ptr()); + + if (new_core_literals.empty()) + break; } - while (!new_core_literals.empty()); } }; }; From 4ec4abd7e38a2a5a1794ddd6a51a8a7b10a2feab Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 23 Jan 2017 13:31:43 -0800 Subject: [PATCH 03/13] fix test for int-value Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index a6f91a0a2..c54cd6091 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -2394,7 +2394,7 @@ def is_int_value(a): >>> is_int_value(RealVal(1)) False """ - return is_arith(a) and a.is_int() and _is_numeral(a.ctx, a.as_ast()) + return isinstance(a, IntNumRef) def is_rational_value(a): """Return `True` if `a` is rational value of sort Real. From 60783e5696e4a741e7ff4aaeb4eb979d3c4bd77c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Jan 2017 13:26:58 -0800 Subject: [PATCH 04/13] fix regression for z3num Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index c54cd6091..77b74336e 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -2394,7 +2394,7 @@ def is_int_value(a): >>> is_int_value(RealVal(1)) False """ - return isinstance(a, IntNumRef) + return is_arith(a) and a.is_int() and _is_numeral(a.ctx, a.as_ast()) def is_rational_value(a): """Return `True` if `a` is rational value of sort Real. @@ -2662,6 +2662,12 @@ class RatNumRef(ArithRef): return self.denominator().as_long() def is_int(self): + return False + + def is_real(self): + return True + + def is_int_value(self): return self.denominator().is_int() and self.denominator_as_long() == 1 def as_long(self): From 4782e19086d751147a7cfc654846ac455c0a7d15 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Jan 2017 16:21:51 -0800 Subject: [PATCH 05/13] fix bug in sat-simplifier decreasing heap values of variables that are not in the heap Signed-off-by: Nikolaj Bjorner --- src/sat/sat_simplifier.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index c42572f1e..62fb99a11 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -896,7 +896,7 @@ namespace sat { unsigned idx = l.index(); if (m_queue.contains(idx)) m_queue.decreased(idx); - else + else m_queue.insert(idx); } literal next() { SASSERT(!empty()); return to_literal(m_queue.erase_min()); } @@ -918,16 +918,19 @@ namespace sat { } void insert(literal l) { - bool_var v = l.var(); - if (s.is_external(v) || s.was_eliminated(v)) - return; m_queue.insert(l); } + bool process_var(bool_var v) { + return !s.is_external(v) && !s.was_eliminated(v); + } + void operator()(unsigned num_vars) { for (bool_var v = 0; v < num_vars; v++) { - insert(literal(v, false)); - insert(literal(v, true)); + if (process_var(v)) { + insert(literal(v, false)); + insert(literal(v, true)); + } } while (!m_queue.empty()) { s.checkpoint(); @@ -941,9 +944,9 @@ namespace sat { void process(literal l) { TRACE("blocked_clause", tout << "processing: " << l << "\n";); model_converter::entry * new_entry = 0; - if (s.is_external(l.var()) || s.was_eliminated(l.var())) + if (!process_var(l.var())) { return; - + } { m_to_remove.reset(); { @@ -963,8 +966,10 @@ namespace sat { mc.insert(*new_entry, c); unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { - if (c[i] != l) - m_queue.decreased(~c[i]); + literal lit = c[i]; + if (lit != l && process_var(lit.var())) { + m_queue.decreased(~lit); + } } } s.unmark_all(c); From 777091e653f0674a81e374634da9e3195ef50cd1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Jan 2017 18:09:27 -0800 Subject: [PATCH 06/13] fix part 1 of #875 Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context.cpp | 2 ++ src/smt/theory_seq.cpp | 28 +++++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index c7aecca88..3c88a75a2 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -2031,11 +2031,13 @@ namespace smt { v.shrink(old_size); } +#if 0 void context::mark_as_deleted(clause * cls) { SASSERT(!cls->deleted()); remove_cls_occs(cls); cls->mark_as_deleted(m_manager); } +#endif /** \brief Undo variable assignments. diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index ed95ef8d7..cdea02c48 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2958,22 +2958,19 @@ void theory_seq::tightest_prefix(expr* s, expr* x) { let i = Index(t, s, offset): offset >= len(t) => i = -1 - - offset fixed to 0: - len(t) != 0 & !contains(t, s) => i = -1 - len(t) != 0 & contains(t, s) => t = xsy & i = len(x) + + + offset = 0 & len(t) != 0 & contains(t, s) => t = xsy & i = len(x) tightest_prefix(x, s) - offset not fixed: 0 <= offset < len(t) => xy = t & len(x) = offset & (-1 = indexof(y, s, 0) => -1 = i) & (indexof(y, s, 0) >= 0 => indexof(t, s, 0) + offset = i) - if offset < 0 - under specified + offset < 0 => i = -1 optional lemmas: (len(s) > len(t) -> i = -1) @@ -2987,17 +2984,19 @@ void theory_seq::add_indexof_axiom(expr* i) { expr_ref minus_one(m_autil.mk_int(-1), m); expr_ref zero(m_autil.mk_int(0), m); expr_ref xsy(m); + + literal cnt = mk_literal(m_util.str.mk_contains(t, s)); + literal i_eq_m1 = mk_eq(i, minus_one, false); + add_axiom(cnt, i_eq_m1); + literal s_eq_empty = mk_eq_empty(s); + add_axiom(~s_eq_empty, mk_eq(i, zero, false)); + add_axiom(s_eq_empty, ~mk_eq_empty(t), i_eq_m1); if (!offset || (m_autil.is_numeral(offset, r) && r.is_zero())) { expr_ref x = mk_skolem(m_indexof_left, t, s); expr_ref y = mk_skolem(m_indexof_right, t, s); xsy = mk_concat(x, s, y); expr_ref lenx(m_util.str.mk_length(x), m); - literal cnt = mk_literal(m_util.str.mk_contains(t, s)); - literal s_eq_empty = mk_eq_empty(s); - add_axiom(cnt, mk_eq(i, minus_one, false)); - add_axiom(~s_eq_empty, mk_eq(i, zero, false)); - add_axiom(s_eq_empty, ~mk_eq_empty(t), mk_eq(i, minus_one, false)); add_axiom(~cnt, s_eq_empty, mk_seq_eq(t, xsy)); add_axiom(~cnt, s_eq_empty, mk_eq(i, lenx, false)); tightest_prefix(s, x); @@ -3024,10 +3023,13 @@ void theory_seq::add_indexof_axiom(expr* i) { add_axiom(~offset_ge_0, offset_ge_len, mk_seq_eq(t, mk_concat(x, y))); add_axiom(~offset_ge_0, offset_ge_len, mk_eq(m_util.str.mk_length(x), offset, false)); add_axiom(~offset_ge_0, offset_ge_len, - ~mk_eq(indexof0, minus_one, false), mk_eq(i, minus_one, false)); + ~mk_eq(indexof0, minus_one, false), i_eq_m1); add_axiom(~offset_ge_0, offset_ge_len, ~mk_literal(m_autil.mk_ge(indexof0, zero)), mk_eq(offset_p_indexof0, i, false)); + + // offset < 0 => -1 = i + add_axiom(offset_ge_0, i_eq_m1); } } From 7386e2f3e942b025d8266323dc299055a7dd1efa Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 25 Jan 2017 18:29:30 -0800 Subject: [PATCH 07/13] add warning for scearios of #876 Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index cdea02c48..3e1436aa6 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -3819,6 +3819,15 @@ void theory_seq::new_eq_eh(dependency* deps, enode* n1, enode* n2) { solve_eqs(m_eqs.size()-1); enforce_length_coherence(n1, n2); } + else if (n1 != n2 && m_util.is_re(n1->get_owner())) { + warning_msg("equality between regular expressions is not yet supported"); + eautomaton* a1 = get_automaton(n1->get_owner()); + eautomaton* a2 = get_automaton(n2->get_owner()); + // eautomaton* b1 = mk_difference(*a1, *a2); + // eautomaton* b2 = mk_difference(*a2, *a1); + // eautomaton* c = mk_union(*b1, *b2); + // then some emptiness check. + } } void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { From a0a81fc2d7c46283c2a37b3ffdf923701a3484dd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 26 Jan 2017 08:37:37 -0800 Subject: [PATCH 08/13] add format #879 Signed-off-by: Nikolaj Bjorner --- scripts/update_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/update_api.py b/scripts/update_api.py index 031b39c75..38b6213c1 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -942,7 +942,7 @@ def def_API(name, result, params): log_c.write(" Au(a%s);\n" % sz) exe_c.write("in.get_int_array(%s)" % i) else: - error ("unsupported parameter for %s, %s" % (ty, name, p)) + error ("unsupported parameter for %s, %s, %s" % (ty, name, p)) elif kind == OUT_ARRAY: sz = param_array_capacity_pos(p) sz_p = params[sz] From 962979b09c991fc47a02e10afb07d68a353308ff Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 26 Jan 2017 13:28:40 -0800 Subject: [PATCH 09/13] rework sat.mus to use restart count for bounded minimization Signed-off-by: Nikolaj Bjorner --- src/sat/sat_mus.cpp | 59 +++++++++++++++++--------------------------- src/sat/sat_mus.h | 3 +-- src/sat/sat_solver.h | 1 + 3 files changed, 24 insertions(+), 39 deletions(-) diff --git a/src/sat/sat_mus.cpp b/src/sat/sat_mus.cpp index 380b8ee94..06851d10d 100644 --- a/src/sat/sat_mus.cpp +++ b/src/sat/sat_mus.cpp @@ -23,7 +23,7 @@ Notes: namespace sat { - mus::mus(solver& s):s(s), m_is_active(false),m_restart(0), m_max_restarts(0) {} + mus::mus(solver& s):s(s), m_is_active(false), m_max_num_restarts(UINT_MAX) {} mus::~mus() {} @@ -31,8 +31,6 @@ namespace sat { m_core.reset(); m_mus.reset(); m_model.reset(); - m_max_restarts = (s.m_stats.m_restart - m_restart) + 10; - m_restart = s.m_stats.m_restart; } void mus::set_core() { @@ -49,12 +47,12 @@ namespace sat { } lbool mus::operator()() { + m_max_num_restarts = s.m_config.m_core_minimize_partial ? s.num_restarts() + 10 : UINT_MAX; flet _disable_min(s.m_config.m_core_minimize, false); flet _is_active(m_is_active, true); - IF_VERBOSE(3, verbose_stream() << "(sat.mus " << s.get_core() << ")\n";); + IF_VERBOSE(3, verbose_stream() << "(sat.mus size: " << s.get_core().size() << " core: [" << s.get_core() << "])\n";); reset(); lbool r = mus1(); - m_restart = s.m_stats.m_restart; return r; } @@ -63,13 +61,13 @@ namespace sat { TRACE("sat", tout << "old core: " << s.get_core() << "\n";); literal_vector& core = get_core(); literal_vector& mus = m_mus; - if (core.size() > 64) { + if (!minimize_partial && core.size() > 64) { return mus2(); } - unsigned delta_time = 0; - unsigned core_miss = 0; while (!core.empty()) { - IF_VERBOSE(3, verbose_stream() << "(opt.mus reducing core: " << core.size() << " mus: " << mus.size() << ")\n";); + IF_VERBOSE(1, verbose_stream() << "(sat.mus num-to-process: " << core.size() << " mus: " << mus.size(); + if (minimize_partial) verbose_stream() << " max-restarts: " << m_max_num_restarts; + verbose_stream() << ")\n";); TRACE("sat", tout << "core: " << core << "\n"; tout << "mus: " << mus << "\n";); @@ -78,34 +76,35 @@ namespace sat { set_core(); return l_undef; } - if (minimize_partial && 3*delta_time > core.size() && core.size() < mus.size()) { - break; - } unsigned num_literals = core.size() + mus.size(); if (num_literals <= 2) { // IF_VERBOSE(0, verbose_stream() << "num literals: " << core << " " << mus << "\n";); break; } - if (s.m_config.m_core_minimize_partial && s.m_stats.m_restart - m_restart > m_max_restarts) { - IF_VERBOSE(1, verbose_stream() << "(sat restart budget exceeded)\n";); - set_core(); - return l_true; - } literal lit = core.back(); core.pop_back(); lbool is_sat; { + flet _restart_bound(s.m_config.m_restart_max, m_max_num_restarts); scoped_append _sa(mus, core); mus.push_back(~lit); is_sat = s.check(mus.size(), mus.c_ptr()); TRACE("sat", tout << "mus: " << mus << "\n";); } + IF_VERBOSE(1, verbose_stream() << "(sat.mus " << is_sat << ")\n";); switch (is_sat) { case l_undef: - core.push_back(lit); - set_core(); - return l_undef; + if (!s.canceled()) { + // treat restart max as sat, so literal is in the mus + mus.push_back(lit); + } + else { + core.push_back(lit); + set_core(); + return l_undef; + } + break; case l_true: { SASSERT(value_at(lit, s.get_model()) == l_false); mus.push_back(lit); @@ -115,11 +114,9 @@ namespace sat { case l_false: literal_vector const& new_core = s.get_core(); if (new_core.contains(~lit)) { - IF_VERBOSE(3, verbose_stream() << "miss core " << lit << "\n";); - ++core_miss; + IF_VERBOSE(3, verbose_stream() << "(sat.mus unit reduction, literal is in both cores " << lit << ")\n";); } else { - core_miss = 0; TRACE("sat", tout << "core: " << new_core << " mus: " << mus << "\n";); core.reset(); for (unsigned i = 0; i < new_core.size(); ++i) { @@ -131,14 +128,6 @@ namespace sat { } break; } - - unsigned new_num_literals = core.size() + mus.size(); - if (new_num_literals == num_literals) { - delta_time++; - } - else { - delta_time = 0; - } } set_core(); IF_VERBOSE(3, verbose_stream() << "(sat.mus.new " << s.m_core << ")\n";); @@ -159,13 +148,9 @@ namespace sat { lbool mus::qx(literal_set& assignment, literal_set& support, bool has_support) { lbool is_sat = l_true; - if (s.m_config.m_core_minimize_partial && s.m_stats.m_restart - m_restart > m_max_restarts) { - IF_VERBOSE(1, verbose_stream() << "(sat restart budget exceeded)\n";); - return l_true; - } if (has_support) { scoped_append _sa(m_mus, support.to_vector()); - is_sat = s.check(m_mus.size(), m_mus.c_ptr()); + is_sat = s.check(m_mus.size(), m_mus.c_ptr()); switch (is_sat) { case l_false: { literal_set core(s.get_core()); @@ -173,7 +158,7 @@ namespace sat { assignment.reset(); return l_true; } - case l_undef: + case l_undef: return l_undef; case l_true: update_model(); diff --git a/src/sat/sat_mus.h b/src/sat/sat_mus.h index 74f6d75f0..946f66ed6 100644 --- a/src/sat/sat_mus.h +++ b/src/sat/sat_mus.h @@ -26,8 +26,7 @@ namespace sat { literal_vector m_mus; bool m_is_active; model m_model; // model obtained during minimal unsat core - unsigned m_restart; - unsigned m_max_restarts; + unsigned m_max_num_restarts; public: diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index dcf7e2acb..aa7e6edea 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -209,6 +209,7 @@ namespace sat { bool inconsistent() const { return m_inconsistent; } unsigned num_vars() const { return m_level.size(); } unsigned num_clauses() const; + unsigned num_restarts() const { return m_restarts; } bool is_external(bool_var v) const { return m_external[v] != 0; } bool was_eliminated(bool_var v) const { return m_eliminated[v] != 0; } unsigned scope_lvl() const { return m_scope_lvl; } From b70f1f0319a0d7f607f3f752e21a4957ee020645 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 27 Jan 2017 09:47:18 -0800 Subject: [PATCH 10/13] fix overflow exposed in #880 Signed-off-by: Nikolaj Bjorner --- src/smt/smt_solver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index ab6be5d91..59a5ddf8f 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -122,7 +122,8 @@ namespace smt { SASSERT(n <= lvl); unsigned new_lvl = lvl - n; unsigned old_sz = m_scopes[new_lvl]; - for (unsigned i = cur_sz - 1; i >= old_sz; i--) { + for (unsigned i = cur_sz; i > old_sz; ) { + --i; expr * key = m_assumptions[i].get(); SASSERT(m_name2assertion.contains(key)); expr * value = m_name2assertion.find(key); From 37ee4c95c3a2d963002fab22b0f698191d0e92d8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Jan 2017 02:09:08 -0800 Subject: [PATCH 11/13] adding parallel threads Signed-off-by: Nikolaj Bjorner --- contrib/cmake/src/sat/CMakeLists.txt | 1 + src/api/c++/z3++.h | 1 + src/sat/sat_config.cpp | 1 + src/sat/sat_config.h | 1 + src/sat/sat_par.cpp | 45 ++++++++ src/sat/sat_par.h | 39 +++++++ src/sat/sat_params.pyg | 1 + src/sat/sat_solver.cpp | 146 ++++++++++++++++++++++++-- src/sat/sat_solver.h | 10 ++ src/sat/sat_solver/inc_sat_solver.cpp | 3 + src/smt/theory_seq.cpp | 4 +- src/tactic/tactical.cpp | 7 -- src/util/rlimit.h | 9 ++ 13 files changed, 250 insertions(+), 18 deletions(-) create mode 100644 src/sat/sat_par.cpp create mode 100644 src/sat/sat_par.h diff --git a/contrib/cmake/src/sat/CMakeLists.txt b/contrib/cmake/src/sat/CMakeLists.txt index 3eec21ec3..d88a73708 100644 --- a/contrib/cmake/src/sat/CMakeLists.txt +++ b/contrib/cmake/src/sat/CMakeLists.txt @@ -12,6 +12,7 @@ z3_add_component(sat sat_integrity_checker.cpp sat_model_converter.cpp sat_mus.cpp + sat_par.cpp sat_probing.cpp sat_scc.cpp sat_simplifier.cpp diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 1556064d6..979d9aed7 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -1816,6 +1816,7 @@ namespace z3 { fmls, fml)); } + param_descrs get_param_descrs() { return param_descrs(ctx(), Z3_solver_get_param_descrs(ctx(), m_solver)); } }; diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index 4e01bfe55..ccf538dfe 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -77,6 +77,7 @@ namespace sat { m_burst_search = p.burst_search(); m_max_conflicts = p.max_conflicts(); + m_num_parallel = p.parallel_threads(); // These parameters are not exposed m_simplify_mult1 = _p.get_uint("simplify_mult1", 300); diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index 910ca0360..405cbd092 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -57,6 +57,7 @@ namespace sat { unsigned m_random_seed; unsigned m_burst_search; unsigned m_max_conflicts; + unsigned m_num_parallel; unsigned m_simplify_mult1; double m_simplify_mult2; diff --git a/src/sat/sat_par.cpp b/src/sat/sat_par.cpp new file mode 100644 index 000000000..7a185a3b5 --- /dev/null +++ b/src/sat/sat_par.cpp @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_par.cpp + +Abstract: + + Utilities for parallel SAT solving. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-1-29. + +Revision History: + +--*/ +#include "sat_par.h" + + +namespace sat { + + par::par() {} + + void par::exchange(literal_vector const& in, unsigned& limit, literal_vector& out) { + #pragma omp critical (par_solver) + { + if (limit < m_units.size()) { + // this might repeat some literals. + out.append(m_units.size() - limit, m_units.c_ptr() + limit); + } + for (unsigned i = 0; i < in.size(); ++i) { + literal lit = in[i]; + if (!m_unit_set.contains(lit.index())) { + m_unit_set.insert(lit.index()); + m_units.push_back(lit); + } + } + limit = m_units.size(); + } + } + +}; + diff --git a/src/sat/sat_par.h b/src/sat/sat_par.h new file mode 100644 index 000000000..2b2592de7 --- /dev/null +++ b/src/sat/sat_par.h @@ -0,0 +1,39 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_par.h + +Abstract: + + Utilities for parallel SAT solving. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-1-29. + +Revision History: + +--*/ +#ifndef SAT_PAR_H_ +#define SAT_PAR_H_ + +#include"sat_types.h" +#include"hashtable.h" +#include"map.h" + +namespace sat { + + class par { + typedef hashtable index_set; + literal_vector m_units; + index_set m_unit_set; + public: + par(); + void exchange(literal_vector const& in, unsigned& limit, literal_vector& out); + }; + +}; + +#endif diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 21a50bea2..60708fd5c 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -22,4 +22,5 @@ def_module_params('sat', ('dyn_sub_res', BOOL, True, 'dynamic subsumption resolution for minimizing learned clauses'), ('core.minimize', BOOL, False, 'minimize computed core'), ('core.minimize_partial', BOOL, False, 'apply partial (cheap) core minimization'), + ('parallel_threads', UINT, 1, 'number of parallel threads to use'), ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'))) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 37fb971fd..bf3dc1988 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -35,6 +35,7 @@ namespace sat { m_rlimit(l), m_config(p), m_ext(ext), + m_par(0), m_cleaner(*this), m_simplifier(*this, p), m_scc(*this, p), @@ -72,6 +73,8 @@ namespace sat { void solver::copy(solver const & src) { SASSERT(m_mc.empty() && src.m_mc.empty()); + SASSERT(scope_lvl() == 0); + SASSERT(src.scope_lvl() == 0); // create new vars if (num_vars() < src.num_vars()) { for (bool_var v = num_vars(); v < src.num_vars(); v++) { @@ -81,19 +84,25 @@ namespace sat { VERIFY(v == mk_var(ext, dvar)); } } + unsigned sz = src.scope_lvl() == 0 ? src.m_trail.size() : src.m_scopes[0].m_trail_lim; + for (unsigned i = 0; i < sz; ++i) { + assign(src.m_trail[i], justification()); + } + { // copy binary clauses - vector::const_iterator it = src.m_watches.begin(); - vector::const_iterator end = src.m_watches.begin(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - watch_list const & wlist = *it; + unsigned sz = src.m_watches.size(); + for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { literal l = ~to_literal(l_idx); - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (!it2->is_binary_non_learned_clause()) + watch_list const & wlist = src.m_watches[l_idx]; + watch_list::const_iterator it = wlist.begin(); + watch_list::const_iterator end = wlist.end(); + for (; it != end; ++it) { + if (!it->is_binary_non_learned_clause()) + continue; + literal l2 = it->get_literal(); + if (l.index() > l2.index()) continue; - literal l2 = it2->get_literal(); mk_clause_core(l, l2); } } @@ -711,6 +720,9 @@ namespace sat { pop_to_base_level(); IF_VERBOSE(2, verbose_stream() << "(sat.sat-solver)\n";); SASSERT(scope_lvl() == 0); + if (m_config.m_num_parallel > 0 && !m_par) { + return check_par(num_lits, lits); + } #ifdef CLONE_BEFORE_SOLVING if (m_mc.empty()) { m_clone = alloc(solver, m_params, 0 /* do not clone extension */); @@ -759,6 +771,7 @@ namespace sat { restart(); simplify_problem(); + exchange_par(); if (check_inconsistent()) return l_false; gc(); @@ -774,6 +787,121 @@ namespace sat { } } + enum par_exception_kind { + DEFAULT_EX, + ERROR_EX + }; + + lbool solver::check_par(unsigned num_lits, literal const* lits) { + int num_threads = static_cast(m_config.m_num_parallel); + scoped_limits scoped_rlimit(rlimit()); + vector rlims(num_threads); + ptr_vector solvers(num_threads); + sat::par par; + for (int i = 0; i < num_threads; ++i) { + m_params.set_uint("random_seed", i); + solvers[i] = alloc(sat::solver, m_params, rlims[i], 0); + solvers[i]->copy(*this); + solvers[i]->set_par(&par); + scoped_rlimit.push_child(&solvers[i]->rlimit()); + } + int finished_id = -1; + std::string ex_msg; + par_exception_kind ex_kind; + unsigned error_code = 0; + lbool result = l_undef; + #pragma omp parallel for + for (int i = 0; i < num_threads; ++i) { + try { + lbool r = solvers[i]->check(num_lits, lits); + bool first = false; + #pragma omp critical (par_solver) + { + if (finished_id == UINT_MAX) { + finished_id = i; + first = true; + result = r; + } + } + if (first) { + if (r == l_true) { + set_model(solvers[i]->get_model()); + } + else if (r == l_false) { + m_core.reset(); + m_core.append(solvers[i]->get_core()); + } + for (int j = 0; j < num_threads; ++j) { + if (i != j) { + rlims[j].cancel(); + } + } + } + } + catch (z3_error & err) { + if (i == 0) { + error_code = err.error_code(); + ex_kind = ERROR_EX; + } + } + catch (z3_exception & ex) { + if (i == 0) { + ex_msg = ex.msg(); + ex_kind = DEFAULT_EX; + } + } + } + for (int i = 0; i < num_threads; ++i) { + dealloc(solvers[i]); + } + if (finished_id == -1) { + switch (ex_kind) { + case ERROR_EX: throw z3_error(error_code); + default: throw default_exception(ex_msg.c_str()); + } + } + return result; + + } + + /* + \brief import lemmas/units from parallel sat solvers. + */ + void solver::exchange_par() { + if (m_par && scope_lvl() == 0) { + unsigned num_in = 0, num_out = 0; + SASSERT(scope_lvl() == 0); // parallel with assumptions is TBD + literal_vector in, out; + for (unsigned i = m_par_limit_out; i < m_trail.size(); ++i) { + literal lit = m_trail[i]; + if (lit.var() < m_par_num_vars) { + ++num_out; + out.push_back(lit); + } + } + m_par_limit_out = m_trail.size(); + m_par->exchange(out, m_par_limit_in, in); + for (unsigned i = 0; !inconsistent() && i < in.size(); ++i) { + literal lit = in[i]; + SASSERT(lit.var() < m_par_num_vars); + if (lvl(lit.var()) != 0 || value(lit) != l_true) { + ++num_in; + assign(lit, justification()); + } + } + if (num_in > 0 || num_out > 0) { + IF_VERBOSE(1, verbose_stream() << "(sat-sync out: " << num_out << " in: " << num_in << ")\n";); + } + } + } + + void solver::set_par(par* p) { + m_par = p; + m_par_num_vars = num_vars(); + m_par_limit_in = 0; + m_par_limit_out = 0; + } + bool_var solver::next_var() { bool_var next; diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index aa7e6edea..54b8a9bb2 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -33,6 +33,7 @@ Revision History: #include"sat_iff3_finder.h" #include"sat_probing.h" #include"sat_mus.h" +#include"sat_par.h" #include"params.h" #include"statistics.h" #include"stopwatch.h" @@ -74,6 +75,7 @@ namespace sat { config m_config; stats m_stats; extension * m_ext; + par* m_par; random_gen m_rand; clause_allocator m_cls_allocator; cleaner m_cleaner; @@ -128,6 +130,10 @@ namespace sat { literal_set m_assumption_set; // set of enabled assumptions literal_vector m_core; // unsat core + unsigned m_par_limit_in; + unsigned m_par_limit_out; + unsigned m_par_num_vars; + void del_clauses(clause * const * begin, clause * const * end); friend class integrity_checker; @@ -241,7 +247,9 @@ namespace sat { m_num_checkpoints = 0; if (memory::get_allocation_size() > m_config.m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); } + void set_par(par* p); bool canceled() { return !m_rlimit.inc(); } + config const& get_config() { return m_config; } typedef std::pair bin_clause; protected: watch_list & get_wlist(literal l) { return m_watches[l.index()]; } @@ -317,6 +325,8 @@ namespace sat { bool check_model(model const & m) const; void restart(); void sort_watch_lits(); + void exchange_par(); + lbool check_par(unsigned num_lits, literal const* lits); // ----------------------- // diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 33d10f428..65a7b021c 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -140,6 +140,7 @@ public: if (r != l_true) return r; r = m_solver.check(m_asms.size(), m_asms.c_ptr()); + switch (r) { case l_true: if (sz > 0) { @@ -276,6 +277,8 @@ public: return r; } + + virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { sat::literal_vector ls; u_map lit2var; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 3e1436aa6..d5251c56b 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2940,8 +2940,8 @@ void theory_seq::deque_axiom(expr* n) { encode that s is not contained in of xs1 where s1 is all of s, except the last element. - lit or s = "" or s = s1*(unit c) - lit or s = "" or !contains(x*s1, s) + s = "" or s = s1*(unit c) + s = "" or !contains(x*s1, s) */ void theory_seq::tightest_prefix(expr* s, expr* x) { expr_ref s1 = mk_first(s); diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index 4cb212265..33f8325fb 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -461,13 +461,6 @@ enum par_exception_kind { class par_tactical : public or_else_tactical { - struct scoped_limits { - reslimit& m_limit; - unsigned m_sz; - scoped_limits(reslimit& lim): m_limit(lim), m_sz(0) {} - ~scoped_limits() { for (unsigned i = 0; i < m_sz; ++i) m_limit.pop_child(); } - void push_child(reslimit* lim) { m_limit.push_child(lim); ++m_sz; } - }; public: par_tactical(unsigned num, tactic * const * ts):or_else_tactical(num, ts) {} diff --git a/src/util/rlimit.h b/src/util/rlimit.h index 913984768..283d7613d 100644 --- a/src/util/rlimit.h +++ b/src/util/rlimit.h @@ -61,4 +61,13 @@ public: }; +struct scoped_limits { + reslimit& m_limit; + unsigned m_sz; + scoped_limits(reslimit& lim): m_limit(lim), m_sz(0) {} + ~scoped_limits() { for (unsigned i = 0; i < m_sz; ++i) m_limit.pop_child(); } + void push_child(reslimit* lim) { m_limit.push_child(lim); ++m_sz; } +}; + + #endif From 92e2d920fd5354a6cb8586b4947403e5c26faab4 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Jan 2017 14:03:27 -0800 Subject: [PATCH 12/13] working on card for sat Signed-off-by: Nikolaj Bjorner --- contrib/cmake/src/sat/CMakeLists.txt | 1 + src/sat/card_extension.cpp | 686 +++++++++++++++++++++++++++ src/sat/card_extension.h | 142 ++++++ src/sat/sat_extension.h | 4 + src/sat/sat_justification.h | 2 +- src/sat/sat_solver.cpp | 4 + src/sat/sat_solver.h | 1 + src/smt/theory_pb.h | 11 - 8 files changed, 839 insertions(+), 12 deletions(-) create mode 100644 src/sat/card_extension.cpp create mode 100644 src/sat/card_extension.h diff --git a/contrib/cmake/src/sat/CMakeLists.txt b/contrib/cmake/src/sat/CMakeLists.txt index d88a73708..41051fd40 100644 --- a/contrib/cmake/src/sat/CMakeLists.txt +++ b/contrib/cmake/src/sat/CMakeLists.txt @@ -1,5 +1,6 @@ z3_add_component(sat SOURCES + card_extension.cpp dimacs.cpp sat_asymm_branch.cpp sat_clause.cpp diff --git a/src/sat/card_extension.cpp b/src/sat/card_extension.cpp new file mode 100644 index 000000000..985579f18 --- /dev/null +++ b/src/sat/card_extension.cpp @@ -0,0 +1,686 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + card_extension.cpp + +Abstract: + + Extension for cardinality reasoning. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-01-30 + +Revision History: + +--*/ + +#include"card_extension.h" +#include"sat_types.h" + +namespace sat { + + card_extension::card::card(unsigned index, literal lit, literal_vector const& lits, unsigned k): + m_index(index), + m_lit(lit), + m_k(k), + m_size(lits.size()), + m_lits(lits) { + } + + void card_extension::card::negate() { + m_lit.neg(); + for (unsigned i = 0; i < m_size; ++i) { + m_lits[i].neg(); + } + m_k = m_size - m_k + 1; + SASSERT(m_size >= m_k && m_k > 0); + } + + void card_extension::init_watch(bool_var v) { + if (m_var_infos.size() <= static_cast(v)) { + m_var_infos.resize(static_cast(v)+100); + } + } + + void card_extension::init_watch(card& c, bool is_true) { + clear_watch(c); + if (c.lit().sign() == is_true) { + c.negate(); + } + SASSERT(s().value(c.lit()) == l_true); + unsigned j = 0, sz = c.size(), bound = c.k(); + if (bound == sz) { + for (unsigned i = 0; i < sz && !s().inconsistent(); ++i) { + assign(c, c[i]); + } + return; + } + // put the non-false literals into the head. + for (unsigned i = 0; i < sz; ++i) { + if (s().value(c[i]) != l_false) { + if (j != i) { + c.swap(i, j); + } + ++j; + } + } + DEBUG_CODE( + bool is_false = false; + for (unsigned k = 0; k < sz; ++k) { + SASSERT(!is_false || s().value(c[k]) == l_false); + is_false = s().value(c[k]) == l_false; + }); + + // j is the number of non-false, sz - j the number of false. + if (j < bound) { + SASSERT(0 < bound && bound < sz); + literal alit = c[j]; + + // + // we need the assignment level of the asserting literal to be maximal. + // such that conflict resolution can use the asserting literal as a starting + // point. + // + + for (unsigned i = bound; i < sz; ++i) { + if (s().lvl(alit) < s().lvl(c[i])) { + c.swap(i, j); + alit = c[j]; + } + } + set_conflict(c, alit); + } + else if (j == bound) { + for (unsigned i = 0; i < bound && !s().inconsistent(); ++i) { + assign(c, c[i]); + } + } + else { + for (unsigned i = 0; i <= bound; ++i) { + watch_literal(c, c[i]); + } + } + } + + void card_extension::clear_watch(card& c) { + unsigned sz = std::min(c.k() + 1, c.size()); + for (unsigned i = 0; i < sz; ++i) { + unwatch_literal(c[i], &c); + } + } + + void card_extension::unwatch_literal(literal lit, card* c) { + if (m_var_infos.size() <= static_cast(lit.var())) { + return; + } + ptr_vector* cards = m_var_infos[lit.var()].m_lit_watch[lit.sign()]; + if (cards) { + remove(*cards, c); + } + } + + void card_extension::remove(ptr_vector& cards, card* c) { + for (unsigned j = 0; j < cards.size(); ++j) { + if (cards[j] == c) { + std::swap(cards[j], cards[cards.size()-1]); + cards.pop_back(); + break; + } + } + } + + void card_extension::assign(card& c, literal lit) { + if (s().value(lit) == l_true) { + return; + } + m_stats.m_num_propagations++; + //TRACE("sat", tout << "#prop: " << m_stats.m_num_propagations << " - " << c.lit() << " => " << lit << "\n";); + SASSERT(validate_unit_propagation(c)); + s().assign(lit, justification::mk_ext_justification(c.index())); + + } + + void card_extension::watch_literal(card& c, literal lit) { + init_watch(lit.var()); + ptr_vector* cards = m_var_infos[lit.var()].m_lit_watch[lit.sign()]; + if (cards == 0) { + cards = alloc(ptr_vector); + m_var_infos[lit.var()].m_lit_watch[lit.sign()] = cards; + } + cards->push_back(&c); + } + + void card_extension::set_conflict(card& c, literal lit) { + SASSERT(validate_conflict(c)); + literal_vector& lits = get_literals(); + SASSERT(s().value(lit) == l_false); + SASSERT(s().value(c.lit()) == l_true); + lits.push_back(~c.lit()); + lits.push_back(lit); + unsigned sz = c.size(); + for (unsigned i = c.k(); i < sz; ++i) { + SASSERT(s().value(c[i]) == l_false); + lits.push_back(c[i]); + } + + m_stats.m_num_conflicts++; + if (!resolve_conflict(c, lits)) { + s().mk_clause_core(lits.size(), lits.c_ptr(), true); + } + SASSERT(s().inconsistent()); + } + + void card_extension::normalize_active_coeffs() { + while (!m_active_var_set.empty()) m_active_var_set.erase(); + unsigned i = 0, j = 0, sz = m_active_vars.size(); + for (; i < sz; ++i) { + bool_var v = m_active_vars[i]; + if (!m_active_var_set.contains(v) && get_coeff(v) != 0) { + m_active_var_set.insert(v); + if (j != i) { + m_active_vars[j] = m_active_vars[i]; + } + ++j; + } + } + sz = j; + m_active_vars.shrink(sz); + } + + void card_extension::inc_coeff(literal l, int offset) { + SASSERT(offset > 0); + bool_var v = l.var(); + SASSERT(v != null_bool_var); + if (static_cast(m_coeffs.size()) <= v) { + m_coeffs.resize(v + 1, 0); + } + int coeff0 = m_coeffs[v]; + if (coeff0 == 0) { + m_active_vars.push_back(v); + } + + int inc = l.sign() ? -offset : offset; + int coeff1 = inc + coeff0; + m_coeffs[v] = coeff1; + + if (coeff0 > 0 && inc < 0) { + m_bound -= coeff0 - std::max(0, coeff1); + } + else if (coeff0 < 0 && inc > 0) { + m_bound -= std::min(0, coeff1) - coeff0; + } + } + + int card_extension::get_coeff(bool_var v) const { + return m_coeffs.get(v, 0); + } + + int card_extension::get_abs_coeff(bool_var v) const { + int coeff = get_coeff(v); + if (coeff < 0) coeff = -coeff; + return coeff; + } + + void card_extension::reset_coeffs() { + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + m_coeffs[m_active_vars[i]] = 0; + } + m_active_vars.reset(); + } + + bool card_extension::resolve_conflict(card& c, literal_vector const& conflict_clause) { + + bool_var v; + m_conflict_lvl = 0; + for (unsigned i = 0; i < c.size(); ++i) { + literal lit = c[i]; + SASSERT(s().value(lit) == l_false); + m_conflict_lvl = std::max(m_conflict_lvl, s().lvl(lit)); + } + if (m_conflict_lvl < s().lvl(c.lit()) || m_conflict_lvl == 0) { + return false; + } + + reset_coeffs(); + m_num_marks = 0; + m_bound = c.k(); + m_conflict.reset(); + literal_vector const& lits = s().m_trail; + unsigned idx = lits.size()-1; + justification js; + literal consequent = ~conflict_clause[1]; + process_card(c, 1); + literal alit; + + while (m_num_marks > 0) { + + v = consequent.var(); + + int offset = get_abs_coeff(v); + + if (offset == 0) { + goto process_next_resolvent; + } + if (offset > 1000) { + goto bail_out; + } + + SASSERT(offset > 0); + + js = s().m_justification[v]; + + int bound = 1; + switch(js.get_kind()) { + case justification::NONE: + break; + case justification::BINARY: + inc_coeff(consequent, offset); + process_antecedent(~(js.get_literal()), offset); + break; + case justification::TERNARY: + inc_coeff(consequent, offset); + process_antecedent(~(js.get_literal1()), offset); + process_antecedent(~(js.get_literal2()), offset); + break; + case justification::CLAUSE: { + inc_coeff(consequent, offset); + clause & c = *(s().m_cls_allocator.get_clause(js.get_clause_offset())); + unsigned i = 0; + if (consequent != null_literal) { + SASSERT(c[0] == consequent || c[1] == consequent); + if (c[0] == consequent) { + i = 1; + } + else { + process_antecedent(~c[0], offset); + i = 2; + } + } + unsigned sz = c.size(); + for (; i < sz; i++) + process_antecedent(~c[i], offset); + break; + } + case justification::EXT_JUSTIFICATION: { + unsigned index = js.get_ext_justification_idx(); + card& c2 = *m_constraints[index]; + process_card(c2, offset); + bound = c2.k(); + break; + } + default: + UNREACHABLE(); + break; + } + m_bound += offset * bound; + + // cut(); + + process_next_resolvent: + + // find the next marked variable in the assignment stack + // + while (true) { + consequent = lits[idx]; + v = consequent.var(); + if (s().is_marked(v)) break; + SASSERT(idx > 0); + --idx; + } + + SASSERT(s().lvl(v) == m_conflict_lvl); + s().reset_mark(v); + --idx; + --m_num_marks; + } + + SASSERT(validate_lemma()); + + normalize_active_coeffs(); + + if (m_bound > 0 && m_active_vars.empty()) { + return false; + } + + int slack = -m_bound; + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + slack += get_abs_coeff(v); + } + + alit = get_asserting_literal(~consequent); + slack -= get_abs_coeff(alit.var()); + m_conflict.push_back(alit); + + for (unsigned i = lits.size(); 0 <= slack && i > 0; ) { + --i; + literal lit = lits[i]; + bool_var v = lit.var(); + if (m_active_var_set.contains(v) && v != alit.var()) { + int coeff = get_coeff(v); + if (coeff < 0 && !lit.sign()) { + slack += coeff; + m_conflict.push_back(~lit); + } + else if (coeff > 0 && lit.sign()) { + slack -= coeff; + m_conflict.push_back(~lit); + } + } + } + SASSERT(slack < 0); + SASSERT(validate_conflict(m_conflict)); + + s().mk_clause_core(m_conflict.size(), m_conflict.c_ptr(), true); + return true; + + bail_out: + while (m_num_marks > 0 && idx > 0) { + v = lits[idx].var(); + if (s().is_marked(v)) { + s().reset_mark(v); + } + --idx; + } + return false; + } + + void card_extension::process_card(card& c, int offset) { + SASSERT(c.k() <= c.size()); + SASSERT(s().value(c.lit()) == l_true); + for (unsigned i = c.k(); i < c.size(); ++i) { + process_antecedent(c[i], offset); + } + for (unsigned i = 0; i < c.k(); ++i) { + inc_coeff(c[i], offset); + } + if (s().lvl(c.lit()) > 0) { + m_conflict.push_back(~c.lit()); + } + } + + void card_extension::process_antecedent(literal l, int offset) { + SASSERT(s().value(l) == l_false); + bool_var v = l.var(); + unsigned lvl = s().lvl(v); + + if (lvl > 0 && !s().is_marked(v) && lvl == m_conflict_lvl) { + s().mark(v); + ++m_num_marks; + } + inc_coeff(l, offset); + } + + literal card_extension::get_asserting_literal(literal p) { + if (get_abs_coeff(p.var()) != 0) { + return p; + } + unsigned lvl = 0; + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + literal lit(v, get_coeff(v) < 0); + if (s().value(lit) == l_false && s().lvl(lit) > lvl) { + p = lit; + } + } + return p; + } + + card_extension::card_extension(): m_solver(0) {} + + card_extension::~card_extension() { + for (unsigned i = 0; i < m_var_infos.size(); ++i) { + m_var_infos[i].reset(); + } + m_var_trail.reset(); + m_var_lim.reset(); + m_stats.reset(); + } + + void card_extension::add_at_least(bool_var v, literal_vector const& lits, unsigned k) { + unsigned index = m_constraints.size(); + card* c = alloc(card, index, literal(v, false), lits, k); + m_constraints.push_back(c); + init_watch(v); + m_var_infos[v].m_card = c; + m_var_trail.push_back(v); + } + + void card_extension::propagate(literal l, ext_constraint_idx idx, bool & keep) { + UNREACHABLE(); + } + + void card_extension::get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) { + card& c = *m_constraints[idx]; + + DEBUG_CODE( + bool found = false; + for (unsigned i = 0; !found && i < c.k(); ++i) { + found = c[i] == l; + } + SASSERT(found);); + + for (unsigned i = c.k(); i < c.size(); ++i) { + SASSERT(s().value(c[i]) == l_false); + r.push_back(c[i]); + } + } + + lbool card_extension::add_assign(card& c, literal alit) { + // literal is assigned to false. + unsigned sz = c.size(); + unsigned bound = c.k(); + TRACE("pb", tout << "assign: " << c.lit() << " " << ~alit << " " << bound << "\n";); + + SASSERT(0 < bound && bound < sz); + SASSERT(s().value(alit) == l_false); + SASSERT(s().value(c.lit()) == l_true); + unsigned index = 0; + for (index = 0; index <= bound; ++index) { + if (c[index] == alit) { + break; + } + } + if (index == bound + 1) { + // literal is no longer watched. + return l_undef; + } + SASSERT(index <= bound); + SASSERT(c[index] == alit); + + // find a literal to swap with: + for (unsigned i = bound + 1; i < sz; ++i) { + literal lit2 = c[i]; + if (s().value(lit2) != l_false) { + c.swap(index, i); + watch_literal(c, lit2); + return l_undef; + } + } + + // conflict + if (bound != index && s().value(c[bound]) == l_false) { + TRACE("sat", tout << "conflict " << c[bound] << " " << alit << "\n";); + set_conflict(c, alit); + return l_false; + } + + TRACE("pb", tout << "no swap " << index << " " << alit << "\n";); + // there are no literals to swap with, + // prepare for unit propagation by swapping the false literal into + // position bound. Then literals in positions 0..bound-1 have to be + // assigned l_true. + if (index != bound) { + c.swap(index, bound); + } + SASSERT(validate_unit_propagation(c)); + + for (unsigned i = 0; i < bound && !s().inconsistent(); ++i) { + assign(c, c[i]); + } + + return s().inconsistent() ? l_false : l_true; + } + + void card_extension::asserted(literal l) { + bool_var v = l.var(); + ptr_vector* cards = m_var_infos[v].m_lit_watch[!l.sign()]; + if (cards != 0 && !cards->empty() && !s().inconsistent()) { + ptr_vector::iterator it = cards->begin(), it2 = it, end = cards->end(); + for (; it != end; ++it) { + card& c = *(*it); + if (s().value(c.lit()) != l_true) { + continue; + } + switch (add_assign(c, l)) { + case l_false: // conflict + for (; it != end; ++it, ++it2) { + *it2 = *it; + } + SASSERT(s().inconsistent()); + cards->set_end(it2); + return; + case l_undef: // watch literal was swapped + break; + case l_true: // unit propagation, keep watching the literal + if (it2 != it) { + *it2 = *it; + } + ++it2; + break; + } + } + cards->set_end(it2); + } + + card* crd = m_var_infos[v].m_card; + if (crd != 0 && !s().inconsistent()) { + init_watch(*crd, !l.sign()); + } + } + + check_result card_extension::check() { return CR_DONE; } + + void card_extension::push() { + m_var_lim.push_back(m_var_trail.size()); + } + + void card_extension::pop(unsigned n) { + unsigned new_lim = m_var_lim.size() - n; + unsigned sz = m_var_lim[new_lim]; + while (m_var_trail.size() > sz) { + bool_var v = m_var_trail.back(); + m_var_trail.pop_back(); + if (v != null_bool_var) { + card* c = m_var_infos[v].m_card; + clear_watch(*c); + m_var_infos[v].m_card = 0; + dealloc(c); + } + } + m_var_lim.resize(new_lim); + } + + void card_extension::simplify() {} + void card_extension::clauses_modifed() {} + lbool card_extension::get_phase(bool_var v) { return l_undef; } + + void card_extension::display_watch(std::ostream& out, bool_var v, bool sign) const { + watch const* w = m_var_infos[v].m_lit_watch[sign]; + if (w) { + watch const& wl = *w; + out << "watch: " << literal(v, sign) << " |-> "; + for (unsigned i = 0; i < wl.size(); ++i) { + out << wl[i]->lit() << " "; + } + out << "\n"; + } + } + + void card_extension::display(std::ostream& out, card& c, bool values) const { + out << c.lit(); + if (c.lit() != null_literal) { + if (values) { + out << "@(" << s().value(c.lit()); + if (s().value(c.lit()) != l_undef) { + out << ":" << s().lvl(c.lit()); + } + out << ")"; + } + out << c.lit() << "\n"; + } + else { + out << " "; + } + for (unsigned i = 0; i < c.size(); ++i) { + literal l = c[i]; + out << l; + if (values) { + out << "@(" << s().value(l); + if (s().value(l) != l_undef) { + out << ":" << s().lvl(l); + } + out << ") "; + } + } + out << " >= " << c.k() << "\n"; + } + + std::ostream& card_extension::display(std::ostream& out) const { + for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { + display_watch(out, vi, false); + display_watch(out, vi, true); + } + for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { + card* c = m_var_infos[vi].m_card; + if (c) { + display(out, *c, true); + } + } + return out; + } + + void card_extension::collect_statistics(statistics& st) const { + st.update("card propagations", m_stats.m_num_propagations); + st.update("card conflicts", m_stats.m_num_conflicts); + } + + bool card_extension::validate_conflict(card& c) { + if (!validate_unit_propagation(c)) return false; + for (unsigned i = 0; i < c.k(); ++i) { + if (s().value(c[i]) == l_false) return true; + } + return false; + } + bool card_extension::validate_unit_propagation(card const& c) { + for (unsigned i = c.k(); i < c.size(); ++i) { + if (s().value(c[i]) != l_false) return false; + } + return true; + } + bool card_extension::validate_lemma() { + int value = -m_bound; + normalize_active_coeffs(); + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + int coeff = get_coeff(v); + SASSERT(coeff != 0); + if (coeff < 0 && s().value(v) != l_true) { + value -= coeff; + } + else if (coeff > 0 && s().value(v) != l_false) { + value += coeff; + } + } + return value < 0; + } + + bool card_extension::validate_assign(literal_vector const& lits, literal lit) { return true; } + bool card_extension::validate_conflict(literal_vector const& lits) { return true; } + + +}; + diff --git a/src/sat/card_extension.h b/src/sat/card_extension.h new file mode 100644 index 000000000..db8e041c2 --- /dev/null +++ b/src/sat/card_extension.h @@ -0,0 +1,142 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + card_extension.h + +Abstract: + + Cardinality extensions. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-01-30 + +Revision History: + +--*/ +#ifndef CARD_EXTENSION_H_ +#define CARD_EXTENSION_H_ + +#include"sat_extension.h" +#include"sat_solver.h" + +namespace sat { + + class card_extension : public extension { + struct stats { + unsigned m_num_propagations; + unsigned m_num_conflicts; + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + }; + + class card { + unsigned m_index; + literal m_lit; + unsigned m_k; + unsigned m_size; + literal_vector m_lits; + public: + card(unsigned index, literal lit, literal_vector const& lits, unsigned k); + unsigned index() const { return m_index; } + literal lit() const { return m_lit; } + literal operator[](unsigned i) const { return m_lits[i]; } + unsigned k() const { return m_k; } + unsigned size() const { return m_size; } + void swap(unsigned i, unsigned j) { std::swap(m_lits[i], m_lits[j]); } + void negate(); + }; + + typedef ptr_vector watch; + struct var_info { + watch* m_lit_watch[2]; + card* m_card; + var_info(): m_card(0) { + m_lit_watch[0] = 0; + m_lit_watch[1] = 0; + } + void reset() { + dealloc(m_card); + dealloc(m_lit_watch[0]); + dealloc(m_lit_watch[1]); + } + }; + + + solver* m_solver; + stats m_stats; + + ptr_vector m_constraints; + + // watch literals + svector m_var_infos; + unsigned_vector m_var_trail; + unsigned_vector m_var_lim; + + // conflict resolution + unsigned m_num_marks; + unsigned m_conflict_lvl; + svector m_coeffs; + svector m_active_vars; + int m_bound; + tracked_uint_set m_active_var_set; + literal_vector m_conflict; + literal_vector m_literals; + + solver& s() const { return *m_solver; } + void init_watch(card& c, bool is_true); + void init_watch(bool_var v); + void assign(card& c, literal lit); + lbool add_assign(card& c, literal lit); + void watch_literal(card& c, literal lit); + void set_conflict(card& c, literal lit); + void clear_watch(card& c); + void reset_coeffs(); + + void unwatch_literal(literal w, card* c); + void remove(ptr_vector& cards, card* c); + + void normalize_active_coeffs(); + void inc_coeff(literal l, int offset); + int get_coeff(bool_var v) const; + int get_abs_coeff(bool_var v) const; + + literal_vector& get_literals() { m_literals.reset(); return m_literals; } + literal get_asserting_literal(literal conseq); + bool resolve_conflict(card& c, literal_vector const& conflict_clause); + void process_antecedent(literal l, int offset); + void process_card(card& c, int offset); + void cut(); + + // validation utilities + bool validate_conflict(card& c); + bool validate_assign(literal_vector const& lits, literal lit); + bool validate_lemma(); + bool validate_unit_propagation(card const& c); + bool validate_conflict(literal_vector const& lits); + + void display(std::ostream& out, card& c, bool values) const; + void display_watch(std::ostream& out, bool_var v, bool sign) const; + public: + card_extension(); + virtual ~card_extension(); + void set_solver(solver* s) { m_solver = s; } + void add_at_least(bool_var v, literal_vector const& lits, unsigned k); + virtual void propagate(literal l, ext_constraint_idx idx, bool & keep); + virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r); + virtual void asserted(literal l); + virtual check_result check(); + virtual void push(); + virtual void pop(unsigned n); + virtual void simplify(); + virtual void clauses_modifed(); + virtual lbool get_phase(bool_var v); + virtual std::ostream& display(std::ostream& out) const; + virtual void collect_statistics(statistics& st) const; + }; + +}; + +#endif diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index b0b1c5d96..f1a48c0a8 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -21,6 +21,7 @@ Revision History: #include"sat_types.h" #include"params.h" +#include"statistics.h" namespace sat { @@ -30,6 +31,7 @@ namespace sat { class extension { public: + virtual ~extension() {} virtual void propagate(literal l, ext_constraint_idx idx, bool & keep) = 0; virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) = 0; virtual void asserted(literal l) = 0; @@ -39,6 +41,8 @@ namespace sat { virtual void simplify() = 0; virtual void clauses_modifed() = 0; virtual lbool get_phase(bool_var v) = 0; + virtual std::ostream& display(std::ostream& out) const = 0; + virtual void collect_statistics(statistics& st) const = 0; }; }; diff --git a/src/sat/sat_justification.h b/src/sat/sat_justification.h index 00da9f30e..8c5331d17 100644 --- a/src/sat/sat_justification.h +++ b/src/sat/sat_justification.h @@ -33,7 +33,7 @@ namespace sat { explicit justification(literal l):m_val1(l.to_uint()), m_val2(BINARY) {} justification(literal l1, literal l2):m_val1(l1.to_uint()), m_val2(TERNARY + (l2.to_uint() << 3)) {} explicit justification(clause_offset cls_off):m_val1(cls_off), m_val2(CLAUSE) {} - justification mk_ext_justification(ext_justification_idx idx) { return justification(idx, EXT_JUSTIFICATION); } + static justification mk_ext_justification(ext_justification_idx idx) { return justification(idx, EXT_JUSTIFICATION); } kind get_kind() const { return static_cast(m_val2 & 7); } diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index bf3dc1988..647c5f9ed 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -2752,6 +2752,7 @@ namespace sat { m_scc.collect_statistics(st); m_asymm_branch.collect_statistics(st); m_probing.collect_statistics(st); + if (m_ext) m_ext->collect_statistics(st); } void solver::reset_statistics() { @@ -2856,6 +2857,9 @@ namespace sat { display_units(out); display_binary(out); out << m_clauses << m_learned; + if (m_ext) { + m_ext->display(out); + } out << ")\n"; } diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index 54b8a9bb2..b42acc680 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -145,6 +145,7 @@ namespace sat { friend class probing; friend class iff3_finder; friend class mus; + friend class card_extension; friend struct mk_stat; public: solver(params_ref const & p, reslimit& l, extension * ext); diff --git a/src/smt/theory_pb.h b/src/smt/theory_pb.h index 56f3d1e76..90673768b 100644 --- a/src/smt/theory_pb.h +++ b/src/smt/theory_pb.h @@ -250,16 +250,6 @@ namespace smt { typedef map arg_map; - struct row_info { - unsigned m_slack; // slack variable in simplex tableau - numeral m_bound; // bound - arg_t m_rep; // representative - row_info(theory_var slack, numeral const& b, arg_t const& r): - m_slack(slack), m_bound(b), m_rep(r) {} - row_info(): m_slack(0) {} - }; - - struct var_info { ineq_watch* m_lit_watch[2]; ineq_watch* m_var_watch; @@ -290,7 +280,6 @@ namespace smt { theory_pb_params m_params; svector m_var_infos; - unsynch_mpq_inf_manager m_mpq_inf_mgr; // Simplex: manage inf_mpq numerals mutable unsynch_mpz_manager m_mpz_mgr; // Simplex: manager mpz numerals unsigned_vector m_ineqs_trail; unsigned_vector m_ineqs_lim; From 32b5e54c8dbd870f216d098483781c41a04be81d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 30 Jan 2017 17:22:06 -0800 Subject: [PATCH 13/13] working on card extension Signed-off-by: Nikolaj Bjorner --- src/sat/card_extension.cpp | 251 +++++++++++++++++++++++++++---------- src/sat/card_extension.h | 23 +++- 2 files changed, 204 insertions(+), 70 deletions(-) diff --git a/src/sat/card_extension.cpp b/src/sat/card_extension.cpp index 985579f18..2af1a5b5e 100644 --- a/src/sat/card_extension.cpp +++ b/src/sat/card_extension.cpp @@ -50,7 +50,7 @@ namespace sat { if (c.lit().sign() == is_true) { c.negate(); } - SASSERT(s().value(c.lit()) == l_true); + SASSERT(value(c.lit()) == l_true); unsigned j = 0, sz = c.size(), bound = c.k(); if (bound == sz) { for (unsigned i = 0; i < sz && !s().inconsistent(); ++i) { @@ -60,7 +60,7 @@ namespace sat { } // put the non-false literals into the head. for (unsigned i = 0; i < sz; ++i) { - if (s().value(c[i]) != l_false) { + if (value(c[i]) != l_false) { if (j != i) { c.swap(i, j); } @@ -70,8 +70,8 @@ namespace sat { DEBUG_CODE( bool is_false = false; for (unsigned k = 0; k < sz; ++k) { - SASSERT(!is_false || s().value(c[k]) == l_false); - is_false = s().value(c[k]) == l_false; + SASSERT(!is_false || value(c[k]) == l_false); + is_false = value(c[k]) == l_false; }); // j is the number of non-false, sz - j the number of false. @@ -86,7 +86,7 @@ namespace sat { // for (unsigned i = bound; i < sz; ++i) { - if (s().lvl(alit) < s().lvl(c[i])) { + if (lvl(alit) < lvl(c[i])) { c.swap(i, j); alit = c[j]; } @@ -133,7 +133,7 @@ namespace sat { } void card_extension::assign(card& c, literal lit) { - if (s().value(lit) == l_true) { + if (value(lit) == l_true) { return; } m_stats.m_num_propagations++; @@ -155,19 +155,20 @@ namespace sat { void card_extension::set_conflict(card& c, literal lit) { SASSERT(validate_conflict(c)); - literal_vector& lits = get_literals(); - SASSERT(s().value(lit) == l_false); - SASSERT(s().value(c.lit()) == l_true); - lits.push_back(~c.lit()); - lits.push_back(lit); - unsigned sz = c.size(); - for (unsigned i = c.k(); i < sz; ++i) { - SASSERT(s().value(c[i]) == l_false); - lits.push_back(c[i]); - } m_stats.m_num_conflicts++; - if (!resolve_conflict(c, lits)) { + if (!resolve_conflict(c, lit)) { + + literal_vector& lits = get_literals(); + SASSERT(value(lit) == l_false); + SASSERT(value(c.lit()) == l_true); + lits.push_back(~c.lit()); + lits.push_back(lit); + unsigned sz = c.size(); + for (unsigned i = c.k(); i < sz; ++i) { + SASSERT(value(c[i]) == l_false); + lits.push_back(c[i]); + } s().mk_clause_core(lits.size(), lits.c_ptr(), true); } SASSERT(s().inconsistent()); @@ -231,16 +232,16 @@ namespace sat { m_active_vars.reset(); } - bool card_extension::resolve_conflict(card& c, literal_vector const& conflict_clause) { + bool card_extension::resolve_conflict(card& c, literal alit) { bool_var v; m_conflict_lvl = 0; for (unsigned i = 0; i < c.size(); ++i) { literal lit = c[i]; - SASSERT(s().value(lit) == l_false); - m_conflict_lvl = std::max(m_conflict_lvl, s().lvl(lit)); + SASSERT(value(lit) == l_false); + m_conflict_lvl = std::max(m_conflict_lvl, lvl(lit)); } - if (m_conflict_lvl < s().lvl(c.lit()) || m_conflict_lvl == 0) { + if (m_conflict_lvl < lvl(c.lit()) || m_conflict_lvl == 0) { return false; } @@ -251,14 +252,14 @@ namespace sat { literal_vector const& lits = s().m_trail; unsigned idx = lits.size()-1; justification js; - literal consequent = ~conflict_clause[1]; + literal consequent = ~alit; process_card(c, 1); - literal alit; + + DEBUG_CODE(active2pb(m_A);); while (m_num_marks > 0) { - + SASSERT(value(consequent) == l_true); v = consequent.var(); - int offset = get_abs_coeff(v); if (offset == 0) { @@ -268,9 +269,11 @@ namespace sat { goto bail_out; } + SASSERT(validate_lemma()); SASSERT(offset > 0); js = s().m_justification[v]; + DEBUG_CODE(justification2pb(js, consequent, offset, m_B);); int bound = 1; switch(js.get_kind()) { @@ -289,15 +292,13 @@ namespace sat { inc_coeff(consequent, offset); clause & c = *(s().m_cls_allocator.get_clause(js.get_clause_offset())); unsigned i = 0; - if (consequent != null_literal) { - SASSERT(c[0] == consequent || c[1] == consequent); - if (c[0] == consequent) { - i = 1; - } - else { - process_antecedent(~c[0], offset); - i = 2; - } + SASSERT(c[0] == consequent || c[1] == consequent); + if (c[0] == consequent) { + i = 1; + } + else { + process_antecedent(~c[0], offset); + i = 2; } unsigned sz = c.size(); for (; i < sz; i++) @@ -316,6 +317,11 @@ namespace sat { break; } m_bound += offset * bound; + + DEBUG_CODE( + active2pb(m_C); + SASSERT(validate_resolvent()); + m_A = m_C;); // cut(); @@ -331,7 +337,7 @@ namespace sat { --idx; } - SASSERT(s().lvl(v) == m_conflict_lvl); + SASSERT(lvl(v) == m_conflict_lvl); s().reset_mark(v); --idx; --m_num_marks; @@ -390,24 +396,24 @@ namespace sat { void card_extension::process_card(card& c, int offset) { SASSERT(c.k() <= c.size()); - SASSERT(s().value(c.lit()) == l_true); + SASSERT(value(c.lit()) == l_true); for (unsigned i = c.k(); i < c.size(); ++i) { process_antecedent(c[i], offset); } for (unsigned i = 0; i < c.k(); ++i) { inc_coeff(c[i], offset); } - if (s().lvl(c.lit()) > 0) { + if (lvl(c.lit()) > 0) { m_conflict.push_back(~c.lit()); } } void card_extension::process_antecedent(literal l, int offset) { - SASSERT(s().value(l) == l_false); + SASSERT(value(l) == l_false); bool_var v = l.var(); - unsigned lvl = s().lvl(v); + unsigned level = lvl(v); - if (lvl > 0 && !s().is_marked(v) && lvl == m_conflict_lvl) { + if (level > 0 && !s().is_marked(v) && level == m_conflict_lvl) { s().mark(v); ++m_num_marks; } @@ -418,12 +424,13 @@ namespace sat { if (get_abs_coeff(p.var()) != 0) { return p; } - unsigned lvl = 0; + unsigned level = 0; for (unsigned i = 0; i < m_active_vars.size(); ++i) { bool_var v = m_active_vars[i]; literal lit(v, get_coeff(v) < 0); - if (s().value(lit) == l_false && s().lvl(lit) > lvl) { + if (value(lit) == l_false && lvl(lit) > level) { p = lit; + level = lvl(lit); } } return p; @@ -463,21 +470,24 @@ namespace sat { } SASSERT(found);); + r.push_back(c.lit()); + SASSERT(value(c.lit()) == l_true); for (unsigned i = c.k(); i < c.size(); ++i) { - SASSERT(s().value(c[i]) == l_false); - r.push_back(c[i]); + SASSERT(value(c[i]) == l_false); + r.push_back(~c[i]); } } + lbool card_extension::add_assign(card& c, literal alit) { // literal is assigned to false. unsigned sz = c.size(); unsigned bound = c.k(); - TRACE("pb", tout << "assign: " << c.lit() << " " << ~alit << " " << bound << "\n";); + TRACE("sat", tout << "assign: " << c.lit() << " " << ~alit << " " << bound << "\n";); SASSERT(0 < bound && bound < sz); - SASSERT(s().value(alit) == l_false); - SASSERT(s().value(c.lit()) == l_true); + SASSERT(value(alit) == l_false); + SASSERT(value(c.lit()) == l_true); unsigned index = 0; for (index = 0; index <= bound; ++index) { if (c[index] == alit) { @@ -494,7 +504,7 @@ namespace sat { // find a literal to swap with: for (unsigned i = bound + 1; i < sz; ++i) { literal lit2 = c[i]; - if (s().value(lit2) != l_false) { + if (value(lit2) != l_false) { c.swap(index, i); watch_literal(c, lit2); return l_undef; @@ -502,13 +512,13 @@ namespace sat { } // conflict - if (bound != index && s().value(c[bound]) == l_false) { + if (bound != index && value(c[bound]) == l_false) { TRACE("sat", tout << "conflict " << c[bound] << " " << alit << "\n";); set_conflict(c, alit); return l_false; } - TRACE("pb", tout << "no swap " << index << " " << alit << "\n";); + TRACE("sat", tout << "no swap " << index << " " << alit << "\n";); // there are no literals to swap with, // prepare for unit propagation by swapping the false literal into // position bound. Then literals in positions 0..bound-1 have to be @@ -532,7 +542,7 @@ namespace sat { ptr_vector::iterator it = cards->begin(), it2 = it, end = cards->end(); for (; it != end; ++it) { card& c = *(*it); - if (s().value(c.lit()) != l_true) { + if (value(c.lit()) != l_true) { continue; } switch (add_assign(c, l)) { @@ -604,9 +614,9 @@ namespace sat { out << c.lit(); if (c.lit() != null_literal) { if (values) { - out << "@(" << s().value(c.lit()); - if (s().value(c.lit()) != l_undef) { - out << ":" << s().lvl(c.lit()); + out << "@(" << value(c.lit()); + if (value(c.lit()) != l_undef) { + out << ":" << lvl(c.lit()); } out << ")"; } @@ -619,9 +629,9 @@ namespace sat { literal l = c[i]; out << l; if (values) { - out << "@(" << s().value(l); - if (s().value(l) != l_undef) { - out << ":" << s().lvl(l); + out << "@(" << value(l); + if (value(l) != l_undef) { + out << ":" << lvl(l); } out << ") "; } @@ -651,35 +661,142 @@ namespace sat { bool card_extension::validate_conflict(card& c) { if (!validate_unit_propagation(c)) return false; for (unsigned i = 0; i < c.k(); ++i) { - if (s().value(c[i]) == l_false) return true; + if (value(c[i]) == l_false) return true; } return false; } bool card_extension::validate_unit_propagation(card const& c) { + if (value(c.lit()) != l_true) return false; for (unsigned i = c.k(); i < c.size(); ++i) { - if (s().value(c[i]) != l_false) return false; + if (value(c[i]) != l_false) return false; } return true; } bool card_extension::validate_lemma() { - int value = -m_bound; + int val = -m_bound; normalize_active_coeffs(); for (unsigned i = 0; i < m_active_vars.size(); ++i) { bool_var v = m_active_vars[i]; int coeff = get_coeff(v); + literal lit(v, false); SASSERT(coeff != 0); - if (coeff < 0 && s().value(v) != l_true) { - value -= coeff; + if (coeff < 0 && value(lit) != l_true) { + val -= coeff; } - else if (coeff > 0 && s().value(v) != l_false) { - value += coeff; + else if (coeff > 0 && value(lit) != l_false) { + val += coeff; } } - return value < 0; + return val < 0; } - bool card_extension::validate_assign(literal_vector const& lits, literal lit) { return true; } - bool card_extension::validate_conflict(literal_vector const& lits) { return true; } + void card_extension::active2pb(ineq& p) { + normalize_active_coeffs(); + p.reset(m_bound); + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + literal lit(v, get_coeff(v) < 0); + p.m_lits.push_back(lit); + p.m_coeffs.push_back(get_abs_coeff(v)); + } + } + + void card_extension::justification2pb(justification const& js, literal lit, unsigned offset, ineq& p) { + switch (js.get_kind()) { + case justification::NONE: + p.reset(0); + break; + case justification::BINARY: + p.reset(offset); + p.push(lit, offset); + p.push(~js.get_literal(), offset); + break; + case justification::TERNARY: + p.reset(offset); + p.push(lit, offset); + p.push(~(js.get_literal1()), offset); + p.push(~(js.get_literal2()), offset); + break; + case justification::CLAUSE: { + p.reset(offset); + clause & c = *(s().m_cls_allocator.get_clause(js.get_clause_offset())); + unsigned sz = c.size(); + for (unsigned i = 0; i < sz; i++) + p.push(~c[i], offset); + break; + } + case justification::EXT_JUSTIFICATION: { + unsigned index = js.get_ext_justification_idx(); + card& c = *m_constraints[index]; + p.reset(offset*c.k()); + for (unsigned i = 0; i < c.size(); ++i) { + p.push(c[i], offset); + } + break; + } + default: + UNREACHABLE(); + break; + } + } + + + // validate that m_A & m_B implies m_C + + bool card_extension::validate_resolvent() { + u_map coeffs; + unsigned k = m_A.m_k + m_B.m_k; + for (unsigned i = 0; i < m_A.m_lits.size(); ++i) { + unsigned coeff = m_A.m_coeffs[i]; + SASSERT(!coeffs.contains(m_A.m_lits[i].index())); + coeffs.insert(m_A.m_lits[i].index(), coeff); + } + for (unsigned i = 0; i < m_B.m_lits.size(); ++i) { + unsigned coeff1 = m_B.m_coeffs[i], coeff2; + literal lit = m_B.m_lits[i]; + if (coeffs.find((~lit).index(), coeff2)) { + if (coeff1 == coeff2) { + coeffs.remove((~lit).index()); + k += coeff1; + } + else if (coeff1 < coeff2) { + coeffs.insert((~lit).index(), coeff2 - coeff1); + k += coeff1; + } + else { + SASSERT(coeff2 < coeff1); + coeffs.remove((~lit).index()); + coeffs.insert(lit.index(), coeff1 - coeff2); + k += coeff2; + } + } + else if (coeffs.find(lit.index(), coeff2)) { + coeffs.insert(lit.index(), coeff1 + coeff2); + } + else { + coeffs.insert(lit.index(), coeff1); + } + } + // C is above the sum of A and B + for (unsigned i = 0; i < m_C.m_lits.size(); ++i) { + literal lit = m_C.m_lits[i]; + unsigned coeff; + if (coeffs.find(lit.index(), coeff)) { + SASSERT(coeff <= m_C.m_coeffs[i]); + coeffs.remove(lit.index()); + } + } + SASSERT(coeffs.empty()); + SASSERT(m_C.m_k <= k); + return true; + } + + bool card_extension::validate_conflict(literal_vector const& lits) { + for (unsigned i = 0; i < lits.size(); ++i) { + if (value(lits[i]) != l_false) return false; + } + return true; + } }; diff --git a/src/sat/card_extension.h b/src/sat/card_extension.h index db8e041c2..1593ef26f 100644 --- a/src/sat/card_extension.h +++ b/src/sat/card_extension.h @@ -23,7 +23,7 @@ Revision History: #include"sat_solver.h" namespace sat { - + class card_extension : public extension { struct stats { unsigned m_num_propagations; @@ -31,7 +31,7 @@ namespace sat { stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; - + class card { unsigned m_index; literal m_lit; @@ -49,6 +49,14 @@ namespace sat { void negate(); }; + struct ineq { + literal_vector m_lits; + unsigned_vector m_coeffs; + unsigned m_k; + void reset(unsigned k) { m_lits.reset(); m_coeffs.reset(); m_k = k; } + void push(literal l, unsigned c) { m_lits.push_back(l); m_coeffs.push_back(c); } + }; + typedef ptr_vector watch; struct var_info { watch* m_lit_watch[2]; @@ -95,6 +103,10 @@ namespace sat { void clear_watch(card& c); void reset_coeffs(); + inline lbool value(literal lit) const { return m_solver->value(lit); } + inline unsigned lvl(literal lit) const { return m_solver->lvl(lit); } + inline unsigned lvl(bool_var v) const { return m_solver->lvl(v); } + void unwatch_literal(literal w, card* c); void remove(ptr_vector& cards, card* c); @@ -105,7 +117,7 @@ namespace sat { literal_vector& get_literals() { m_literals.reset(); return m_literals; } literal get_asserting_literal(literal conseq); - bool resolve_conflict(card& c, literal_vector const& conflict_clause); + bool resolve_conflict(card& c, literal alit); void process_antecedent(literal l, int offset); void process_card(card& c, int offset); void cut(); @@ -117,6 +129,11 @@ namespace sat { bool validate_unit_propagation(card const& c); bool validate_conflict(literal_vector const& lits); + ineq m_A, m_B, m_C; + void active2pb(ineq& p); + void justification2pb(justification const& j, literal lit, unsigned offset, ineq& p); + bool validate_resolvent(); + void display(std::ostream& out, card& c, bool values) const; void display_watch(std::ostream& out, bool_var v, bool sign) const; public: