3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-03-03 12:16:54 +00:00
z3/src/smt/theory_pb.cpp
Nikolaj Bjorner 975474f560 fixing bounds calculation
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
2017-01-13 17:05:51 -08:00

2525 lines
84 KiB
C++

/*++
Copyright (c) 2013 Microsoft Corporation
Module Name:
theory_pb.cpp
Abstract:
Pseudo-Boolean theory plugin.
Author:
Nikolaj Bjorner (nbjorner) 2013-11-05
Notes:
--*/
#include <typeinfo>
#include "theory_pb.h"
#include "smt_context.h"
#include "ast_pp.h"
#include "sorting_network.h"
#include "uint_set.h"
#include "smt_model_generator.h"
#include "pb_rewriter_def.h"
#include "sparse_matrix_def.h"
#include "simplex_def.h"
#include "mpz.h"
namespace smt {
class pb_lit_rewriter_util {
public:
typedef std::pair<literal, rational> arg_t;
typedef vector<arg_t> args_t;
typedef rational numeral;
literal negate(literal l) {
return ~l;
}
void display(std::ostream& out, literal l) {
out << l;
}
bool is_negated(literal l) const {
return l.sign();
}
bool is_true(literal l) const {
return l == true_literal;
}
bool is_false(literal l) const {
return l == false_literal;
}
struct compare {
bool operator()(arg_t const& a, arg_t const& b) {
return a.first < b.first;
}
};
};
unsigned theory_pb::arg_t::get_hash() const {
return get_composite_hash<arg_t, arg_t::kind_hash, arg_t::child_hash>(*this, size());
}
bool theory_pb::arg_t::operator==(arg_t const& other) const {
if (size() != other.size()) return false;
for (unsigned i = 0; i < size(); ++i) {
if (lit(i) != other.lit(i)) return false;
if (coeff(i) != other.coeff(i)) return false;
}
return true;
}
void theory_pb::arg_t::remove_negations() {
for (unsigned i = 0; i < size(); ++i) {
if (lit(i).sign()) {
(*this)[i].first.neg();
(*this)[i].second.neg();
m_k += coeff(i);
}
}
}
void theory_pb::arg_t::negate() {
numeral sum(0);
for (unsigned i = 0; i < size(); ++i) {
(*this)[i].first.neg();
sum += coeff(i);
}
m_k = sum - m_k + numeral::one();
VERIFY(l_undef == normalize(false));
}
lbool theory_pb::arg_t::normalize(bool is_eq) {
pb_lit_rewriter_util pbu;
pb_rewriter_util<pb_lit_rewriter_util> util(pbu);
return util.normalize(*this, m_k, is_eq);
}
void theory_pb::arg_t::prune(bool is_eq) {
pb_lit_rewriter_util pbu;
pb_rewriter_util<pb_lit_rewriter_util> util(pbu);
util.prune(*this, m_k, is_eq);
}
std::ostream& theory_pb::arg_t::display(context& ctx, std::ostream& out, bool values) const {
for (unsigned i = 0; i < size(); ++i) {
literal l(lit(i));
if (!coeff(i).is_one()) {
out << coeff(i) << "*";
}
out << l;
if (values) {
out << "@(" << ctx.get_assignment(l);
if (ctx.get_assignment(l) != l_undef) {
out << ":" << ctx.get_assign_level(l);
}
out << ")";
}
if (i + 1 < size()) {
out << " + ";
}
}
out << " ~ " << k() << "\n";
return out;
}
app_ref theory_pb::arg_t::to_expr(bool is_eq, context& ctx, ast_manager& m) {
expr_ref tmp(m);
app_ref result(m);
svector<rational> coeffs;
expr_ref_vector args(m);
for (unsigned i = 0; i < size(); ++i) {
ctx.literal2expr(lit(i), tmp);
args.push_back(tmp);
coeffs.push_back(coeff(i));
}
pb_util pb(m);
if (is_eq) {
result = pb.mk_eq(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), k());
}
else {
result = pb.mk_ge(coeffs.size(), coeffs.c_ptr(), args.c_ptr(), k());
}
return result;
}
void theory_pb::ineq::reset() {
m_max_watch.reset();
m_watch_sz = 0;
m_watch_sum.reset();
m_num_propagations = 0;
m_compilation_threshold = UINT_MAX;
m_compiled = l_false;
m_args[0].reset();
m_args[0].m_k.reset();
m_args[1].reset();
m_args[1].m_k.reset();
m_nfixed = 0;
m_max_sum.reset();
m_min_sum.reset();
}
void theory_pb::ineq::unique() {
pb_lit_rewriter_util pbu;
pb_rewriter_util<pb_lit_rewriter_util> util(pbu);
util.unique(m_args[0], m_args[0].m_k, m_is_eq);
}
void theory_pb::ineq::post_prune() {
if (!m_args[0].empty() && is_ge()) {
m_args[0].negate();
m_args[0].negate();
m_args[1].reset();
m_args[1].m_k = m_args[0].m_k;
m_args[1].append(m_args[0]);
m_args[1].negate();
SASSERT(m_args[0].size() == m_args[1].size());
SASSERT(m_args[0].well_formed());
SASSERT(m_args[1].well_formed());
}
}
void theory_pb::ineq::negate() {
SASSERT(!m_is_eq);
m_lit.neg();
}
void theory_pb::ineq::prune() {
m_args[0].prune(m_is_eq);
}
lbool theory_pb::ineq::normalize() {
return m_args[0].normalize(m_is_eq);
}
app_ref theory_pb::ineq::to_expr(context& ctx, ast_manager& m) {
return args().to_expr(m_is_eq, ctx, m);
}
bool theory_pb::arg_t::well_formed() const {
SASSERT(k().is_pos());
uint_set vars;
numeral sum = numeral::zero();
for (unsigned i = 0; i < size(); ++i) {
SASSERT(coeff(i) <= k());
SASSERT(numeral::one() <= coeff(i));
SASSERT(lit(i) != true_literal);
SASSERT(lit(i) != false_literal);
SASSERT(lit(i) != null_literal);
SASSERT(!vars.contains(lit(i).var()));
vars.insert(lit(i).var());
sum += coeff(i);
}
SASSERT(sum >= k());
return true;
}
// -----------------------------
// cardinality constraints
void theory_pb::card::negate() {
m_lit.neg();
unsigned sz = size();
for (unsigned i = 0; i < sz; ++i) {
m_args[i].neg();
}
m_bound = sz - m_bound + 1;
}
lbool theory_pb::card::assign(theory_pb& th, literal lit) {
TRACE("pb", tout << "assign: " << m_lit << " " << ~lit << " " << m_bound << "\n";);
// literal is assigned to false.
context& ctx = th.get_context();
SASSERT(m_bound > 0);
SASSERT(ctx.get_assignment(lit) == l_false);
unsigned index = m_bound + 1;
//
// We give preference to a watched literal in position 1..m_bound.
// Notice, that if a literal occurs multiple
// times in m_args, within [0..m_bound] then it is inserted into the watch
// list for this cardinality constraint. For each occurrence, a callback
// to assign is made.
//
for (unsigned i = m_bound + 1; i > 0; ) {
--i;
if (m_args[i] == lit) {
index = i;
break;
}
}
SASSERT(index <= m_bound);
SASSERT(m_args[index] == lit);
unsigned sz = size();
// find a literal to swap with:
for (unsigned i = m_bound + 1; i < sz; ++i) {
literal lit2 = m_args[i];
if (ctx.get_assignment(lit2) != l_false) {
TRACE("pb", tout << "swap " << lit2 << "\n";);
std::swap(m_args[index], m_args[i]);
th.watch_literal(lit2, this);
return l_undef;
}
}
// conflict
if (0 != index && ctx.get_assignment(m_args[0]) == l_false) {
TRACE("pb", tout << "conflict " << m_args[0] << " " << lit << "\n";);
set_conflict(th, lit);
return l_false;
}
TRACE("pb", tout << "no swap " << index << " " << lit << "\n";);
// there are no literals to swap with,
// prepare for unit propagation by swapping the false literal into
// position 0. Then literals in positions 1..m_bound have to be
// assigned l_true.
if (index != 0) {
std::swap(m_args[index], m_args[0]);
}
SASSERT(m_args[0] == lit);
literal_vector lits;
lits.push_back(m_lit);
lits.push_back(~lit);
for (unsigned i = m_bound + 1; i < sz; ++i) {
SASSERT(ctx.get_assignment(m_args[i]) == l_false);
lits.push_back(~m_args[i]);
}
for (unsigned i = 1; i <= m_bound; ++i) {
literal lit2 = m_args[i];
lbool value = ctx.get_assignment(lit2);
switch (value) {
case l_true:
break;
case l_false:
TRACE("pb", tout << "conflict: " << lit << " " << lit2 << "\n";);
set_conflict(th, lit2);
return l_false;
case l_undef:
SASSERT(validate_assign(th, lits, lit2));
th.add_assign(*this, lits, lit2);
break;
}
}
return l_true;
}
/**
\brief The conflict clause position for cardinality constraint have the following properties:
0. The position for the literal corresponding to the cardinality constraint.
1. The literal at position 0 of the cardinality constraint.
2. The asserting literal.
3. .. the remaining false literals.
*/
void theory_pb::card::set_conflict(theory_pb& th, literal l) {
SASSERT(validate_conflict(th));
context& ctx = th.get_context();
literal l0 = m_args[0];
literal_vector lits;
SASSERT(ctx.get_assignment(l0) == l_false);
SASSERT(ctx.get_assignment(l) == l_false);
SASSERT(ctx.get_assignment(lit()) == l_true);
lits.push_back(~lit());
lits.push_back(l0);
lits.push_back(l);
unsigned sz = size();
for (unsigned i = m_bound + 1; i < sz; ++i) {
SASSERT(ctx.get_assignment(m_args[i]) == l_false);
lits.push_back(m_args[i]);
}
th.add_clause(*this, lits);
}
bool theory_pb::card::validate_conflict(theory_pb& th) {
context& ctx = th.get_context();
unsigned num_false = 0;
for (unsigned i = 0; i < size(); ++i) {
if (ctx.get_assignment(m_args[i]) == l_false) {
++num_false;
}
}
return size() - num_false < m_bound;
}
bool theory_pb::card::validate_assign(theory_pb& th, literal_vector const& lits, literal l) {
context& ctx = th.get_context();
SASSERT(ctx.get_assignment(l) == l_undef);
for (unsigned i = 0; i < lits.size(); ++i) {
SASSERT(ctx.get_assignment(lits[i]) == l_true);
}
return size() - lits.size() <= m_bound;
}
void theory_pb::card::init_watch(theory_pb& th, bool is_true) {
context& ctx = th.get_context();
if (lit().sign() == is_true) {
negate();
}
// put the non-false literals into the head.
unsigned i = 0, j = 0, sz = size();
for (i = 0; i < sz; ++i) {
if (ctx.get_assignment(lit(i)) != l_false) {
if (j != i) {
std::swap(m_args[i], m_args[j]);
}
++j;
}
}
// j is the number of non-false, sz - j the number of false.
if (j < m_bound) {
std::swap(m_args[0], m_args[m_bound-1]);
//
// 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.
if (ctx.get_assign_level(m_args[0]) > ctx.get_assign_level(m_args[m_bound])) {
std::swap(m_args[0], m_args[m_bound]);
}
for (i = m_bound + 1; i < sz; ++i) {
if (ctx.get_assign_level(m_args[i]) > ctx.get_assign_level(m_args[m_bound])) {
std::swap(m_args[i], m_args[m_bound]);
}
}
set_conflict(th, m_args[m_bound]);
}
else if (j == m_bound) {
literal_vector lits(size() - m_bound, m_args.c_ptr() + m_bound);
for (i = 0; i < j; ++i) {
if (ctx.get_assignment(lit(i)) == l_undef) {
th.add_assign(*this, lits, lit(i));
}
}
}
else {
for (unsigned i = 0; i <= m_bound; ++i) {
th.watch_literal(lit(i), this);
}
}
}
void theory_pb::card::add_arg(literal lit) {
if (lit == false_literal) {
return;
}
else if (lit == true_literal) {
if (m_bound > 0) {
--m_bound;
}
}
else {
m_args.push_back(lit);
}
}
void theory_pb::card::inc_propagations(theory_pb& th) {
++m_num_propagations;
if (m_compiled == l_false && m_num_propagations >= m_compilation_threshold) {
// m_compiled = l_undef;
// th.m_to_compile.push_back(&c);
}
}
// ------------------------
// theory_pb
theory_pb::theory_pb(ast_manager& m, theory_pb_params& p):
theory(m.mk_family_id("pb")),
m_params(p),
m_simplex(m.limit()),
m_util(m),
m_max_compiled_coeff(rational(8))
{
m_learn_complements = p.m_pb_learn_complements;
m_conflict_frequency = p.m_pb_conflict_frequency;
m_enable_compilation = p.m_pb_enable_compilation;
m_enable_simplex = p.m_pb_enable_simplex;
}
theory_pb::~theory_pb() {
reset_eh();
}
theory * theory_pb::mk_fresh(context * new_ctx) {
return alloc(theory_pb, new_ctx->get_manager(), m_params);
}
class theory_pb::remove_var : public trail<context> {
theory_pb& pb;
unsigned v;
public:
remove_var(theory_pb& pb, unsigned v): pb(pb), v(v) {}
virtual void undo(context& ctx) {
pb.m_vars.remove(v);
pb.m_simplex.unset_lower(v);
pb.m_simplex.unset_upper(v);
}
};
class theory_pb::undo_bound : public trail<context> {
theory_pb& pb;
unsigned m_v;
bool m_is_lower;
scoped_eps_numeral m_last_bound;
bool m_last_bound_valid;
literal m_last_explain;
public:
undo_bound(theory_pb& pb, unsigned v,
bool is_lower,
scoped_eps_numeral& last_bound,
bool last_bound_valid,
literal last_explain):
pb(pb),
m_v(v),
m_is_lower(is_lower),
m_last_bound(last_bound),
m_last_bound_valid(last_bound_valid),
m_last_explain(last_explain) {}
virtual void undo(context& ctx) {
if (m_is_lower) {
if (m_last_bound_valid) {
pb.m_simplex.set_lower(m_v, m_last_bound);
}
else {
pb.m_simplex.unset_lower(m_v);
}
pb.set_explain(pb.m_explain_lower, m_v, m_last_explain);
}
else {
if (m_last_bound_valid) {
pb.m_simplex.set_upper(m_v, m_last_bound);
}
else {
pb.m_simplex.unset_upper(m_v);
}
pb.set_explain(pb.m_explain_upper, m_v, m_last_explain);
}
m_last_bound.reset();
}
};
literal theory_pb::set_explain(literal_vector& explains, unsigned var, literal expl) {
if (var >= explains.size()) {
explains.resize(var+1, null_literal);
}
literal last_explain = explains[var];
explains[var] = expl;
return last_explain;
}
bool theory_pb::update_bound(bool_var v, literal explain, bool is_lower, mpq_inf const& bound) {
if (is_lower) {
if (m_simplex.above_lower(v, bound)) {
scoped_eps_numeral last_bound(m_mpq_inf_mgr);
if (m_simplex.upper_valid(v)) {
m_simplex.get_upper(v, last_bound);
if (m_mpq_inf_mgr.gt(bound, last_bound)) {
literal lit = m_explain_upper.get(v, null_literal);
TRACE("pb", tout << ~lit << " " << ~explain << "\n";);
get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain));
return false;
}
}
bool last_bound_valid = m_simplex.lower_valid(v);
if (last_bound_valid) {
m_simplex.get_lower(v, last_bound);
}
m_simplex.set_lower(v, bound);
literal last_explain = set_explain(m_explain_lower, v, explain);
get_context().push_trail(undo_bound(*this, v, true, last_bound, last_bound_valid, last_explain));
}
}
else {
if (m_simplex.below_upper(v, bound)) {
scoped_eps_numeral last_bound(m_mpq_inf_mgr);
if (m_simplex.lower_valid(v)) {
m_simplex.get_lower(v, last_bound);
if (m_mpq_inf_mgr.gt(last_bound, bound)) {
literal lit = m_explain_lower.get(v, null_literal);
TRACE("pb", tout << ~lit << " " << ~explain << "\n";);
get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain));
return false;
}
}
bool last_bound_valid = m_simplex.upper_valid(v);
if (last_bound_valid) {
m_simplex.get_upper(v, last_bound);
}
m_simplex.set_upper(v, bound);
literal last_explain = set_explain(m_explain_upper, v, explain);
get_context().push_trail(undo_bound(*this, v, false, last_bound, last_bound_valid, last_explain));
}
}
return true;
};
bool theory_pb::check_feasible() {
context& ctx = get_context();
lbool is_sat = m_simplex.make_feasible();
if (l_false != is_sat) {
return true;
}
row r = m_simplex.get_infeasible_row();
mpz const& coeff = m_simplex.get_base_coeff(r);
bool_var base_var = m_simplex.get_base_var(r);
SASSERT(m_simplex.below_lower(base_var) || m_simplex.above_upper(base_var));
bool cant_increase = m_simplex.below_lower(base_var)?m_mpz_mgr.is_pos(coeff):m_mpz_mgr.is_neg(coeff);
literal_vector explains;
row_iterator it = m_simplex.row_begin(r), end = m_simplex.row_end(r);
for (; it != end; ++it) {
bool_var v = it->m_var;
if (v == base_var) {
if (m_simplex.below_lower(base_var)) {
explains.push_back(m_explain_lower.get(v, null_literal));
}
else {
explains.push_back(m_explain_upper.get(v, null_literal));
}
}
else if (cant_increase == m_mpz_mgr.is_pos(it->m_coeff)) {
explains.push_back(m_explain_lower.get(v, null_literal));
}
else {
explains.push_back(m_explain_upper.get(v, null_literal));
}
}
literal_vector lits;
for (unsigned i = 0; i < explains.size(); ++i) {
literal lit(explains[i]);
if (lit != null_literal) {
lits.push_back(~lit);
}
}
m_stats.m_num_conflicts++;
justification* js = 0;
if (proofs_enabled()) {
js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr());
}
TRACE("pb", tout << lits << "\n";);
ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0);
return false;
}
bool theory_pb::internalize_atom(app * atom, bool gate_ctx) {
context& ctx = get_context();
TRACE("pb", tout << mk_pp(atom, get_manager()) << "\n";);
if (ctx.b_internalized(atom)) {
return false;
}
SASSERT(!ctx.b_internalized(atom));
m_stats.m_num_predicates++;
if (m_util.is_aux_bool(atom)) {
bool_var abv = ctx.mk_bool_var(atom);
ctx.set_var_theory(abv, get_id());
return true;
}
if (internalize_card(atom, gate_ctx)) {
return true;
}
SASSERT(m_util.is_at_most_k(atom) || m_util.is_le(atom) ||
m_util.is_ge(atom) || m_util.is_at_least_k(atom) ||
m_util.is_eq(atom));
unsigned num_args = atom->get_num_args();
bool_var abv = ctx.mk_bool_var(atom);
ctx.set_var_theory(abv, get_id());
ineq* c = alloc(ineq, m_mpz_mgr, literal(abv), m_util.is_eq(atom));
c->m_args[0].m_k = m_util.get_k(atom);
numeral& k = c->m_args[0].m_k;
arg_t& args = c->m_args[0];
// extract literals and coefficients.
for (unsigned i = 0; i < num_args; ++i) {
expr* arg = atom->get_arg(i);
literal l = compile_arg(arg);
numeral c = m_util.get_coeff(atom, i);
args.push_back(std::make_pair(l, c));
}
if (m_util.is_at_most_k(atom) || m_util.is_le(atom)) {
// turn W <= k into -W >= -k
for (unsigned i = 0; i < args.size(); ++i) {
args[i].second = -args[i].second;
}
k = -k;
}
else {
SASSERT(m_util.is_at_least_k(atom) || m_util.is_ge(atom) || m_util.is_eq(atom));
}
TRACE("pb", display(tout, *c););
//app_ref fml1(m), fml2(m);
//fml1 = c->to_expr(ctx, m);
c->unique();
lbool is_true = c->normalize();
c->prune();
c->post_prune();
//fml2 = c->to_expr(ctx, m);
//expr_ref validate_pb = pb_rewriter(m).mk_validate_rewrite(fml1, fml2);
//pb_rewriter(m).dump_pb_rewrite(validate_pb);
literal lit(abv);
TRACE("pb", display(tout, *c); tout << " := " << lit << "\n";);
switch(is_true) {
case l_false:
lit = ~lit;
// fall-through
case l_true:
ctx.mk_th_axiom(get_id(), 1, &lit);
dealloc(c);
return true;
case l_undef:
break;
}
if (c->k().is_one() && c->is_ge() && !m_enable_simplex) {
literal_vector& lits = get_lits();
lits.push_back(~lit);
for (unsigned i = 0; i < c->size(); ++i) {
lits.push_back(c->lit(i));
SASSERT(c->coeff(i).is_one());
ctx.mk_th_axiom(get_id(), lit, ~c->lit(i));
}
ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
return true;
}
// maximal coefficient:
scoped_mpz& max_watch = c->m_max_watch;
max_watch.reset();
for (unsigned i = 0; i < args.size(); ++i) {
mpz const& num = args[i].second.to_mpq().numerator();
if (m_mpz_mgr.lt(max_watch, num)) {
max_watch = num;
}
}
// pre-compile threshold for cardinality
bool enable_compile = m_enable_compilation && c->is_ge() && !c->k().is_one();
for (unsigned i = 0; enable_compile && i < args.size(); ++i) {
enable_compile = (args[i].second <= m_max_compiled_coeff);
}
if (enable_compile) {
unsigned log = 1, n = 1;
while (n <= args.size()) {
++log;
n *= 2;
}
unsigned th = args.size()*log*log;
c->m_compilation_threshold = th;
IF_VERBOSE(2, verbose_stream() << "(smt.pb setting compilation threshold to " << th << ")\n";);
TRACE("pb", tout << "compilation threshold: " << th << "\n";);
}
else {
c->m_compilation_threshold = UINT_MAX;
}
init_watch_var(*c);
init_watch(abv);
m_var_infos[abv].m_ineq = c;
m_ineqs_trail.push_back(abv);
if (m_enable_simplex) {
//
// TBD: using abv as slack identity doesn't quite
// work if psuedo-Booleans are used
// in a nested way. So assume
//
arg_t rep(c->args());
rep.remove_negations(); // normalize representative
numeral k = rep.k();
theory_var slack;
bool_var abv2;
TRACE("pb", display(tout << abv <<"\n", rep););
if (m_ineq_rep.find(rep, abv2)) {
slack = abv2;
TRACE("pb",
tout << "Old row: " << abv << " |-> " << slack << " ";
tout << m_ineq_row_info.find(abv2).m_bound << " vs. " << k << "\n";
display(tout, rep););
}
else {
m_ineq_rep.insert(rep, abv);
svector<unsigned> vars;
scoped_mpz_vector coeffs(m_mpz_mgr);
for (unsigned i = 0; i < rep.size(); ++i) {
unsigned v = rep.lit(i).var();
m_simplex.ensure_var(v);
vars.push_back(v);
if (!m_vars.contains(v)) {
mpq_inf zero(mpq(0),mpq(0)), one(mpq(1),mpq(0));
switch(ctx.get_assignment(rep.lit(i))) {
case l_true:
VERIFY(update_bound(v, literal(v), true, one));
m_simplex.set_lower(v, one);
break;
case l_false:
VERIFY(update_bound(v, ~literal(v), false, zero));
m_simplex.set_upper(v, zero);
break;
default:
m_simplex.set_lower(v, zero);
m_simplex.set_upper(v, one);
break;
}
m_vars.insert(v);
ctx.push_trail(remove_var(*this, v));
}
coeffs.push_back(rep.coeff(i).to_mpq().numerator());
}
slack = abv;
m_simplex.ensure_var(slack);
vars.push_back(slack);
coeffs.push_back(mpz(-1));
m_simplex.add_row(slack, vars.size(), vars.c_ptr(), coeffs.c_ptr());
TRACE("pb", tout << "New row: " << abv << " " << k << "\n"; display(tout, rep););
}
m_ineq_row_info.insert(abv, row_info(slack, k, rep));
}
TRACE("pb", display(tout, *c););
return true;
}
literal theory_pb::compile_arg(expr* arg) {
context& ctx = get_context();
ast_manager& m = get_manager();
bool_var bv;
bool has_bv = false;
bool negate = m.is_not(arg, arg);
SASSERT(!m.is_not(arg));
if (!ctx.b_internalized(arg)) {
ctx.internalize(arg, false);
}
if (ctx.b_internalized(arg)) {
bv = ctx.get_bool_var(arg);
if (is_uninterp(arg) && null_theory_var == ctx.get_var_theory(bv)) {
ctx.set_var_theory(bv, get_id());
}
has_bv = (ctx.get_var_theory(bv) == get_id());
}
else if (m.is_true(arg)) {
bv = true_bool_var;
has_bv = true;
}
else if (m.is_false(arg)) {
bv = true_bool_var;
has_bv = true;
negate = !negate;
}
// assumes relevancy level = 2 or 0.
// TBD: should should have been like an uninterpreted
// function internalize, where enodes for each argument
// is available.
if (!has_bv) {
app_ref tmp(m), fml(m);
pb_util pb(m);
tmp = pb.mk_fresh_bool();
fml = m.mk_iff(tmp, arg);
TRACE("pb", tout << "create proxy " << fml << "\n";);
ctx.internalize(fml, false);
SASSERT(ctx.b_internalized(tmp));
bv = ctx.get_bool_var(tmp);
SASSERT(get_id() == ctx.get_var_theory(bv));
literal lit(ctx.get_bool_var(fml));
ctx.mk_th_axiom(get_id(), 1, &lit);
ctx.mark_as_relevant(tmp.get());
}
return negate?~literal(bv):literal(bv);
}
void theory_pb::del_watch(ineq_watch& watch, unsigned index, ineq& c, unsigned ineq_index) {
SASSERT(c.is_ge());
if (index < watch.size()) {
std::swap(watch[index], watch[watch.size()-1]);
}
watch.pop_back();
SASSERT(ineq_index < c.watch_size());
scoped_mpz coeff(m_mpz_mgr);
coeff = c.ncoeff(ineq_index);
if (ineq_index + 1 < c.watch_size()) {
std::swap(c.args()[ineq_index], c.args()[c.watch_size()-1]);
}
--c.m_watch_sz;
c.m_watch_sum -= coeff;
if (coeff == c.max_watch()) {
coeff = c.ncoeff(0);
for (unsigned i = 1; coeff != c.max_watch() && i < c.watch_size(); ++i) {
if (coeff < c.ncoeff(i)) coeff = c.ncoeff(i);
}
c.set_max_watch(coeff);
}
// current index of unwatched literal is c.watch_size().
}
void theory_pb::add_watch(ineq& c, unsigned i) {
SASSERT(c.is_ge());
literal lit = c.lit(i);
scoped_mpz coeff(m_mpz_mgr);
coeff = c.ncoeff(i);
c.m_watch_sum += coeff;
SASSERT(i >= c.watch_size());
if (i > c.watch_size()) {
std::swap(c.args()[i], c.args()[c.watch_size()]);
}
++c.m_watch_sz;
if (coeff > c.max_watch()) {
c.set_max_watch(coeff);
}
watch_literal(lit, &c);
}
void theory_pb::init_watch(bool_var v) {
if (m_var_infos.size() <= static_cast<unsigned>(v)) {
m_var_infos.resize(static_cast<unsigned>(v)+100);
}
}
void theory_pb::watch_literal(literal lit, ineq* c) {
init_watch(lit.var());
ptr_vector<ineq>* ineqs = m_var_infos[lit.var()].m_lit_watch[lit.sign()];
if (ineqs == 0) {
ineqs = alloc(ptr_vector<ineq>);
m_var_infos[lit.var()].m_lit_watch[lit.sign()] = ineqs;
}
ineqs->push_back(c);
}
void theory_pb::watch_var(bool_var v, ineq* c) {
init_watch(v);
ptr_vector<ineq>* ineqs = m_var_infos[v].m_var_watch;
if (ineqs == 0) {
ineqs = alloc(ptr_vector<ineq>);
m_var_infos[v].m_var_watch = ineqs;
}
ineqs->push_back(c);
}
void theory_pb::unwatch_var(bool_var v, ineq* c) {
ptr_vector<ineq>* ineqs = m_var_infos[v].m_var_watch;
if (ineqs) {
remove(*ineqs, c);
}
}
void theory_pb::unwatch_literal(literal lit, ineq* c) {
ptr_vector<ineq>* ineqs = m_var_infos[lit.var()].m_lit_watch[lit.sign()];
if (ineqs) {
remove(*ineqs, c);
}
}
void theory_pb::remove(ptr_vector<ineq>& ineqs, ineq* c) {
for (unsigned j = 0; j < ineqs.size(); ++j) {
if (ineqs[j] == c) {
std::swap(ineqs[j], ineqs[ineqs.size()-1]);
ineqs.pop_back();
break;
}
}
}
// ----------------------------
// cardinality constraints
class theory_pb::card_justification : public theory_propagation_justification {
card& m_card;
public:
card_justification(card& c, family_id fid, region & r,
unsigned num_lits, literal const * lits, literal consequent):
theory_propagation_justification(fid, r, num_lits, lits, consequent),
m_card(c)
{}
card& get_card() { return m_card; }
};
bool theory_pb::is_cardinality_constraint(app * atom) {
if (m_util.is_ge(atom) && m_util.has_unit_coefficients(atom)) {
return true;
}
if (m_util.is_at_most_k(atom)) {
return true;
}
// TBD: other conditions
return false;
}
bool theory_pb::internalize_card(app * atom, bool gate_ctx) {
if (!is_cardinality_constraint(atom)) {
return false;
}
context& ctx = get_context();
unsigned num_args = atom->get_num_args();
bool_var abv = ctx.mk_bool_var(atom);
ctx.set_var_theory(abv, get_id());
unsigned bound = m_util.get_k(atom).get_unsigned();
card* c = alloc(card, literal(abv), bound);
for (unsigned i = 0; i < num_args; ++i) {
expr* arg = atom->get_arg(i);
c->add_arg(compile_arg(arg));
}
literal lit(abv);
if (c->k() == 0) {
ctx.mk_th_axiom(get_id(), 1, &lit);
dealloc(c);
}
else if (c->k() > c->size()) {
lit.neg();
ctx.mk_th_axiom(get_id(), 1, &lit);
dealloc(c);
}
else if (c->k() == c->size()) {
literal_vector lits;
for (unsigned i = 0; i < c->size(); ++i) {
lits.push_back(~c->lit(i));
}
lits.push_back(lit);
ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr());
for (unsigned i = 0; i < c->size(); ++i) {
literal lits2[2] = { ~lit, c->lit(i) };
ctx.mk_th_axiom(get_id(), 2, lits2);
}
dealloc(c);
}
else {
SASSERT(0 < c->k() && c->k() < c->size());
// initialize compilation thresholds, TBD
init_watch(abv);
m_var_infos[abv].m_card = c;
m_card_trail.push_back(abv);
}
return true;
}
void theory_pb::watch_literal(literal lit, card* c) {
init_watch(lit.var());
ptr_vector<card>* cards = m_var_infos[lit.var()].m_lit_cwatch[lit.sign()];
if (cards == 0) {
cards = alloc(ptr_vector<card>);
m_var_infos[lit.var()].m_lit_cwatch[lit.sign()] = cards;
}
cards->push_back(c);
}
void theory_pb::unwatch_literal(literal lit, card* c) {
if (m_var_infos.size() <= static_cast<unsigned>(lit.var())) {
return;
}
ptr_vector<card>* cards = m_var_infos[lit.var()].m_lit_cwatch[lit.sign()];
if (cards) {
remove(*cards, c);
}
}
void theory_pb::remove(ptr_vector<card>& cards, card* c) {
for (unsigned j = 0; j < cards.size(); ++j) {
if (cards[j] == c) {
std::swap(cards[j], cards[cards.size()-1]);
cards.pop_back();
break;
}
}
}
std::ostream& theory_pb::display(std::ostream& out, card const& c, bool values) const {
ast_manager& m = get_manager();
context& ctx = get_context();
out << c.lit();
if (c.lit() != null_literal) {
if (values) {
out << "@(" << ctx.get_assignment(c.lit());
if (ctx.get_assignment(c.lit()) != l_undef) {
out << ":" << ctx.get_assign_level(c.lit());
}
out << ")";
}
ctx.display_literal_verbose(out, c.lit()); out << "\n";
}
else {
out << " ";
}
for (unsigned i = 0; i < c.size(); ++i) {
literal l = c.lit(i);
out << l;
if (values) {
out << "@(" << ctx.get_assignment(l);
if (ctx.get_assignment(l) != l_undef) {
out << ":" << ctx.get_assign_level(l);
}
out << ") ";
}
}
out << " >= " << c.k() << "\n";
if (c.num_propagations()) out << "propagations: " << c.num_propagations() << "\n";
return out;
}
void theory_pb::add_clause(card& c, literal_vector const& lits) {
m_stats.m_num_conflicts++;
context& ctx = get_context();
justification* js = 0;
if (proofs_enabled()) {
js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr());
}
resolve_conflict(c, lits);
c.inc_propagations(*this);
ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0);
}
void theory_pb::add_assign(card& c, literal_vector const& lits, literal l) {
c.inc_propagations(*this);
m_stats.m_num_propagations++;
context& ctx = get_context();
TRACE("pb", tout << "#prop: " << c.num_propagations() << " - " << lits << " " << c.lit() << " => " << l << "\n";);
ctx.assign(l, ctx.mk_justification(
card_justification(
c, get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), l)));
}
void theory_pb::clear_watch(card& c) {
for (unsigned i = 0; i <= c.k(); ++i) {
literal w = c.lit(i);
unwatch_literal(w, &c);
}
}
//
void theory_pb::collect_statistics(::statistics& st) const {
st.update("pb conflicts", m_stats.m_num_conflicts);
st.update("pb propagations", m_stats.m_num_propagations);
st.update("pb predicates", m_stats.m_num_predicates);
st.update("pb compilations", m_stats.m_num_compiles);
st.update("pb compiled clauses", m_stats.m_num_compiled_clauses);
st.update("pb compiled vars", m_stats.m_num_compiled_vars);
m_simplex.collect_statistics(st);
}
void theory_pb::reset_eh() {
for (unsigned i = 0; i < m_var_infos.size(); ++i) {
m_var_infos[i].reset();
}
m_ineqs_trail.reset();
m_ineqs_lim.reset();
m_card_trail.reset();
m_card_lim.reset();
m_stats.reset();
m_to_compile.reset();
}
void theory_pb::new_eq_eh(theory_var v1, theory_var v2) {
UNREACHABLE();
}
final_check_status theory_pb::final_check_eh() {
TRACE("pb", display(tout););
DEBUG_CODE(validate_final_check(););
return FC_DONE;
}
void theory_pb::assign_eh(bool_var v, bool is_true) {
ptr_vector<ineq>* ineqs = 0;
context& ctx = get_context();
literal nlit(v, is_true);
init_watch(v);
TRACE("pb", tout << "assign: " << ~nlit << "\n";);
ineqs = m_var_infos[v].m_lit_watch[nlit.sign()];
if (ineqs != 0) {
if (m_enable_simplex) {
mpq_inf num(mpq(is_true?1:0),mpq(0));
if (!update_bound(v, ~nlit, is_true, num)) {
return;
}
if (!check_feasible()) {
return;
}
}
for (unsigned i = 0; i < ineqs->size(); ++i) {
SASSERT((*ineqs)[i]->is_ge());
if (assign_watch_ge(v, is_true, *ineqs, i)) {
// i was removed from watch list.
--i;
}
}
}
ineqs = m_var_infos[v].m_var_watch;
if (ineqs != 0) {
for (unsigned i = 0; i < ineqs->size(); ++i) {
ineq* c = (*ineqs)[i];
assign_watch(v, is_true, *c);
}
}
ineq* c = m_var_infos[v].m_ineq;
if (c != 0) {
if (m_enable_simplex) {
row_info const& info = m_ineq_row_info.find(v);
scoped_eps_numeral coeff(m_mpq_inf_mgr);
coeff = std::make_pair(info.m_bound.to_mpq(), mpq(0));
unsigned slack = info.m_slack;
if (is_true) {
update_bound(slack, literal(v), true, coeff);
if (c->is_eq()) {
update_bound(slack, literal(v), false, coeff);
}
}
else if (c->is_ge()) {
m_mpq_inf_mgr.sub(coeff, std::make_pair(mpq(1),mpq(0)), coeff);
update_bound(slack, ~literal(v), false, coeff);
}
if (!check_feasible()) {
return;
}
}
if (c->is_ge()) {
assign_ineq(*c, is_true);
}
else {
assign_eq(*c, is_true);
}
}
ptr_vector<card>* cards = m_var_infos[v].m_lit_cwatch[nlit.sign()];
if (cards != 0 && !ctx.inconsistent()) {
ptr_vector<card>::iterator it = cards->begin(), it2 = it, end = cards->end();
for (; it != end; ++it) {
if (ctx.get_assignment((*it)->lit()) != l_true) {
continue;
}
switch ((*it)->assign(*this, nlit)) {
case l_false: // conflict
for (; it != end; ++it, ++it2) {
*it2 = *it;
}
cards->set_end(it2);
return;
case l_undef: // watch literal was swapped
break;
case l_true: // unit propagation, keep watching the literal
if (it2 != it) {
*it2 = *it;
}
++it2;
break;
}
}
cards->set_end(it2);
}
card* crd = m_var_infos[v].m_card;
if (crd != 0 && !ctx.inconsistent()) {
crd->init_watch(*this, is_true);
}
}
literal_vector& theory_pb::get_all_literals(ineq& c, bool negate) {
context& ctx = get_context();
literal_vector& lits = get_lits();
for (unsigned i = 0; i < c.size(); ++i) {
literal l = c.lit(i);
switch(ctx.get_assignment(l)) {
case l_true:
lits.push_back(negate?(~l):l);
break;
case l_false:
lits.push_back(negate?l:(~l));
break;
default:
break;
}
}
return lits;
}
literal_vector& theory_pb::get_helpful_literals(ineq& c, bool negate) {
scoped_mpz sum(m_mpz_mgr);
mpz const& k = c.mpz_k();
context& ctx = get_context();
literal_vector& lits = get_lits();
for (unsigned i = 0; sum < k && i < c.size(); ++i) {
literal l = c.lit(i);
if (ctx.get_assignment(l) == l_true) {
sum += c.ncoeff(i);
if (negate) l = ~l;
lits.push_back(l);
}
}
SASSERT(sum >= k);
return lits;
}
literal_vector& theory_pb::get_unhelpful_literals(ineq& c, bool negate) {
context& ctx = get_context();
literal_vector& lits = get_lits();
for (unsigned i = 0; i < c.size(); ++i) {
literal l = c.lit(i);
if (ctx.get_assignment(l) == l_false) {
if (negate) l = ~l;
lits.push_back(l);
}
}
return lits;
}
class theory_pb::rewatch_vars : public trail<context> {
theory_pb& pb;
ineq& c;
public:
rewatch_vars(theory_pb& p, ineq& c): pb(p), c(c) {}
virtual void undo(context& ctx) {
for (unsigned i = 0; i < c.size(); ++i) {
pb.watch_var(c.lit(i).var(), &c);
}
}
};
class theory_pb::negate_ineq : public trail<context> {
ineq& c;
public:
negate_ineq(ineq& c): c(c) {}
virtual void undo(context& ctx) {
c.negate();
}
};
/**
\brief propagate assignment to inequality.
This is a basic, non-optimized implementation based
on the assumption that inequalities are mostly units
and/or relatively few compared to number of argumets.
*/
void theory_pb::assign_ineq(ineq& c, bool is_true) {
context& ctx = get_context();
ctx.push_trail(value_trail<context, scoped_mpz>(c.m_max_sum));
ctx.push_trail(value_trail<context, scoped_mpz>(c.m_min_sum));
ctx.push_trail(value_trail<context, unsigned>(c.m_nfixed));
ctx.push_trail(rewatch_vars(*this, c));
SASSERT(c.is_ge());
unsigned sz = c.size();
if (c.lit().sign() == is_true) {
c.negate();
ctx.push_trail(negate_ineq(c));
}
scoped_mpz maxsum(m_mpz_mgr), mininc(m_mpz_mgr);
for (unsigned i = 0; i < sz; ++i) {
lbool asgn = ctx.get_assignment(c.lit(i));
if (asgn != l_false) {
maxsum += c.ncoeff(i);
}
if (asgn == l_undef && (mininc.is_zero() || mininc > c.ncoeff(i))) {
mininc = c.ncoeff(i);
}
}
TRACE("pb",
tout << "assign: " << c.lit() << "\n";
display(tout, c); );
if (maxsum < c.mpz_k()) {
literal_vector& lits = get_unhelpful_literals(c, false);
lits.push_back(~c.lit());
add_clause(c, lits);
}
else {
init_watch_literal(c);
SASSERT(c.m_watch_sum >= c.mpz_k());
DEBUG_CODE(validate_watch(c););
}
// perform unit propagation
if (maxsum >= c.mpz_k() && maxsum - mininc < c.mpz_k()) {
literal_vector& lits = get_unhelpful_literals(c, true);
lits.push_back(c.lit());
for (unsigned i = 0; i < sz; ++i) {
if (ctx.get_assignment(c.lit(i)) == l_undef) {
DEBUG_CODE(validate_assign(c, lits, c.lit(i)););
add_assign(c, lits, c.lit(i));
}
}
}
}
/**
\brief propagate assignment to equality.
*/
void theory_pb::assign_eq(ineq& c, bool is_true) {
SASSERT(c.is_eq());
}
/**
Propagation rules:
nfixed = N & minsum = k -> T
nfixed = N & minsum != k -> F
minsum > k or maxsum < k -> F
minsum = k & = -> fix 0 variables
nfixed+1 = N & = -> fix unassigned variable or conflict
nfixed+1 = N & != -> maybe forced unassigned to ensure disequal
minsum >= k -> T
maxsum < k -> F
*/
void theory_pb::assign_watch(bool_var v, bool is_true, ineq& c) {
context& ctx = get_context();
unsigned i;
literal l = c.lit();
lbool asgn = ctx.get_assignment(l);
if (c.max_sum() < c.mpz_k() && asgn == l_false) {
return;
}
if (c.is_ge() && c.min_sum() >= c.mpz_k() && asgn == l_true) {
return;
}
for (i = 0; i < c.size(); ++i) {
if (c.lit(i).var() == v) {
break;
}
}
TRACE("pb", display(tout << "assign watch " << literal(v,!is_true) << " ", c, true););
SASSERT(i < c.size());
if (c.lit(i).sign() == is_true) {
ctx.push_trail(value_trail<context, scoped_mpz>(c.m_max_sum));
c.m_max_sum -= c.ncoeff(i);
}
else {
ctx.push_trail(value_trail<context, scoped_mpz>(c.m_min_sum));
c.m_min_sum += c.ncoeff(i);
}
DEBUG_CODE(
scoped_mpz sum(m_mpz_mgr);
scoped_mpz maxs(m_mpz_mgr);
for (unsigned i = 0; i < c.size(); ++i) {
if (ctx.get_assignment(c.lit(i)) == l_true) sum += c.ncoeff(i);
if (ctx.get_assignment(c.lit(i)) != l_false) maxs += c.ncoeff(i);
}
CTRACE("pb", (maxs > c.max_sum()), display(tout, c, true););
SASSERT(c.min_sum() <= sum);
SASSERT(sum <= maxs);
SASSERT(maxs <= c.max_sum());
);
SASSERT(c.min_sum() <= c.max_sum());
SASSERT(!m_mpz_mgr.is_neg(c.min_sum()));
ctx.push_trail(value_trail<context, unsigned>(c.m_nfixed));
++c.m_nfixed;
SASSERT(c.nfixed() <= c.size());
if (c.is_ge() && c.min_sum() >= c.mpz_k() && asgn != l_true) {
TRACE("pb", display(tout << "Set " << l << "\n", c, true););
add_assign(c, get_helpful_literals(c, false), l);
}
else if (c.max_sum() < c.mpz_k() && asgn != l_false) {
TRACE("pb", display(tout << "Set " << ~l << "\n", c, true););
add_assign(c, get_unhelpful_literals(c, true), ~l);
}
else if (c.is_eq() && c.nfixed() == c.size() && c.min_sum() == c.mpz_k() && asgn != l_true) {
TRACE("pb", display(tout << "Set " << l << "\n", c, true););
add_assign(c, get_all_literals(c, false), l);
}
else if (c.is_eq() && c.nfixed() == c.size() && c.min_sum() != c.mpz_k() && asgn != l_false) {
TRACE("pb", display(tout << "Set " << ~l << "\n", c, true););
add_assign(c, get_all_literals(c, false), ~l);
}
#if 0
else if (c.is_eq() && c.min_sum() > c.mpz_k() && asgn != l_false) {
TRACE("pb", display(tout << "Set " << ~l << "\n", c, true););
add_assign(c, get_all_literals(c, false), ~l);
}
else if (c.is_eq() && asgn == l_true && c.min_sum() == c.mpz_k() && c.max_sum() > c.mpz_k()) {
literal_vector& lits = get_all_literals(c, false);
lits.push_back(c.lit());
for (unsigned i = 0; i < c.size(); ++i) {
if (ctx.get_assignment(c.lit(i)) == l_undef) {
add_assign(c, lits, ~c.lit(i));
}
}
}
#endif
else {
IF_VERBOSE(14, display(verbose_stream() << "no propagation ", c, true););
}
}
/**
\brief v is assigned in inequality c. Update current bounds and watch list.
Optimize for case where the c.lit() is True. This covers the case where
inequalities are unit literals and formulas in negation normal form
(inequalities are closed under negation).
*/
bool theory_pb::assign_watch_ge(bool_var v, bool is_true, ineq_watch& watch, unsigned watch_index) {
bool removed = false;
context& ctx = get_context();
ineq& c = *watch[watch_index];
unsigned w = c.find_lit(v, 0, c.watch_size());
SASSERT(ctx.get_assignment(c.lit()) == l_true);
SASSERT(is_true == c.lit(w).sign());
//
// watch_sum is decreased.
// Adjust set of watched literals.
//
scoped_mpz k_coeff(m_mpz_mgr), k(m_mpz_mgr);
k = c.mpz_k();
k_coeff = k;
k_coeff += c.ncoeff(w);
bool add_more = c.watch_sum() < k_coeff + c.max_watch();
for (unsigned i = c.watch_size(); add_more && i < c.size(); ++i) {
if (ctx.get_assignment(c.lit(i)) != l_false) {
add_watch(c, i);
add_more = c.watch_sum() < k_coeff + c.max_watch();
}
}
if (c.watch_sum() < k_coeff) {
//
// L: 3*x1 + 2*x2 + x4 >= 3, but x1 <- 0, x2 <- 0
// create clause x1 or x2 or ~L
//
literal_vector& lits = get_unhelpful_literals(c, false);
lits.push_back(~c.lit());
add_clause(c, lits);
}
else {
del_watch(watch, watch_index, c, w);
removed = true;
SASSERT(c.watch_sum() >= k);
if (c.watch_sum() < k + c.max_watch()) {
//
// opportunities for unit propagation for unassigned
// literals whose coefficients satisfy
// c.watch_sum() < k
//
// L: 3*x1 + 2*x2 + x4 >= 3, but x1 <- 0
// Create clauses x1 or ~L or x2
// x1 or ~L or x4
//
literal_vector& lits = get_unhelpful_literals(c, true);
lits.push_back(c.lit());
scoped_mpz deficit(m_mpz_mgr);
deficit = c.watch_sum() - k;
for (unsigned i = 0; i < c.size(); ++i) {
if (ctx.get_assignment(c.lit(i)) == l_undef && deficit < c.ncoeff(i)) {
DEBUG_CODE(validate_assign(c, lits, c.lit(i)););
add_assign(c, lits, c.lit(i));
// break;
}
}
}
//
// else: c.watch_sum() >= k + c.max_watch()
//
}
TRACE("pb",
tout << "assign: " << literal(v,!is_true) << "\n";
display(tout, c); );
return removed;
}
struct theory_pb::psort_expr {
context& ctx;
ast_manager& m;
theory_pb& th;
pb_util pb;
typedef smt::literal literal;
typedef smt::literal_vector literal_vector;
psort_expr(context& c, theory_pb& th):
ctx(c),
m(c.get_manager()),
th(th),
pb(m) {}
literal fresh() {
app_ref y(m);
y = pb.mk_fresh_bool();
return literal(ctx.mk_bool_var(y));
}
literal mk_max(literal a, literal b) {
if (a == b) return a;
expr_ref t1(m), t2(m), t3(m);
ctx.literal2expr(a, t1);
ctx.literal2expr(b, t2);
t3 = m.mk_or(t1, t2);
bool_var v = ctx.b_internalized(t3)?ctx.get_bool_var(t3):ctx.mk_bool_var(t3);
return literal(v);
}
literal mk_min(literal a, literal b) {
if (a == b) return a;
expr_ref t1(m), t2(m), t3(m);
ctx.literal2expr(a, t1);
ctx.literal2expr(b, t2);
t3 = m.mk_and(t1, t2);
bool_var v = ctx.b_internalized(t3)?ctx.get_bool_var(t3):ctx.mk_bool_var(t3);
return literal(v);
}
literal mk_not(literal a) { return ~a; }
void mk_clause(unsigned n, literal const* ls) {
literal_vector tmp(n, ls);
ctx.mk_clause(n, tmp.c_ptr(), th.justify(tmp), CLS_AUX, 0);
}
literal mk_false() { return false_literal; }
literal mk_true() { return true_literal; }
std::ostream& pp(std::ostream& out, literal l) { return out << l; }
};
// for testing
literal theory_pb::assert_ge(context& ctx, unsigned k, unsigned n, literal const* xs) {
theory_pb_params p;
theory_pb th(ctx.get_manager(), p);
psort_expr ps(ctx, th);
psort_nw<psort_expr> sort(ps);
return sort.ge(false, k, n, xs);
}
void theory_pb::inc_propagations(ineq& c) {
++c.m_num_propagations;
if (c.m_compiled == l_false && c.m_num_propagations >= c.m_compilation_threshold) {
c.m_compiled = l_undef;
m_to_compile.push_back(&c);
}
}
void theory_pb::restart_eh() {
for (unsigned i = 0; i < m_to_compile.size(); ++i) {
compile_ineq(*m_to_compile[i]);
}
m_to_compile.reset();
}
void theory_pb::compile_ineq(ineq& c) {
++m_stats.m_num_compiles;
context& ctx = get_context();
// only cardinality constraints are compiled.
SASSERT(c.m_compilation_threshold < UINT_MAX);
DEBUG_CODE(for (unsigned i = 0; i < c.size(); ++i) SASSERT(c.coeff(i).is_int()); );
unsigned k = c.k().get_unsigned();
unsigned num_args = c.size();
literal thl = c.lit();
literal at_least_k;
literal_vector in;
for (unsigned i = 0; i < num_args; ++i) {
rational n = c.coeff(i);
literal lit = c.lit(i);
lbool val = ctx.get_assignment(lit);
if (val != l_undef && ctx.get_assign_level(lit) == ctx.get_base_level()) {
if (val == l_true) {
unsigned m = n.get_unsigned();
if (k < m) {
return;
}
k -= m;
}
continue;
}
while (n.is_pos()) {
in.push_back(c.lit(i));
n -= rational::one();
}
}
TRACE("pb", tout << in << " >= " << k << "\n";);
psort_expr ps(ctx, *this);
psort_nw<psort_expr> sortnw(ps);
sortnw.m_stats.reset();
if (ctx.get_assignment(thl) == l_true &&
ctx.get_assign_level(thl) == ctx.get_base_level()) {
at_least_k = sortnw.ge(false, k, in.size(), in.c_ptr());
TRACE("pb", tout << ~thl << " " << at_least_k << "\n";);
ctx.mk_clause(~thl, at_least_k, justify(~thl, at_least_k));
}
else {
literal at_least_k = sortnw.ge(true, k, in.size(), in.c_ptr());
TRACE("pb", tout << ~thl << " " << at_least_k << "\n";);
ctx.mk_clause(~thl, at_least_k, justify(~thl, at_least_k));
ctx.mk_clause(~at_least_k, thl, justify(thl, ~at_least_k));
}
m_stats.m_num_compiled_vars += sortnw.m_stats.m_num_compiled_vars;
m_stats.m_num_compiled_clauses += sortnw.m_stats.m_num_compiled_clauses;
IF_VERBOSE(2, verbose_stream()
<< "(smt.pb compile sorting network bound: "
<< k << " literals: " << in.size()
<< " clauses: " << sortnw.m_stats.m_num_compiled_clauses
<< " vars: " << sortnw.m_stats.m_num_compiled_vars << ")\n";);
// auxiliary clauses get removed when popping scopes.
// we have to recompile the circuit after back-tracking.
c.m_compiled = l_false;
ctx.push_trail(value_trail<context, lbool>(c.m_compiled));
c.m_compiled = l_true;
}
void theory_pb::init_search_eh() {
}
void theory_pb::push_scope_eh() {
m_ineqs_lim.push_back(m_ineqs_trail.size());
m_card_lim.push_back(m_card_trail.size());
}
void theory_pb::pop_scope_eh(unsigned num_scopes) {
// remove inequalities.
unsigned new_lim = m_ineqs_lim.size()-num_scopes;
unsigned sz = m_ineqs_lim[new_lim];
while (m_ineqs_trail.size() > sz) {
bool_var v = m_ineqs_trail.back();
ineq* c = m_var_infos[v].m_ineq;
clear_watch(*c);
m_var_infos[v].m_ineq = 0;
m_ineqs_trail.pop_back();
if (m_enable_simplex) {
row_info r_info;
VERIFY(m_ineq_row_info.find(v, r_info));
m_ineq_row_info.erase(v);
bool_var v2 = m_ineq_rep.find(r_info.m_rep);
if (v == v2) {
m_simplex.del_row(r_info.m_slack);
m_ineq_rep.erase(r_info.m_rep);
}
}
m_to_compile.erase(c);
dealloc(c);
}
m_ineqs_lim.resize(new_lim);
new_lim = m_card_lim.size() - num_scopes;
sz = m_card_lim[new_lim];
while (m_card_trail.size() > sz) {
bool_var v = m_card_trail.back();
m_card_trail.pop_back();
card* c = m_var_infos[v].m_card;
clear_watch(*c);
m_var_infos[v].m_card = 0;
dealloc(c);
}
m_card_lim.resize(new_lim);
}
void theory_pb::clear_watch(ineq& c) {
for (unsigned i = 0; i < c.size(); ++i) {
literal w = c.lit(i);
unwatch_var(w.var(), &c);
unwatch_literal(w, &c);
}
c.m_watch_sum.reset();
c.m_watch_sz = 0;
c.m_max_watch.reset();
c.m_nfixed = 0;
c.m_max_sum.reset();
c.m_min_sum.reset();
}
class theory_pb::unwatch_ge : public trail<context> {
theory_pb& pb;
ineq& c;
public:
unwatch_ge(theory_pb& p, ineq& c): pb(p), c(c) {}
virtual void undo(context& ctx) {
for (unsigned i = 0; i < c.watch_size(); ++i) {
pb.unwatch_literal(c.lit(i), &c);
}
c.m_watch_sz = 0;
c.m_watch_sum.reset();
c.m_max_watch.reset();
}
};
void theory_pb::init_watch_literal(ineq& c) {
context& ctx = get_context();
scoped_mpz max_k(m_mpz_mgr);
c.m_watch_sum.reset();
c.m_watch_sz = 0;
c.m_max_watch.reset();
bool watch_more = true;
for (unsigned i = 0; watch_more && i < c.size(); ++i) {
if (ctx.get_assignment(c.lit(i)) != l_false) {
add_watch(c, i);
max_k = c.mpz_k();
max_k += c.max_watch();
watch_more = c.m_watch_sum < max_k;
}
}
ctx.push_trail(unwatch_ge(*this, c));
}
void theory_pb::init_watch_var(ineq& c) {
c.m_min_sum.reset();
c.m_max_sum.reset();
c.m_nfixed = 0;
c.m_watch_sum.reset();
c.m_max_watch.reset();
c.m_watch_sz = 0;
for (unsigned i = 0; i < c.size(); ++i) {
watch_var(c.lit(i).var(), &c);
c.m_max_sum += c.ncoeff(i);
}
}
literal_vector& theory_pb::get_lits() {
m_literals.reset();
return m_literals;
}
class theory_pb::pb_justification : public theory_propagation_justification {
ineq& m_ineq;
public:
pb_justification(ineq& c, family_id fid, region & r,
unsigned num_lits, literal const * lits, literal consequent):
theory_propagation_justification(fid, r, num_lits, lits, consequent),
m_ineq(c)
{}
ineq& get_ineq() { return m_ineq; }
};
void theory_pb::add_assign(ineq& c, literal_vector const& lits, literal l) {
inc_propagations(c);
m_stats.m_num_propagations++;
context& ctx = get_context();
TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - " << lits;
tout << " => " << l << "\n";
display(tout, c, true););
ctx.assign(l, ctx.mk_justification(
pb_justification(
c, get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), l)));
}
void theory_pb::add_clause(ineq& c, literal_vector const& lits) {
inc_propagations(c);
m_stats.m_num_conflicts++;
context& ctx = get_context();
TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - " << lits << "\n";
display(tout, c, true););
justification* js = 0;
if (proofs_enabled()) {
js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr());
}
ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0);
}
int theory_pb::get_coeff(bool_var v) const {
return m_coeffs.get(v, 0);
}
void theory_pb::reset_coeffs() {
for (unsigned i = 0; i < m_active_coeffs.size(); ++i) {
m_coeffs[m_active_coeffs[i]] = 0;
}
m_active_coeffs.reset();
}
void theory_pb::process_antecedent(literal l, int offset) {
context& ctx = get_context();
SASSERT(ctx.get_assignment(l) == l_false);
bool_var v = l.var();
unsigned lvl = ctx.get_assign_level(v);
TRACE("pb", tout << l << " " << ctx.is_marked(v) << " " << m_conflict_lvl << " " << ctx.get_base_level() << "\n";);
if (lvl > ctx.get_base_level() && !ctx.is_marked(v) && lvl == m_conflict_lvl) {
ctx.set_mark(v);
++m_num_marks;
}
inc_coeff(l, offset);
}
void theory_pb::process_card(card& c, int offset) {
context& ctx = get_context();
process_antecedent(c.lit(0), offset);
for (unsigned i = c.k() + 1; i < c.size(); ++i) {
process_antecedent(c.lit(i), offset);
}
for (unsigned i = 1; i <= c.k(); ++i) {
inc_coeff(c.lit(i), offset);
}
if (ctx.get_assign_level(c.lit()) > ctx.get_base_level()) {
m_antecedents.push_back(c.lit());
}
SASSERT(ctx.get_assignment(c.lit()) == l_true);
}
void theory_pb::validate_lemma() {
uint_set seen;
int value = -m_bound;
context& ctx = get_context();
for (unsigned i = 0; i < m_active_coeffs.size(); ++i) {
bool_var v = m_active_coeffs[i];
if (seen.contains(v)) {
continue;
}
seen.insert(v);
int coeff = get_coeff(v);
if (coeff == 0) continue;
if (coeff < 0 && ctx.get_assignment(v) != l_true) {
value -= coeff;
}
else if (coeff > 0 && ctx.get_assignment(v) != l_false) {
value += coeff;
}
}
std::cout << "bound: " << m_bound << " value " << value << " lemma is " << (value >= 0 ? "sat" : "unsat") << "\n";
}
void theory_pb::inc_coeff(literal l, int offset) {
SASSERT(offset > 0);
bool_var v = l.var();
SASSERT(v != null_bool_var);
if (static_cast<bool_var>(m_coeffs.size()) <= v) {
m_coeffs.resize(v + 1, 0);
}
int coeff0 = m_coeffs[v];
if (coeff0 == 0) {
m_active_coeffs.push_back(v);
}
int inc = l.sign() ? -offset : offset;
int coeff1 = inc + coeff0;
m_coeffs[v] = coeff1;
if (coeff0 > 0 && 0 > coeff1) {
m_bound += coeff1;
}
else if (coeff0 < 0 && 0 < coeff1) {
m_bound += coeff0;
}
}
/**
\brief attempt a cut and simplification of constraints.
*/
void theory_pb::cut() {
unsigned g = 0;
for (unsigned i = 0; i < m_active_coeffs.size(); ++i) {
bool_var v = m_active_coeffs[i];
int coeff = m_coeffs[v];
if (coeff == 0) {
m_active_coeffs[i] = m_active_coeffs.back();
m_active_coeffs.pop_back();
continue;
}
if (coeff < 0) {
coeff = -coeff;
}
if (m_bound < coeff) {
m_coeffs[v] = m_bound;
}
if (g == 0) {
g = static_cast<unsigned>(coeff);
}
else if (g != 1) {
g = u_gcd(g, static_cast<unsigned>(coeff));
}
}
if (g != 1 && g != 0) {
uint_set seen;
for (unsigned i = 0; i < m_active_coeffs.size(); ++i) {
bool_var v = m_active_coeffs[i];
if (!seen.contains(v)) {
seen.insert(v);
m_coeffs[v] /= g;
}
}
m_bound /= g;
TRACE("pb", display_resolved_lemma(tout << "cut\n"););
}
}
#if 0
void theory_pb::reduce2(int s1, int alpha, bool_var v, card& asserting) {
// m_coeffs one for each boolean variable.
int beta = coeff_of(v);
if (beta == 1) {
process_card(asserting, alpha);
return;
}
int s2 = slack(asserting);
while (beta * s1 + s2 * alpha >= 0) {
bool_var x = pick_var(asserting);
reduce();
reduce_degree();
s2 = s2 + delta_slack;
}
}
#endif
bool theory_pb::resolve_conflict(card& c, literal_vector const& confl) {
TRACE("pb", display(tout, c, true); );
bool_var v;
context& ctx = get_context();
m_conflict_lvl = 0;
for (unsigned i = 0; i < confl.size(); ++i) {
literal lit = confl[i];
SASSERT(ctx.get_assignment(lit) == l_false);
m_conflict_lvl = std::max(m_conflict_lvl, ctx.get_assign_level(lit));
}
if (m_conflict_lvl < ctx.get_assign_level(c.lit()) || m_conflict_lvl == ctx.get_base_level()) {
return false;
}
reset_coeffs();
m_num_marks = 0;
m_bound = c.k();
m_antecedents.reset();
m_resolved.reset();
literal_vector ante;
process_card(c, 1);
// point into stack of assigned literals
literal_vector const& lits = ctx.assigned_literals();
SASSERT(!lits.empty());
unsigned idx = lits.size()-1;
b_justification js;
literal conseq = ~confl[2];
while (m_num_marks > 0) {
v = conseq.var();
TRACE("pb", display_resolved_lemma(tout << conseq << "\n"););
int offset = get_coeff(v);
if (offset == 0) {
goto process_next_resolvent;
}
else if (offset < 0) {
offset = -offset;
}
SASSERT(offset > 0);
js = ctx.get_justification(v);
validate_lemma();
// ctx.display(std::cout, js);
inc_coeff(conseq, offset);
//
// Resolve selected conseq with antecedents.
//
int bound = 1;
switch(js.get_kind()) {
case b_justification::CLAUSE: {
clause& cls = *js.get_clause();
justification* cjs = cls.get_justification();
if (cjs && !is_proof_justification(*cjs)) {
TRACE("pb", tout << "skipping justification for clause over: " << conseq << " "
<< typeid(*cjs).name() << "\n";);
break;
}
unsigned num_lits = cls.get_num_literals();
if (cls.get_literal(0) == conseq) {
process_antecedent(cls.get_literal(1), offset);
}
else {
SASSERT(cls.get_literal(1) == conseq);
process_antecedent(cls.get_literal(0), offset);
}
for (unsigned i = 2; i < num_lits; ++i) {
process_antecedent(cls.get_literal(i), offset);
}
TRACE("pb", tout << literal_vector(cls.get_num_literals(), cls.begin_literals()) << "\n";);
break;
}
case b_justification::BIN_CLAUSE:
process_antecedent(~js.get_literal(), offset);
TRACE("pb", tout << "binary: " << js.get_literal() << "\n";);
break;
case b_justification::AXIOM:
TRACE("pb", tout << "axiom " << conseq << "\n";);
break;
case b_justification::JUSTIFICATION: {
justification* j = js.get_justification();
card_justification* pbj = 0;
if (j->get_from_theory() == get_id()) {
pbj = dynamic_cast<card_justification*>(j);
}
if (pbj == 0) {
TRACE("pb", tout << "skip justification for " << conseq << "\n";);
}
else {
process_card(pbj->get_card(), offset);
bound = pbj->get_card().k();
}
break;
}
default:
UNREACHABLE();
}
m_bound += offset * bound;
process_next_resolvent:
// find the next marked variable in the assignment stack
//
while (true) {
conseq = lits[idx];
v = conseq.var();
if (ctx.is_marked(v)) break;
SASSERT(idx > 0);
--idx;
}
SASSERT(ctx.get_assign_level(v) == m_conflict_lvl);
ctx.unset_mark(v);
m_resolved.push_back(idx);
--idx;
--m_num_marks;
}
validate_lemma();
TRACE("pb", display_resolved_lemma(tout << "done\n"););
uint_set seen;
int count = 0, sz = 0;
for (unsigned i = 0; i < m_active_coeffs.size(); ++i) {
bool_var v = m_active_coeffs[i];
if (seen.contains(v)) {
continue;
}
seen.insert(v);
int coeff = get_coeff(v);
if (coeff == 0) continue;
if (coeff < 0) coeff = -coeff;
++sz;
count += coeff;
}
std::cout << "New " << count << "(" << sz << ") >= " << m_bound << " " << c.size() << " >= " << c.k() << " new diff: " << count - m_bound << " old diff: " << c.size() - c.k() << "\n";
return true;
}
bool theory_pb::is_proof_justification(justification const& j) const {
return typeid(smt::justification_proof_wrapper) == typeid(j);
}
justification* theory_pb::justify(literal l1, literal l2) {
literal lits[2] = { l1, l2 };
justification* js = 0;
if (proofs_enabled()) {
js = get_context().mk_justification(theory_axiom_justification(get_id(), get_context().get_region(), 2, lits));
}
return js;
}
justification* theory_pb::justify(literal_vector const& lits) {
justification* js = 0;
if (proofs_enabled()) {
js = get_context().mk_justification(theory_axiom_justification(get_id(), get_context().get_region(), lits.size(), lits.c_ptr()));
}
return js;
}
// debug methods
void theory_pb::validate_watch(ineq const& c) const {
scoped_mpz sum(m_mpz_mgr), max(m_mpz_mgr);
for (unsigned i = 0; i < c.watch_size(); ++i) {
sum += c.ncoeff(i);
if (max < c.ncoeff(i)) {
max = c.ncoeff(i);
}
}
SASSERT(c.watch_sum() == sum);
SASSERT(sum >= c.mpz_k());
SASSERT(max == c.max_watch());
}
void theory_pb::validate_assign(ineq const& c, literal_vector const& lits, literal l) const {
uint_set nlits;
for (unsigned i = 0; i < lits.size(); ++i) {
SASSERT(get_context().get_assignment(lits[i]) == l_true);
nlits.insert((~lits[i]).index());
}
SASSERT(get_context().get_assignment(l) == l_undef);
SASSERT(get_context().get_assignment(c.lit()) == l_true);
nlits.insert(l.index());
numeral sum = numeral::zero();
for (unsigned i = 0; i < c.size(); ++i) {
literal lit = c.lit(i);
if (!nlits.contains(lit.index())) {
sum += c.coeff(i);
}
}
CTRACE("pb", (sum >= c.k()),
display(tout << "invalid assign" , c, true);
for (unsigned i = 0; i < lits.size(); ++i) {
tout << lits[i] << " ";
}
tout << " => " << l << "\n";);
SASSERT(sum < c.k());
}
void theory_pb::validate_final_check() {
for (unsigned i = 0; i < m_var_infos.size(); ++i) {
ineq* c = m_var_infos[i].m_ineq;
if (c) {
validate_final_check(*c);
}
}
}
void theory_pb::validate_final_check(ineq& c) {
context& ctx = get_context();
if (ctx.get_assignment(c.lit()) == l_undef) {
return;
}
if (!ctx.is_relevant(c.lit())) {
return;
}
numeral sum = numeral::zero(), maxsum = numeral::zero();
for (unsigned i = 0; i < c.size(); ++i) {
switch(ctx.get_assignment(c.lit(i))) {
case l_true:
sum += c.coeff(i);
case l_undef:
maxsum += c.coeff(i);
break;
case l_false:
break;
}
}
TRACE("pb", display(tout << "validate: ", c, true);
tout << "sum: " << sum << " " << maxsum << " ";
tout << ctx.get_assignment(c.lit()) << "\n";
);
SASSERT(sum <= maxsum);
SASSERT(!c.is_ge() || (sum >= c.k()) == (ctx.get_assignment(c.lit()) == l_true));
SASSERT(!c.is_ge() || (maxsum < c.k()) == (ctx.get_assignment(c.lit()) == l_false));
SASSERT(!c.is_eq() || (sum == c.k()) == (ctx.get_assignment(c.lit()) == l_true));
}
// display methods
void theory_pb::display_resolved_lemma(std::ostream& out) const {
context& ctx = get_context();
literal_vector const& lits = ctx.assigned_literals();
bool_var v;
unsigned lvl;
out << "num marks: " << m_num_marks << "\n";
out << "conflict level: " << m_conflict_lvl << "\n";
for (unsigned i = 0; i < m_resolved.size(); ++i) {
v = lits[m_resolved[i]].var();
lvl = ctx.get_assign_level(v);
out << lvl << ": " << lits[i] << " ";
ctx.display(out, ctx.get_justification(v));
}
if (!m_antecedents.empty()) {
out << m_antecedents << " ==> ";
}
uint_set seen;
bool first = true;
for (unsigned i = 0; i < m_active_coeffs.size(); ++i) {
bool_var v = m_active_coeffs[i];
if (seen.contains(v)) {
continue;
}
seen.insert(v);
int coeff = get_coeff(v);
if (coeff == 0) {
continue;
}
if (!first) {
out << " + ";
}
if (coeff == 1) {
out << literal(v);
}
else if (coeff == -1) {
out << literal(v, true);
}
else if (coeff > 0) {
out << coeff << " * " << literal(v);
}
else {
out << (-coeff) << " * " << literal(v, true);
}
first = false;
}
out << " >= " << m_bound << "\n";
}
std::ostream& theory_pb::display(std::ostream& out, arg_t const& c, bool values) const {
return c.display(get_context(), out, values);
}
std::ostream& theory_pb::display(std::ostream& out, ineq const& c, bool values) const {
ast_manager& m = get_manager();
context& ctx = get_context();
out << c.lit();
if (c.lit() != null_literal) {
if (values) {
out << "@(" << ctx.get_assignment(c.lit());
if (ctx.get_assignment(c.lit()) != l_undef) {
out << ":" << ctx.get_assign_level(c.lit());
}
out << ")";
}
expr_ref tmp(m);
ctx.literal2expr(c.lit(), tmp);
out << " " << tmp << "\n";
}
else {
out << " ";
}
for (unsigned i = 0; i < c.size(); ++i) {
literal l(c.lit(i));
if (!c.coeff(i).is_one()) {
out << c.coeff(i) << "*";
}
out << l;
if (values) {
out << "@(" << ctx.get_assignment(l);
if (ctx.get_assignment(l) != l_undef) {
out << ":" << ctx.get_assign_level(l);
}
out << ")";
}
if (i + 1 == c.watch_size()) {
out << " .w ";
}
if (i + 1 < c.size()) {
out << " + ";
}
}
out << (c.is_ge()?" >= ":" = ") << c.k() << "\n";
if (c.m_num_propagations) out << "propagations: " << c.m_num_propagations << " ";
if (c.m_max_watch.is_pos()) out << "max_watch: " << c.max_watch() << " ";
if (c.watch_size()) out << "watch size: " << c.watch_size() << " ";
if (c.m_watch_sum.is_pos()) out << "watch-sum: " << c.watch_sum() << " ";
if (!c.m_max_sum.is_zero()) out << "sum: [" << c.min_sum() << ":" << c.max_sum() << "] ";
if (c.m_num_propagations || c.m_max_watch.is_pos() || c.watch_size() ||
c.m_watch_sum.is_pos() || !c.m_max_sum.is_zero()) out << "\n";
return out;
}
class theory_pb::pb_model_value_proc : public model_value_proc {
app* m_app;
svector<model_value_dependency> m_dependencies;
public:
pb_model_value_proc(app* a):
m_app(a) {}
void add(enode* n) {
m_dependencies.push_back(model_value_dependency(n));
}
virtual void get_dependencies(buffer<model_value_dependency> & result) {
result.append(m_dependencies.size(), m_dependencies.c_ptr());
}
virtual app * mk_value(model_generator & mg, ptr_vector<expr> & values) {
ast_manager& m = mg.get_manager();
SASSERT(values.size() == m_dependencies.size());
SASSERT(values.size() == m_app->get_num_args());
pb_util u(m);
rational sum(0);
for (unsigned i = 0; i < m_app->get_num_args(); ++i) {
if (!m.is_true(values[i]) && !m.is_false(values[i])) {
return m_app;
}
if (m.is_true(values[i])) {
sum += u.get_coeff(m_app, i);
}
}
rational k = u.get_k(m_app);
switch(m_app->get_decl_kind()) {
case OP_AT_MOST_K:
return (sum <= k)?m.mk_true():m.mk_false();
case OP_AT_LEAST_K:
return (sum >= k)?m.mk_true():m.mk_false();
case OP_PB_LE:
return (sum <= k)?m.mk_true():m.mk_false();
case OP_PB_GE:
return (sum >= k)?m.mk_true():m.mk_false();
default:
UNREACHABLE();
return 0;
}
return 0;
}
};
class pb_factory : public value_factory {
public:
pb_factory(ast_manager& m, family_id fid):
value_factory(m, fid) {}
virtual expr * get_some_value(sort * s) {
return m_manager.mk_true();
}
virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) {
v1 = m_manager.mk_true();
v2 = m_manager.mk_false();
return true;
}
virtual expr * get_fresh_value(sort * s) {
return 0;
}
virtual void register_value(expr * n) { }
};
void theory_pb::init_model(model_generator & m) {
m.register_factory(alloc(pb_factory, get_manager(), get_id()));
}
model_value_proc * theory_pb::mk_value(enode * n, model_generator & mg) {
context& ctx = get_context();
app* a = n->get_owner();
pb_model_value_proc* p = alloc(pb_model_value_proc, a);
for (unsigned i = 0; i < a->get_num_args(); ++i) {
p->add(ctx.get_enode(a->get_arg(i)));
}
return p;
}
void theory_pb::display_watch(std::ostream& out, bool_var v, bool sign) const {
ineq_watch const* w = m_var_infos[v].m_lit_watch[sign];
if (!w) return;
ineq_watch const& wl = *w;
out << "watch: " << literal(v, sign) << " |-> ";
for (unsigned i = 0; i < wl.size(); ++i) {
out << wl[i]->lit() << " ";
}
out << "\n";
}
void theory_pb::display(std::ostream& out) const {
for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) {
display_watch(out, vi, false);
display_watch(out, vi, true);
}
for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) {
ineq_watch const* w = m_var_infos[vi].m_var_watch;
if (!w) continue;
out << "watch (v): " << literal(vi) << " |-> ";
ineq_watch const& wl = *w;
for (unsigned i = 0; i < wl.size(); ++i) {
out << wl[i]->lit() << " ";
}
out << "\n";
}
for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) {
ineq* c = m_var_infos[vi].m_ineq;
if (c) {
display(out, *c, true);
}
}
for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) {
card* c = m_var_infos[vi].m_card;
if (c) {
display(out, *c, true);
}
}
}
}