mirror of
https://github.com/Z3Prover/z3
synced 2025-04-07 01:54:08 +00:00
3982 lines
129 KiB
C++
3982 lines
129 KiB
C++
/*++
|
|
Copyright (c) 2017 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
ba_solver.cpp
|
|
|
|
Abstract:
|
|
|
|
Extension for cardinality and xor reasoning.
|
|
|
|
Author:
|
|
|
|
Nikolaj Bjorner (nbjorner) 2017-01-30
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "sat/ba_solver.h"
|
|
#include "sat/sat_types.h"
|
|
#include "util/lp/lar_solver.h"
|
|
|
|
|
|
namespace sat {
|
|
|
|
static unsigned _bad_id = 11111111; // 2759; //
|
|
#define BADLOG(_cmd_) if (p.id() == _bad_id) { _cmd_; }
|
|
|
|
ba_solver::card& ba_solver::constraint::to_card() {
|
|
SASSERT(is_card());
|
|
return static_cast<card&>(*this);
|
|
}
|
|
|
|
ba_solver::card const& ba_solver::constraint::to_card() const{
|
|
SASSERT(is_card());
|
|
return static_cast<card const&>(*this);
|
|
}
|
|
|
|
ba_solver::pb& ba_solver::constraint::to_pb() {
|
|
SASSERT(is_pb());
|
|
return static_cast<pb&>(*this);
|
|
}
|
|
|
|
ba_solver::pb const& ba_solver::constraint::to_pb() const{
|
|
SASSERT(is_pb());
|
|
return static_cast<pb const&>(*this);
|
|
}
|
|
|
|
ba_solver::xor& ba_solver::constraint::to_xor() {
|
|
SASSERT(is_xor());
|
|
return static_cast<xor&>(*this);
|
|
}
|
|
|
|
ba_solver::xor const& ba_solver::constraint::to_xor() const{
|
|
SASSERT(is_xor());
|
|
return static_cast<xor const&>(*this);
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& out, ba_solver::constraint const& cnstr) {
|
|
if (cnstr.lit() != null_literal) out << cnstr.lit() << " == ";
|
|
switch (cnstr.tag()) {
|
|
case ba_solver::card_t: {
|
|
ba_solver::card const& c = cnstr.to_card();
|
|
for (literal l : c) {
|
|
out << l << " ";
|
|
}
|
|
out << " >= " << c.k();
|
|
break;
|
|
}
|
|
case ba_solver::pb_t: {
|
|
ba_solver::pb const& p = cnstr.to_pb();
|
|
for (ba_solver::wliteral wl : p) {
|
|
if (wl.first != 1) out << wl.first << " * ";
|
|
out << wl.second << " ";
|
|
}
|
|
out << " >= " << p.k();
|
|
break;
|
|
}
|
|
case ba_solver::xor_t: {
|
|
ba_solver::xor const& x = cnstr.to_xor();
|
|
for (unsigned i = 0; i < x.size(); ++i) {
|
|
out << x[i] << " ";
|
|
if (i + 1 < x.size()) out << "x ";
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
return out;
|
|
}
|
|
|
|
|
|
// -----------------------
|
|
// pb_base
|
|
|
|
bool ba_solver::pb_base::well_formed() const {
|
|
uint_set vars;
|
|
if (lit() != null_literal) vars.insert(lit().var());
|
|
for (unsigned i = 0; i < size(); ++i) {
|
|
bool_var v = get_lit(i).var();
|
|
if (vars.contains(v)) return false;
|
|
if (get_coeff(i) > k()) return false;
|
|
vars.insert(v);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ----------------------
|
|
// card
|
|
|
|
ba_solver::card::card(unsigned id, literal lit, literal_vector const& lits, unsigned k):
|
|
pb_base(card_t, id, lit, lits.size(), get_obj_size(lits.size()), k) {
|
|
for (unsigned i = 0; i < size(); ++i) {
|
|
m_lits[i] = lits[i];
|
|
}
|
|
}
|
|
|
|
void ba_solver::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);
|
|
}
|
|
|
|
bool ba_solver::card::is_watching(literal l) const {
|
|
unsigned sz = std::min(k() + 1, size());
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
if ((*this)[i] == l) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------
|
|
// pb
|
|
|
|
ba_solver::pb::pb(unsigned id, literal lit, svector<ba_solver::wliteral> const& wlits, unsigned k):
|
|
pb_base(pb_t, id, lit, wlits.size(), get_obj_size(wlits.size()), k),
|
|
m_slack(0),
|
|
m_num_watch(0),
|
|
m_max_sum(0) {
|
|
for (unsigned i = 0; i < size(); ++i) {
|
|
m_wlits[i] = wlits[i];
|
|
}
|
|
update_max_sum();
|
|
}
|
|
|
|
void ba_solver::pb::update_max_sum() {
|
|
m_max_sum = 0;
|
|
for (unsigned i = 0; i < size(); ++i) {
|
|
m_wlits[i].first = std::min(k(), m_wlits[i].first);
|
|
if (m_max_sum + m_wlits[i].first < m_max_sum) {
|
|
throw default_exception("addition of pb coefficients overflows");
|
|
}
|
|
m_max_sum += m_wlits[i].first;
|
|
}
|
|
}
|
|
|
|
void ba_solver::pb::negate() {
|
|
m_lit.neg();
|
|
unsigned w = 0;
|
|
for (unsigned i = 0; i < m_size; ++i) {
|
|
m_wlits[i].second.neg();
|
|
w += m_wlits[i].first;
|
|
}
|
|
m_k = w - m_k + 1;
|
|
SASSERT(w >= m_k && m_k > 0);
|
|
}
|
|
|
|
bool ba_solver::pb::is_watching(literal l) const {
|
|
for (unsigned i = 0; i < m_num_watch; ++i) {
|
|
if ((*this)[i].second == l) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool ba_solver::pb::is_cardinality() const {
|
|
if (size() == 0) return false;
|
|
unsigned w = (*this)[0].first;
|
|
for (wliteral wl : *this) if (w != wl.first) return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
// -----------------------------------
|
|
// xor
|
|
|
|
ba_solver::xor::xor(unsigned id, literal lit, literal_vector const& lits):
|
|
constraint(xor_t, id, lit, lits.size(), get_obj_size(lits.size())) {
|
|
for (unsigned i = 0; i < size(); ++i) {
|
|
m_lits[i] = lits[i];
|
|
}
|
|
}
|
|
|
|
bool ba_solver::xor::is_watching(literal l) const {
|
|
return
|
|
l == (*this)[0] || l == (*this)[1] ||
|
|
~l == (*this)[0] || ~l == (*this)[1];
|
|
}
|
|
|
|
bool ba_solver::xor::well_formed() const {
|
|
uint_set vars;
|
|
if (lit() != null_literal) vars.insert(lit().var());
|
|
for (literal l : *this) {
|
|
bool_var v = l.var();
|
|
if (vars.contains(v)) return false;
|
|
vars.insert(v);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ----------------------------
|
|
// card
|
|
|
|
bool ba_solver::init_watch(card& c) {
|
|
clear_watch(c);
|
|
literal root = c.lit();
|
|
if (root != null_literal && value(root) == l_false) {
|
|
c.negate();
|
|
root.neg();
|
|
}
|
|
if (root != null_literal) {
|
|
if (!is_watched(root, c)) watch_literal(root, c);
|
|
if (!is_watched(~root, c)) watch_literal(~root, c);
|
|
}
|
|
TRACE("ba", display(tout << "init watch: ", c, true););
|
|
SASSERT(root == null_literal || value(root) == l_true);
|
|
unsigned j = 0, sz = c.size(), bound = c.k();
|
|
// put the non-false literals into the head.
|
|
|
|
if (bound == sz) {
|
|
for (literal l : c) assign(c, l);
|
|
return false;
|
|
}
|
|
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
if (value(c[i]) != l_false) {
|
|
if (j != i) {
|
|
c.swap(i, j);
|
|
}
|
|
++j;
|
|
}
|
|
}
|
|
DEBUG_CODE(
|
|
bool is_false = false;
|
|
for (literal l : c) {
|
|
SASSERT(!is_false || value(l) == l_false);
|
|
is_false = value(l) == 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 (lvl(alit) < lvl(c[i])) {
|
|
c.swap(i, j);
|
|
alit = c[j];
|
|
}
|
|
}
|
|
set_conflict(c, alit);
|
|
return false;
|
|
}
|
|
else if (j == bound) {
|
|
for (unsigned i = 0; i < bound; ++i) {
|
|
assign(c, c[i]);
|
|
}
|
|
return false;
|
|
}
|
|
else {
|
|
for (unsigned i = 0; i <= bound; ++i) {
|
|
watch_literal(c[i], c);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void ba_solver::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);
|
|
}
|
|
}
|
|
|
|
// -----------------------
|
|
// constraint
|
|
|
|
void ba_solver::set_conflict(constraint& c, literal lit) {
|
|
m_stats.m_num_conflicts++;
|
|
TRACE("ba", display(tout, c, true); );
|
|
if (!validate_conflict(c)) {
|
|
display(std::cout, c, true);
|
|
UNREACHABLE();
|
|
}
|
|
SASSERT(validate_conflict(c));
|
|
if (c.is_xor() && value(lit) == l_true) lit.neg();
|
|
SASSERT(value(lit) == l_false);
|
|
set_conflict(justification::mk_ext_justification(c.index()), ~lit);
|
|
SASSERT(inconsistent());
|
|
}
|
|
|
|
void ba_solver::assign(constraint& c, literal lit) {
|
|
if (inconsistent()) return;
|
|
switch (value(lit)) {
|
|
case l_true:
|
|
break;
|
|
case l_false:
|
|
set_conflict(c, lit);
|
|
break;
|
|
default:
|
|
m_stats.m_num_propagations++;
|
|
m_num_propagations_since_pop++;
|
|
//TRACE("ba", tout << "#prop: " << m_stats.m_num_propagations << " - " << c.lit() << " => " << lit << "\n";);
|
|
SASSERT(validate_unit_propagation(c, lit));
|
|
if (get_config().m_drat) {
|
|
svector<drat::premise> ps;
|
|
literal_vector lits;
|
|
get_antecedents(lit, c, lits);
|
|
lits.push_back(lit);
|
|
ps.push_back(drat::premise(drat::s_ext(), c.lit())); // null_literal case.
|
|
drat_add(lits, ps);
|
|
}
|
|
assign(lit, justification::mk_ext_justification(c.index()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// -------------------
|
|
// pb_base
|
|
|
|
void ba_solver::simplify(pb_base& p) {
|
|
SASSERT(s().at_base_lvl());
|
|
if (p.lit() != null_literal && value(p.lit()) == l_false) {
|
|
TRACE("ba", tout << "pb: flip sign " << p << "\n";);
|
|
IF_VERBOSE(0, verbose_stream() << "sign is flipped " << p << "\n";);
|
|
return;
|
|
}
|
|
bool nullify = p.lit() != null_literal && value(p.lit()) == l_true;
|
|
if (nullify) {
|
|
IF_VERBOSE(100, display(verbose_stream() << "nullify tracking literal\n", p, true););
|
|
SASSERT(lvl(p.lit()) == 0);
|
|
nullify_tracking_literal(p);
|
|
init_watch(p);
|
|
}
|
|
|
|
SASSERT(p.lit() == null_literal || value(p.lit()) != l_false);
|
|
|
|
unsigned true_val = 0, slack = 0, num_false = 0;
|
|
for (unsigned i = 0; i < p.size(); ++i) {
|
|
literal l = p.get_lit(i);
|
|
if (s().was_eliminated(l.var())) {
|
|
SASSERT(p.learned());
|
|
remove_constraint(p, "contains eliminated");
|
|
return;
|
|
}
|
|
switch (value(l)) {
|
|
case l_true: true_val += p.get_coeff(i); break;
|
|
case l_false: ++num_false; break;
|
|
default: slack += p.get_coeff(i); break;
|
|
}
|
|
}
|
|
if (p.k() == 1 && p.lit() == null_literal) {
|
|
literal_vector lits(p.literals());
|
|
s().mk_clause(lits.size(), lits.c_ptr(), p.learned());
|
|
IF_VERBOSE(100, display(verbose_stream() << "add clause: " << lits << "\n", p, true););
|
|
remove_constraint(p, "implies clause");
|
|
}
|
|
else if (true_val == 0 && num_false == 0) {
|
|
if (p.lit() == null_literal || value(p.lit()) == l_true) {
|
|
init_watch(p);
|
|
}
|
|
}
|
|
else if (true_val >= p.k()) {
|
|
if (p.lit() != null_literal) {
|
|
IF_VERBOSE(100, display(verbose_stream() << "assign true literal ", p, true););
|
|
s().assign(p.lit(), justification());
|
|
}
|
|
remove_constraint(p, "is true");
|
|
}
|
|
else if (slack + true_val < p.k()) {
|
|
if (p.lit() != null_literal) {
|
|
IF_VERBOSE(100, display(verbose_stream() << "assign false literal ", p, true););
|
|
s().assign(~p.lit(), justification());
|
|
}
|
|
else {
|
|
IF_VERBOSE(0, verbose_stream() << "unsat during simplification\n";);
|
|
s().set_conflict(justification());
|
|
}
|
|
remove_constraint(p, "is false");
|
|
}
|
|
else if (slack + true_val == p.k()) {
|
|
literal_vector lits(p.literals());
|
|
assert_unconstrained(p.lit(), lits);
|
|
remove_constraint(p, "is tight");
|
|
}
|
|
else {
|
|
|
|
unsigned sz = p.size();
|
|
clear_watch(p);
|
|
unsigned j = 0;
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
literal l = p.get_lit(i);
|
|
if (value(l) == l_undef) {
|
|
if (i != j) p.swap(i, j);
|
|
++j;
|
|
}
|
|
}
|
|
sz = j;
|
|
// _bad_id = p.id();
|
|
BADLOG(display(verbose_stream() << "simplify ", p, true));
|
|
|
|
p.set_size(sz);
|
|
p.set_k(p.k() - true_val);
|
|
|
|
if (p.k() == 1 && p.lit() == null_literal) {
|
|
literal_vector lits(sz, p.literals().c_ptr());
|
|
s().mk_clause(sz, lits.c_ptr(), p.learned());
|
|
remove_constraint(p, "is clause");
|
|
return;
|
|
}
|
|
else if (p.lit() == null_literal || value(p.lit()) == l_true) {
|
|
init_watch(p);
|
|
}
|
|
else {
|
|
SASSERT(value(p.lit()) == l_undef);
|
|
}
|
|
BADLOG(display(verbose_stream() << "simplified ", p, true); verbose_stream() << "\n");
|
|
// display(verbose_stream(), c, true);
|
|
_bad_id = 11111111;
|
|
SASSERT(p.well_formed());
|
|
if (p.is_pb()) simplify2(p.to_pb());
|
|
m_simplify_change = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
\brief slit PB constraint into two because root is reused in arguments.
|
|
|
|
x <=> a*x + B*y >= k
|
|
|
|
x => a*x + By >= k
|
|
~x => a*x + By < k
|
|
|
|
k*~x + a*x + By >= k
|
|
(B+a-k + 1)*x + a*~x + B*~y >= B + a - k + 1
|
|
|
|
(k - a) * ~x + By >= k - a
|
|
k' * x + B'y >= k'
|
|
|
|
*/
|
|
|
|
void ba_solver::split_root(pb_base& p) {
|
|
SASSERT(p.lit() != null_literal);
|
|
SASSERT(!p.learned());
|
|
m_weights.resize(2*s().num_vars(), 0);
|
|
unsigned k = p.k();
|
|
unsigned w, w1, w2;
|
|
literal root = p.lit();
|
|
m_weights[(~root).index()] = k;
|
|
for (unsigned i = 0; i < p.size(); ++i) {
|
|
m_weights[p.get_lit(i).index()] += p.get_coeff(i);
|
|
}
|
|
literal_vector lits(p.literals());
|
|
lits.push_back(~root);
|
|
|
|
for (literal l : lits) {
|
|
w1 = m_weights[l.index()];
|
|
w2 = m_weights[(~l).index()];
|
|
if (w1 >= w2) {
|
|
if (w2 >= k) {
|
|
// constraint is true
|
|
return;
|
|
}
|
|
k -= w2;
|
|
m_weights[(~l).index()] = 0;
|
|
m_weights[l.index()] = w1 - w2;
|
|
}
|
|
}
|
|
SASSERT(k > 0);
|
|
|
|
// ~root * (k - a) + p >= k - a
|
|
|
|
m_wlits.reset();
|
|
for (literal l : lits) {
|
|
w = m_weights[l.index()];
|
|
if (w != 0) {
|
|
m_wlits.push_back(wliteral(w, l));
|
|
}
|
|
m_weights[l.index()] = 0;
|
|
}
|
|
|
|
add_pb_ge(null_literal, m_wlits, k, false);
|
|
}
|
|
|
|
|
|
// -------------------
|
|
// pb
|
|
|
|
|
|
// watch a prefix of literals, such that the slack of these is >= k
|
|
bool ba_solver::init_watch(pb& p) {
|
|
clear_watch(p);
|
|
if (p.lit() != null_literal && value(p.lit()) == l_false) {
|
|
p.negate();
|
|
}
|
|
|
|
VERIFY(p.lit() == null_literal || value(p.lit()) == l_true);
|
|
unsigned sz = p.size(), bound = p.k();
|
|
|
|
// put the non-false literals into the head.
|
|
unsigned slack = 0, slack1 = 0, num_watch = 0, j = 0;
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
if (value(p[i].second) != l_false) {
|
|
if (j != i) {
|
|
p.swap(i, j);
|
|
}
|
|
if (slack <= bound) {
|
|
slack += p[j].first;
|
|
++num_watch;
|
|
}
|
|
else {
|
|
slack1 += p[j].first;
|
|
}
|
|
++j;
|
|
}
|
|
}
|
|
BADLOG(verbose_stream() << "watch " << num_watch << " out of " << sz << "\n");
|
|
|
|
DEBUG_CODE(
|
|
bool is_false = false;
|
|
for (unsigned k = 0; k < sz; ++k) {
|
|
SASSERT(!is_false || value(p[k].second) == l_false);
|
|
SASSERT(k < j == (value(p[k].second) != l_false));
|
|
is_false = value(p[k].second) == l_false;
|
|
});
|
|
|
|
if (slack < bound) {
|
|
literal lit = p[j].second;
|
|
VERIFY(value(lit) == l_false);
|
|
for (unsigned i = j + 1; i < sz; ++i) {
|
|
if (lvl(lit) < lvl(p[i].second)) {
|
|
lit = p[i].second;
|
|
}
|
|
}
|
|
set_conflict(p, lit);
|
|
return false;
|
|
}
|
|
else {
|
|
for (unsigned i = 0; i < num_watch; ++i) {
|
|
watch_literal(p[i], p);
|
|
}
|
|
p.set_slack(slack);
|
|
p.set_num_watch(num_watch);
|
|
|
|
SASSERT(validate_watch(p, null_literal));
|
|
|
|
TRACE("ba", display(tout << "init watch: ", p, true););
|
|
|
|
// slack is tight:
|
|
if (slack + slack1 == bound) {
|
|
SASSERT(slack1 == 0);
|
|
SASSERT(j == num_watch);
|
|
for (unsigned i = 0; i < j; ++i) {
|
|
assign(p, p[i].second);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Chai Kuhlmann:
|
|
Lw - set of watched literals
|
|
Lu - set of unwatched literals that are not false
|
|
|
|
Lw = Lw \ { alit }
|
|
Sw -= value
|
|
a_max = max { a | l in Lw u Lu, l = undef }
|
|
while (Sw < k + a_max & Lu != 0) {
|
|
a_s = max { a | l in Lu }
|
|
Sw += a_s
|
|
Lw = Lw u {l_s}
|
|
Lu = Lu \ {l_s}
|
|
}
|
|
if (Sw < k) return conflict
|
|
for (li in Lw | Sw < k + ai)
|
|
assign li
|
|
return no-conflict
|
|
|
|
a_max index: index of non-false literal with maximal weight.
|
|
*/
|
|
|
|
void ba_solver::add_index(pb& p, unsigned index, literal lit) {
|
|
if (value(lit) == l_undef) {
|
|
m_pb_undef.push_back(index);
|
|
if (p[index].first > m_a_max) {
|
|
m_a_max = p[index].first;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
\brief propagate assignment to alit in constraint p.
|
|
|
|
TBD:
|
|
- consider reordering literals in watch list so that the search for watched literal takes average shorter time.
|
|
- combine with caching literals that are assigned to 'true' to a cold store where they are not being revisited.
|
|
Since 'true' literals may be unassigned (unless they are assigned at level 0) the cache has to be backtrack
|
|
friendly (and the overhead of backtracking has to be taken into account).
|
|
*/
|
|
lbool ba_solver::add_assign(pb& p, literal alit) {
|
|
BADLOG(display(verbose_stream() << "assign: " << alit << " watch: " << p.num_watch() << " size: " << p.size(), p, true));
|
|
TRACE("ba", display(tout << "assign: " << alit << "\n", p, true););
|
|
SASSERT(!inconsistent());
|
|
unsigned sz = p.size();
|
|
unsigned bound = p.k();
|
|
unsigned num_watch = p.num_watch();
|
|
unsigned slack = p.slack();
|
|
SASSERT(value(alit) == l_false);
|
|
SASSERT(p.lit() == null_literal || value(p.lit()) == l_true);
|
|
SASSERT(num_watch <= sz);
|
|
SASSERT(num_watch > 0);
|
|
unsigned index = 0;
|
|
m_a_max = 0;
|
|
m_pb_undef.reset();
|
|
for (; index < num_watch; ++index) {
|
|
literal lit = p[index].second;
|
|
if (lit == alit) {
|
|
break;
|
|
}
|
|
add_index(p, index, lit);
|
|
}
|
|
if (index == num_watch || num_watch == 0) {
|
|
_bad_id = p.id();
|
|
BADLOG(
|
|
verbose_stream() << "BAD: " << p.id() << "\n";
|
|
display(verbose_stream(), p, true);
|
|
verbose_stream() << "alit: " << alit << "\n";
|
|
verbose_stream() << "num watch " << num_watch << "\n");
|
|
UNREACHABLE();
|
|
exit(0);
|
|
return l_undef;
|
|
}
|
|
|
|
SASSERT(validate_watch(p, null_literal));
|
|
// SASSERT(validate_watch(p, null_literal));
|
|
|
|
SASSERT(index < num_watch);
|
|
unsigned index1 = index + 1;
|
|
for (; m_a_max == 0 && index1 < num_watch; ++index1) {
|
|
add_index(p, index1, p[index1].second);
|
|
}
|
|
|
|
unsigned val = p[index].first;
|
|
SASSERT(value(p[index].second) == l_false);
|
|
SASSERT(val <= slack);
|
|
slack -= val;
|
|
|
|
// find literals to swap with:
|
|
for (unsigned j = num_watch; j < sz && slack < bound + m_a_max; ++j) {
|
|
literal lit = p[j].second;
|
|
if (value(lit) != l_false) {
|
|
slack += p[j].first;
|
|
SASSERT(!is_watched(p[j].second, p));
|
|
watch_literal(p[j], p);
|
|
p.swap(num_watch, j);
|
|
add_index(p, num_watch, lit);
|
|
BADLOG(verbose_stream() << "add watch: " << lit << " num watch: " << num_watch << " max: " << m_a_max << " slack " << slack << "\n");
|
|
++num_watch;
|
|
}
|
|
}
|
|
|
|
SASSERT(!inconsistent());
|
|
DEBUG_CODE(for (auto idx : m_pb_undef) { SASSERT(value(p[idx].second) == l_undef); });
|
|
|
|
if (slack < bound) {
|
|
// maintain watching the literal
|
|
slack += val;
|
|
p.set_slack(slack);
|
|
p.set_num_watch(num_watch);
|
|
SASSERT(validate_watch(p, null_literal));
|
|
BADLOG(display(verbose_stream() << "conflict: " << alit << " watch: " << p.num_watch() << " size: " << p.size(), p, true));
|
|
SASSERT(bound <= slack);
|
|
TRACE("ba", tout << "conflict " << alit << "\n";);
|
|
set_conflict(p, alit);
|
|
return l_false;
|
|
}
|
|
|
|
if (num_watch == 1) { _bad_id = p.id(); }
|
|
|
|
BADLOG(verbose_stream() << "size: " << p.size() << " index: " << index << " num watch: " << num_watch << "\n");
|
|
|
|
// swap out the watched literal.
|
|
--num_watch;
|
|
SASSERT(num_watch > 0);
|
|
p.set_slack(slack);
|
|
p.set_num_watch(num_watch);
|
|
p.swap(num_watch, index);
|
|
|
|
|
|
//
|
|
// slack >= bound, but slack - w(l) < bound
|
|
// l must be true.
|
|
//
|
|
if (slack < bound + m_a_max) {
|
|
BADLOG(verbose_stream() << "slack " << slack << " " << bound << " " << m_a_max << "\n";);
|
|
TRACE("ba", tout << p << "\n"; for(auto j : m_pb_undef) tout << j << " "; tout << "\n";);
|
|
for (unsigned index1 : m_pb_undef) {
|
|
if (index1 == num_watch) {
|
|
index1 = index;
|
|
}
|
|
wliteral wl = p[index1];
|
|
literal lit = wl.second;
|
|
SASSERT(value(lit) == l_undef);
|
|
if (slack < bound + wl.first) {
|
|
BADLOG(verbose_stream() << "Assign " << lit << " " << wl.first << "\n");
|
|
assign(p, lit);
|
|
}
|
|
}
|
|
}
|
|
|
|
SASSERT(validate_watch(p, alit)); // except that alit is still watched.
|
|
|
|
TRACE("ba", display(tout << "assign: " << alit << "\n", p, true););
|
|
|
|
BADLOG(verbose_stream() << "unwatch " << alit << " watch: " << p.num_watch() << " size: " << p.size() << " slack: " << p.slack() << " " << inconsistent() << "\n");
|
|
|
|
return l_undef;
|
|
}
|
|
|
|
void ba_solver::watch_literal(wliteral l, pb& p) {
|
|
watch_literal(l.second, p);
|
|
}
|
|
|
|
void ba_solver::clear_watch(pb& p) {
|
|
for (unsigned i = 0; i < p.num_watch(); ++i) {
|
|
unwatch_literal(p[i].second, p);
|
|
}
|
|
p.set_num_watch(0);
|
|
|
|
DEBUG_CODE(for (wliteral wl : p) VERIFY(!is_watched(wl.second, p)););
|
|
}
|
|
|
|
void ba_solver::recompile(pb& p) {
|
|
// IF_VERBOSE(2, verbose_stream() << "re: " << p << "\n";);
|
|
SASSERT(p.num_watch() == 0);
|
|
m_weights.resize(2*s().num_vars(), 0);
|
|
for (wliteral wl : p) {
|
|
m_weights[wl.second.index()] += wl.first;
|
|
}
|
|
unsigned k = p.k();
|
|
unsigned sz = p.size();
|
|
bool all_units = true;
|
|
unsigned j = 0;
|
|
for (unsigned i = 0; i < sz && 0 < k; ++i) {
|
|
literal l = p[i].second;
|
|
unsigned w1 = m_weights[l.index()];
|
|
unsigned w2 = m_weights[(~l).index()];
|
|
if (w1 == 0 || w1 < w2) {
|
|
continue;
|
|
}
|
|
else if (k <= w2) {
|
|
k = 0;
|
|
break;
|
|
}
|
|
else {
|
|
SASSERT(w2 <= w1 && w2 < k);
|
|
k -= w2;
|
|
w1 -= w2;
|
|
m_weights[l.index()] = 0;
|
|
m_weights[(~l).index()] = 0;
|
|
if (w1 == 0) {
|
|
continue;
|
|
}
|
|
else {
|
|
p[j] = wliteral(w1, l);
|
|
all_units &= w1 == 1;
|
|
++j;
|
|
}
|
|
}
|
|
}
|
|
sz = j;
|
|
// clear weights
|
|
for (wliteral wl : p) {
|
|
m_weights[wl.second.index()] = 0;
|
|
m_weights[(~wl.second).index()] = 0;
|
|
}
|
|
|
|
if (k == 0) {
|
|
if (p.lit() != null_literal) {
|
|
s().assign(p.lit(), justification());
|
|
}
|
|
remove_constraint(p, "recompiled to true");
|
|
return;
|
|
}
|
|
|
|
else if (k == 1 && p.lit() == null_literal) {
|
|
literal_vector lits(sz, p.literals().c_ptr());
|
|
s().mk_clause(sz, lits.c_ptr(), p.learned());
|
|
remove_constraint(p, "recompiled to clause");
|
|
return;
|
|
}
|
|
|
|
else if (all_units) {
|
|
literal_vector lits(sz, p.literals().c_ptr());
|
|
add_at_least(p.lit(), lits, k, p.learned());
|
|
remove_constraint(p, "recompiled to cardinality");
|
|
return;
|
|
}
|
|
|
|
else {
|
|
p.set_size(sz);
|
|
p.set_k(k);
|
|
SASSERT(p.well_formed());
|
|
|
|
if (p.lit() == null_literal || value(p.lit()) == l_true) {
|
|
init_watch(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ba_solver::simplify2(pb& p) {
|
|
return;
|
|
|
|
if (p.is_cardinality()) {
|
|
literal_vector lits(p.literals());
|
|
unsigned k = (p.k() + p[0].first - 1) / p[0].first;
|
|
add_at_least(p.lit(), lits, k, p.learned());
|
|
remove_constraint(p, "simplified to cardinality");
|
|
}
|
|
else if (p.lit() == null_literal) {
|
|
for (wliteral wl : p) {
|
|
if (p.k() > p.max_sum() - wl.first) {
|
|
TRACE("ba",
|
|
tout << "unit literal " << wl.second << "\n";
|
|
display(tout, p, true););
|
|
|
|
s().assign(wl.second, justification());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ba_solver::display(std::ostream& out, pb const& p, bool values) const {
|
|
if (p.lit() != null_literal) out << p.lit() << " == ";
|
|
if (values) {
|
|
out << "[watch: " << p.num_watch() << ", slack: " << p.slack() << "]";
|
|
}
|
|
if (p.lit() != null_literal && values) {
|
|
out << "@(" << value(p.lit());
|
|
if (value(p.lit()) != l_undef) {
|
|
out << ":" << lvl(p.lit());
|
|
}
|
|
out << "): ";
|
|
}
|
|
unsigned i = 0;
|
|
for (wliteral wl : p) {
|
|
literal l = wl.second;
|
|
unsigned w = wl.first;
|
|
if (i++ == p.num_watch()) out << " | ";
|
|
if (w > 1) out << w << " * ";
|
|
out << l;
|
|
if (values) {
|
|
out << "@(" << value(l);
|
|
if (value(l) != l_undef) {
|
|
out << ":" << lvl(l);
|
|
}
|
|
out << ") ";
|
|
}
|
|
else {
|
|
out << " ";
|
|
}
|
|
}
|
|
out << ">= " << p.k() << "\n";
|
|
}
|
|
|
|
|
|
// --------------------
|
|
// xor:
|
|
|
|
void ba_solver::clear_watch(xor& x) {
|
|
unwatch_literal(x[0], x);
|
|
unwatch_literal(x[1], x);
|
|
unwatch_literal(~x[0], x);
|
|
unwatch_literal(~x[1], x);
|
|
}
|
|
|
|
bool ba_solver::parity(xor const& x, unsigned offset) const {
|
|
bool odd = false;
|
|
unsigned sz = x.size();
|
|
for (unsigned i = offset; i < sz; ++i) {
|
|
SASSERT(value(x[i]) != l_undef);
|
|
if (value(x[i]) == l_true) {
|
|
odd = !odd;
|
|
}
|
|
}
|
|
return odd;
|
|
}
|
|
|
|
bool ba_solver::init_watch(xor& x) {
|
|
clear_watch(x);
|
|
if (x.lit() != null_literal && value(x.lit()) == l_false) {
|
|
x.negate();
|
|
}
|
|
TRACE("ba", display(tout, x, true););
|
|
unsigned sz = x.size();
|
|
unsigned j = 0;
|
|
for (unsigned i = 0; i < sz && j < 2; ++i) {
|
|
if (value(x[i]) == l_undef) {
|
|
x.swap(i, j);
|
|
++j;
|
|
}
|
|
}
|
|
switch (j) {
|
|
case 0:
|
|
if (!parity(x, 0)) {
|
|
unsigned l = lvl(x[0]);
|
|
j = 1;
|
|
for (unsigned i = 1; i < sz; ++i) {
|
|
if (lvl(x[i]) > l) {
|
|
j = i;
|
|
l = lvl(x[i]);
|
|
}
|
|
}
|
|
SASSERT(x.lit() == null_literal || value(x.lit()) == l_true);
|
|
set_conflict(x, x[j]);
|
|
}
|
|
return false;
|
|
case 1:
|
|
SASSERT(x.lit() == null_literal || value(x.lit()) == l_true);
|
|
assign(x, parity(x, 1) ? ~x[0] : x[0]);
|
|
return false;
|
|
default:
|
|
SASSERT(j == 2);
|
|
watch_literal(x[0], x);
|
|
watch_literal(x[1], x);
|
|
watch_literal(~x[0], x);
|
|
watch_literal(~x[1], x);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
lbool ba_solver::add_assign(xor& x, literal alit) {
|
|
// literal is assigned
|
|
unsigned sz = x.size();
|
|
TRACE("ba", tout << "assign: " << x.lit() << ": " << ~alit << "@" << lvl(~alit) << "\n";);
|
|
|
|
SASSERT(x.lit() == null_literal || value(x.lit()) == l_true);
|
|
SASSERT(value(alit) != l_undef);
|
|
unsigned index = 0;
|
|
for (; index <= 2; ++index) {
|
|
if (x[index].var() == alit.var()) break;
|
|
}
|
|
if (index == 2) {
|
|
// literal is no longer watched.
|
|
UNREACHABLE();
|
|
return l_undef;
|
|
}
|
|
SASSERT(x[index].var() == alit.var());
|
|
|
|
// find a literal to swap with:
|
|
for (unsigned i = 2; i < sz; ++i) {
|
|
literal lit2 = x[i];
|
|
if (value(lit2) == l_undef) {
|
|
x.swap(index, i);
|
|
watch_literal(lit2, x);
|
|
return l_undef;
|
|
}
|
|
}
|
|
if (index == 0) {
|
|
x.swap(0, 1);
|
|
}
|
|
// alit resides at index 1.
|
|
SASSERT(x[1].var() == alit.var());
|
|
if (value(x[0]) == l_undef) {
|
|
bool p = parity(x, 1);
|
|
assign(x, p ? ~x[0] : x[0]);
|
|
}
|
|
else if (!parity(x, 0)) {
|
|
set_conflict(x, ~x[1]);
|
|
}
|
|
return inconsistent() ? l_false : l_true;
|
|
}
|
|
|
|
// ---------------------------
|
|
// conflict resolution
|
|
|
|
void ba_solver::normalize_active_coeffs() {
|
|
reset_active_var_set();
|
|
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;
|
|
}
|
|
}
|
|
m_active_vars.shrink(j);
|
|
}
|
|
|
|
void ba_solver::inc_coeff(literal l, unsigned offset) {
|
|
SASSERT(offset > 0);
|
|
bool_var v = l.var();
|
|
SASSERT(v != null_bool_var);
|
|
m_coeffs.reserve(v + 1, 0);
|
|
|
|
int64 coeff0 = m_coeffs[v];
|
|
if (coeff0 == 0) {
|
|
m_active_vars.push_back(v);
|
|
}
|
|
|
|
int64 loffset = static_cast<int64>(offset);
|
|
int64 inc = l.sign() ? -loffset : loffset;
|
|
int64 coeff1 = inc + coeff0;
|
|
m_coeffs[v] = coeff1;
|
|
if (coeff1 > INT_MAX || coeff1 < INT_MIN) {
|
|
m_overflow = true;
|
|
return;
|
|
}
|
|
|
|
if (coeff0 > 0 && inc < 0) {
|
|
inc_bound(std::max(0LL, coeff1) - coeff0);
|
|
}
|
|
else if (coeff0 < 0 && inc > 0) {
|
|
inc_bound(coeff0 - std::min(0LL, coeff1));
|
|
}
|
|
int64 lbound = static_cast<int64>(m_bound);
|
|
|
|
// reduce coefficient to be no larger than bound.
|
|
if (coeff1 > lbound) {
|
|
m_coeffs[v] = lbound;
|
|
}
|
|
else if (coeff1 < 0 && -coeff1 > lbound) {
|
|
m_coeffs[v] = -lbound;
|
|
}
|
|
}
|
|
|
|
int64 ba_solver::get_coeff(bool_var v) const {
|
|
return m_coeffs.get(v, 0);
|
|
}
|
|
|
|
unsigned ba_solver::get_abs_coeff(bool_var v) const {
|
|
int64 c = get_coeff(v);
|
|
if (c < INT_MIN+1 || c > UINT_MAX) {
|
|
m_overflow = true;
|
|
return UINT_MAX;
|
|
}
|
|
return static_cast<unsigned>(abs(c));
|
|
}
|
|
|
|
int ba_solver::get_int_coeff(bool_var v) const {
|
|
int64 c = m_coeffs.get(v, 0);
|
|
if (c < INT_MIN || c > INT_MAX) {
|
|
m_overflow = true;
|
|
return 0;
|
|
}
|
|
return static_cast<int>(c);
|
|
}
|
|
|
|
void ba_solver::inc_bound(int64 i) {
|
|
if (i < INT_MIN || i > INT_MAX) {
|
|
m_overflow = true;
|
|
return;
|
|
}
|
|
int64 new_bound = m_bound;
|
|
new_bound += i;
|
|
if (new_bound < 0) {
|
|
m_overflow = true;
|
|
}
|
|
else if (new_bound > UINT_MAX) {
|
|
m_overflow = true;
|
|
}
|
|
else {
|
|
m_bound = static_cast<unsigned>(new_bound);
|
|
}
|
|
}
|
|
|
|
void ba_solver::reset_coeffs() {
|
|
for (unsigned i = 0; i < m_active_vars.size(); ++i) {
|
|
m_coeffs[m_active_vars[i]] = 0;
|
|
}
|
|
m_active_vars.reset();
|
|
}
|
|
|
|
static bool _debug_conflict = false;
|
|
static literal _debug_consequent = null_literal;
|
|
static unsigned_vector _debug_var2position;
|
|
|
|
// #define DEBUG_CODE(_x_) _x_
|
|
|
|
lbool ba_solver::resolve_conflict() {
|
|
if (0 == m_num_propagations_since_pop) {
|
|
return l_undef;
|
|
}
|
|
m_overflow = false;
|
|
reset_coeffs();
|
|
m_num_marks = 0;
|
|
m_bound = 0;
|
|
literal consequent = s().m_not_l;
|
|
justification js = s().m_conflict;
|
|
TRACE("ba", tout << consequent << " " << js << "\n";);
|
|
m_conflict_lvl = s().get_max_lvl(consequent, js);
|
|
if (consequent != null_literal) {
|
|
consequent.neg();
|
|
process_antecedent(consequent, 1);
|
|
}
|
|
literal_vector const& lits = s().m_trail;
|
|
unsigned idx = lits.size() - 1;
|
|
unsigned offset = 1;
|
|
DEBUG_CODE(active2pb(m_A););
|
|
|
|
unsigned init_marks = m_num_marks;
|
|
|
|
do {
|
|
|
|
if (m_overflow || offset > (1 << 12)) {
|
|
IF_VERBOSE(20, verbose_stream() << "offset: " << offset << "\n";
|
|
active2pb(m_A);
|
|
display(verbose_stream(), m_A););
|
|
goto bail_out;
|
|
}
|
|
|
|
if (offset == 0) {
|
|
goto process_next_resolvent;
|
|
}
|
|
|
|
TRACE("sat_verbose", display(tout, m_A););
|
|
TRACE("ba", tout << "process consequent: " << consequent << " : "; s().display_justification(tout, js) << "\n";);
|
|
SASSERT(offset > 0);
|
|
|
|
DEBUG_CODE(justification2pb(js, consequent, offset, m_B););
|
|
|
|
if (_debug_conflict) {
|
|
IF_VERBOSE(0,
|
|
verbose_stream() << consequent << "\n";
|
|
s().display_justification(verbose_stream(), js);
|
|
verbose_stream() << "\n";);
|
|
_debug_consequent = consequent;
|
|
}
|
|
switch(js.get_kind()) {
|
|
case justification::NONE:
|
|
SASSERT (consequent != null_literal);
|
|
inc_bound(offset);
|
|
break;
|
|
case justification::BINARY:
|
|
inc_bound(offset);
|
|
SASSERT (consequent != null_literal);
|
|
inc_coeff(consequent, offset);
|
|
process_antecedent(js.get_literal(), offset);
|
|
break;
|
|
case justification::TERNARY:
|
|
inc_bound(offset);
|
|
SASSERT (consequent != null_literal);
|
|
inc_coeff(consequent, offset);
|
|
process_antecedent(js.get_literal1(), offset);
|
|
process_antecedent(js.get_literal2(), offset);
|
|
break;
|
|
case justification::CLAUSE: {
|
|
inc_bound(offset);
|
|
clause & c = s().get_clause(js);
|
|
unsigned i = 0;
|
|
if (consequent != null_literal) {
|
|
inc_coeff(consequent, offset);
|
|
if (c[0] == consequent) {
|
|
i = 1;
|
|
}
|
|
else {
|
|
SASSERT(c[1] == consequent);
|
|
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: {
|
|
constraint& cnstr = index2constraint(js.get_ext_justification_idx());
|
|
++m_stats.m_num_resolves;
|
|
switch (cnstr.tag()) {
|
|
case card_t: {
|
|
card& c = cnstr.to_card();
|
|
inc_bound(static_cast<int64>(offset) * c.k());
|
|
process_card(c, offset);
|
|
break;
|
|
}
|
|
case pb_t: {
|
|
pb& p = cnstr.to_pb();
|
|
m_lemma.reset();
|
|
inc_bound(offset);
|
|
inc_coeff(consequent, offset);
|
|
get_antecedents(consequent, p, m_lemma);
|
|
TRACE("ba", display(tout, p, true); tout << m_lemma << "\n";);
|
|
if (_debug_conflict) {
|
|
verbose_stream() << consequent << " ";
|
|
verbose_stream() << "antecedents: " << m_lemma << "\n";
|
|
}
|
|
for (literal l : m_lemma) process_antecedent(~l, offset);
|
|
break;
|
|
}
|
|
case xor_t: {
|
|
// jus.push_back(js);
|
|
m_lemma.reset();
|
|
inc_bound(offset);
|
|
inc_coeff(consequent, offset);
|
|
get_xor_antecedents(consequent, idx, js, m_lemma);
|
|
for (literal l : m_lemma) process_antecedent(~l, offset);
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
|
|
SASSERT(validate_lemma());
|
|
|
|
DEBUG_CODE(
|
|
active2pb(m_C);
|
|
VERIFY(validate_resolvent());
|
|
m_A = m_C;);
|
|
|
|
TRACE("ba", display(tout << "conflict: ", m_A););
|
|
|
|
cut();
|
|
|
|
process_next_resolvent:
|
|
|
|
// find the next marked variable in the assignment stack
|
|
//
|
|
bool_var v;
|
|
while (true) {
|
|
consequent = lits[idx];
|
|
v = consequent.var();
|
|
if (s().is_marked(v)) break;
|
|
if (idx == 0) {
|
|
IF_VERBOSE(2, verbose_stream() << "did not find marked literal\n";);
|
|
goto bail_out;
|
|
}
|
|
SASSERT(idx > 0);
|
|
--idx;
|
|
}
|
|
|
|
SASSERT(lvl(v) == m_conflict_lvl);
|
|
s().reset_mark(v);
|
|
--idx;
|
|
TRACE("sat_verbose", tout << "Unmark: v" << v << "\n";);
|
|
--m_num_marks;
|
|
js = s().m_justification[v];
|
|
offset = get_abs_coeff(v);
|
|
if (offset > m_bound) {
|
|
int64 bound64 = static_cast<int64>(m_bound);
|
|
m_coeffs[v] = (get_coeff(v) < 0) ? -bound64 : bound64;
|
|
offset = m_bound;
|
|
DEBUG_CODE(active2pb(m_A););
|
|
}
|
|
SASSERT(value(consequent) == l_true);
|
|
}
|
|
while (m_num_marks > 0);
|
|
|
|
DEBUG_CODE(for (bool_var i = 0; i < static_cast<bool_var>(s().num_vars()); ++i) SASSERT(!s().is_marked(i)););
|
|
SASSERT(validate_lemma());
|
|
|
|
normalize_active_coeffs();
|
|
|
|
if (!create_asserting_lemma()) {
|
|
goto bail_out;
|
|
}
|
|
|
|
DEBUG_CODE(VERIFY(validate_conflict(m_lemma, m_A)););
|
|
|
|
TRACE("ba", tout << m_lemma << "\n";);
|
|
|
|
if (get_config().m_drat) {
|
|
svector<drat::premise> ps; // TBD fill in
|
|
drat_add(m_lemma, ps);
|
|
}
|
|
|
|
s().m_lemma.reset();
|
|
s().m_lemma.append(m_lemma);
|
|
for (unsigned i = 1; i < m_lemma.size(); ++i) {
|
|
CTRACE("ba", s().is_marked(m_lemma[i].var()), tout << "marked: " << m_lemma[i] << "\n";);
|
|
s().mark(m_lemma[i].var());
|
|
}
|
|
|
|
return l_true;
|
|
|
|
bail_out:
|
|
|
|
m_overflow = false;
|
|
|
|
while (m_num_marks > 0 && idx >= 0) {
|
|
bool_var v = lits[idx].var();
|
|
if (s().is_marked(v)) {
|
|
s().reset_mark(v);
|
|
--m_num_marks;
|
|
}
|
|
if (idx == 0 && !_debug_conflict) {
|
|
_debug_conflict = true;
|
|
_debug_var2position.reserve(s().num_vars());
|
|
for (unsigned i = 0; i < lits.size(); ++i) {
|
|
_debug_var2position[lits[i].var()] = i;
|
|
}
|
|
IF_VERBOSE(0,
|
|
active2pb(m_A);
|
|
uint64 c = 0;
|
|
for (uint64 c1 : m_A.m_coeffs) c += c1;
|
|
verbose_stream() << "sum of coefficients: " << c << "\n";
|
|
display(verbose_stream(), m_A, true);
|
|
verbose_stream() << "conflicting literal: " << s().m_not_l << "\n";);
|
|
|
|
for (literal l : lits) {
|
|
if (s().is_marked(l.var())) {
|
|
IF_VERBOSE(0, verbose_stream() << "missing mark: " << l << "\n";);
|
|
s().reset_mark(l.var());
|
|
}
|
|
}
|
|
m_num_marks = 0;
|
|
resolve_conflict();
|
|
}
|
|
--idx;
|
|
}
|
|
return l_undef;
|
|
}
|
|
|
|
bool ba_solver::create_asserting_lemma() {
|
|
bool adjusted = false;
|
|
|
|
adjust_conflict_level:
|
|
int64 bound64 = m_bound;
|
|
int64 slack = -bound64;
|
|
for (bool_var v : m_active_vars) {
|
|
slack += get_abs_coeff(v);
|
|
}
|
|
m_lemma.reset();
|
|
m_lemma.push_back(null_literal);
|
|
unsigned num_skipped = 0;
|
|
int64 asserting_coeff = 0;
|
|
for (unsigned i = 0; 0 <= slack && i < m_active_vars.size(); ++i) {
|
|
bool_var v = m_active_vars[i];
|
|
int64 coeff = get_coeff(v);
|
|
lbool val = value(v);
|
|
bool is_true = val == l_true;
|
|
bool append = coeff != 0 && val != l_undef && (coeff < 0 == is_true);
|
|
if (append) {
|
|
literal lit(v, !is_true);
|
|
unsigned acoeff = get_abs_coeff(v);
|
|
if (lvl(lit) == m_conflict_lvl) {
|
|
if (m_lemma[0] == null_literal) {
|
|
asserting_coeff = abs(coeff);
|
|
slack -= asserting_coeff;
|
|
m_lemma[0] = ~lit;
|
|
}
|
|
else {
|
|
++num_skipped;
|
|
if (asserting_coeff < abs(coeff)) {
|
|
m_lemma[0] = ~lit;
|
|
slack -= (abs(coeff) - asserting_coeff);
|
|
asserting_coeff = abs(coeff);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
slack -= abs(coeff);
|
|
m_lemma.push_back(~lit);
|
|
}
|
|
}
|
|
}
|
|
if (slack >= 0) {
|
|
IF_VERBOSE(20, verbose_stream() << "(sat.card slack: " << slack << " skipped: " << num_skipped << ")\n";);
|
|
return false;
|
|
}
|
|
if (m_overflow) {
|
|
return false;
|
|
}
|
|
if (m_lemma[0] == null_literal) {
|
|
if (m_lemma.size() == 1) {
|
|
s().set_conflict(justification());
|
|
return false;
|
|
}
|
|
return false;
|
|
unsigned old_level = m_conflict_lvl;
|
|
m_conflict_lvl = 0;
|
|
for (unsigned i = 1; i < m_lemma.size(); ++i) {
|
|
m_conflict_lvl = std::max(m_conflict_lvl, lvl(m_lemma[i]));
|
|
}
|
|
IF_VERBOSE(1, verbose_stream() << "(sat.backjump :new-level " << m_conflict_lvl << " :old-level " << old_level << ")\n";);
|
|
adjusted = true;
|
|
goto adjust_conflict_level;
|
|
}
|
|
if (!adjusted) {
|
|
active2card();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
\brief compute a cut for current resolvent.
|
|
*/
|
|
|
|
void ba_solver::cut() {
|
|
|
|
// bypass cut if there is a unit coefficient
|
|
for (bool_var v : m_active_vars) {
|
|
if (1 == get_abs_coeff(v)) return;
|
|
}
|
|
|
|
unsigned g = 0;
|
|
|
|
for (unsigned i = 0; g != 1 && i < m_active_vars.size(); ++i) {
|
|
bool_var v = m_active_vars[i];
|
|
unsigned coeff = get_abs_coeff(v);
|
|
if (coeff == 0) {
|
|
continue;
|
|
}
|
|
if (m_bound < coeff) {
|
|
int64 bound64 = m_bound;
|
|
if (get_coeff(v) > 0) {
|
|
m_coeffs[v] = bound64;
|
|
}
|
|
else {
|
|
m_coeffs[v] = -bound64;
|
|
}
|
|
coeff = m_bound;
|
|
}
|
|
SASSERT(0 < coeff && coeff <= m_bound);
|
|
if (g == 0) {
|
|
g = coeff;
|
|
}
|
|
else {
|
|
g = u_gcd(g, coeff);
|
|
}
|
|
}
|
|
|
|
if (g >= 2) {
|
|
normalize_active_coeffs();
|
|
for (bool_var v : m_active_vars) {
|
|
m_coeffs[v] /= static_cast<int>(g);
|
|
}
|
|
m_bound = (m_bound + g - 1) / g;
|
|
++m_stats.m_num_cut;
|
|
}
|
|
}
|
|
|
|
void ba_solver::process_card(card& c, unsigned offset) {
|
|
literal lit = c.lit();
|
|
SASSERT(c.k() <= c.size());
|
|
SASSERT(lit == null_literal || value(lit) == l_true);
|
|
SASSERT(0 < offset);
|
|
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 (lit != null_literal) {
|
|
uint64 offset1 = static_cast<uint64>(offset) * c.k();
|
|
if (offset1 > UINT_MAX) {
|
|
m_overflow = true;
|
|
}
|
|
else {
|
|
process_antecedent(~lit, static_cast<unsigned>(offset1));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ba_solver::process_antecedent(literal l, unsigned offset) {
|
|
SASSERT(value(l) == l_false);
|
|
bool_var v = l.var();
|
|
unsigned level = lvl(v);
|
|
|
|
if (level > 0 && !s().is_marked(v) && level == m_conflict_lvl) {
|
|
s().mark(v);
|
|
TRACE("sat_verbose", tout << "Mark: v" << v << "\n";);
|
|
++m_num_marks;
|
|
if (_debug_conflict && _debug_consequent != null_literal && _debug_var2position[_debug_consequent.var()] < _debug_var2position[l.var()]) {
|
|
IF_VERBOSE(0, verbose_stream() << "antecedent " << l << " is above consequent in stack\n";);
|
|
}
|
|
}
|
|
inc_coeff(l, offset);
|
|
}
|
|
|
|
literal ba_solver::get_asserting_literal(literal p) {
|
|
if (get_abs_coeff(p.var()) != 0) {
|
|
return p;
|
|
}
|
|
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 (value(lit) == l_false && lvl(lit) > level) {
|
|
p = lit;
|
|
level = lvl(lit);
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
ba_solver::ba_solver(): m_solver(0), m_lookahead(0), m_unit_walk(0), m_constraint_id(0), m_ba(*this), m_sort(m_ba) {
|
|
TRACE("ba", tout << this << "\n";);
|
|
}
|
|
|
|
ba_solver::~ba_solver() {
|
|
m_stats.reset();
|
|
for (constraint* c : m_constraints) {
|
|
m_allocator.deallocate(c->obj_size(), c);
|
|
}
|
|
for (constraint* c : m_learned) {
|
|
m_allocator.deallocate(c->obj_size(), c);
|
|
}
|
|
}
|
|
|
|
void ba_solver::add_at_least(bool_var v, literal_vector const& lits, unsigned k) {
|
|
literal lit = v == null_bool_var ? null_literal : literal(v, false);
|
|
add_at_least(lit, lits, k, false);
|
|
}
|
|
|
|
ba_solver::constraint* ba_solver::add_at_least(literal lit, literal_vector const& lits, unsigned k, bool learned) {
|
|
if (k == 1 && lit == null_literal) {
|
|
literal_vector _lits(lits);
|
|
s().mk_clause(_lits.size(), _lits.c_ptr(), learned);
|
|
return 0;
|
|
}
|
|
void * mem = m_allocator.allocate(card::get_obj_size(lits.size()));
|
|
card* c = new (mem) card(next_id(), lit, lits, k);
|
|
c->set_learned(learned);
|
|
add_constraint(c);
|
|
return c;
|
|
}
|
|
|
|
void ba_solver::add_constraint(constraint* c) {
|
|
literal_vector lits(c->literals());
|
|
if (c->learned()) {
|
|
m_learned.push_back(c);
|
|
}
|
|
else {
|
|
SASSERT(!m_solver || s().at_base_lvl());
|
|
m_constraints.push_back(c);
|
|
}
|
|
literal lit = c->lit();
|
|
if (c->learned() && !s().at_base_lvl()) {
|
|
SASSERT(lit == null_literal);
|
|
// gets initialized after backjump.
|
|
m_constraint_to_reinit.push_back(c);
|
|
}
|
|
else if (lit == null_literal) {
|
|
init_watch(*c);
|
|
}
|
|
else {
|
|
if (m_solver) m_solver->set_external(lit.var());
|
|
watch_literal(lit, *c);
|
|
watch_literal(~lit, *c);
|
|
}
|
|
SASSERT(c->well_formed());
|
|
}
|
|
|
|
|
|
bool ba_solver::init_watch(constraint& c) {
|
|
if (inconsistent()) return false;
|
|
switch (c.tag()) {
|
|
case card_t: return init_watch(c.to_card());
|
|
case pb_t: return init_watch(c.to_pb());
|
|
case xor_t: return init_watch(c.to_xor());
|
|
}
|
|
UNREACHABLE();
|
|
return false;
|
|
}
|
|
|
|
lbool ba_solver::add_assign(constraint& c, literal l) {
|
|
switch (c.tag()) {
|
|
case card_t: return add_assign(c.to_card(), l);
|
|
case pb_t: return add_assign(c.to_pb(), l);
|
|
case xor_t: return add_assign(c.to_xor(), l);
|
|
}
|
|
UNREACHABLE();
|
|
return l_undef;
|
|
}
|
|
|
|
ba_solver::constraint* ba_solver::add_pb_ge(literal lit, svector<wliteral> const& wlits, unsigned k, bool learned) {
|
|
bool units = true;
|
|
for (wliteral wl : wlits) units &= wl.first == 1;
|
|
if (k == 0 && lit == null_literal) {
|
|
return 0;
|
|
}
|
|
if (units || k == 1) {
|
|
literal_vector lits;
|
|
for (wliteral wl : wlits) lits.push_back(wl.second);
|
|
return add_at_least(lit, lits, k, learned);
|
|
}
|
|
void * mem = m_allocator.allocate(pb::get_obj_size(wlits.size()));
|
|
pb* p = new (mem) pb(next_id(), lit, wlits, k);
|
|
p->set_learned(learned);
|
|
add_constraint(p);
|
|
return p;
|
|
}
|
|
|
|
void ba_solver::add_pb_ge(bool_var v, svector<wliteral> const& wlits, unsigned k) {
|
|
literal lit = v == null_bool_var ? null_literal : literal(v, false);
|
|
add_pb_ge(lit, wlits, k, false);
|
|
}
|
|
|
|
void ba_solver::add_xor(bool_var v, literal_vector const& lits) {
|
|
add_xor(literal(v, false), lits, false);
|
|
}
|
|
|
|
ba_solver::constraint* ba_solver::add_xor(literal lit, literal_vector const& lits, bool learned) {
|
|
void * mem = m_allocator.allocate(xor::get_obj_size(lits.size()));
|
|
xor* x = new (mem) xor(next_id(), lit, lits);
|
|
x->set_learned(learned);
|
|
add_constraint(x);
|
|
for (literal l : lits) s().set_external(l.var()); // TBD: determine if goal2sat does this.
|
|
return x;
|
|
}
|
|
|
|
/*
|
|
\brief return true to keep watching literal.
|
|
*/
|
|
bool ba_solver::propagate(literal l, ext_constraint_idx idx) {
|
|
SASSERT(value(l) == l_true);
|
|
constraint& c = index2constraint(idx);
|
|
if (c.lit() != null_literal && l.var() == c.lit().var()) {
|
|
init_watch(c);
|
|
return true;
|
|
}
|
|
else if (c.lit() != null_literal && value(c.lit()) != l_true) {
|
|
return true;
|
|
}
|
|
else {
|
|
return l_undef != add_assign(c, ~l);
|
|
}
|
|
}
|
|
|
|
double ba_solver::get_reward(card const& c, literal_occs_fun& literal_occs) const {
|
|
unsigned k = c.k(), slack = 0;
|
|
bool do_add = get_config().m_lookahead_reward == heule_schur_reward;
|
|
double to_add = do_add ? 0: 1;
|
|
for (literal l : c) {
|
|
switch (value(l)) {
|
|
case l_true: --k; if (k == 0) return 0;
|
|
case l_undef:
|
|
if (do_add) to_add += literal_occs(l);
|
|
++slack; break;
|
|
case l_false: break;
|
|
}
|
|
}
|
|
if (k >= slack) return 1;
|
|
return pow(0.5, slack - k + 1) * to_add;
|
|
}
|
|
|
|
double ba_solver::get_reward(pb const& c, literal_occs_fun& occs) const {
|
|
unsigned k = c.k(), slack = 0;
|
|
bool do_add = get_config().m_lookahead_reward == heule_schur_reward;
|
|
double to_add = do_add ? 0 : 1;
|
|
double undefs = 0;
|
|
for (wliteral wl : c) {
|
|
literal l = wl.second;
|
|
unsigned w = wl.first;
|
|
switch (value(l)) {
|
|
case l_true: if (k <= w) return 0;
|
|
case l_undef:
|
|
if (do_add) to_add += occs(l);
|
|
++undefs;
|
|
slack += w;
|
|
break; // TBD multiplier factor on this
|
|
case l_false: break;
|
|
}
|
|
}
|
|
if (k >= slack || 0 == undefs) return 0;
|
|
double avg = slack / undefs;
|
|
return pow(0.5, (slack - k + 1)/avg) * to_add;
|
|
}
|
|
|
|
double ba_solver::get_reward(literal l, ext_justification_idx idx, literal_occs_fun& occs) const {
|
|
constraint const& c = index2constraint(idx);
|
|
unsigned sz = c.size();
|
|
switch (c.tag()) {
|
|
case card_t: return get_reward(c.to_card(), occs);
|
|
case pb_t: return get_reward(c.to_pb(), occs);
|
|
case xor_t: return 0;
|
|
default: UNREACHABLE(); return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ba_solver::ensure_parity_size(bool_var v) {
|
|
if (m_parity_marks.size() <= static_cast<unsigned>(v)) {
|
|
m_parity_marks.resize(static_cast<unsigned>(v) + 1, 0);
|
|
}
|
|
}
|
|
|
|
unsigned ba_solver::get_parity(bool_var v) {
|
|
return m_parity_marks.get(v, 0);
|
|
}
|
|
|
|
void ba_solver::inc_parity(bool_var v) {
|
|
ensure_parity_size(v);
|
|
m_parity_marks[v]++;
|
|
}
|
|
|
|
void ba_solver::reset_parity(bool_var v) {
|
|
ensure_parity_size(v);
|
|
m_parity_marks[v] = 0;
|
|
}
|
|
|
|
/**
|
|
\brief perform parity resolution on xor premises.
|
|
The idea is to collect premises based on xor resolvents.
|
|
Variables that are repeated an even number of times cancel out.
|
|
*/
|
|
void ba_solver::get_xor_antecedents(literal l, unsigned index, justification js, literal_vector& r) {
|
|
unsigned level = lvl(l);
|
|
bool_var v = l.var();
|
|
SASSERT(js.get_kind() == justification::EXT_JUSTIFICATION);
|
|
TRACE("ba", tout << l << ": " << js << "\n"; tout << s().m_trail << "\n";);
|
|
|
|
unsigned num_marks = 0;
|
|
unsigned count = 0;
|
|
while (true) {
|
|
++count;
|
|
if (js.get_kind() == justification::EXT_JUSTIFICATION) {
|
|
constraint& c = index2constraint(js.get_ext_justification_idx());
|
|
if (!c.is_xor()) {
|
|
r.push_back(l);
|
|
}
|
|
else {
|
|
xor& x = c.to_xor();
|
|
if (x.lit() != null_literal && lvl(x.lit()) > 0) r.push_back(x.lit());
|
|
if (x[1].var() == l.var()) {
|
|
x.swap(0, 1);
|
|
}
|
|
SASSERT(x[0].var() == l.var());
|
|
for (unsigned i = 1; i < x.size(); ++i) {
|
|
literal lit(value(x[i]) == l_true ? x[i] : ~x[i]);
|
|
inc_parity(lit.var());
|
|
if (lvl(lit) == level) {
|
|
++num_marks;
|
|
}
|
|
else {
|
|
m_parity_trail.push_back(lit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
r.push_back(l);
|
|
}
|
|
while (num_marks > 0) {
|
|
l = s().m_trail[index];
|
|
v = l.var();
|
|
unsigned n = get_parity(v);
|
|
if (n > 0) {
|
|
reset_parity(v);
|
|
if (n % 2 == 1) {
|
|
break;
|
|
}
|
|
--num_marks;
|
|
}
|
|
--index;
|
|
}
|
|
if (num_marks == 0) {
|
|
break;
|
|
}
|
|
--index;
|
|
--num_marks;
|
|
js = s().m_justification[v];
|
|
}
|
|
|
|
// now walk the defined literals
|
|
|
|
for (unsigned i = 0; i < m_parity_trail.size(); ++i) {
|
|
literal lit = m_parity_trail[i];
|
|
if (get_parity(lit.var()) % 2 == 1) {
|
|
r.push_back(lit);
|
|
}
|
|
else {
|
|
// IF_VERBOSE(2, verbose_stream() << "skip even parity: " << lit << "\n";);
|
|
}
|
|
reset_parity(lit.var());
|
|
}
|
|
m_parity_trail.reset();
|
|
TRACE("ba", tout << r << "\n";);
|
|
}
|
|
|
|
/**
|
|
\brief retrieve a sufficient set of literals from p that imply l.
|
|
|
|
Find partition:
|
|
|
|
- Ax + coeff*l + B*y >= k
|
|
- all literals in x are false.
|
|
- B < k
|
|
|
|
Then x is an explanation for l
|
|
|
|
*/
|
|
|
|
bool ba_solver::assigned_above(literal above, literal below) {
|
|
unsigned l = lvl(above);
|
|
SASSERT(l == lvl(below));
|
|
if (l == 0) return false;
|
|
unsigned start = s().m_scopes[l-1].m_trail_lim;
|
|
literal_vector const& lits = s().m_trail;
|
|
|
|
#if 0
|
|
IF_VERBOSE(10, verbose_stream() << "level " << l << " scope level " << s().scope_lvl() << " tail lim start: "
|
|
<< start << " size of lits: " << lits.size() << " num scopes " << s().m_scopes.size() << "\n";);
|
|
#endif
|
|
|
|
for (unsigned sz = lits.size(); sz-- > start; ) {
|
|
if (lits[sz] == above) return true;
|
|
if (lits[sz] == below) return false;
|
|
}
|
|
UNREACHABLE();
|
|
return false;
|
|
}
|
|
|
|
void ba_solver::get_antecedents(literal l, pb const& p, literal_vector& r) {
|
|
TRACE("ba", display(tout << l << " level: " << s().scope_lvl() << " ", p, true););
|
|
SASSERT(p.lit() == null_literal || value(p.lit()) == l_true);
|
|
|
|
if (p.lit() != null_literal) {
|
|
r.push_back(p.lit());
|
|
}
|
|
|
|
unsigned k = p.k();
|
|
|
|
if (_debug_conflict) {
|
|
IF_VERBOSE(0, display(verbose_stream(), p, true);
|
|
verbose_stream() << "literal: " << l << " value: " << value(l) << " num-watch: " << p.num_watch() << " slack: " << p.slack() << "\n";);
|
|
}
|
|
|
|
if (value(l) == l_false) {
|
|
// The literal comes from a conflict.
|
|
// it is forced true, but assigned to false.
|
|
unsigned slack = 0;
|
|
for (wliteral wl : p) {
|
|
if (value(wl.second) != l_false) {
|
|
slack += wl.first;
|
|
}
|
|
}
|
|
SASSERT(slack < k);
|
|
for (wliteral wl : p) {
|
|
literal lit = wl.second;
|
|
if (lit != l && value(lit) == l_false) {
|
|
unsigned w = wl.first;
|
|
if (slack + w < k) {
|
|
slack += w;
|
|
}
|
|
else {
|
|
r.push_back(~lit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// comes from a unit propagation
|
|
SASSERT(value(l) == l_true);
|
|
unsigned coeff = 0, j = 0;
|
|
for (; j < p.size(); ++j) {
|
|
if (p[j].second == l) {
|
|
coeff = p[j].first;
|
|
break;
|
|
}
|
|
}
|
|
|
|
++j;
|
|
if (j < p.num_watch()) {
|
|
j = p.num_watch();
|
|
}
|
|
CTRACE("ba", coeff == 0, display(tout << l << " coeff: " << coeff << "\n", p, true););
|
|
|
|
if (_debug_conflict) {
|
|
std::cout << "coeff " << coeff << "\n";
|
|
}
|
|
|
|
SASSERT(coeff > 0);
|
|
unsigned slack = p.max_sum() - coeff;
|
|
|
|
// we need antecedents to be deeper than alit.
|
|
for (; j < p.size(); ++j) {
|
|
literal lit = p[j].second;
|
|
unsigned w = p[j].first;
|
|
if (l_false != value(lit)) {
|
|
// skip
|
|
}
|
|
else if (lvl(lit) > lvl(l)) {
|
|
// skip
|
|
}
|
|
else if (lvl(lit) == lvl(l) && assigned_above(~lit, l)) {
|
|
// skip
|
|
}
|
|
else if (slack + w < k) {
|
|
slack += w;
|
|
}
|
|
else {
|
|
r.push_back(~lit);
|
|
}
|
|
}
|
|
}
|
|
SASSERT(validate_unit_propagation(p, r, l));
|
|
}
|
|
|
|
bool ba_solver::is_extended_binary(ext_justification_idx idx, literal_vector & r) {
|
|
constraint const& c = index2constraint(idx);
|
|
switch (c.tag()) {
|
|
case card_t: {
|
|
card const& ca = c.to_card();
|
|
if (ca.size() == ca.k() + 1 && ca.lit() == null_literal) {
|
|
r.reset();
|
|
for (literal l : ca) r.push_back(l);
|
|
return true;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void ba_solver::simplify(xor& x) {
|
|
// no-op
|
|
}
|
|
|
|
void ba_solver::get_antecedents(literal l, card const& c, literal_vector& r) {
|
|
DEBUG_CODE(
|
|
bool found = false;
|
|
for (unsigned i = 0; !found && i < c.k(); ++i) {
|
|
found = c[i] == l;
|
|
}
|
|
SASSERT(found););
|
|
|
|
if (c.lit() != null_literal) r.push_back(c.lit());
|
|
SASSERT(c.lit() == null_literal || value(c.lit()) == l_true);
|
|
for (unsigned i = c.k(); i < c.size(); ++i) {
|
|
SASSERT(value(c[i]) == l_false);
|
|
r.push_back(~c[i]);
|
|
}
|
|
}
|
|
|
|
void ba_solver::get_antecedents(literal l, xor const& x, literal_vector& r) {
|
|
if (x.lit() != null_literal) r.push_back(x.lit());
|
|
// TRACE("ba", display(tout << l << " ", x, true););
|
|
SASSERT(x.lit() == null_literal || value(x.lit()) == l_true);
|
|
SASSERT(x[0].var() == l.var() || x[1].var() == l.var());
|
|
if (x[0].var() == l.var()) {
|
|
SASSERT(value(x[1]) != l_undef);
|
|
r.push_back(value(x[1]) == l_true ? x[1] : ~x[1]);
|
|
}
|
|
else {
|
|
SASSERT(value(x[0]) != l_undef);
|
|
r.push_back(value(x[0]) == l_true ? x[0] : ~x[0]);
|
|
}
|
|
for (unsigned i = 2; i < x.size(); ++i) {
|
|
SASSERT(value(x[i]) != l_undef);
|
|
r.push_back(value(x[i]) == l_true ? x[i] : ~x[i]);
|
|
}
|
|
}
|
|
|
|
// ----------------------------
|
|
// constraint generic methods
|
|
|
|
void ba_solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) {
|
|
get_antecedents(l, index2constraint(idx), r);
|
|
}
|
|
|
|
bool ba_solver::is_watched(literal lit, constraint const& c) const {
|
|
return get_wlist(~lit).contains(watched(c.index()));
|
|
}
|
|
|
|
void ba_solver::unwatch_literal(literal lit, constraint& c) {
|
|
get_wlist(~lit).erase(watched(c.index()));
|
|
}
|
|
|
|
void ba_solver::watch_literal(literal lit, constraint& c) {
|
|
get_wlist(~lit).push_back(watched(c.index()));
|
|
}
|
|
|
|
void ba_solver::get_antecedents(literal l, constraint const& c, literal_vector& r) {
|
|
switch (c.tag()) {
|
|
case card_t: get_antecedents(l, c.to_card(), r); break;
|
|
case pb_t: get_antecedents(l, c.to_pb(), r); break;
|
|
case xor_t: get_antecedents(l, c.to_xor(), r); break;
|
|
default: UNREACHABLE(); break;
|
|
}
|
|
}
|
|
|
|
void ba_solver::nullify_tracking_literal(constraint& c) {
|
|
if (c.lit() != null_literal) {
|
|
unwatch_literal(c.lit(), c);
|
|
unwatch_literal(~c.lit(), c);
|
|
c.nullify_literal();
|
|
}
|
|
}
|
|
|
|
void ba_solver::clear_watch(constraint& c) {
|
|
switch (c.tag()) {
|
|
case card_t:
|
|
clear_watch(c.to_card());
|
|
break;
|
|
case pb_t:
|
|
clear_watch(c.to_pb());
|
|
break;
|
|
case xor_t:
|
|
clear_watch(c.to_xor());
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void ba_solver::remove_constraint(constraint& c, char const* reason) {
|
|
IF_VERBOSE(21, display(verbose_stream() << "remove " << reason << " ", c, true););
|
|
nullify_tracking_literal(c);
|
|
clear_watch(c);
|
|
c.set_removed();
|
|
m_constraint_removed = true;
|
|
}
|
|
|
|
// --------------------------------
|
|
// validation
|
|
|
|
bool ba_solver::validate_unit_propagation(constraint const& c, literal l) const {
|
|
return true;
|
|
switch (c.tag()) {
|
|
case card_t: return validate_unit_propagation(c.to_card(), l);
|
|
case pb_t: return validate_unit_propagation(c.to_pb(), l);
|
|
case xor_t: return true;
|
|
default: UNREACHABLE(); break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ba_solver::validate_conflict(constraint const& c) const {
|
|
return eval(c) == l_false;
|
|
}
|
|
|
|
lbool ba_solver::eval(constraint const& c) const {
|
|
lbool v1 = c.lit() == null_literal ? l_true : value(c.lit());
|
|
switch (c.tag()) {
|
|
case card_t: return eval(v1, eval(c.to_card()));
|
|
case pb_t: return eval(v1, eval(c.to_pb()));
|
|
case xor_t: return eval(v1, eval(c.to_xor()));
|
|
default: UNREACHABLE(); break;
|
|
}
|
|
return l_undef;
|
|
}
|
|
|
|
lbool ba_solver::eval(lbool a, lbool b) const {
|
|
if (a == l_undef || b == l_undef) return l_undef;
|
|
return (a == b) ? l_true : l_false;
|
|
}
|
|
|
|
lbool ba_solver::eval(card const& c) const {
|
|
unsigned trues = 0, undefs = 0;
|
|
for (literal l : c) {
|
|
switch (value(l)) {
|
|
case l_true: trues++; break;
|
|
case l_undef: undefs++; break;
|
|
default: break;
|
|
}
|
|
}
|
|
if (trues + undefs < c.k()) return l_false;
|
|
if (trues >= c.k()) return l_true;
|
|
return l_undef;
|
|
}
|
|
|
|
lbool ba_solver::eval(pb const& p) const {
|
|
unsigned trues = 0, undefs = 0;
|
|
for (wliteral wl : p) {
|
|
switch (value(wl.second)) {
|
|
case l_true: trues += wl.first; break;
|
|
case l_undef: undefs += wl.first; break;
|
|
default: break;
|
|
}
|
|
}
|
|
if (trues + undefs < p.k()) return l_false;
|
|
if (trues >= p.k()) return l_true;
|
|
return l_undef;
|
|
}
|
|
|
|
lbool ba_solver::eval(xor const& x) const {
|
|
bool odd = false;
|
|
|
|
for (auto l : x) {
|
|
switch (value(l)) {
|
|
case l_true: odd = !odd; break;
|
|
case l_false: break;
|
|
default: return l_undef;
|
|
}
|
|
}
|
|
return odd ? l_true : l_false;
|
|
}
|
|
|
|
bool ba_solver::validate() {
|
|
if (!validate_watch_literals()) {
|
|
return false;
|
|
}
|
|
for (constraint* c : m_constraints) {
|
|
if (!validate_watched_constraint(*c))
|
|
return false;
|
|
}
|
|
for (constraint* c : m_learned) {
|
|
if (!validate_watched_constraint(*c))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ba_solver::validate_watch_literals() const {
|
|
for (unsigned v = 0; v < s().num_vars(); ++v) {
|
|
literal lit(v, false);
|
|
if (lvl(lit) == 0) continue;
|
|
if (!validate_watch_literal(lit)) return false;
|
|
if (!validate_watch_literal(~lit)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ba_solver::validate_watch_literal(literal lit) const {
|
|
if (lvl(lit) == 0) return true;
|
|
for (auto const & w : get_wlist(lit)) {
|
|
if (w.get_kind() == watched::EXT_CONSTRAINT) {
|
|
constraint const& c = index2constraint(w.get_ext_constraint_idx());
|
|
if (!c.is_watching(~lit) && lit.var() != c.lit().var()) {
|
|
IF_VERBOSE(0, display(verbose_stream() << lit << " " << lvl(lit) << " is not watched in " << c << "\n", c, true););
|
|
UNREACHABLE();
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ba_solver::validate_watched_constraint(constraint const& c) const {
|
|
if (c.is_pb() && !validate_watch(c.to_pb(), null_literal)) {
|
|
return false;
|
|
}
|
|
if (c.lit() != null_literal && value(c.lit()) != l_true) return true;
|
|
SASSERT(c.lit() == null_literal || lvl(c.lit()) == 0 || (is_watched(c.lit(), c) && is_watched(~c.lit(), c)));
|
|
if (eval(c) == l_true) {
|
|
return true;
|
|
}
|
|
literal_vector lits(c.literals());
|
|
for (literal l : lits) {
|
|
if (lvl(l) == 0) continue;
|
|
bool found = is_watched(l, c);
|
|
if (found != c.is_watching(l)) {
|
|
|
|
IF_VERBOSE(0,
|
|
verbose_stream() << "Discrepancy of watched literal: " << l << " id: " << c.id()
|
|
<< " clause: " << c << (found?" is watched, but shouldn't be":" not watched, but should be") << "\n";
|
|
display_watch_list(verbose_stream() << l << ": ", s().m_cls_allocator, get_wlist(l)) << "\n";
|
|
display_watch_list(verbose_stream() << ~l << ": ", s().m_cls_allocator, get_wlist(~l)) << "\n";
|
|
verbose_stream() << "value: " << value(l) << " level: " << lvl(l) << "\n";
|
|
display(verbose_stream(), c, true);
|
|
if (c.lit() != null_literal) verbose_stream() << value(c.lit()) << "\n";);
|
|
|
|
UNREACHABLE();
|
|
exit(1);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ba_solver::validate_watch(pb const& p, literal alit) const {
|
|
for (unsigned i = 0; i < p.size(); ++i) {
|
|
literal l = p[i].second;
|
|
if (l != alit && lvl(l) != 0 && is_watched(l, p) != i < p.num_watch()) {
|
|
IF_VERBOSE(0, display(verbose_stream(), p, true);
|
|
verbose_stream() << "literal " << l << " at position " << i << " " << is_watched(l, p) << "\n";);
|
|
UNREACHABLE();
|
|
return false;
|
|
}
|
|
}
|
|
unsigned slack = 0;
|
|
for (unsigned i = 0; i < p.num_watch(); ++i) {
|
|
slack += p[i].first;
|
|
}
|
|
if (slack != p.slack()) {
|
|
IF_VERBOSE(0, display(verbose_stream(), p, true););
|
|
UNREACHABLE();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/**
|
|
\brief Lex on (glue, size)
|
|
*/
|
|
struct constraint_glue_psm_lt {
|
|
bool operator()(ba_solver::constraint const * c1, ba_solver::constraint const * c2) const {
|
|
return
|
|
(c1->glue() < c2->glue()) ||
|
|
(c1->glue() == c2->glue() &&
|
|
(c1->psm() < c2->psm() ||
|
|
(c1->psm() == c2->psm() && c1->size() < c2->size())));
|
|
}
|
|
};
|
|
|
|
void ba_solver::update_psm(constraint& c) const {
|
|
unsigned r = 0;
|
|
switch (c.tag()) {
|
|
case card_t:
|
|
for (literal l : c.to_card()) {
|
|
if (s().m_phase[l.var()] == (l.sign() ? NEG_PHASE : POS_PHASE)) ++r;
|
|
}
|
|
break;
|
|
case pb_t:
|
|
for (wliteral l : c.to_pb()) {
|
|
if (s().m_phase[l.second.var()] == (l.second.sign() ? NEG_PHASE : POS_PHASE)) ++r;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
c.set_psm(r);
|
|
}
|
|
|
|
void ba_solver::gc() {
|
|
if (m_learned.size() >= 2 * m_constraints.size()) {
|
|
for (auto & c : m_learned) update_psm(*c);
|
|
std::stable_sort(m_learned.begin(), m_learned.end(), constraint_glue_psm_lt());
|
|
gc_half("glue-psm");
|
|
cleanup_constraints(m_learned, true);
|
|
}
|
|
}
|
|
|
|
void ba_solver::gc_half(char const* st_name) {
|
|
TRACE("ba", tout << "gc\n";);
|
|
unsigned sz = m_learned.size();
|
|
unsigned new_sz = sz/2;
|
|
unsigned removed = 0;
|
|
for (unsigned i = new_sz; i < sz; i++) {
|
|
constraint* c = m_learned[i];
|
|
if (!m_constraint_to_reinit.contains(c)) {
|
|
remove_constraint(*c, "gc");
|
|
++removed;
|
|
}
|
|
}
|
|
m_stats.m_num_gc += removed;
|
|
m_learned.shrink(new_sz);
|
|
IF_VERBOSE(2, verbose_stream() << "(sat-gc :strategy " << st_name << " :deleted " << removed << ")\n";);
|
|
|
|
}
|
|
|
|
lbool ba_solver::add_assign(card& c, literal alit) {
|
|
// literal is assigned to false.
|
|
unsigned sz = c.size();
|
|
unsigned bound = c.k();
|
|
TRACE("ba", tout << "assign: " << c.lit() << ": " << ~alit << "@" << lvl(~alit) << "\n";);
|
|
|
|
SASSERT(0 < bound && bound <= sz);
|
|
if (bound == sz) {
|
|
set_conflict(c, alit);
|
|
return l_false;
|
|
}
|
|
SASSERT(value(alit) == l_false);
|
|
SASSERT(c.lit() == null_literal || 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;
|
|
}
|
|
VERIFY(index <= bound);
|
|
VERIFY(c[index] == alit);
|
|
|
|
// find a literal to swap with:
|
|
for (unsigned i = bound + 1; i < sz; ++i) {
|
|
literal lit2 = c[i];
|
|
if (value(lit2) != l_false) {
|
|
c.swap(index, i);
|
|
watch_literal(lit2, c);
|
|
return l_undef;
|
|
}
|
|
}
|
|
|
|
// conflict
|
|
if (bound != index && value(c[bound]) == l_false) {
|
|
TRACE("ba", tout << "conflict " << c[bound] << " " << alit << "\n";);
|
|
set_conflict(c, alit);
|
|
return l_false;
|
|
}
|
|
|
|
// TRACE("ba", 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);
|
|
}
|
|
for (unsigned i = 0; i < bound; ++i) {
|
|
assign(c, c[i]);
|
|
}
|
|
|
|
if (c.learned() && c.glue() > 2) {
|
|
unsigned glue;
|
|
if (s().num_diff_false_levels_below(c.size(), c.begin(), c.glue()-1, glue)) {
|
|
c.set_glue(glue);
|
|
}
|
|
}
|
|
|
|
return inconsistent() ? l_false : l_true;
|
|
}
|
|
|
|
void ba_solver::asserted(literal l) {
|
|
}
|
|
|
|
|
|
check_result ba_solver::check() { return CR_DONE; }
|
|
|
|
void ba_solver::push() {
|
|
m_constraint_to_reinit_lim.push_back(m_constraint_to_reinit.size());
|
|
}
|
|
|
|
void ba_solver::pop(unsigned n) {
|
|
TRACE("sat_verbose", tout << "pop:" << n << "\n";);
|
|
unsigned new_lim = m_constraint_to_reinit_lim.size() - n;
|
|
m_constraint_to_reinit_last_sz = m_constraint_to_reinit_lim[new_lim];
|
|
m_constraint_to_reinit_lim.shrink(new_lim);
|
|
m_num_propagations_since_pop = 0;
|
|
}
|
|
|
|
void ba_solver::pop_reinit() {
|
|
unsigned sz = m_constraint_to_reinit_last_sz;
|
|
for (unsigned i = sz; i < m_constraint_to_reinit.size(); ++i) {
|
|
constraint* c = m_constraint_to_reinit[i];
|
|
if (!init_watch(*c) && !s().at_base_lvl()) {
|
|
m_constraint_to_reinit[sz++] = c;
|
|
}
|
|
}
|
|
m_constraint_to_reinit.shrink(sz);
|
|
}
|
|
|
|
|
|
void ba_solver::simplify(constraint& c) {
|
|
SASSERT(s().at_base_lvl());
|
|
switch (c.tag()) {
|
|
case card_t:
|
|
simplify(c.to_card());
|
|
break;
|
|
case pb_t:
|
|
simplify(c.to_pb());
|
|
break;
|
|
case xor_t:
|
|
simplify(c.to_xor());
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void ba_solver::simplify() {
|
|
if (!s().at_base_lvl()) s().pop_to_base_level();
|
|
unsigned trail_sz;
|
|
do {
|
|
trail_sz = s().init_trail_size();
|
|
m_simplify_change = false;
|
|
m_clause_removed = false;
|
|
m_constraint_removed = false;
|
|
for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) simplify(*m_constraints[i]);
|
|
for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) simplify(*m_learned[i]);
|
|
init_use_lists();
|
|
remove_unused_defs();
|
|
set_non_external();
|
|
if (get_config().m_elim_vars) elim_pure();
|
|
for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) subsumption(*m_constraints[i]);
|
|
for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) subsumption(*m_learned[i]);
|
|
cleanup_clauses();
|
|
cleanup_constraints();
|
|
}
|
|
while (m_simplify_change || trail_sz < s().init_trail_size());
|
|
|
|
IF_VERBOSE(1, verbose_stream() << "(ba.simplify"
|
|
<< " :vars " << s().num_vars() - trail_sz
|
|
<< " :constraints " << m_constraints.size()
|
|
<< " :lemmas " << m_learned.size()
|
|
<< " :subsumes " << m_stats.m_num_bin_subsumes
|
|
+ m_stats.m_num_clause_subsumes
|
|
+ m_stats.m_num_pb_subsumes
|
|
<< " :gc " << m_stats.m_num_gc
|
|
<< ")\n";);
|
|
|
|
// mutex_reduction();
|
|
// if (s().m_clauses.size() < 80000) lp_lookahead_reduction();
|
|
|
|
}
|
|
|
|
void ba_solver::mutex_reduction() {
|
|
literal_vector lits;
|
|
for (unsigned v = 0; v < s().num_vars(); ++v) {
|
|
lits.push_back(literal(v, false));
|
|
lits.push_back(literal(v, true));
|
|
}
|
|
vector<literal_vector> mutexes;
|
|
s().find_mutexes(lits, mutexes);
|
|
for (literal_vector& mux : mutexes) {
|
|
if (mux.size() > 2) {
|
|
IF_VERBOSE(1, verbose_stream() << "mux: " << mux << "\n";);
|
|
for (unsigned i = 0; i < mux.size(); ++i) mux[i].neg();
|
|
add_at_least(null_literal, mux, mux.size() - 1, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
// -------------------------
|
|
// sorting networks
|
|
literal ba_solver::ba_sort::mk_false() {
|
|
return ~mk_true();
|
|
}
|
|
|
|
literal ba_solver::ba_sort::mk_true() {
|
|
if (m_true == null_literal) {
|
|
bool_var v = s.s().mk_var(false, false);
|
|
m_true = literal(v, false);
|
|
s.s().mk_clause(1,&m_true);
|
|
}
|
|
VERIFY(m_true != null_literal);
|
|
return m_true;
|
|
}
|
|
|
|
literal ba_solver::ba_sort::mk_not(literal l) {
|
|
return ~l;
|
|
}
|
|
|
|
literal ba_solver::ba_sort::fresh(char const*) {
|
|
bool_var v = s.s().mk_var(false, true);
|
|
return literal(v, false);
|
|
}
|
|
|
|
literal ba_solver::ba_sort::mk_max(literal l1, literal l2) {
|
|
VERIFY(l1 != null_literal);
|
|
VERIFY(l2 != null_literal);
|
|
if (l1 == m_true) return l1;
|
|
if (l2 == m_true) return l2;
|
|
if (l1 == ~m_true) return l2;
|
|
if (l2 == ~m_true) return l1;
|
|
literal max = fresh("max");
|
|
s.s().mk_clause(~l1, max);
|
|
s.s().mk_clause(~l2, max);
|
|
s.s().mk_clause(~max, l1, l2);
|
|
return max;
|
|
}
|
|
|
|
literal ba_solver::ba_sort::mk_min(literal l1, literal l2) {
|
|
return ~mk_max(~l1, ~l2);
|
|
}
|
|
|
|
void ba_solver::ba_sort::mk_clause(unsigned n, literal const* lits) {
|
|
literal_vector _lits(n, lits);
|
|
s.s().mk_clause(n, _lits.c_ptr());
|
|
}
|
|
|
|
// -------------------------------
|
|
// set literals equivalent
|
|
|
|
bool ba_solver::set_root(literal l, literal r) {
|
|
if (s().is_assumption(l.var())) {
|
|
return false;
|
|
}
|
|
m_root_vars.reserve(s().num_vars(), false);
|
|
for (unsigned i = m_roots.size(); i < 2 * s().num_vars(); ++i) {
|
|
m_roots.push_back(to_literal(i));
|
|
}
|
|
m_roots[l.index()] = r;
|
|
m_roots[(~l).index()] = ~r;
|
|
m_root_vars[l.var()] = true;
|
|
return true;
|
|
}
|
|
|
|
void ba_solver::flush_roots() {
|
|
if (m_roots.empty()) return;
|
|
|
|
// validate();
|
|
m_visited.resize(s().num_vars()*2, false);
|
|
m_constraint_removed = false;
|
|
for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i)
|
|
flush_roots(*m_constraints[i]);
|
|
for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i)
|
|
flush_roots(*m_learned[i]);
|
|
cleanup_constraints();
|
|
// validate();
|
|
}
|
|
|
|
void ba_solver::recompile(constraint& c) {
|
|
if (c.id() == _bad_id) {
|
|
IF_VERBOSE(0, display(verbose_stream() << "recompile\n", c, true););
|
|
}
|
|
switch (c.tag()) {
|
|
case card_t:
|
|
recompile(c.to_card());
|
|
break;
|
|
case pb_t:
|
|
recompile(c.to_pb());
|
|
break;
|
|
case xor_t:
|
|
NOT_IMPLEMENTED_YET();
|
|
break;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
void ba_solver::recompile(card& c) {
|
|
SASSERT(c.lit() == null_literal || is_watched(c.lit(), c));
|
|
|
|
// pre-condition is that the literals, except c.lit(), in c are unwatched.
|
|
if (c.id() == _bad_id) std::cout << "recompile: " << c << "\n";
|
|
// IF_VERBOSE(0, verbose_stream() << "re: " << c << "\n";);
|
|
m_weights.resize(2*s().num_vars(), 0);
|
|
for (literal l : c) {
|
|
++m_weights[l.index()];
|
|
}
|
|
unsigned k = c.k();
|
|
bool all_units = true;
|
|
unsigned sz = c.size();
|
|
unsigned_vector coeffs;
|
|
literal_vector lits;
|
|
unsigned j = 0;
|
|
for (unsigned i = 0; i < sz && 0 < k; ++i) {
|
|
literal l = c[i];
|
|
unsigned w = m_weights[l.index()];
|
|
unsigned w2 = m_weights[(~l).index()];
|
|
if (w == 0 || w < w2) {
|
|
continue;
|
|
}
|
|
else if (k <= w2) {
|
|
k = 0;
|
|
break;
|
|
}
|
|
else {
|
|
SASSERT(w2 <= w && w2 < k);
|
|
k -= w2;
|
|
w -= w2;
|
|
m_weights[(~l).index()] = 0;
|
|
m_weights[l.index()] = 0;
|
|
if (w == 0) {
|
|
continue;
|
|
}
|
|
else {
|
|
all_units &= (w == 1);
|
|
coeffs.push_back(w);
|
|
c[j++] = l;
|
|
}
|
|
}
|
|
}
|
|
sz = j;
|
|
|
|
// clear weights
|
|
for (literal l : c) {
|
|
m_weights[l.index()] = 0;
|
|
m_weights[(~l).index()] = 0;
|
|
}
|
|
|
|
if (k == 0 && c.lit() == null_literal) {
|
|
remove_constraint(c, "recompiled to true");
|
|
return;
|
|
}
|
|
|
|
if (k == 1 && c.lit() == null_literal) {
|
|
literal_vector lits(sz, c.literals().c_ptr());
|
|
s().mk_clause(sz, lits.c_ptr(), c.learned());
|
|
remove_constraint(c, "recompiled to clause");
|
|
return;
|
|
}
|
|
|
|
c.set_size(sz);
|
|
c.set_k(k);
|
|
|
|
if (clausify(c)) {
|
|
return;
|
|
}
|
|
|
|
if (!all_units) {
|
|
TRACE("ba", tout << "replacing by pb: " << c << "\n";);
|
|
m_wlits.reset();
|
|
for (unsigned i = 0; i < sz; ++i) {
|
|
m_wlits.push_back(wliteral(coeffs[i], c[i]));
|
|
}
|
|
literal root = c.lit();
|
|
remove_constraint(c, "recompiled to pb");
|
|
add_pb_ge(root, m_wlits, k, c.learned());
|
|
}
|
|
else {
|
|
if (c.lit() == null_literal || value(c.lit()) == l_true) {
|
|
init_watch(c);
|
|
}
|
|
SASSERT(c.lit() == null_literal || is_watched(c.lit(), c));
|
|
SASSERT(c.well_formed());
|
|
}
|
|
}
|
|
|
|
bool ba_solver::clausify(card& c) {
|
|
#if 0
|
|
if (get_config().m_card_solver)
|
|
return false;
|
|
|
|
//
|
|
// TBD: conditions for when to clausify are TBD and
|
|
// handling of conditional cardinality as well.
|
|
//
|
|
if (c.lit() == null_literal) {
|
|
if (!c.learned() && !std::any_of(c.begin(), c.end(), [&](literal l) { return s().was_eliminated(l.var()); })) {
|
|
IF_VERBOSE(0, verbose_stream() << "clausify " << c << "\n";
|
|
m_sort.ge(false, c.k(), c.size(), c.begin());
|
|
}
|
|
remove_constraint(c, "recompiled to clauses");
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
|
|
void ba_solver::split_root(constraint& c) {
|
|
switch (c.tag()) {
|
|
case card_t: split_root(c.to_card()); break;
|
|
case pb_t: split_root(c.to_pb()); break;
|
|
case xor_t: NOT_IMPLEMENTED_YET(); break;
|
|
}
|
|
}
|
|
|
|
void ba_solver::flush_roots(constraint& c) {
|
|
if (c.lit() != null_literal && !is_watched(c.lit(), c)) {
|
|
watch_literal(c.lit(), c);
|
|
watch_literal(~c.lit(), c);
|
|
}
|
|
SASSERT(c.lit() == null_literal || is_watched(c.lit(), c));
|
|
bool found = c.lit() != null_literal && m_root_vars[c.lit().var()];
|
|
for (unsigned i = 0; !found && i < c.size(); ++i) {
|
|
found = m_root_vars[c.get_lit(i).var()];
|
|
}
|
|
if (!found) return;
|
|
clear_watch(c);
|
|
|
|
// this could create duplicate literals
|
|
for (unsigned i = 0; i < c.size(); ++i) {
|
|
literal lit = m_roots[c.get_lit(i).index()];
|
|
c.set_lit(i, lit);
|
|
}
|
|
|
|
literal root = c.lit();
|
|
if (root != null_literal && m_roots[root.index()] != root) {
|
|
root = m_roots[root.index()];
|
|
nullify_tracking_literal(c);
|
|
c.update_literal(root);
|
|
watch_literal(root, c);
|
|
watch_literal(~root, c);
|
|
}
|
|
|
|
bool found_dup = false;
|
|
bool found_root = false;
|
|
for (unsigned i = 0; i < c.size(); ++i) {
|
|
literal l = c.get_lit(i);
|
|
if (is_marked(l)) {
|
|
found_dup = true;
|
|
break;
|
|
}
|
|
else {
|
|
mark_visited(l);
|
|
mark_visited(~l);
|
|
}
|
|
}
|
|
for (unsigned i = 0; i < c.size(); ++i) {
|
|
literal l = c.get_lit(i);
|
|
unmark_visited(l);
|
|
unmark_visited(~l);
|
|
found_root |= l.var() == root.var();
|
|
}
|
|
|
|
if (found_root) {
|
|
split_root(c);
|
|
c.negate();
|
|
split_root(c);
|
|
remove_constraint(c, "flush roots");
|
|
}
|
|
else if (found_dup) {
|
|
recompile(c);
|
|
}
|
|
else {
|
|
if (c.lit() == null_literal || value(c.lit()) != l_undef) init_watch(c);
|
|
SASSERT(c.well_formed());
|
|
}
|
|
}
|
|
|
|
unsigned ba_solver::get_num_unblocked_bin(literal l) {
|
|
return s().m_simplifier.get_num_unblocked_bin(l);
|
|
}
|
|
|
|
/*
|
|
\brief garbage collection.
|
|
This entails
|
|
- finding pure literals,
|
|
- setting literals that are not used in the extension to non-external.
|
|
- subsumption
|
|
- resolution
|
|
- blocked literals
|
|
*/
|
|
void ba_solver::init_use_lists() {
|
|
m_visited.resize(s().num_vars()*2, false);
|
|
m_clause_use_list.init(s().num_vars());
|
|
m_cnstr_use_list.reset();
|
|
m_cnstr_use_list.resize(2*s().num_vars());
|
|
for (clause* c : s().m_clauses) {
|
|
if (!c->frozen())
|
|
m_clause_use_list.insert(*c);
|
|
}
|
|
for (constraint* cp : m_constraints) {
|
|
literal lit = cp->lit();
|
|
if (lit != null_literal) {
|
|
m_cnstr_use_list[lit.index()].push_back(cp);
|
|
m_cnstr_use_list[(~lit).index()].push_back(cp);
|
|
}
|
|
switch (cp->tag()) {
|
|
case card_t: {
|
|
card& c = cp->to_card();
|
|
for (literal l : c) {
|
|
m_cnstr_use_list[l.index()].push_back(&c);
|
|
if (lit != null_literal) m_cnstr_use_list[(~l).index()].push_back(&c);
|
|
}
|
|
break;
|
|
}
|
|
case pb_t: {
|
|
pb& p = cp->to_pb();
|
|
for (wliteral wl : p) {
|
|
literal l = wl.second;
|
|
m_cnstr_use_list[l.index()].push_back(&p);
|
|
if (lit != null_literal) m_cnstr_use_list[(~l).index()].push_back(&p);
|
|
}
|
|
break;
|
|
}
|
|
case xor_t: {
|
|
xor& x = cp->to_xor();
|
|
for (literal l : x) {
|
|
m_cnstr_use_list[l.index()].push_back(&x);
|
|
m_cnstr_use_list[(~l).index()].push_back(&x);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ba_solver::remove_unused_defs() {
|
|
// remove constraints where indicator literal isn't used.
|
|
for (constraint* cp : m_constraints) {
|
|
constraint& c = *cp;
|
|
literal lit = c.lit();
|
|
switch (c.tag()) {
|
|
case card_t:
|
|
case pb_t: {
|
|
if (lit != null_literal &&
|
|
value(lit) == l_undef &&
|
|
use_count(lit) == 1 &&
|
|
use_count(~lit) == 1 &&
|
|
get_num_unblocked_bin(lit) == 0 &&
|
|
get_num_unblocked_bin(~lit) == 0) {
|
|
remove_constraint(c, "unused def");
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
unsigned ba_solver::set_non_external() {
|
|
// set variables to be non-external if they are not used in theory constraints.
|
|
unsigned ext = 0;
|
|
for (unsigned v = 0; v < s().num_vars(); ++v) {
|
|
literal lit(v, false);
|
|
if (s().is_external(v) &&
|
|
m_cnstr_use_list[lit.index()].empty() &&
|
|
m_cnstr_use_list[(~lit).index()].empty() &&
|
|
!s().is_assumption(v)) {
|
|
s().set_non_external(v);
|
|
++ext;
|
|
}
|
|
}
|
|
// ensure that lemmas use only external variables.
|
|
for (constraint* cp : m_learned) {
|
|
constraint& c = *cp;
|
|
if (c.was_removed()) continue;
|
|
SASSERT(c.lit() == null_literal);
|
|
for (unsigned i = 0; i < c.size(); ++i) {
|
|
bool_var v = c.get_lit(i).var();
|
|
if (s().was_eliminated(v)) {
|
|
remove_constraint(c, "contains eliminated var");
|
|
break;
|
|
}
|
|
s().set_external(v);
|
|
}
|
|
}
|
|
return ext;
|
|
}
|
|
|
|
bool ba_solver::elim_pure(literal lit) {
|
|
if (value(lit) == l_undef && !m_cnstr_use_list[lit.index()].empty() &&
|
|
use_count(~lit) == 0 && get_num_unblocked_bin(~lit) == 0) {
|
|
IF_VERBOSE(100, verbose_stream() << "pure literal: " << lit << "\n";);
|
|
s().assign(lit, justification());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
unsigned ba_solver::elim_pure() {
|
|
// eliminate pure literals
|
|
unsigned pure_literals = 0;
|
|
for (unsigned v = 0; v < s().num_vars(); ++v) {
|
|
literal lit(v, false);
|
|
if (value(v) != l_undef) continue;
|
|
if (m_cnstr_use_list[lit.index()].empty() &&
|
|
m_cnstr_use_list[(~lit).index()].empty()) continue;
|
|
|
|
if (elim_pure(lit) || elim_pure(~lit)) {
|
|
++pure_literals;
|
|
}
|
|
}
|
|
return pure_literals;
|
|
}
|
|
|
|
void ba_solver::subsumption(constraint& cnstr) {
|
|
if (cnstr.was_removed()) return;
|
|
switch (cnstr.tag()) {
|
|
case card_t: {
|
|
card& c = cnstr.to_card();
|
|
if (c.k() > 1) subsumption(c);
|
|
break;
|
|
}
|
|
case pb_t: {
|
|
pb& p = cnstr.to_pb();
|
|
if (p.k() > 1) subsumption(p);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ba_solver::cleanup_clauses() {
|
|
if (!m_clause_removed) return;
|
|
// version in simplify first clears
|
|
// all watch literals, then reinserts them.
|
|
// this ensures linear time cleanup.
|
|
clause_vector::iterator it = s().m_clauses.begin();
|
|
clause_vector::iterator end = s().m_clauses.end();
|
|
clause_vector::iterator it2 = it;
|
|
for (; it != end; ++it) {
|
|
clause* c = *it;
|
|
if (c->was_removed() && s().can_delete(*c)) {
|
|
s().detach_clause(*c);
|
|
s().del_clause(*c);
|
|
}
|
|
else {
|
|
if (it2 != it) {
|
|
*it2 = *it;
|
|
}
|
|
++it2;
|
|
}
|
|
}
|
|
s().m_clauses.set_end(it2);
|
|
}
|
|
|
|
void ba_solver::cleanup_constraints() {
|
|
if (!m_constraint_removed) return;
|
|
cleanup_constraints(m_constraints, false);
|
|
cleanup_constraints(m_learned, true);
|
|
m_constraint_removed = false;
|
|
}
|
|
|
|
void ba_solver::ensure_external(constraint const& c) {
|
|
literal_vector lits(c.literals());
|
|
for (literal l : lits) {
|
|
s().set_external(l.var());
|
|
}
|
|
}
|
|
|
|
|
|
void ba_solver::cleanup_constraints(ptr_vector<constraint>& cs, bool learned) {
|
|
ptr_vector<constraint>::iterator it = cs.begin();
|
|
ptr_vector<constraint>::iterator it2 = it;
|
|
ptr_vector<constraint>::iterator end = cs.end();
|
|
for (; it != end; ++it) {
|
|
constraint& c = *(*it);
|
|
if (c.was_removed()) {
|
|
clear_watch(c);
|
|
nullify_tracking_literal(c);
|
|
m_allocator.deallocate(c.obj_size(), &c);
|
|
}
|
|
else if (learned && !c.learned()) {
|
|
ensure_external(c);
|
|
m_constraints.push_back(&c);
|
|
}
|
|
else {
|
|
if (it != it2) {
|
|
*it2 = *it;
|
|
}
|
|
++it2;
|
|
}
|
|
}
|
|
cs.set_end(it2);
|
|
}
|
|
|
|
/*
|
|
\brief subsumption between two cardinality constraints
|
|
- A >= k subsumes A + B >= k' for k' <= k
|
|
- A + A' >= k subsumes A + B >= k' for k' + |A'| <= k
|
|
- A + lit >= k self subsumes A + ~lit + B >= k' into A + B >= k' for k' <= k
|
|
- TBD: consider version that generalizes self-subsumption to more than one literal
|
|
A + ~L + B >= k' => A + B >= k' if A + A' + L >= k and k' + |L| + |A'| <= k
|
|
*/
|
|
bool ba_solver::subsumes(card& c1, card& c2, literal_vector & comp) {
|
|
if (c2.lit() != null_literal) return false;
|
|
|
|
unsigned c2_exclusive = 0;
|
|
unsigned common = 0;
|
|
comp.reset();
|
|
for (literal l : c2) {
|
|
if (is_marked(l)) {
|
|
++common;
|
|
}
|
|
else if (is_marked(~l)) {
|
|
comp.push_back(l);
|
|
}
|
|
else {
|
|
++c2_exclusive;
|
|
}
|
|
}
|
|
|
|
unsigned c1_exclusive = c1.size() - common - comp.size();
|
|
return c1_exclusive + c2.k() + comp.size() <= c1.k();
|
|
}
|
|
|
|
bool ba_solver::subsumes(card& c1, clause& c2, literal_vector & comp) {
|
|
unsigned c2_exclusive = 0;
|
|
unsigned common = 0;
|
|
comp.reset();
|
|
for (literal l : c2) {
|
|
if (is_marked(l)) {
|
|
++common;
|
|
}
|
|
else if (is_marked(~l)) {
|
|
comp.push_back(l);
|
|
}
|
|
else {
|
|
++c2_exclusive;
|
|
}
|
|
}
|
|
|
|
if (!comp.empty()) {
|
|
// self-subsumption is TBD.
|
|
return false;
|
|
}
|
|
unsigned c1_exclusive = c1.size() - common - comp.size();
|
|
return c1_exclusive + 1 <= c1.k();
|
|
}
|
|
|
|
/*
|
|
\brief Ax >= k subsumes By >= k' if
|
|
all coefficients in A are <= B and k >= k'
|
|
*/
|
|
bool ba_solver::subsumes(pb const& p1, pb_base const& p2) {
|
|
if (p1.k() < p2.k() || p1.size() > p2.size()) return false;
|
|
unsigned num_sub = 0;
|
|
for (unsigned i = 0; i < p2.size(); ++i) {
|
|
literal l = p2.get_lit(i);
|
|
if (is_marked(l) && m_weights[l.index()] <= p2.get_coeff(i)) {
|
|
++num_sub;
|
|
}
|
|
}
|
|
return num_sub == p1.size();
|
|
}
|
|
|
|
void ba_solver::subsumes(pb& p1, literal lit) {
|
|
for (constraint* c : m_cnstr_use_list[lit.index()]) {
|
|
if (c == &p1 || c->was_removed()) continue;
|
|
bool s = false;
|
|
switch (c->tag()) {
|
|
case card_t:
|
|
s = subsumes(p1, c->to_card());
|
|
break;
|
|
case pb_t:
|
|
s = subsumes(p1, c->to_pb());
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (s) {
|
|
++m_stats.m_num_pb_subsumes;
|
|
p1.set_learned(false);
|
|
remove_constraint(*c, "subsumed");
|
|
}
|
|
}
|
|
}
|
|
|
|
literal ba_solver::get_min_occurrence_literal(card const& c) {
|
|
unsigned occ_count = UINT_MAX;
|
|
literal lit = null_literal;
|
|
for (literal l : c) {
|
|
unsigned occ_count1 = m_cnstr_use_list[l.index()].size();
|
|
if (occ_count1 < occ_count) {
|
|
lit = l;
|
|
occ_count = occ_count1;
|
|
}
|
|
}
|
|
return lit;
|
|
}
|
|
|
|
void ba_solver::card_subsumption(card& c1, literal lit) {
|
|
literal_vector slit;
|
|
for (constraint* c : m_cnstr_use_list[lit.index()]) {
|
|
if (!c->is_card() || c == &c1 || c->was_removed()) {
|
|
continue;
|
|
}
|
|
card& c2 = c->to_card();
|
|
|
|
SASSERT(c1.index() != c2.index());
|
|
if (subsumes(c1, c2, slit)) {
|
|
if (slit.empty()) {
|
|
TRACE("ba", tout << "subsume cardinality\n" << c1.index() << ":" << c1 << "\n" << c2.index() << ":" << c2 << "\n";);
|
|
remove_constraint(c2, "subsumed");
|
|
++m_stats.m_num_pb_subsumes;
|
|
c1.set_learned(false);
|
|
}
|
|
else {
|
|
TRACE("ba", tout << "self subsume cardinality\n";);
|
|
IF_VERBOSE(0,
|
|
verbose_stream() << "self-subsume cardinality is TBD\n";
|
|
verbose_stream() << c1 << "\n";
|
|
verbose_stream() << c2 << "\n";);
|
|
#if 0
|
|
clear_watch(c2);
|
|
for (unsigned i = 0; i < c2.size(); ++i) {
|
|
if (slit == c2[i]) {
|
|
c2.swap(i, c2.size() -1);
|
|
break;
|
|
}
|
|
}
|
|
c2.set_size(c2.size() - 1);
|
|
init_watch(c2);
|
|
m_simplify_change = true;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ba_solver::clause_subsumption(card& c1, literal lit, clause_vector& removed_clauses) {
|
|
SASSERT(!c1.was_removed());
|
|
literal_vector slit;
|
|
clause_use_list::iterator it = m_clause_use_list.get(lit).mk_iterator();
|
|
while (!it.at_end()) {
|
|
clause& c2 = it.curr();
|
|
if (!c2.was_removed() && subsumes(c1, c2, slit)) {
|
|
if (slit.empty()) {
|
|
TRACE("ba", tout << "remove\n" << c1 << "\n" << c2 << "\n";);
|
|
removed_clauses.push_back(&c2);
|
|
++m_stats.m_num_clause_subsumes;
|
|
c1.set_learned(false);
|
|
}
|
|
else {
|
|
IF_VERBOSE(0, verbose_stream() << "self-subsume clause is TBD\n";);
|
|
// remove literal slit from c2.
|
|
TRACE("ba", tout << "TBD remove literals " << slit << " from " << c2 << "\n";);
|
|
}
|
|
}
|
|
it.next();
|
|
}
|
|
}
|
|
|
|
void ba_solver::binary_subsumption(card& c1, literal lit) {
|
|
if (c1.k() + 1 != c1.size()) return;
|
|
SASSERT(is_marked(lit));
|
|
SASSERT(!c1.was_removed());
|
|
watch_list & wlist = get_wlist(~lit);
|
|
watch_list::iterator it = wlist.begin();
|
|
watch_list::iterator it2 = it;
|
|
watch_list::iterator end = wlist.end();
|
|
for (; it != end; ++it) {
|
|
watched w = *it;
|
|
if (w.is_binary_clause() && is_marked(w.get_literal())) {
|
|
++m_stats.m_num_bin_subsumes;
|
|
// IF_VERBOSE(10, verbose_stream() << c1 << " subsumes (" << lit << " " << w.get_literal() << ")\n";);
|
|
if (!w.is_learned()) {
|
|
c1.set_learned(false);
|
|
}
|
|
}
|
|
else {
|
|
if (it != it2) {
|
|
*it2 = *it;
|
|
}
|
|
++it2;
|
|
}
|
|
}
|
|
wlist.set_end(it2);
|
|
}
|
|
|
|
void ba_solver::subsumption(card& c1) {
|
|
if (c1.was_removed() || c1.lit() != null_literal) {
|
|
return;
|
|
}
|
|
clause_vector removed_clauses;
|
|
for (literal l : c1) mark_visited(l);
|
|
for (unsigned i = 0; i < std::min(c1.size(), c1.k() + 1); ++i) {
|
|
literal lit = c1[i];
|
|
card_subsumption(c1, lit);
|
|
clause_subsumption(c1, lit, removed_clauses);
|
|
binary_subsumption(c1, lit);
|
|
}
|
|
for (literal l : c1) unmark_visited(l);
|
|
m_clause_removed |= !removed_clauses.empty();
|
|
for (clause *c : removed_clauses) {
|
|
c->set_removed(true);
|
|
m_clause_use_list.erase(*c);
|
|
}
|
|
}
|
|
|
|
void ba_solver::subsumption(pb& p1) {
|
|
if (p1.was_removed() || p1.lit() != null_literal) {
|
|
return;
|
|
}
|
|
for (wliteral l : p1) {
|
|
SASSERT(m_weights[l.second.index()] == 0);
|
|
m_weights.setx(l.second.index(), l.first, 0);
|
|
mark_visited(l.second);
|
|
}
|
|
for (unsigned i = 0; i < p1.num_watch(); ++i) {
|
|
subsumes(p1, p1[i].second);
|
|
}
|
|
for (wliteral l : p1) {
|
|
m_weights[l.second.index()] = 0;
|
|
unmark_visited(l.second);
|
|
}
|
|
}
|
|
|
|
void ba_solver::clauses_modifed() {}
|
|
|
|
lbool ba_solver::get_phase(bool_var v) { return l_undef; }
|
|
|
|
/*
|
|
\brief lit <=> conjunction of unconstrained lits
|
|
*/
|
|
void ba_solver::assert_unconstrained(literal lit, literal_vector const& lits) {
|
|
if (lit == null_literal) {
|
|
for (literal l : lits) {
|
|
if (value(l) == l_undef) {
|
|
s().assign(l, justification());
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// add clauses for: lit <=> conjunction of undef literals
|
|
SASSERT(value(lit) == l_undef);
|
|
literal_vector cl;
|
|
cl.push_back(lit);
|
|
for (literal l : lits) {
|
|
if (value(l) == l_undef) {
|
|
s().mk_clause(~lit, l);
|
|
cl.push_back(~l);
|
|
}
|
|
}
|
|
s().mk_clause(cl);
|
|
}
|
|
}
|
|
|
|
extension* ba_solver::copy(solver* s) {
|
|
ba_solver* result = alloc(ba_solver);
|
|
result->set_solver(s);
|
|
copy_core(result);
|
|
return result;
|
|
}
|
|
|
|
extension* ba_solver::copy(lookahead* s) {
|
|
ba_solver* result = alloc(ba_solver);
|
|
result->set_lookahead(s);
|
|
copy_core(result);
|
|
return result;
|
|
}
|
|
|
|
void ba_solver::copy_core(ba_solver* result) {
|
|
literal_vector lits;
|
|
svector<wliteral> wlits;
|
|
for (constraint* cp : m_constraints) {
|
|
switch (cp->tag()) {
|
|
case card_t: {
|
|
card const& c = cp->to_card();
|
|
lits.reset();
|
|
for (literal l : c) lits.push_back(l);
|
|
result->add_at_least(c.lit(), lits, c.k(), c.learned());
|
|
break;
|
|
}
|
|
case pb_t: {
|
|
pb const& p = cp->to_pb();
|
|
wlits.reset();
|
|
for (wliteral w : p) {
|
|
wlits.push_back(w);
|
|
}
|
|
result->add_pb_ge(p.lit(), wlits, p.k(), p.learned());
|
|
break;
|
|
}
|
|
case xor_t: {
|
|
xor const& x = cp->to_xor();
|
|
lits.reset();
|
|
for (literal l : x) lits.push_back(l);
|
|
result->add_xor(x.lit(), lits, x.learned());
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ba_solver::init_use_list(ext_use_list& ul) {
|
|
ul.init(s().num_vars());
|
|
for (constraint const* cp : m_constraints) {
|
|
ext_constraint_idx idx = cp->index();
|
|
if (cp->lit() != null_literal) {
|
|
ul.insert(cp->lit(), idx);
|
|
ul.insert(~cp->lit(), idx);
|
|
}
|
|
switch (cp->tag()) {
|
|
case card_t: {
|
|
card const& c = cp->to_card();
|
|
for (literal l : c) {
|
|
ul.insert(l, idx);
|
|
}
|
|
break;
|
|
}
|
|
case pb_t: {
|
|
pb const& p = cp->to_pb();
|
|
for (wliteral w : p) {
|
|
ul.insert(w.second, idx);
|
|
}
|
|
break;
|
|
}
|
|
case xor_t: {
|
|
xor const& x = cp->to_xor();
|
|
for (literal l : x) {
|
|
ul.insert(l, idx);
|
|
ul.insert(~l, idx);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// literal is used in a clause (C or l), it
|
|
// it occurs negatively in constraint c.
|
|
// all literals in C are marked
|
|
//
|
|
bool ba_solver::is_blocked(literal l, ext_constraint_idx idx) {
|
|
constraint const& c = index2constraint(idx);
|
|
simplifier& sim = s().m_simplifier;
|
|
if (c.lit() != null_literal) return false;
|
|
switch (c.tag()) {
|
|
case card_t: {
|
|
card const& ca = c.to_card();
|
|
unsigned weight = 0;
|
|
for (literal l2 : ca) {
|
|
if (sim.is_marked(~l2)) ++weight;
|
|
}
|
|
return weight >= ca.k();
|
|
}
|
|
case pb_t: {
|
|
pb const& p = c.to_pb();
|
|
unsigned weight = 0, offset = 0;
|
|
for (wliteral l2 : p) {
|
|
if (~l2.second == l) {
|
|
offset = l2.first;
|
|
break;
|
|
}
|
|
}
|
|
SASSERT(offset != 0);
|
|
for (wliteral l2 : p) {
|
|
if (sim.is_marked(~l2.second)) {
|
|
weight += std::min(offset, l2.first);
|
|
}
|
|
}
|
|
return weight >= p.k();
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void ba_solver::find_mutexes(literal_vector& lits, vector<literal_vector> & mutexes) {
|
|
literal_set slits(lits);
|
|
bool change = false;
|
|
for (constraint* cp : m_constraints) {
|
|
if (!cp->is_card()) continue;
|
|
card const& c = cp->to_card();
|
|
if (c.size() == c.k() + 1) {
|
|
literal_vector mux;
|
|
for (literal lit : c) {
|
|
if (slits.contains(~lit)) {
|
|
mux.push_back(~lit);
|
|
}
|
|
}
|
|
if (mux.size() <= 1) {
|
|
continue;
|
|
}
|
|
|
|
for (literal m : mux) {
|
|
slits.remove(m);
|
|
}
|
|
change = true;
|
|
mutexes.push_back(mux);
|
|
}
|
|
}
|
|
if (!change) return;
|
|
lits.reset();
|
|
for (literal l : slits) {
|
|
lits.push_back(l);
|
|
}
|
|
}
|
|
|
|
void ba_solver::display(std::ostream& out, ineq& ineq, bool values) const {
|
|
for (unsigned i = 0; i < ineq.m_lits.size(); ++i) {
|
|
out << ineq.m_coeffs[i] << "*" << ineq.m_lits[i] << " ";
|
|
if (values) out << value(ineq.m_lits[i]) << " ";
|
|
}
|
|
out << ">= " << ineq.m_k << "\n";
|
|
}
|
|
|
|
void ba_solver::display(std::ostream& out, xor const& x, bool values) const {
|
|
out << "xor " << x.lit();
|
|
if (x.lit() != null_literal && values) {
|
|
out << "@(" << value(x.lit());
|
|
if (value(x.lit()) != l_undef) {
|
|
out << ":" << lvl(x.lit());
|
|
}
|
|
out << "): ";
|
|
}
|
|
else {
|
|
out << ": ";
|
|
}
|
|
for (unsigned i = 0; i < x.size(); ++i) {
|
|
literal l = x[i];
|
|
out << l;
|
|
if (values) {
|
|
out << "@(" << value(l);
|
|
if (value(l) != l_undef) {
|
|
out << ":" << lvl(l);
|
|
}
|
|
out << ") ";
|
|
}
|
|
else {
|
|
out << " ";
|
|
}
|
|
}
|
|
out << "\n";
|
|
}
|
|
|
|
void ba_solver::display(std::ostream& out, card const& c, bool values) const {
|
|
if (c.lit() != null_literal) {
|
|
if (values) {
|
|
out << c.lit() << "[" << c.size() << "]";
|
|
out << "@(" << value(c.lit());
|
|
if (value(c.lit()) != l_undef) {
|
|
out << ":" << lvl(c.lit());
|
|
}
|
|
out << "): ";
|
|
}
|
|
else {
|
|
out << c.lit() << " == ";
|
|
}
|
|
}
|
|
for (unsigned i = 0; i < c.size(); ++i) {
|
|
literal l = c[i];
|
|
out << l;
|
|
if (values) {
|
|
out << "@(" << value(l);
|
|
if (value(l) != l_undef) {
|
|
out << ":" << lvl(l);
|
|
}
|
|
out << ") ";
|
|
}
|
|
else {
|
|
out << " ";
|
|
}
|
|
}
|
|
out << ">= " << c.k() << "\n";
|
|
}
|
|
|
|
std::ostream& ba_solver::display(std::ostream& out) const {
|
|
for (constraint const* c : m_constraints) {
|
|
out << (*c) << "\n";
|
|
}
|
|
if (!m_learned.empty()) {
|
|
out << "learned:\n";
|
|
}
|
|
for (constraint const* c : m_learned) {
|
|
out << (*c) << "\n";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
std::ostream& ba_solver::display_justification(std::ostream& out, ext_justification_idx idx) const {
|
|
return out << index2constraint(idx);
|
|
}
|
|
|
|
void ba_solver::display(std::ostream& out, constraint const& c, bool values) const {
|
|
switch (c.tag()) {
|
|
case card_t: display(out, c.to_card(), values); break;
|
|
case pb_t: display(out, c.to_pb(), values); break;
|
|
case xor_t: display(out, c.to_xor(), values); break;
|
|
default: UNREACHABLE(); break;
|
|
}
|
|
}
|
|
|
|
void ba_solver::collect_statistics(statistics& st) const {
|
|
st.update("ba propagations", m_stats.m_num_propagations);
|
|
st.update("ba conflicts", m_stats.m_num_conflicts);
|
|
st.update("ba resolves", m_stats.m_num_resolves);
|
|
st.update("ba cuts", m_stats.m_num_cut);
|
|
st.update("ba gc", m_stats.m_num_gc);
|
|
}
|
|
|
|
bool ba_solver::validate_unit_propagation(card const& c, literal alit) const {
|
|
(void) alit;
|
|
if (c.lit() != null_literal && value(c.lit()) != l_true) return false;
|
|
for (unsigned i = c.k(); i < c.size(); ++i) {
|
|
if (value(c[i]) != l_false) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ba_solver::validate_unit_propagation(pb const& p, literal alit) const {
|
|
if (p.lit() != null_literal && value(p.lit()) != l_true) {
|
|
return false;
|
|
}
|
|
|
|
unsigned sum = 0;
|
|
TRACE("ba", display(tout << "validate: " << alit << "\n", p, true););
|
|
for (wliteral wl : p) {
|
|
literal lit = wl.second;
|
|
lbool val = value(lit);
|
|
if (val != l_false && lit != alit) {
|
|
sum += wl.first;
|
|
}
|
|
}
|
|
return sum < p.k();
|
|
}
|
|
|
|
bool ba_solver::validate_unit_propagation(pb const& p, literal_vector const& r, literal alit) const {
|
|
// all elements of r are true,
|
|
for (literal l : r) {
|
|
if (value(l) != l_true) {
|
|
IF_VERBOSE(0, verbose_stream() << "value of " << l << " is " << value(l) << "\n";
|
|
display(verbose_stream(), p, true););
|
|
return false;
|
|
}
|
|
if (value(alit) == l_true && lvl(l) > lvl(alit)) {
|
|
IF_VERBOSE(0,
|
|
verbose_stream() << "level of premise " << l << " is " << lvl(l) << "\n";
|
|
verbose_stream() << "level of asserting literal " << alit << " is " << lvl(alit) << "\n";
|
|
display(verbose_stream(), p, true););
|
|
return false;
|
|
}
|
|
// if (value(alit) == l_true && lvl(l) == lvl(alit)) {
|
|
// std::cout << "same level " << alit << " " << l << "\n";
|
|
// }
|
|
}
|
|
// the sum of elements not in r or alit add up to less than k.
|
|
unsigned sum = 0;
|
|
//
|
|
// a*x + b*alit + c*r >= k
|
|
// sum a < k
|
|
// val(r) = false
|
|
// hence alit has to be true.
|
|
for (wliteral wl : p) {
|
|
literal lit = wl.second;
|
|
if (lit != alit && !r.contains(~lit)) {
|
|
sum += wl.first;
|
|
}
|
|
}
|
|
if (sum >= p.k()) {
|
|
IF_VERBOSE(0,
|
|
verbose_stream() << "sum is " << sum << " >= " << p.k() << "\n";
|
|
display(verbose_stream(), p, true);
|
|
verbose_stream() << "id: " << p.id() << "\n";
|
|
sum = 0;
|
|
for (wliteral wl : p) sum += wl.first;
|
|
verbose_stream() << "overall sum " << sum << "\n";
|
|
verbose_stream() << "asserting literal: " << alit << "\n";
|
|
verbose_stream() << "reason: " << r << "\n";);
|
|
return false;
|
|
}
|
|
for (wliteral wl : p) {
|
|
if (alit == wl.second) {
|
|
return true;
|
|
}
|
|
}
|
|
IF_VERBOSE(0, verbose_stream() << alit << " not found among literals\n";);
|
|
return false;
|
|
}
|
|
|
|
bool ba_solver::validate_unit_propagation(xor const& x, literal alit) const {
|
|
if (value(x.lit()) != l_true) return false;
|
|
for (unsigned i = 1; i < x.size(); ++i) {
|
|
if (value(x[i]) == l_undef) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ba_solver::validate_lemma() {
|
|
int64 bound64 = m_bound;
|
|
int64 val = -bound64;
|
|
reset_active_var_set();
|
|
for (bool_var v : m_active_vars) {
|
|
if (m_active_var_set.contains(v)) continue;
|
|
int64 coeff = get_coeff(v);
|
|
if (coeff == 0) continue;
|
|
m_active_var_set.insert(v);
|
|
literal lit(v, false);
|
|
if (coeff < 0 && value(lit) != l_true) {
|
|
val -= coeff;
|
|
}
|
|
else if (coeff > 0 && value(lit) != l_false) {
|
|
val += coeff;
|
|
}
|
|
}
|
|
CTRACE("ba", val >= 0, active2pb(m_A); display(tout, m_A););
|
|
return val < 0;
|
|
}
|
|
|
|
void ba_solver::reset_active_var_set() {
|
|
while (!m_active_var_set.empty()) m_active_var_set.erase();
|
|
}
|
|
|
|
void ba_solver::active2pb(ineq& p) {
|
|
reset_active_var_set();
|
|
p.reset(m_bound);
|
|
for (bool_var v : m_active_vars) {
|
|
if (m_active_var_set.contains(v)) continue;
|
|
int64 coeff = get_coeff(v);
|
|
if (coeff == 0) continue;
|
|
m_active_var_set.insert(v);
|
|
literal lit(v, coeff < 0);
|
|
p.m_lits.push_back(lit);
|
|
p.m_coeffs.push_back(abs(coeff));
|
|
}
|
|
}
|
|
|
|
ba_solver::constraint* ba_solver::active2constraint() {
|
|
reset_active_var_set();
|
|
m_wlits.reset();
|
|
uint64 sum = 0;
|
|
if (m_bound == 1) return 0;
|
|
if (m_overflow) return 0;
|
|
|
|
for (bool_var v : m_active_vars) {
|
|
int coeff = get_int_coeff(v);
|
|
if (m_active_var_set.contains(v) || coeff == 0) continue;
|
|
m_active_var_set.insert(v);
|
|
literal lit(v, coeff < 0);
|
|
m_wlits.push_back(wliteral(get_abs_coeff(v), lit));
|
|
sum += get_abs_coeff(v);
|
|
}
|
|
|
|
if (m_overflow || sum >= UINT_MAX/2) {
|
|
return 0;
|
|
}
|
|
else {
|
|
return add_pb_ge(null_literal, m_wlits, m_bound, true);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Chai Kuhlmann:
|
|
|
|
a1*l1 + ... + a_n*l_n >= k
|
|
s.t.
|
|
a1 >= a2 >= .. >= a_n
|
|
|
|
let m be such that
|
|
|
|
sum_{i = 1}^{m-1} a_i < k <= sum_{i = 1}^{m}
|
|
|
|
then
|
|
|
|
l1 + ... + l_n >= m
|
|
|
|
furthermore, for the largest n' <= n, such that
|
|
|
|
sum_{i = n'+1}^n a_i + sum_{i = 1}^{m-1} a_i < k
|
|
|
|
then
|
|
|
|
l1 + ... + l_n' >= m
|
|
|
|
*/
|
|
struct compare_wlit {
|
|
bool operator()(ba_solver::wliteral l1, ba_solver::wliteral l2) const {
|
|
return l1.first > l2.first;
|
|
}
|
|
};
|
|
|
|
|
|
ba_solver::constraint* ba_solver::active2card() {
|
|
normalize_active_coeffs();
|
|
m_wlits.reset();
|
|
for (bool_var v : m_active_vars) {
|
|
int coeff = get_int_coeff(v);
|
|
m_wlits.push_back(std::make_pair(get_abs_coeff(v), literal(v, coeff < 0)));
|
|
}
|
|
std::sort(m_wlits.begin(), m_wlits.end(), compare_wlit());
|
|
unsigned k = 0;
|
|
uint64 sum = 0, sum0 = 0;
|
|
for (wliteral wl : m_wlits) {
|
|
if (sum >= m_bound) break;
|
|
sum0 = sum;
|
|
sum += wl.first;
|
|
++k;
|
|
}
|
|
if (k == 1) {
|
|
return 0;
|
|
}
|
|
while (!m_wlits.empty()) {
|
|
wliteral wl = m_wlits.back();
|
|
if (wl.first + sum0 >= m_bound) break;
|
|
m_wlits.pop_back();
|
|
sum0 += wl.first;
|
|
}
|
|
|
|
unsigned slack = 0;
|
|
unsigned max_level = 0;
|
|
unsigned num_max_level = 0;
|
|
for (wliteral wl : m_wlits) {
|
|
if (value(wl.second) != l_false) ++slack;
|
|
unsigned level = lvl(wl.second);
|
|
if (level > max_level) {
|
|
max_level = level;
|
|
num_max_level = 1;
|
|
}
|
|
else if (max_level == level) {
|
|
++num_max_level;
|
|
}
|
|
}
|
|
if (m_overflow) return 0;
|
|
|
|
if (slack >= k) {
|
|
#if 0
|
|
return active2constraint();
|
|
active2pb(m_A);
|
|
std::cout << "not asserting\n";
|
|
display(std::cout, m_A, true);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
// produce asserting cardinality constraint
|
|
literal_vector lits;
|
|
for (wliteral wl : m_wlits) {
|
|
lits.push_back(wl.second);
|
|
}
|
|
constraint* c = add_at_least(null_literal, lits, k, true);
|
|
|
|
if (c) {
|
|
// IF_VERBOSE(0, verbose_stream() << *c << "\n";);
|
|
lits.reset();
|
|
for (wliteral wl : m_wlits) {
|
|
if (value(wl.second) == l_false) lits.push_back(wl.second);
|
|
}
|
|
unsigned glue = s().num_diff_levels(lits.size(), lits.c_ptr());
|
|
c->set_glue(glue);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
|
|
void ba_solver::justification2pb(justification const& js, literal lit, unsigned offset, ineq& ineq) {
|
|
switch (js.get_kind()) {
|
|
case justification::NONE:
|
|
ineq.reset(offset);
|
|
ineq.push(lit, offset);
|
|
break;
|
|
case justification::BINARY:
|
|
ineq.reset(offset);
|
|
ineq.push(lit, offset);
|
|
ineq.push(js.get_literal(), offset);
|
|
break;
|
|
case justification::TERNARY:
|
|
ineq.reset(offset);
|
|
ineq.push(lit, offset);
|
|
ineq.push(js.get_literal1(), offset);
|
|
ineq.push(js.get_literal2(), offset);
|
|
break;
|
|
case justification::CLAUSE: {
|
|
ineq.reset(offset);
|
|
clause & c = s().get_clause(js);
|
|
for (literal l : c) ineq.push(l, offset);
|
|
break;
|
|
}
|
|
case justification::EXT_JUSTIFICATION: {
|
|
ext_justification_idx index = js.get_ext_justification_idx();
|
|
constraint& cnstr = index2constraint(index);
|
|
switch (cnstr.tag()) {
|
|
case card_t: {
|
|
card& c = cnstr.to_card();
|
|
ineq.reset(offset*c.k());
|
|
for (literal l : c) ineq.push(l, offset);
|
|
if (c.lit() != null_literal) ineq.push(~c.lit(), offset*c.k());
|
|
break;
|
|
}
|
|
case pb_t: {
|
|
pb& p = cnstr.to_pb();
|
|
ineq.reset(p.k());
|
|
for (wliteral wl : p) ineq.push(wl.second, wl.first);
|
|
if (p.lit() != null_literal) ineq.push(~p.lit(), p.k());
|
|
break;
|
|
}
|
|
case xor_t: {
|
|
xor& x = cnstr.to_xor();
|
|
literal_vector ls;
|
|
get_antecedents(lit, x, ls);
|
|
ineq.reset(offset);
|
|
for (literal l : ls) ineq.push(~l, offset);
|
|
literal lxor = x.lit();
|
|
if (lxor != null_literal) ineq.push(~lxor, offset);
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
UNREACHABLE();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// validate that m_A & m_B implies m_C
|
|
|
|
bool ba_solver::validate_resolvent() {
|
|
return true;
|
|
u_map<uint64> coeffs;
|
|
uint64 k = m_A.m_k + m_B.m_k;
|
|
for (unsigned i = 0; i < m_A.m_lits.size(); ++i) {
|
|
uint64 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) {
|
|
uint64 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];
|
|
uint64 coeff;
|
|
if (coeffs.find(lit.index(), coeff)) {
|
|
if (coeff > m_C.m_coeffs[i] && m_C.m_coeffs[i] < m_C.m_k) {
|
|
goto violated;
|
|
}
|
|
coeffs.remove(lit.index());
|
|
}
|
|
}
|
|
if (!coeffs.empty()) goto violated;
|
|
if (m_C.m_k > k) goto violated;
|
|
SASSERT(coeffs.empty());
|
|
SASSERT(m_C.m_k <= k);
|
|
return true;
|
|
|
|
violated:
|
|
// last ditch effort by translating to SAT.
|
|
solver s0(s().m_params, s().rlimit());
|
|
u_map<bool_var> translation;
|
|
literal l1 = translate_to_sat(s0, translation, m_A);
|
|
if (l1 == null_literal) return true;
|
|
literal l2 = translate_to_sat(s0, translation, m_B);
|
|
if (l2 == null_literal) return true;
|
|
ineq notC = negate(m_B);
|
|
literal l3 = translate_to_sat(s0, translation, notC);
|
|
if (l3 == null_literal) return true;
|
|
s0.assign(l1, justification());
|
|
s0.assign(l2, justification());
|
|
s0.assign(l3, justification());
|
|
lbool is_sat = s0.check();
|
|
TRACE("ba", s0.display(tout << "trying sat encoding"););
|
|
if (is_sat == l_false) return true;
|
|
|
|
IF_VERBOSE(0,
|
|
display(verbose_stream(), m_A);
|
|
display(verbose_stream(), m_B);
|
|
display(verbose_stream(), m_C);
|
|
for (auto& e : coeffs) {
|
|
verbose_stream() << to_literal(e.m_key) << ": " << e.m_value << "\n";
|
|
});
|
|
|
|
UNREACHABLE();
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
\brief translate PB inequality to SAT formula.
|
|
*/
|
|
literal ba_solver::translate_to_sat(solver& s, u_map<bool_var>& translation, ineq const& pb) {
|
|
SASSERT(pb.m_k > 0);
|
|
if (pb.m_lits.size() > 1) {
|
|
ineq a, b;
|
|
a.reset(pb.m_k);
|
|
b.reset(pb.m_k);
|
|
for (unsigned i = 0; i < pb.m_lits.size()/2; ++i) {
|
|
a.push(pb.m_lits[i], pb.m_coeffs[i]);
|
|
}
|
|
for (unsigned i = pb.m_lits.size()/2; i < pb.m_lits.size(); ++i) {
|
|
b.push(pb.m_lits[i], pb.m_coeffs[i]);
|
|
}
|
|
bool_var v = s.mk_var();
|
|
literal lit(v, false);
|
|
literal_vector lits;
|
|
lits.push_back(~lit);
|
|
push_lit(lits, translate_to_sat(s, translation, a));
|
|
push_lit(lits, translate_to_sat(s, translation, b));
|
|
push_lit(lits, translate_to_sat(s, translation, a, b));
|
|
s.mk_clause(lits);
|
|
return lit;
|
|
}
|
|
if (pb.m_coeffs[0] >= pb.m_k) {
|
|
return translate_to_sat(s, translation, pb.m_lits[0]);
|
|
}
|
|
else {
|
|
return null_literal;
|
|
}
|
|
}
|
|
|
|
/*
|
|
\brief encode the case where Sum(a) >= k-1 & Sum(b) >= 1 \/ ... \/ Sum(a) >= 1 & Sum(b) >= k-1
|
|
*/
|
|
literal ba_solver::translate_to_sat(solver& s, u_map<bool_var>& translation, ineq& a, ineq& b) {
|
|
uint64 k0 = a.m_k;
|
|
literal_vector lits;
|
|
for (unsigned k = 1; k < a.m_k - 1; ++k) {
|
|
a.m_k = k; b.m_k = k0 - k;
|
|
literal lit1 = translate_to_sat(s, translation, a);
|
|
literal lit2 = translate_to_sat(s, translation, b);
|
|
if (lit1 != null_literal && lit2 != null_literal) {
|
|
bool_var v = s.mk_var();
|
|
literal lit(v, false);
|
|
s.mk_clause(~lit, lit1);
|
|
s.mk_clause(~lit, lit2);
|
|
lits.push_back(lit);
|
|
}
|
|
}
|
|
a.m_k = k0;
|
|
b.m_k = k0;
|
|
switch (lits.size()) {
|
|
case 0: return null_literal;
|
|
case 1: return lits[0];
|
|
default: {
|
|
bool_var v = s.mk_var();
|
|
literal lit(v, false);
|
|
lits.push_back(~lit);
|
|
s.mk_clause(lits);
|
|
return lit;
|
|
}
|
|
}
|
|
}
|
|
|
|
literal ba_solver::translate_to_sat(solver& s, u_map<bool_var>& translation, literal lit) {
|
|
bool_var v;
|
|
if (!translation.find(lit.var(), v)) {
|
|
v = s.mk_var();
|
|
translation.insert(lit.var(), v);
|
|
}
|
|
return literal(v, lit.sign());
|
|
}
|
|
|
|
ba_solver::ineq ba_solver::negate(ineq const& a) const {
|
|
ineq result;
|
|
uint64 sum = 0;
|
|
for (unsigned i = 0; i < a.m_lits.size(); ++i) {
|
|
result.push(~a.m_lits[i], a.m_coeffs[i]);
|
|
sum += a.m_coeffs[i];
|
|
}
|
|
SASSERT(sum >= a.m_k + 1);
|
|
result.m_k = sum + 1 - a.m_k;
|
|
return result;
|
|
}
|
|
|
|
void ba_solver::push_lit(literal_vector& lits, literal lit) {
|
|
if (lit != null_literal) {
|
|
lits.push_back(lit);
|
|
}
|
|
}
|
|
|
|
bool ba_solver::validate_conflict(literal_vector const& lits, ineq& p) {
|
|
for (literal l : lits) {
|
|
if (value(l) != l_false) {
|
|
TRACE("ba", tout << "literal " << l << " is not false\n";);
|
|
return false;
|
|
}
|
|
if (!p.m_lits.contains(l)) {
|
|
TRACE("ba", tout << "lemma contains literal " << l << " not in inequality\n";);
|
|
return false;
|
|
}
|
|
}
|
|
uint64 value = 0;
|
|
for (unsigned i = 0; i < p.m_lits.size(); ++i) {
|
|
uint64 coeff = p.m_coeffs[i];
|
|
if (!lits.contains(p.m_lits[i])) {
|
|
value += coeff;
|
|
}
|
|
}
|
|
CTRACE("ba", value >= p.m_k, tout << "slack: " << value << " bound " << p.m_k << "\n";
|
|
display(tout, p);
|
|
tout << lits << "\n";);
|
|
return value < p.m_k;
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|