3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00
This commit is contained in:
Nikolaj Bjorner 2023-12-12 14:41:31 -08:00
parent 4cadf6d9f2
commit 06ebf9a02a
6 changed files with 272 additions and 107 deletions

View file

@ -942,3 +942,8 @@ app * bv_util::mk_bv2int(expr* e) {
parameter p(s);
return m_manager.mk_app(get_fid(), OP_BV2INT, 1, &p, 1, &e);
}
app* bv_util::mk_int2bv(unsigned sz, expr* e) {
parameter p(sz);
return m_manager.mk_app(get_fid(), OP_INT2BV, 1, &p, 1, &e);
}

View file

@ -522,6 +522,7 @@ public:
app * mk_bv_lshr(expr* arg1, expr* arg2) { return m_manager.mk_app(get_fid(), OP_BLSHR, arg1, arg2); }
app * mk_bv2int(expr* e);
app * mk_int2bv(unsigned sz, expr* e);
// TODO: all these binary ops commute (right?) but it'd be more logical to swap `n` & `m` in the `return`
app * mk_bvsmul_no_ovfl(expr* m, expr* n) { return m_manager.mk_app(get_fid(), OP_BSMUL_NO_OVFL, n, m); }

View file

@ -29,8 +29,7 @@ namespace intblast {
bv(m),
a(m),
m_args(m),
m_translate(m),
m_pinned(m)
m_translate(m)
{}
euf::theory_var solver::mk_var(euf::enode* n) {
@ -85,14 +84,38 @@ namespace intblast {
return true;
}
void solver::eq_internalized(euf::enode* n) {
expr* e = n->get_expr();
expr* x, * y;
VERIFY(m.is_eq(n->get_expr(), x, y));
m_args.reset();
m_args.push_back(translated(x));
m_args.push_back(translated(y));
add_equiv(expr2literal(e), eq_internalize(umod(x, 0), umod(x, 1)));
}
void solver::internalize_bv(app* e) {
ensure_args(e);
m_args.reset();
for (auto arg : *e)
m_args.push_back(translated(arg));
translate_bv(e);
if (m.is_bool(e))
add_equiv(expr2literal(e), mk_literal(translated(e)));
if (m.is_bool(e))
add_equiv(expr2literal(e), mk_literal(translated(e)));
add_bound_axioms();
}
void solver::add_bound_axioms() {
if (m_vars_qhead == m_vars.size())
return;
ctx.push(value_trail(m_vars_qhead));
for (; m_vars_qhead < m_vars.size(); ++m_vars_qhead) {
auto v = m_vars[m_vars_qhead];
auto w = translated(v);
auto sz = rational::power_of_two(bv.get_bv_size(v->get_sort()));
add_unit(ctx.mk_literal(a.mk_ge(w, a.mk_int(0))));
add_unit(ctx.mk_literal(a.mk_le(w, a.mk_int(sz - 1))));
}
}
void solver::ensure_args(app* e) {
@ -106,17 +129,17 @@ namespace intblast {
return;
for (unsigned i = 0; i < todo.size(); ++i) {
expr* e = todo[i];
if (is_app(e)) {
if (m.is_bool(e))
continue;
else if (is_app(e)) {
for (auto arg : *to_app(e))
if (!visited.is_marked(arg)) {
visited.mark(arg);
todo.push_back(arg);
}
}
else if (is_quantifier(e) && !visited.is_marked(to_quantifier(e)->get_expr())) {
visited.mark(to_quantifier(e)->get_expr());
todo.push_back(to_quantifier(e)->get_expr());
}
else if (is_lambda(e))
throw default_exception("lambdas are not supported in intblaster");
}
std::stable_sort(todo.begin(), todo.end(), [&](expr* a, expr* b) { return get_depth(a) < get_depth(b); });
@ -176,8 +199,8 @@ namespace intblast {
}
m_core.reset();
m_vars.reset();
m_translate.reset();
m_is_plugin = false;
m_solver = mk_smt2_solver(m, s.params(), symbol::null);
expr_ref_vector es(m);
@ -186,8 +209,9 @@ namespace intblast {
translate(es);
for (auto const& [src, vi] : m_vars) {
auto const& [v, b] = vi;
for (auto e : m_vars) {
auto v = translated(e);
auto b = rational::power_of_two(bv.get_bv_size(e));
m_solver->assert_expr(a.mk_le(a.mk_int(0), v));
m_solver->assert_expr(a.mk_lt(v, a.mk_int(b)));
}
@ -296,19 +320,68 @@ namespace intblast {
es[i] = translated(es.get(i));
}
expr* solver::mk_mod(expr* x) {
if (m_vars.contains(x))
sat::check_result solver::check() {
// ensure that bv2int is injective
for (auto e : m_bv2int) {
euf::enode* n = expr2enode(e);
euf::enode* r1 = n->get_arg(0)->get_root();
for (auto sib : euf::enode_class(n)) {
if (sib == n)
continue;
if (!bv.is_bv2int(sib->get_expr()))
continue;
if (sib->get_arg(0)->get_root() == r1)
continue;
add_clause(~eq_internalize(n, sib), eq_internalize(sib->get_arg(0), n->get_arg(0)), nullptr);
return sat::check_result::CR_CONTINUE;
}
}
// ensure that int2bv respects values
// bv2int(int2bv(x)) = x mod N
for (auto e : m_int2bv) {
auto n = expr2enode(e);
auto x = n->get_arg(0)->get_expr();
auto bv2int = bv.mk_bv2int(e);
ctx.internalize(bv2int);
auto N = rational::power_of_two(bv.get_bv_size(e));
auto xModN = a.mk_mod(x, a.mk_int(N));
ctx.internalize(xModN);
auto nBv2int = ctx.get_enode(bv2int);
auto nxModN = ctx.get_enode(xModN);
if (nBv2int->get_root() != nxModN->get_root()) {
add_unit(eq_internalize(nBv2int, nxModN));
return sat::check_result::CR_CONTINUE;
}
}
return sat::check_result::CR_DONE;
}
expr* solver::umod(expr* bv_expr, unsigned i) {
expr* x = arg(i);
rational r;
rational N = bv_size(bv_expr);
if (a.is_numeral(x, r)) {
if (0 <= r && r < N)
return x;
return a.mk_int(mod(r, N));
}
if (any_of(m_vars, [&](expr* v) { return translated(v) == x; }))
return x;
return to_expr(a.mk_mod(x, a.mk_int(bv_size())));
return to_expr(a.mk_mod(x, a.mk_int(N)));
}
expr* solver::mk_smod(expr* x) {
auto shift = bv_size() / 2;
return a.mk_mod(a.mk_add(x, a.mk_int(shift)), a.mk_int(bv_size()));
expr* solver::smod(expr* bv_expr, unsigned i) {
expr* x = arg(i);
auto N = bv_size(bv_expr);
auto shift = N / 2;
rational r;
if (a.is_numeral(x, r))
return a.mk_int(mod(r + shift, N));
return a.mk_mod(a.mk_add(x, a.mk_int(shift)), a.mk_int(N));
}
rational solver::bv_size() {
return rational::power_of_two(bv.get_bv_size(bv_expr->get_sort()));
rational solver::bv_size(expr* bv_expr) {
return rational::power_of_two(bv.get_bv_size(bv_expr->get_sort()));
}
void solver::translate_expr(expr* e) {
@ -318,7 +391,6 @@ namespace intblast {
translate_var(to_var(e));
else {
app* ap = to_app(e);
bv_expr = e;
m_args.reset();
for (auto arg : *ap)
m_args.push_back(translated(arg));
@ -333,6 +405,12 @@ namespace intblast {
}
void solver::translate_quantifier(quantifier* q) {
if (is_lambda(q))
throw default_exception("lambdas are not supported in intblaster");
if (m_is_plugin) {
set_translated(q, q);
return;
}
expr* b = q->get_expr();
unsigned nd = q->get_num_decls();
ptr_vector<sort> sorts;
@ -357,37 +435,47 @@ namespace intblast {
set_translated(v, v);
}
// Translate functions that are not built-in or bit-vectors.
// Base method uses fresh functions.
// Other method could use bv2int, int2bv axioms and coercions.
// f(args) = bv2int(f(int2bv(args'))
//
void solver::translate_app(app* e) {
bool has_bv_arg = any_of(*e, [&](expr* arg) { return bv.is_bv(arg); });
bool has_bv_sort = bv.is_bv(e);
func_decl* f = e->get_decl();
if (has_bv_arg) {
verbose_stream() << mk_pp(e, m) << "\n";
// need to update args with mod where they are bit-vectors.
NOT_IMPLEMENTED_YET();
if (m_is_plugin && m.is_bool(e)) {
set_translated(e, e);
return;
}
if (has_bv_arg || has_bv_sort) {
ptr_vector<sort> domain;
for (auto* arg : *e) {
sort* s = arg->get_sort();
domain.push_back(bv.is_bv_sort(s) ? a.mk_int() : s);
bool has_bv_sort = bv.is_bv(e);
func_decl* f = e->get_decl();
for (unsigned i = 0; i < m_args.size(); ++i)
if (bv.is_bv(e->get_arg(i)))
m_args[i] = bv.mk_int2bv(bv.get_bv_size(e->get_arg(i)), m_args.get(i));
if (has_bv_sort)
m_vars.push_back(e);
if (m_is_plugin) {
expr* r = m.mk_app(f, m_args);
if (has_bv_sort) {
ctx.push(push_back_vector(m_vars));
r = bv.mk_bv2int(r);
}
sort* range = bv.is_bv(e) ? a.mk_int() : e->get_sort();
set_translated(e, r);
return;
}
else if (has_bv_sort) {
func_decl* g = nullptr;
if (!m_new_funs.find(f, g)) {
g = m.mk_fresh_func_decl(e->get_decl()->get_name(), symbol("bv"), domain.size(), domain.data(), range);
g = m.mk_fresh_func_decl(e->get_decl()->get_name(), symbol("bv"), f->get_arity(), f->get_domain(), a.mk_int());
m_new_funs.insert(f, g);
m_pinned.push_back(f);
m_pinned.push_back(g);
}
f = g;
}
set_translated(e, m.mk_app(f, m_args));
if (has_bv_sort)
m_vars.insert(e, { translated(e), bv_size()});
set_translated(e, m.mk_app(f, m_args));
}
void solver::translate_bv(app* e) {
@ -403,61 +491,59 @@ namespace intblast {
return r;
};
bv_expr = e;
expr* bv_expr = e;
expr* r = nullptr;
auto const& args = m_args;
switch (e->get_decl_kind()) {
case OP_BADD:
r = (a.mk_add(args));
r = a.mk_add(args);
break;
case OP_BSUB:
r = (a.mk_sub(args.size(), args.data()));
r = a.mk_sub(args.size(), args.data());
break;
case OP_BMUL:
r = (a.mk_mul(args));
r = a.mk_mul(args);
break;
case OP_ULEQ:
bv_expr = e->get_arg(0);
r = (a.mk_le(mk_mod(arg(0)), mk_mod(arg(1))));
r = a.mk_le(umod(bv_expr, 0), umod(bv_expr, 1));
break;
case OP_UGEQ:
bv_expr = e->get_arg(0);
r = (a.mk_ge(mk_mod(arg(0)), mk_mod(arg(1))));
r = a.mk_ge(umod(bv_expr, 0), umod(bv_expr, 1));
break;
case OP_ULT:
bv_expr = e->get_arg(0);
r = (a.mk_lt(mk_mod(arg(0)), mk_mod(arg(1))));
r = a.mk_lt(umod(bv_expr, 0), umod(bv_expr, 1));
break;
case OP_UGT:
bv_expr = e->get_arg(0);
r = (a.mk_gt(mk_mod(arg(0)), mk_mod(arg(1))));
r = a.mk_gt(umod(bv_expr, 0), umod(bv_expr, 1));
break;
case OP_SLEQ:
bv_expr = e->get_arg(0);
r = (a.mk_le(mk_smod(arg(0)), mk_smod(arg(1))));
r = a.mk_le(smod(bv_expr, 0), smod(bv_expr, 1));
break;
case OP_SGEQ:
r = (a.mk_ge(mk_smod(arg(0)), mk_smod(arg(1))));
r = a.mk_ge(smod(bv_expr, 0), smod(bv_expr, 1));
break;
case OP_SLT:
bv_expr = e->get_arg(0);
r = (a.mk_lt(mk_smod(arg(0)), mk_smod(arg(1))));
r = a.mk_lt(smod(bv_expr, 0), smod(bv_expr, 1));
break;
case OP_SGT:
bv_expr = e->get_arg(0);
r = (a.mk_gt(mk_smod(arg(0)), mk_smod(arg(1))));
r = a.mk_gt(smod(bv_expr, 0), smod(bv_expr, 1));
break;
case OP_BNEG:
r = (a.mk_uminus(arg(0)));
r = a.mk_uminus(arg(0));
break;
case OP_CONCAT: {
r = a.mk_int(0);
unsigned sz = 0;
for (unsigned i = 0; i < args.size(); ++i) {
expr* old_arg = e->get_arg(i);
expr* new_arg = arg(i);
bv_expr = old_arg;
new_arg = mk_mod(new_arg);
expr* new_arg = umod(old_arg, i);
if (sz > 0) {
new_arg = a.mk_mul(new_arg, a.mk_int(rational::power_of_two(sz)));
r = a.mk_add(r, new_arg);
@ -482,23 +568,22 @@ namespace intblast {
rational val;
unsigned sz;
VERIFY(bv.is_numeral(e, val, sz));
r = (a.mk_int(val));
r = a.mk_int(val);
break;
}
case OP_BUREM_I: {
expr* x = arg(0), * y = arg(1);
r = (m.mk_ite(m.mk_eq(y, a.mk_int(0)), a.mk_int(0), a.mk_mod(x, y)));
r = m.mk_ite(m.mk_eq(y, a.mk_int(0)), a.mk_int(0), a.mk_mod(x, y));
break;
}
case OP_BUDIV_I: {
expr* x = arg(0), * y = arg(1);
r = (m.mk_ite(m.mk_eq(y, a.mk_int(0)), a.mk_int(0), a.mk_idiv(x, y)));
r = m.mk_ite(m.mk_eq(y, a.mk_int(0)), a.mk_int(0), a.mk_idiv(x, umod(bv_expr, 1)));
break;
}
case OP_BUMUL_NO_OVFL: {
expr* x = arg(0), * y = arg(1);
bv_expr = e->get_arg(0);
r = (a.mk_lt(a.mk_mul(mk_mod(x), mk_mod(y)), a.mk_int(bv_size())));
r = a.mk_lt(a.mk_mul(umod(bv_expr, 0), umod(bv_expr, 1)), a.mk_int(bv_size(bv_expr)));
break;
}
case OP_BSHL: {
@ -509,7 +594,7 @@ namespace intblast {
break;
}
case OP_BNOT:
r = (bnot(arg(0)));
r = bnot(arg(0));
break;
case OP_BLSHR: {
expr* x = arg(0), * y = arg(1);
@ -518,7 +603,7 @@ namespace intblast {
r = m.mk_ite(a.mk_eq(y, a.mk_int(i)), a.mk_idiv(x, a.mk_int(rational::power_of_two(i))), r);
break;
}
// Or use (p + q) - band(p, q)?
// Or use (p + q) - band(p, q)?
case OP_BOR: {
r = arg(0);
for (unsigned i = 1; i < args.size(); ++i)
@ -537,46 +622,43 @@ namespace intblast {
case OP_BXNOR:
case OP_BXOR: {
unsigned sz = bv.get_bv_size(e);
expr* p = arg(0);
r = arg(0);
for (unsigned i = 1; i < args.size(); ++i) {
expr* q = arg(i);
p = a.mk_sub(a.mk_add(p, q), a.mk_mul(a.mk_int(2), a.mk_band(sz, p, q)));
r = a.mk_sub(a.mk_add(r, q), a.mk_mul(a.mk_int(2), a.mk_band(sz, r, q)));
}
if (e->get_decl_kind() == OP_BXNOR)
p = bnot(p);
r = (p);
r = bnot(r);
break;
}
case OP_BUDIV: {
bv_rewriter_params p(ctx.s().params());
expr* x = arg(0), * y = arg(1);
if (p.hi_div0())
r = (m.mk_ite(m.mk_eq(y, a.mk_int(0)), a.mk_int(0), a.mk_idiv(x, y)));
r = m.mk_ite(m.mk_eq(y, a.mk_int(0)), a.mk_int(0), a.mk_idiv(x, y));
else
r = (a.mk_idiv(x, y));
r = a.mk_idiv(x, y);
break;
}
case OP_BUREM: {
bv_rewriter_params p(ctx.s().params());
expr* x = arg(0), * y = arg(1);
if (p.hi_div0())
r = (m.mk_ite(m.mk_eq(y, a.mk_int(0)), a.mk_int(0), a.mk_mod(x, y)));
r = m.mk_ite(m.mk_eq(y, a.mk_int(0)), a.mk_int(0), a.mk_mod(x, y));
else
r = (a.mk_mod(x, y));
r = a.mk_mod(x, y);
break;
}
//
// ashr(x, y)
// if y = k & x >= 0 -> x / 2^k
// if y = k & x < 0 -> - (x / 2^k)
//
case OP_BASHR: {
expr* x = arg(0), * y = arg(1);
rational N = rational::power_of_two(bv.get_bv_size(e));
bv_expr = e;
x = mk_mod(x);
y = mk_mod(y);
expr* x = umod(e, 0);
expr* y = umod(e, 1);
expr* signbit = a.mk_ge(x, a.mk_int(N / 2));
r = m.mk_ite(signbit, a.mk_int(N - 1), a.mk_int(0));
for (unsigned i = 0; i < bv.get_bv_size(e); ++i) {
@ -587,15 +669,39 @@ namespace intblast {
}
break;
}
case OP_ZERO_EXT:
bv_expr = e->get_arg(0);
r = umod(bv_expr, 0);
SASSERT(bv.get_bv_size(e) >= bv.get_bv_size(bv_expr));
break;
case OP_SIGN_EXT: {
bv_expr = e->get_arg(0);
r = umod(bv_expr, 0);
SASSERT(bv.get_bv_size(e) >= bv.get_bv_size(bv_expr));
unsigned arg_sz = bv.get_bv_size(bv_expr);
unsigned sz = bv.get_bv_size(e);
rational N = rational::power_of_two(sz);
rational M = rational::power_of_two(arg_sz);
expr* signbit = a.mk_ge(r, a.mk_int(M / 2));
r = m.mk_ite(signbit, a.mk_uminus(r), r);
break;
}
case OP_INT2BV:
m_int2bv.push_back(e);
ctx.push(push_back_vector(m_int2bv));
r = arg(0);
break;
case OP_BV2INT:
m_bv2int.push_back(e);
ctx.push(push_back_vector(m_bv2int));
r = arg(0);
break;
case OP_BCOMP:
case OP_ROTATE_LEFT:
case OP_ROTATE_RIGHT:
case OP_EXT_ROTATE_LEFT:
case OP_EXT_ROTATE_RIGHT:
case OP_REPEAT:
case OP_ZERO_EXT:
case OP_SIGN_EXT:
case OP_BREDOR:
case OP_BREDAND:
case OP_BSDIV:
@ -610,19 +716,19 @@ namespace intblast {
}
set_translated(e, r);
}
void solver::translate_basic(app* e) {
if (m.is_eq(e)) {
bool has_bv_arg = any_of(*e, [&](expr* arg) { return bv.is_bv(arg); });
if (has_bv_arg) {
bv_expr = e->get_arg(0);
set_translated(e, m.mk_eq(mk_mod(arg(0)), mk_mod(arg(1))));
expr* bv_expr = e->get_arg(0);
set_translated(e, m.mk_eq(umod(bv_expr, 0), umod(bv_expr, 1)));
}
else
set_translated(e, m.mk_eq(arg(0), arg(1)));
else
set_translated(e, m.mk_eq(arg(0), arg(1)));
}
else
set_translated(e, m.mk_app(e->get_decl(), m_args));
else
set_translated(e, m.mk_app(e->get_decl(), m_args));
}
rational solver::get_value(expr* e) const {
@ -630,11 +736,9 @@ namespace intblast {
model_ref mdl;
m_solver->get_model(mdl);
expr_ref r(m);
var_info vi;
r = translated(e);
rational val;
if (!m_vars.find(e, vi))
return rational::zero();
if (!mdl->eval_expr(vi.dst, r, true))
if (!mdl->eval_expr(r, r, true))
return rational::zero();
if (!a.is_numeral(r, val))
return rational::zero();
@ -642,6 +746,32 @@ namespace intblast {
}
void solver::add_value(euf::enode* n, model& mdl, expr_ref_vector& values) {
if (m_is_plugin)
add_value_plugin(n, mdl, values);
else
add_value_solver(n, mdl, values);
}
bool solver::add_dep(euf::enode* n, top_sort<euf::enode>& dep) {
// bv2int
auto e = ctx.get_enode(translated(n->get_expr()));
if (!e)
return false;
dep.add(n, e);
}
// TODO: handle dependencies properly by using arithmetical model to retrieve values of translated
// bit-vectors directly.
void solver::add_value_plugin(euf::enode* n, model& mdl, expr_ref_vector& values) {
SASSERT(bv.is_bv(n->get_expr()));
rational N = rational::power_of_two(bv.get_bv_size(n->get_expr()));
auto e = ctx.get_enode(translated(n->get_expr()));
expr_ref value(m);
value = values.get(e->get_root_id());
values.setx(n->get_root_id(), value);
}
void solver::add_value_solver(euf::enode* n, model& mdl, expr_ref_vector& values) {
expr_ref value(m);
if (n->interpreted())
value = n->get_expr();

View file

@ -46,23 +46,18 @@ namespace euf {
namespace intblast {
class solver : public euf::th_euf_solver {
struct var_info {
expr* dst;
rational sz;
};
euf::solver& ctx;
sat::solver& s;
ast_manager& m;
bv_util bv;
arith_util a;
scoped_ptr<::solver> m_solver;
obj_map<expr, var_info> m_vars;
obj_map<func_decl, func_decl*> m_new_funs;
expr_ref_vector m_translate, m_args;
ast_ref_vector m_pinned;
sat::literal_vector m_core;
ptr_vector<app> m_bv2int, m_int2bv;
statistics m_stats;
bool m_is_plugin = true; // when the solver is used as a plugin, then do not translate below quantifiers.
bool is_bv(sat::literal lit);
void translate(expr_ref_vector& es);
@ -70,14 +65,13 @@ namespace intblast {
rational get_value(expr* e) const;
expr* translated(expr* e) { expr* r = m_translate.get(e->get_id(), nullptr); SASSERT(r); return r; }
expr* translated(expr* e) const { expr* r = m_translate.get(e->get_id(), nullptr); SASSERT(r); return r; }
void set_translated(expr* e, expr* r) { m_translate.setx(e->get_id(), r); }
expr* arg(unsigned i) { return m_args.get(i); }
expr* mk_mod(expr* x);
expr* mk_smod(expr* x);
expr* bv_expr = nullptr;
rational bv_size();
expr* umod(expr* bv_expr, unsigned i);
expr* smod(expr* bv_expr, unsigned i);
rational bv_size(expr* bv_expr);
void translate_expr(expr* e);
void translate_bv(app* e);
@ -89,8 +83,15 @@ namespace intblast {
void ensure_args(app* e);
void internalize_bv(app* e);
unsigned m_vars_qhead = 0;
ptr_vector<expr> m_vars;
void add_bound_axioms();
euf::theory_var mk_var(euf::enode* n) override;
void add_value_plugin(euf::enode* n, model& mdl, expr_ref_vector& values);
void add_value_solver(euf::enode* n, model& mdl, expr_ref_vector& values);
public:
solver(euf::solver& ctx);
@ -102,12 +103,12 @@ namespace intblast {
void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override;
bool add_dep(euf::enode* n, top_sort<euf::enode>& dep) override;
std::ostream& display(std::ostream& out) const override;
void collect_statistics(statistics& st) const override;
bool unit_propagate() override { return false; }
void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector& r, bool probing) override {}
@ -130,7 +131,7 @@ namespace intblast {
sat::literal internalize(expr* e, bool, bool) override;
void eq_internalized(euf::enode* n) override {}
void eq_internalized(euf::enode* n) override;
};

View file

@ -77,6 +77,8 @@ namespace polysat {
}
void umul_ovfl_constraint::propagate(core& c, lbool value, dependency const& dep) {
if (value == l_undef)
return;
auto& C = c.cs();
auto p1 = c.subst(p());
auto q1 = c.subst(q());

View file

@ -256,6 +256,32 @@ namespace polysat {
void solver::add_polysat_clause(char const* name, core_vector cs, bool is_redundant) {
sat::literal_vector lits;
signed_constraint sc;
unsigned constraint_count = 0;
for (auto e : cs) {
if (std::holds_alternative<signed_constraint>(e)) {
sc = *std::get_if<signed_constraint>(&e);
constraint_count++;
}
}
if (constraint_count == 1) {
auto lit = ctx.mk_literal(constraint2expr(sc));
svector<euf::enode_pair> eqs;
for (auto e : cs) {
if (std::holds_alternative<dependency>(e)) {
auto d = *std::get_if<dependency>(&e);
if (d.is_literal())
lits.push_back(d.literal());
else if (d.is_eq()) {
auto [v1, v2] = d.eq();
eqs.push_back({ var2enode(v1), var2enode(v2) });
}
}
}
ctx.propagate(lit, euf::th_explain::propagate(*this, lits, eqs, lit, nullptr));
return;
}
for (auto e : cs) {
if (std::holds_alternative<dependency>(e)) {
auto d = *std::get_if<dependency>(&e);