mirror of
https://github.com/Z3Prover/z3
synced 2025-06-25 07:13:41 +00:00
gt encoding of pb constraints
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
b70096a97f
commit
41e1b9f3fe
2 changed files with 183 additions and 17 deletions
|
@ -5,7 +5,7 @@ Module Name:
|
||||||
|
|
||||||
pb2bv_rewriter.cpp
|
pb2bv_rewriter.cpp
|
||||||
|
|
||||||
Abstract:
|
Abstralct:
|
||||||
|
|
||||||
Conversion from pseudo-booleans to bit-vectors.
|
Conversion from pseudo-booleans to bit-vectors.
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ Notes:
|
||||||
#include"ast_util.h"
|
#include"ast_util.h"
|
||||||
#include"ast_pp.h"
|
#include"ast_pp.h"
|
||||||
#include"lbool.h"
|
#include"lbool.h"
|
||||||
|
#include"uint_set.h"
|
||||||
|
|
||||||
const unsigned g_primes[7] = { 2, 3, 5, 7, 11, 13, 17};
|
const unsigned g_primes[7] = { 2, 3, 5, 7, 11, 13, 17};
|
||||||
|
|
||||||
|
@ -112,6 +113,12 @@ struct pb2bv_rewriter::imp {
|
||||||
return expr_ref((is_le == l_false)?m.mk_true():m.mk_false(), m);
|
return expr_ref((is_le == l_false)?m.mk_true():m.mk_false(), m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr_ref result(m);
|
||||||
|
switch (is_le) {
|
||||||
|
case l_true: if (mk_le_tot(sz, args, k, result)) return result; else break;
|
||||||
|
case l_false: if (mk_ge_tot(sz, args, k, result)) return result; else break;
|
||||||
|
case l_undef: break;
|
||||||
|
}
|
||||||
#if 0
|
#if 0
|
||||||
expr_ref result(m);
|
expr_ref result(m);
|
||||||
switch (is_le) {
|
switch (is_le) {
|
||||||
|
@ -172,6 +179,141 @@ struct pb2bv_rewriter::imp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Totalizer encoding. Based on a version by Miguel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool mk_le_tot(unsigned sz, expr * const * args, rational const& _k, expr_ref& result) {
|
||||||
|
SASSERT(sz == m_coeffs.size());
|
||||||
|
if (!_k.is_unsigned() || sz == 0) return false;
|
||||||
|
unsigned k = _k.get_unsigned();
|
||||||
|
expr_ref_vector args1(m);
|
||||||
|
rational bound;
|
||||||
|
flip(sz, args, args1, _k, bound);
|
||||||
|
if (bound.get_unsigned() < k) {
|
||||||
|
return mk_ge_tot(sz, args1.c_ptr(), bound, result);
|
||||||
|
}
|
||||||
|
if (k > 20) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = m.mk_not(bounded_addition(sz, args, k + 1));
|
||||||
|
TRACE("pb", tout << result << "\n";);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mk_ge_tot(unsigned sz, expr * const * args, rational const& _k, expr_ref& result) {
|
||||||
|
SASSERT(sz == m_coeffs.size());
|
||||||
|
if (!_k.is_unsigned() || sz == 0) return false;
|
||||||
|
unsigned k = _k.get_unsigned();
|
||||||
|
expr_ref_vector args1(m);
|
||||||
|
rational bound;
|
||||||
|
flip(sz, args, args1, _k, bound);
|
||||||
|
if (bound.get_unsigned() < k) {
|
||||||
|
return mk_le_tot(sz, args1.c_ptr(), bound, result);
|
||||||
|
}
|
||||||
|
if (k > 20) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = bounded_addition(sz, args, k);
|
||||||
|
TRACE("pb", tout << result << "\n";);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void flip(unsigned sz, expr* const* args, expr_ref_vector& args1, rational const& k, rational& bound) {
|
||||||
|
bound = -k;
|
||||||
|
for (unsigned i = 0; i < sz; ++i) {
|
||||||
|
args1.push_back(mk_not(args[i]));
|
||||||
|
bound += m_coeffs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref bounded_addition(unsigned sz, expr * const * args, unsigned k) {
|
||||||
|
SASSERT(sz > 0);
|
||||||
|
expr_ref result(m);
|
||||||
|
vector<expr_ref_vector> es;
|
||||||
|
vector<unsigned_vector> coeffs;
|
||||||
|
for (unsigned i = 0; i < m_coeffs.size(); ++i) {
|
||||||
|
unsigned_vector v;
|
||||||
|
expr_ref_vector e(m);
|
||||||
|
unsigned c = m_coeffs[i].get_unsigned();
|
||||||
|
v.push_back(c >= k ? k : c);
|
||||||
|
e.push_back(args[i]);
|
||||||
|
es.push_back(e);
|
||||||
|
coeffs.push_back(v);
|
||||||
|
}
|
||||||
|
while (es.size() > 1) {
|
||||||
|
for (unsigned i = 0; i + 1 < es.size(); i += 2) {
|
||||||
|
expr_ref_vector o(m);
|
||||||
|
unsigned_vector oc;
|
||||||
|
tot_adder(es[i], coeffs[i], es[i + 1], coeffs[i + 1], k, o, oc);
|
||||||
|
es[i / 2].set(o);
|
||||||
|
coeffs[i / 2] = oc;
|
||||||
|
}
|
||||||
|
if ((es.size() % 2) == 1) {
|
||||||
|
es[es.size() / 2].set(es.back());
|
||||||
|
coeffs[es.size() / 2] = coeffs.back();
|
||||||
|
}
|
||||||
|
es.shrink((1 + es.size())/2);
|
||||||
|
coeffs.shrink((1 + coeffs.size())/2);
|
||||||
|
}
|
||||||
|
SASSERT(coeffs.size() == 1);
|
||||||
|
SASSERT(coeffs[0].back() <= k);
|
||||||
|
if (coeffs[0].back() == k) {
|
||||||
|
result = es[0].back();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result = m.mk_false();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tot_adder(expr_ref_vector const& l, unsigned_vector const& lc,
|
||||||
|
expr_ref_vector const& r, unsigned_vector const& rc,
|
||||||
|
unsigned k,
|
||||||
|
expr_ref_vector& o, unsigned_vector & oc) {
|
||||||
|
SASSERT(l.size() == lc.size());
|
||||||
|
SASSERT(r.size() == rc.size());
|
||||||
|
uint_set sums;
|
||||||
|
vector<expr_ref_vector> trail;
|
||||||
|
u_map<unsigned> sum2def;
|
||||||
|
for (unsigned i = 0; i <= l.size(); ++i) {
|
||||||
|
for (unsigned j = (i == 0) ? 1 : 0; j <= r.size(); ++j) {
|
||||||
|
unsigned sum = std::min(k, ((i == 0) ? 0 : lc[i - 1]) + ((j == 0) ? 0 : rc[j - 1]));
|
||||||
|
sums.insert(sum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
uint_set::iterator it = sums.begin(), end = sums.end();
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
oc.push_back(*it);
|
||||||
|
}
|
||||||
|
std::sort(oc.begin(), oc.end());
|
||||||
|
DEBUG_CODE(
|
||||||
|
for (unsigned i = 0; i + 1 < oc.size(); ++i) {
|
||||||
|
SASSERT(oc[i] < oc[i+1]);
|
||||||
|
});
|
||||||
|
for (unsigned i = 0; i < oc.size(); ++i) {
|
||||||
|
sum2def.insert(oc[i], i);
|
||||||
|
trail.push_back(expr_ref_vector(m));
|
||||||
|
}
|
||||||
|
for (unsigned i = 0; i <= l.size(); ++i) {
|
||||||
|
for (unsigned j = (i == 0) ? 1 : 0; j <= r.size(); ++j) {
|
||||||
|
if (i != 0 && j != 0 && (lc[i - 1] >= k || rc[j - 1] >= k)) continue;
|
||||||
|
unsigned sum = std::min(k, ((i == 0) ? 0 : lc[i - 1]) + ((j == 0) ? 0 : rc[j - 1]));
|
||||||
|
expr_ref_vector ands(m);
|
||||||
|
if (i != 0) {
|
||||||
|
ands.push_back(l[i - 1]);
|
||||||
|
}
|
||||||
|
if (j != 0) {
|
||||||
|
ands.push_back(r[j - 1]);
|
||||||
|
}
|
||||||
|
trail[sum2def.find(sum)].push_back(::mk_and(ands));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (unsigned i = 0; i < oc.size(); ++i) {
|
||||||
|
o.push_back(::mk_or(trail[sum2def.find(oc[i])]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief MiniSat+ based encoding of PB constraints.
|
\brief MiniSat+ based encoding of PB constraints.
|
||||||
The procedure is described in "Translating Pseudo-Boolean Constraints into SAT "
|
The procedure is described in "Translating Pseudo-Boolean Constraints into SAT "
|
||||||
|
|
|
@ -282,8 +282,10 @@ namespace sat {
|
||||||
inc_bstamp();
|
inc_bstamp();
|
||||||
set_bstamp(l);
|
set_bstamp(l);
|
||||||
literal_vector const& conseq = m_binary[l.index()];
|
literal_vector const& conseq = m_binary[l.index()];
|
||||||
for (unsigned i = 0; i < conseq.size(); ++i) {
|
literal_vector::const_iterator it = conseq.begin();
|
||||||
set_bstamp(conseq[i]);
|
literal_vector::const_iterator end = conseq.end();
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
set_bstamp(*it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool is_stamped(literal l) const { return m_bstamp[l.index()] == m_bstamp_id; }
|
bool is_stamped(literal l) const { return m_bstamp[l.index()] == m_bstamp_id; }
|
||||||
|
@ -557,6 +559,7 @@ namespace sat {
|
||||||
literal_vector::iterator it = m_binary[l.index()].begin(), end = m_binary[l.index()].end();
|
literal_vector::iterator it = m_binary[l.index()].begin(), end = m_binary[l.index()].end();
|
||||||
for (; it != end; ++it) {
|
for (; it != end; ++it) {
|
||||||
if (is_undef(*it)) sum += h[it->index()];
|
if (is_undef(*it)) sum += h[it->index()];
|
||||||
|
// if (m_freevars.contains(it->var())) sum += h[it->index()];
|
||||||
}
|
}
|
||||||
watch_list& wlist = m_watches[l.index()];
|
watch_list& wlist = m_watches[l.index()];
|
||||||
watch_list::iterator wit = wlist.begin(), wend = wlist.end();
|
watch_list::iterator wit = wlist.begin(), wend = wlist.end();
|
||||||
|
@ -568,9 +571,8 @@ namespace sat {
|
||||||
case watched::TERNARY: {
|
case watched::TERNARY: {
|
||||||
literal l1 = wit->get_literal1();
|
literal l1 = wit->get_literal1();
|
||||||
literal l2 = wit->get_literal2();
|
literal l2 = wit->get_literal2();
|
||||||
if (is_undef(l1) && is_undef(l2)) {
|
// if (is_undef(l1) && is_undef(l2))
|
||||||
tsum += h[l1.index()] * h[l2.index()];
|
tsum += h[l1.index()] * h[l2.index()];
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case watched::CLAUSE: {
|
case watched::CLAUSE: {
|
||||||
|
@ -1155,14 +1157,19 @@ namespace sat {
|
||||||
// convert windfalls to binary clauses.
|
// convert windfalls to binary clauses.
|
||||||
if (!unsat) {
|
if (!unsat) {
|
||||||
literal nlit = ~lit;
|
literal nlit = ~lit;
|
||||||
|
|
||||||
for (unsigned i = 0; i < m_wstack.size(); ++i) {
|
for (unsigned i = 0; i < m_wstack.size(); ++i) {
|
||||||
++m_stats.m_windfall_binaries;
|
|
||||||
literal l2 = m_wstack[i];
|
literal l2 = m_wstack[i];
|
||||||
//update_prefix(~lit);
|
//update_prefix(~lit);
|
||||||
//update_prefix(m_wstack[i]);
|
//update_prefix(m_wstack[i]);
|
||||||
TRACE("sat", tout << "windfall: " << nlit << " " << l2 << "\n";);
|
TRACE("sat", tout << "windfall: " << nlit << " " << l2 << "\n";);
|
||||||
|
// if we use try_add_binary, then this may produce new assignments
|
||||||
|
// these assignments get put on m_trail, and they are cleared by
|
||||||
|
// reset_wnb. We would need to distinguish the trail that comes
|
||||||
|
// from lookahead levels and the main search level for this to work.
|
||||||
add_binary(nlit, l2);
|
add_binary(nlit, l2);
|
||||||
}
|
}
|
||||||
|
m_stats.m_windfall_binaries += m_wstack.size();
|
||||||
}
|
}
|
||||||
m_wstack.reset();
|
m_wstack.reset();
|
||||||
}
|
}
|
||||||
|
@ -1180,6 +1187,15 @@ namespace sat {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// The current version is modeled after CDCL SAT solving data-structures.
|
||||||
|
// It borrows from the watch list data-structure. The cost tradeoffs are somewhat
|
||||||
|
// biased towards CDCL search overheads.
|
||||||
|
// If we walk over the positive occurrences of l, then those clauses can be retired so
|
||||||
|
// that they don't interfere with calculation of H. Instead of removing clauses from the watch
|
||||||
|
// list one can swap them to the "back" and adjust a size indicator of the watch list
|
||||||
|
// Only the size indicator needs to be updated on backtracking.
|
||||||
|
//
|
||||||
void propagate_clauses(literal l) {
|
void propagate_clauses(literal l) {
|
||||||
SASSERT(is_true(l));
|
SASSERT(is_true(l));
|
||||||
if (inconsistent()) return;
|
if (inconsistent()) return;
|
||||||
|
@ -1237,17 +1253,17 @@ namespace sat {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case watched::CLAUSE: {
|
case watched::CLAUSE: {
|
||||||
clause_offset cls_off = it->get_clause_offset();
|
|
||||||
clause & c = *(s.m_cls_allocator.get_clause(cls_off));
|
|
||||||
if (is_true(it->get_blocked_literal())) {
|
if (is_true(it->get_blocked_literal())) {
|
||||||
*it2 = *it;
|
*it2 = *it;
|
||||||
++it2;
|
++it2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
clause_offset cls_off = it->get_clause_offset();
|
||||||
|
clause & c = *(s.m_cls_allocator.get_clause(cls_off));
|
||||||
if (c[0] == ~l)
|
if (c[0] == ~l)
|
||||||
std::swap(c[0], c[1]);
|
std::swap(c[0], c[1]);
|
||||||
if (is_true(c[0])) {
|
if (is_true(c[0])) {
|
||||||
|
it->set_blocked_literal(c[0]);
|
||||||
*it2 = *it;
|
*it2 = *it;
|
||||||
it2++;
|
it2++;
|
||||||
break;
|
break;
|
||||||
|
@ -1337,13 +1353,21 @@ namespace sat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void propagate() {
|
void propagate() {
|
||||||
for (; m_qhead < m_trail.size(); ++m_qhead) {
|
while (!inconsistent() && m_qhead < m_trail.size()) {
|
||||||
if (inconsistent()) break;
|
unsigned i = m_qhead;
|
||||||
literal l = m_trail[m_qhead];
|
unsigned sz = m_trail.size();
|
||||||
|
for (; i < sz && !inconsistent(); ++i) {
|
||||||
|
literal l = m_trail[i];
|
||||||
TRACE("sat", tout << "propagate " << l << " @ " << m_level << "\n";);
|
TRACE("sat", tout << "propagate " << l << " @ " << m_level << "\n";);
|
||||||
propagate_binary(l);
|
propagate_binary(l);
|
||||||
propagate_clauses(l);
|
|
||||||
}
|
}
|
||||||
|
i = m_qhead;
|
||||||
|
for (; i < sz && !inconsistent(); ++i) {
|
||||||
|
propagate_clauses(m_trail[i]);
|
||||||
|
}
|
||||||
|
m_qhead = sz;
|
||||||
|
}
|
||||||
|
|
||||||
TRACE("sat_verbose", display(tout << scope_lvl() << " " << (inconsistent()?"unsat":"sat") << "\n"););
|
TRACE("sat_verbose", display(tout << scope_lvl() << " " << (inconsistent()?"unsat":"sat") << "\n"););
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue