3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-06-16 19:06:17 +00:00

fixes to pb solver

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2013-11-13 13:41:14 -05:00
parent 64daa2977d
commit 133ba2d02a
3 changed files with 303 additions and 84 deletions

View file

@ -44,6 +44,18 @@ namespace datalog {
transf.register_plugin(alloc(datalog::mk_coi_filter, ctx)); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx));
transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx)); transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx));
if (ctx.get_params().quantify_arrays()) {
transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, ctx, 38000));
}
transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, ctx, 37000));
transf.register_plugin(alloc(datalog::mk_scale, ctx, 36030));
if (ctx.get_params().magic()) {
transf.register_plugin(alloc(datalog::mk_magic_symbolic, ctx, 36020));
}
transf.register_plugin(alloc(datalog::mk_karr_invariants, ctx, 36010));
transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 36000));
transf.register_plugin(alloc(datalog::mk_bit_blast, ctx, 35000));
transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 35005)); transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 35005));
transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 35000)); transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 35000));
transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34990)); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34990));
@ -64,19 +76,8 @@ namespace datalog {
transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34880)); transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34880));
if (ctx.get_params().quantify_arrays()) {
transf.register_plugin(alloc(datalog::mk_quantifier_abstraction, ctx, 33000));
transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 32500));
}
transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, ctx, 32000));
transf.register_plugin(alloc(datalog::mk_bit_blast, ctx, 35000));
transf.register_plugin(alloc(datalog::mk_array_blast, ctx, 36000));
transf.register_plugin(alloc(datalog::mk_karr_invariants, ctx, 36010));
if (ctx.get_params().magic()) {
transf.register_plugin(alloc(datalog::mk_magic_symbolic, ctx, 36020));
}
transf.register_plugin(alloc(datalog::mk_scale, ctx, 36030));
ctx.transform_rules(transf); ctx.transform_rules(transf);
} }
} }

View file

@ -22,15 +22,10 @@ Notes:
Example: Example:
((_ at-most 3) x1 x1 x2 x2) == ((_ at-most 1) x1 x2) ((_ at-most 3) x1 x1 x2 x2) == ((_ at-most 1) x1 x2)
- count number of clauses per cardinality constraint. - When number of conflicts exceeds n*log(n),
- TBD: when number of conflicts exceeds n^2 or n*log(n),
then create a sorting circuit. then create a sorting circuit.
where n is the arity of the cardinality constraint. where n is the arity of the cardinality constraint.
- TBD: do clauses get re-created? keep track of gc
status of created clauses.
- TBD: add conflict resolution - TBD: add conflict resolution
The idea is that if cardinality constraints c1, c2 The idea is that if cardinality constraints c1, c2
are repeatedly asserted together, then are repeatedly asserted together, then
@ -38,11 +33,18 @@ Notes:
c1 /\ c2 -> c3 c1 /\ c2 -> c3
That is, during a propagation, assignment or conflict resolution
step track cutting-planes.
- TBD: profile overhead of (re)creating sorting circuits.
Possibly delay creating them until restart.
--*/ --*/
#include "theory_card.h" #include "theory_card.h"
#include "smt_context.h" #include "smt_context.h"
#include "ast_pp.h" #include "ast_pp.h"
#include "sorting_network.h"
namespace smt { namespace smt {
@ -78,19 +80,8 @@ namespace smt {
SASSERT(!ctx.b_internalized(atom)); SASSERT(!ctx.b_internalized(atom));
bool_var abv = ctx.mk_bool_var(atom); bool_var abv = ctx.mk_bool_var(atom);
if (k >= static_cast<int>(num_args)) { card* c = alloc(card, m, atom, abv, k, get_compilation_threshold(atom));
bool all_pos = true;
for (unsigned i = 0; all_pos && i < num_args; ++i) {
all_pos = 0 < m_util.get_le_coeff(atom, i);
}
if (all_pos) {
literal lit(abv);
ctx.mk_th_axiom(get_id(), 1, &lit);
return true;
}
}
card* c = alloc(card, abv, k);
for (unsigned i = 0; i < num_args; ++i) { for (unsigned i = 0; i < num_args; ++i) {
expr* arg = atom->get_arg(i); expr* arg = atom->get_arg(i);
if (!ctx.b_internalized(arg)) { if (!ctx.b_internalized(arg)) {
@ -223,6 +214,7 @@ namespace smt {
void theory_card::collect_statistics(::statistics& st) const { void theory_card::collect_statistics(::statistics& st) const {
st.update("pb axioms", m_stats.m_num_axioms); st.update("pb axioms", m_stats.m_num_axioms);
st.update("pb predicates", m_stats.m_num_predicates); st.update("pb predicates", m_stats.m_num_predicates);
st.update("pb compilations", m_stats.m_num_compiles);
} }
void theory_card::reset_eh() { void theory_card::reset_eh() {
@ -245,14 +237,14 @@ namespace smt {
m_stats.reset(); m_stats.reset();
} }
void theory_card::update_min_max(bool_var v, bool is_true, card* c) { void theory_card::update_min_max(bool_var v, bool is_true, card& c) {
context& ctx = get_context(); context& ctx = get_context();
ast_manager& m = get_manager(); ast_manager& m = get_manager();
arg_t const& args = c->m_args; arg_t const& args = c.m_args;
int inc = find_inc(v, args); int inc = find_inc(v, args);
int& min = c->m_current_min; int& min = c.m_current_min;
int& max = c->m_current_max; int& max = c.m_current_max;
int k = c->m_k; int k = c.m_k;
// inc > 0 & is_true -> min += inc // inc > 0 & is_true -> min += inc
// inc < 0 & is_true -> max += inc // inc < 0 & is_true -> max += inc
// inc > 0 & !is_true -> max -= inc // inc > 0 & !is_true -> max -= inc
@ -278,7 +270,7 @@ namespace smt {
SASSERT(min <= max); SASSERT(min <= max);
} }
void theory_card::assign_use(bool_var v, bool is_true, card* c) { void theory_card::assign_use(bool_var v, bool is_true, card& c) {
update_min_max(v, is_true, c); update_min_max(v, is_true, c);
propagate_assignment(c); propagate_assignment(c);
} }
@ -307,11 +299,11 @@ namespace smt {
} }
} }
int theory_card::accumulate_min(literal_vector& lits, card* c) { int theory_card::accumulate_min(literal_vector& lits, card& c) {
context& ctx = get_context(); context& ctx = get_context();
int k = c->m_k; int k = c.m_k;
arg_t const& args = c->m_args; arg_t const& args = c.m_args;
int curr_min = c->m_abs_min; int curr_min = c.m_abs_min;
for (unsigned i = 0; i < args.size() && curr_min <= k; ++i) { for (unsigned i = 0; i < args.size() && curr_min <= k; ++i) {
bool_var bv = args[i].first; bool_var bv = args[i].first;
int inc = args[i].second; int inc = args[i].second;
@ -324,11 +316,11 @@ namespace smt {
return curr_min; return curr_min;
} }
int theory_card::accumulate_max(literal_vector& lits, card* c) { int theory_card::accumulate_max(literal_vector& lits, card& c) {
context& ctx = get_context(); context& ctx = get_context();
arg_t const& args = c->m_args; arg_t const& args = c.m_args;
int k = c->m_k; int k = c.m_k;
int curr_max = c->m_abs_max; int curr_max = c.m_abs_max;
for (unsigned i = 0; i < args.size() && k < curr_max; ++i) { for (unsigned i = 0; i < args.size() && k < curr_max; ++i) {
bool_var bv = args[i].first; bool_var bv = args[i].first;
int inc = args[i].second; int inc = args[i].second;
@ -341,19 +333,33 @@ namespace smt {
return curr_max; return curr_max;
} }
void theory_card::propagate_assignment(card* c) { void theory_card::propagate_assignment(card& c) {
if (c.m_compiled) {
return;
}
if (should_compile(c)) {
compile_at_most(c);
return;
}
context& ctx = get_context(); context& ctx = get_context();
arg_t const& args = c->m_args; ast_manager& m = get_manager();
bool_var abv = c->m_bv; arg_t const& args = c.m_args;
int min = c->m_current_min; bool_var abv = c.m_bv;
int max = c->m_current_max; int min = c.m_current_min;
int k = c->m_k; int max = c.m_current_max;
int k = c.m_k;
TRACE("card",
tout << mk_pp(c.m_app, m) << " min: "
<< min << " max: " << max << "\n";);
// //
// if min > k && abv != l_false -> force abv false // if min > k && abv != l_false -> force abv false
// if max <= k && abv != l_true -> force abv true // if max <= k && abv != l_true -> force abv true
// if min == k && abv == l_true -> force positive unassigned literals false // if min == k && abv == l_true -> force positive
// if max == k + 1 && abv == l_false -> force negative unassigned literals false // unassigned literals false
// if max == k + 1 && abv == l_false -> force negative
// unassigned literals false
// //
lbool aval = ctx.get_assignment(abv); lbool aval = ctx.get_assignment(abv);
if (min > k && aval != l_false) { if (min > k && aval != l_false) {
@ -361,21 +367,21 @@ namespace smt {
lits.push_back(~literal(abv)); lits.push_back(~literal(abv));
int curr_min = accumulate_min(lits, c); int curr_min = accumulate_min(lits, c);
SASSERT(curr_min > k); SASSERT(curr_min > k);
add_clause(lits); add_clause(c, lits);
} }
else if (max <= k && aval != l_true) { else if (max <= k && aval != l_true) {
literal_vector& lits = get_lits(); literal_vector& lits = get_lits();
lits.push_back(literal(abv)); lits.push_back(literal(abv));
int curr_max = accumulate_max(lits, c); int curr_max = accumulate_max(lits, c);
SASSERT(curr_max <= k); SASSERT(curr_max <= k);
add_clause(lits); add_clause(c, lits);
} }
else if (min == k && aval == l_true) { else if (min == k && aval == l_true) {
literal_vector& lits = get_lits(); literal_vector& lits = get_lits();
lits.push_back(~literal(abv)); lits.push_back(~literal(abv));
int curr_min = accumulate_min(lits, c); int curr_min = accumulate_min(lits, c);
if (curr_min > k) { if (curr_min > k) {
add_clause(lits); add_clause(c, lits);
} }
else { else {
SASSERT(curr_min == k); SASSERT(curr_min == k);
@ -383,9 +389,9 @@ namespace smt {
bool_var bv = args[i].first; bool_var bv = args[i].first;
int inc = args[i].second; int inc = args[i].second;
if (inc_min(inc, ctx.get_assignment(bv)) == l_undef) { if (inc_min(inc, ctx.get_assignment(bv)) == l_undef) {
lits.push_back(literal(bv, inc > 0)); // avoid incrementing min. literal_vector lits_save(lits); // add_clause has a side-effect on literals.
add_clause(lits); lits_save.push_back(literal(bv, inc > 0)); // avoid incrementing min.
lits.pop_back(); add_clause(c, lits_save);
} }
} }
} }
@ -395,16 +401,16 @@ namespace smt {
lits.push_back(literal(abv)); lits.push_back(literal(abv));
int curr_max = accumulate_max(lits, c); int curr_max = accumulate_max(lits, c);
if (curr_max <= k) { if (curr_max <= k) {
add_clause(lits); add_clause(c, lits);
} }
else if (curr_max == k + 1) { else if (curr_max == k + 1) {
for (unsigned i = 0; i < args.size(); ++i) { for (unsigned i = 0; i < args.size(); ++i) {
bool_var bv = args[i].first; bool_var bv = args[i].first;
int inc = args[i].second; int inc = args[i].second;
if (dec_max(inc, ctx.get_assignment(bv)) == l_undef) { if (dec_max(inc, ctx.get_assignment(bv)) == l_undef) {
lits.push_back(literal(bv, inc < 0)); // avoid decrementing max. literal_vector lits_save(lits); // add_clause has a side-effect on literals.
add_clause(lits); lits_save.push_back(literal(bv, inc < 0)); // avoid decrementing max.
lits.pop_back(); add_clause(c, lits_save);
} }
} }
} }
@ -420,11 +426,11 @@ namespace smt {
if (m_watch.find(v, cards)) { if (m_watch.find(v, cards)) {
for (unsigned i = 0; i < cards->size(); ++i) { for (unsigned i = 0; i < cards->size(); ++i) {
assign_use(v, is_true, (*cards)[i]); assign_use(v, is_true, *(*cards)[i]);
} }
} }
if (m_cards.find(v, c)) { if (m_cards.find(v, c)) {
propagate_assignment(c); propagate_assignment(*c);
} }
} }
@ -449,6 +455,203 @@ namespace smt {
return vars[mid].second; return vars[mid].second;
} }
struct theory_card::sort_expr {
theory_card& th;
context& ctx;
ast_manager& m;
expr_ref_vector m_trail;
sort_expr(theory_card& th):
th(th),
ctx(th.get_context()),
m(th.get_manager()),
m_trail(m) {}
typedef expr* T;
typedef expr_ref_vector vector;
T mk_ite(T a, T b, T c) {
if (m.is_true(a)) {
return b;
}
if (m.is_false(a)) {
return c;
}
if (b == c) {
return b;
}
m_trail.push_back(m.mk_ite(a, b, c));
return m_trail.back();
}
T mk_le(T a, T b) {
return mk_ite(b, a, m.mk_true());
}
T mk_default() {
return m.mk_false();
}
literal internalize(card& ca, expr* e) {
obj_map<expr, literal> cache;
for (unsigned i = 0; i < ca.m_args.size(); ++i) {
cache.insert(ca.m_app->get_arg(i), literal(ca.m_args[i].first));
}
cache.insert(m.mk_false(), false_literal);
cache.insert(m.mk_true(), true_literal);
ptr_vector<expr> todo;
todo.push_back(e);
expr *a, *b, *c;
literal la, lb, lc;
while (!todo.empty()) {
expr* t = todo.back();
if (cache.contains(t)) {
todo.pop_back();
continue;
}
VERIFY(m.is_ite(t, a, b, c));
unsigned sz = todo.size();
if (!cache.find(a, la)) {
todo.push_back(a);
}
if (!cache.find(b, lb)) {
todo.push_back(b);
}
if (!cache.find(c, lc)) {
todo.push_back(c);
}
if (sz != todo.size()) {
continue;
}
todo.pop_back();
cache.insert(t, mk_ite(ca, t, la, lb, lc));
}
return cache.find(e);
}
literal mk_ite(card& ca, expr* t, literal a, literal b, literal c) {
if (a == true_literal) {
return b;
}
else if (a == false_literal) {
return c;
}
else if (b == true_literal && c == false_literal) {
return a;
}
else if (b == false_literal && c == true_literal) {
return ~a;
}
else if (b == c) {
return b;
}
else {
expr_ref p(m);
expr* r;
if (ca.m_replay.find(t, r)) {
p = r;
}
else {
p = m.mk_fresh_const("s", m.mk_bool_sort());
ca.m_replay.insert(t, p);
ca.m_trail.push_back(p);
}
literal l;
if (ctx.b_internalized(p)) {
l = literal(ctx.get_bool_var(p));
}
else {
l = literal(ctx.mk_bool_var(p));
}
add_clause(~l, ~a, b);
add_clause(~l, a, c);
add_clause(l, ~a, ~b);
add_clause(l, a, ~c);
TRACE("card", tout << p << " ::= (if ";
ctx.display_detailed_literal(tout, a);
ctx.display_detailed_literal(tout << " ", b);
ctx.display_detailed_literal(tout << " ", c);
tout << ")\n";);
return l;
}
}
// auxiliary clauses don't get garbage collected.
void add_clause(literal a, literal b, literal c) {
literal_vector& lits = th.get_lits();
if (a != null_literal) lits.push_back(a);
if (b != null_literal) lits.push_back(b);
if (c != null_literal) lits.push_back(c);
justification* js = 0;
TRACE("card",
ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";);
ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX, 0);
}
void add_clause(literal l1, literal l2) {
add_clause(l1, l2, null_literal);
}
};
unsigned theory_card::get_compilation_threshold(app* a) {
if (!m_util.is_at_most_k(a)) {
return UINT_MAX;
}
unsigned num_args = a->get_num_args();
unsigned log = 1, n = 1;
while (n <= num_args) {
++log;
n *= 2;
}
TRACE("card", tout << "threshold:" << (num_args*log) << "\n";);
return num_args*log;
}
bool theory_card::should_compile(card& c) {
if (!m_util.is_at_most_k(c.m_app)) {
return false;
}
return c.m_num_propagations >= c.m_compilation_threshold;
}
void theory_card::compile_at_most(card& c) {
++m_stats.m_num_compiles;
ast_manager& m = get_manager();
context& ctx = get_context();
app* a = c.m_app;
SASSERT(m_util.is_at_most_k(a));
literal atmostk;
int k = m_util.get_k(a);
unsigned num_args = a->get_num_args();
sort_expr se(*this);
if (k >= static_cast<int>(num_args)) {
atmostk = true_literal;
}
else if (k < 0) {
atmostk = false_literal;
}
else {
sorting_network<sort_expr> sn(se);
expr_ref_vector in(m), out(m);
for (unsigned i = 0; i < num_args; ++i) {
in.push_back(c.m_app->get_arg(i));
}
sn(in, out);
atmostk = ~se.internalize(c, out[k].get()); // k'th output is 0.
TRACE("card", tout << "~atmost: " << mk_pp(out[k].get(), m) << "\n";);
}
literal thl = literal(c.m_bv);
se.add_clause(~thl, atmostk);
se.add_clause(thl, ~atmostk);
TRACE("card", tout << mk_pp(a, m) << "\n";);
// auxiliary clauses get removed when popping scopes.
// we have to recompile the circuit after back-tracking.
ctx.push_trail(value_trail<context, bool>(c.m_compiled));
c.m_compiled = true;
}
void theory_card::init_search_eh() { void theory_card::init_search_eh() {
} }
@ -483,16 +686,14 @@ namespace smt {
return m_literals; return m_literals;
} }
void theory_card::add_clause(literal_vector const& lits) { void theory_card::add_clause(card& c, literal_vector const& lits) {
++c.m_num_propagations;
m_stats.m_num_axioms++; m_stats.m_num_axioms++;
context& ctx = get_context(); context& ctx = get_context();
TRACE("card", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); TRACE("card", tout << "#prop:" << c.m_num_propagations << " - "; ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";);
justification* js = 0; justification* js = 0;
ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0);
IF_VERBOSE(1, IF_VERBOSE(1, ctx.display_literals_verbose(verbose_stream(), lits.size(), lits.c_ptr());
for (unsigned i = 0; i < lits.size(); ++i) {
verbose_stream() << lits[i] << " ";
}
verbose_stream() << "\n";); verbose_stream() << "\n";);
// ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); // ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
} }

View file

@ -22,20 +22,25 @@ Notes:
#include "smt_theory.h" #include "smt_theory.h"
#include "card_decl_plugin.h" #include "card_decl_plugin.h"
#include "smt_clause.h"
namespace smt { namespace smt {
class theory_card : public theory { class theory_card : public theory {
struct sort_expr;
typedef svector<std::pair<bool_var, int> > arg_t; typedef svector<std::pair<bool_var, int> > arg_t;
struct stats { struct stats {
unsigned m_num_axioms; unsigned m_num_axioms;
unsigned m_num_predicates; unsigned m_num_predicates;
unsigned m_num_compiles;
void reset() { memset(this, 0, sizeof(*this)); } void reset() { memset(this, 0, sizeof(*this)); }
stats() { reset(); } stats() { reset(); }
}; };
struct card { struct card {
app* m_app;
int m_k; int m_k;
bool_var m_bv; bool_var m_bv;
int m_current_min; int m_current_min;
@ -43,9 +48,17 @@ namespace smt {
int m_abs_min; int m_abs_min;
int m_abs_max; int m_abs_max;
arg_t m_args; arg_t m_args;
card(bool_var bv, int k): unsigned m_num_propagations;
m_k(k), m_bv(bv) unsigned m_compilation_threshold;
{} bool m_compiled;
obj_map<expr, expr*> m_replay;
expr_ref_vector m_trail;
card(ast_manager& m, app* a, bool_var bv, int k, unsigned threshold):
m_app(a), m_k(k), m_bv(bv),
m_num_propagations(0), m_compilation_threshold(threshold), m_compiled(false),
m_trail(m)
{
}
}; };
u_map<ptr_vector<card>*> m_watch; // use-list of literals. u_map<ptr_vector<card>*> m_watch; // use-list of literals.
@ -61,18 +74,22 @@ namespace smt {
void add_watch(bool_var bv, card* c); void add_watch(bool_var bv, card* c);
void add_card(card* c); void add_card(card* c);
void add_clause(literal_vector const& lits); void add_clause(card& c, literal_vector const& lits);
literal_vector& get_lits(); literal_vector& get_lits();
int find_inc(bool_var bv, svector<std::pair<bool_var, int> >const& vars); int find_inc(bool_var bv, svector<std::pair<bool_var, int> >const& vars);
void theory_card::propagate_assignment(card* c); void propagate_assignment(card& c);
int theory_card::accumulate_max(literal_vector& lits, card* c); int accumulate_max(literal_vector& lits, card& c);
int theory_card::accumulate_min(literal_vector& lits, card* c); int accumulate_min(literal_vector& lits, card& c);
lbool theory_card::dec_max(int inc, lbool val); lbool dec_max(int inc, lbool val);
lbool theory_card::inc_min(int inc, lbool val); lbool inc_min(int inc, lbool val);
void theory_card::assign_use(bool_var v, bool is_true, card* c); void assign_use(bool_var v, bool is_true, card& c);
void theory_card::update_min_max(bool_var v, bool is_true, card* c); void update_min_max(bool_var v, bool is_true, card& c);
void compile_at_most(card& c);
expr_ref nnf(expr* e);
bool should_compile(card& c);
unsigned get_compilation_threshold(app* atom);
public: public:
theory_card(ast_manager& m); theory_card(ast_manager& m);