mirror of
https://github.com/Z3Prover/z3
synced 2025-04-07 18:05:21 +00:00
Computing missing coeff for assign-bounds lemma
This commit is contained in:
parent
1764bb8785
commit
8e57ab5d97
|
@ -73,6 +73,129 @@ namespace spacer {
|
|||
|
||||
|
||||
|
||||
class linear_combinator {
|
||||
struct scaled_lit {
|
||||
bool is_pos;
|
||||
app *lit;
|
||||
rational coeff;
|
||||
scaled_lit(bool is_pos, app *lit, const rational &coeff) :
|
||||
is_pos(is_pos), lit(lit), coeff(coeff) {}
|
||||
};
|
||||
ast_manager &m;
|
||||
th_rewriter m_rw;
|
||||
arith_util m_arith;
|
||||
expr_ref m_sum;
|
||||
bool m_is_strict;
|
||||
rational m_lc;
|
||||
vector<scaled_lit> m_lits;
|
||||
public:
|
||||
linear_combinator(ast_manager &m) : m(m), m_rw(m), m_arith(m),
|
||||
m_sum(m), m_is_strict(false),
|
||||
m_lc(1) {}
|
||||
|
||||
void add_lit(app* lit, rational const &coeff, bool is_pos = true) {
|
||||
m_lits.push_back(scaled_lit(is_pos, lit, coeff));
|
||||
}
|
||||
|
||||
void normalize_coeff() {
|
||||
for (auto &lit : m_lits)
|
||||
m_lc = lcm(m_lc, denominator(lit.coeff));
|
||||
if (!m_lc.is_one()) {
|
||||
for (auto &lit : m_lits)
|
||||
lit.coeff *= m_lc;
|
||||
}
|
||||
}
|
||||
|
||||
rational const &lc() const {return m_lc;}
|
||||
|
||||
bool process_lit(scaled_lit &lit0) {
|
||||
arith_util a(m);
|
||||
app* lit = lit0.lit;
|
||||
rational &coeff = lit0.coeff;
|
||||
bool is_pos = lit0.is_pos;
|
||||
|
||||
|
||||
if (m.is_not(lit)) {
|
||||
lit = to_app(lit->get_arg(0));
|
||||
is_pos = !is_pos;
|
||||
}
|
||||
if (!m_arith.is_le(lit) && !m_arith.is_lt(lit) &&
|
||||
!m_arith.is_ge(lit) && !m_arith.is_gt(lit) && !m.is_eq(lit)) {
|
||||
return false;
|
||||
}
|
||||
SASSERT(lit->get_num_args() == 2);
|
||||
sort* s = m.get_sort(lit->get_arg(0));
|
||||
bool is_int = m_arith.is_int(s);
|
||||
if (!is_int && m_arith.is_int_expr(lit->get_arg(0))) {
|
||||
is_int = true;
|
||||
s = m_arith.mk_int();
|
||||
}
|
||||
|
||||
if (!is_int && is_pos && (m_arith.is_gt(lit) || m_arith.is_lt(lit))) {
|
||||
m_is_strict = true;
|
||||
}
|
||||
if (!is_int && !is_pos && (m_arith.is_ge(lit) || m_arith.is_le(lit))) {
|
||||
m_is_strict = true;
|
||||
}
|
||||
|
||||
|
||||
SASSERT(m_arith.is_int(s) || m_arith.is_real(s));
|
||||
expr_ref sign1(m), sign2(m), term(m);
|
||||
sign1 = m_arith.mk_numeral(m.is_eq(lit)?coeff:abs(coeff), s);
|
||||
sign2 = m_arith.mk_numeral(m.is_eq(lit)?-coeff:-abs(coeff), s);
|
||||
if (!m_sum.get()) {
|
||||
m_sum = m_arith.mk_numeral(rational(0), s);
|
||||
}
|
||||
|
||||
expr* a0 = lit->get_arg(0);
|
||||
expr* a1 = lit->get_arg(1);
|
||||
|
||||
if (is_pos && (m_arith.is_ge(lit) || m_arith.is_gt(lit))) {
|
||||
std::swap(a0, a1);
|
||||
}
|
||||
if (!is_pos && (m_arith.is_le(lit) || m_arith.is_lt(lit))) {
|
||||
std::swap(a0, a1);
|
||||
}
|
||||
|
||||
//
|
||||
// Multiplying by coefficients over strict
|
||||
// and non-strict inequalities:
|
||||
//
|
||||
// (a <= b) * 2
|
||||
// (a - b <= 0) * 2
|
||||
// (2a - 2b <= 0)
|
||||
|
||||
// (a < b) * 2 <=>
|
||||
// (a +1 <= b) * 2 <=>
|
||||
// 2a + 2 <= 2b <=>
|
||||
// 2a+2-2b <= 0
|
||||
|
||||
bool strict_ineq =
|
||||
is_pos?(m_arith.is_gt(lit) || m_arith.is_lt(lit)):(m_arith.is_ge(lit) || m_arith.is_le(lit));
|
||||
|
||||
if (is_int && strict_ineq) {
|
||||
m_sum = m_arith.mk_add(m_sum, sign1);
|
||||
}
|
||||
|
||||
term = m_arith.mk_mul(sign1, a0);
|
||||
m_sum = m_arith.mk_add(m_sum, term);
|
||||
term = m_arith.mk_mul(sign2, a1);
|
||||
m_sum = m_arith.mk_add(m_sum, term);
|
||||
|
||||
m_rw(m_sum);
|
||||
return true;
|
||||
}
|
||||
|
||||
expr_ref operator()(){
|
||||
if (!m_sum) normalize_coeff();
|
||||
m_sum.reset();
|
||||
for (auto &lit : m_lits) {
|
||||
if (!process_lit(lit)) return expr_ref(m);
|
||||
}
|
||||
return m_sum;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* ====================================
|
||||
* methods for transforming proofs
|
||||
|
@ -101,6 +224,60 @@ namespace spacer {
|
|||
return pf;
|
||||
}
|
||||
|
||||
static bool match_mul(expr *e, expr_ref &var, expr_ref &val, arith_util &a) {
|
||||
expr *e1 = nullptr, *e2 = nullptr;
|
||||
if (!a.is_mul(e, e1, e2)) {
|
||||
if (a.is_numeral(e)) return false;
|
||||
if (!var || var == e) {
|
||||
var = e;
|
||||
val = a.mk_numeral(rational(1), get_sort(e));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!a.is_numeral(e1)) std::swap(e1, e2);
|
||||
if (!a.is_numeral(e1)) return false;
|
||||
|
||||
// if variable is given, match it as well
|
||||
if (!var || var == e2) {
|
||||
var = e2;
|
||||
val = e1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static expr_ref get_coeff(expr *lit0, expr_ref &var) {
|
||||
ast_manager &m = var.m();
|
||||
arith_util a(m);
|
||||
|
||||
expr *lit = nullptr;
|
||||
if (!m.is_not(lit0, lit)) lit = lit0;
|
||||
|
||||
expr *e1 = nullptr, *e2 = nullptr;
|
||||
// assume e2 is numeral and ignore it
|
||||
if ((a.is_le(lit, e1, e2) || a.is_lt(lit, e1, e2) ||
|
||||
a.is_ge(lit, e1, e2) || a.is_gt(lit, e1, e2) ||
|
||||
m.is_eq(lit, e1, e2))) {
|
||||
if (a.is_numeral(e1)) std::swap(e1, e2);
|
||||
}
|
||||
else {
|
||||
e1 = lit;
|
||||
}
|
||||
|
||||
expr_ref val(m);
|
||||
if (!a.is_add(e1)) {
|
||||
if (match_mul(e1, var, val, a)) return val;
|
||||
}
|
||||
else {
|
||||
for (auto *arg : *to_app(e1)) {
|
||||
if (match_mul(arg, var, val, a)) return val;
|
||||
}
|
||||
}
|
||||
return expr_ref(m);
|
||||
}
|
||||
|
||||
// convert assign-bounds lemma to a farkas lemma by adding missing coeff
|
||||
// assume that missing coeff is for premise at position 0
|
||||
static proof_ref mk_fk_from_ab(ast_manager &m,
|
||||
|
@ -108,9 +285,44 @@ namespace spacer {
|
|||
unsigned num_params,
|
||||
parameter const *params) {
|
||||
SASSERT(num_params == parents.size() + 1 /* one param is missing */);
|
||||
|
||||
// compute missing coefficient
|
||||
linear_combinator lcb(m);
|
||||
for (unsigned i = 1, sz = parents.size(); i < sz; ++i) {
|
||||
app *p = to_app(m.get_fact(parents.get(i)));
|
||||
rational const &r = params[i+1].get_rational();
|
||||
lcb.add_lit(p, r);
|
||||
}
|
||||
|
||||
TRACE("spacer.fkab",
|
||||
tout << "lit0 is: " << mk_pp(m.get_fact(parents.get(0)), m) << "\n"
|
||||
<< "LCB is: " << lcb() << "\n";);
|
||||
|
||||
expr_ref var(m), val1(m), val2(m);
|
||||
val1 = get_coeff(m.get_fact(parents.get(0)), var);
|
||||
val2 = get_coeff(lcb(), var);
|
||||
TRACE("spacer.fkab",
|
||||
tout << "var: " << var
|
||||
<< " val1: " << val1 << " val2: " << val2 << "\n";);
|
||||
|
||||
rational rat1, rat2, coeff0;
|
||||
arith_util a(m);
|
||||
if (a.is_numeral(val1, rat1) && a.is_numeral(val2, rat2)) {
|
||||
coeff0 = abs(rat2/rat1);
|
||||
coeff0 = coeff0 / lcb.lc();
|
||||
TRACE("spacer.fkab", tout << "coeff0: " << coeff0 << "\n";);
|
||||
}
|
||||
else {
|
||||
IF_VERBOSE(1, verbose_stream()
|
||||
<< "\n\n\nFAILED TO FIND COEFFICIENT\n\n\n";);
|
||||
// failed to find a coefficient
|
||||
return proof_ref(m);
|
||||
}
|
||||
|
||||
|
||||
buffer<parameter> v;
|
||||
v.push_back(parameter(symbol("farkas")));
|
||||
v.push_back(parameter(rational(1)));
|
||||
v.push_back(parameter(coeff0));
|
||||
for (unsigned i = 2; i < num_params; ++i)
|
||||
v.push_back(params[i]);
|
||||
|
||||
|
@ -124,11 +336,11 @@ namespace spacer {
|
|||
v.size(), v.c_ptr());
|
||||
|
||||
SASSERT(is_arith_lemma(m, pf));
|
||||
|
||||
DEBUG_CODE(
|
||||
proof_checker pc(m);
|
||||
expr_ref_vector side(m);
|
||||
SASSERT(pc.check(pf, side));
|
||||
);
|
||||
ENSURE(pc.check(pf, side)););
|
||||
return pf;
|
||||
|
||||
}
|
||||
|
@ -179,7 +391,9 @@ namespace spacer {
|
|||
d->get_num_parameters(),
|
||||
d->get_parameters());
|
||||
}
|
||||
else {
|
||||
|
||||
// fall back to th-lemma
|
||||
if (!th_lemma) {
|
||||
th_lemma = mk_th_lemma(m, hyps,
|
||||
d->get_num_parameters(),
|
||||
d->get_parameters());
|
||||
|
|
Loading…
Reference in a new issue