3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

wip - more general ranges for add_mul_bound

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2022-12-28 14:09:51 -08:00
parent 658877365c
commit ab9a9d2308
3 changed files with 146 additions and 61 deletions

View file

@ -1441,69 +1441,10 @@ namespace polysat {
* The range is a "forbidden interval" for y and is implied. It is much stronger than resolving on y0.
*
*/
#if 0
// outline of what should be a more general approach
pdd p = a_l_b.lhs(), q = a_l_b.rhs();
if (p.degree(x) > 1 || q.degree(x) > 1)
return false;
if (p.degree(x) == 0 && q.degree(x) == 0)
return false;
vector<signed_constraint> bounds;
if (!m_viable.has_max_forbidden(x, lo_x, hi_x, bounds))
return false;
SASSERT(lo_x != hi_x);
if (lo_x > hi_x)
lo_x += m.two_to_N();
SASSERT(lo_x < hi_x);
auto is_bounded = [&](pdd& p, rational& lo, rational& hi) {
if (!lower_bound(x, lo_x, p, lo))
return false;
if (!upper_bound(x, hi_x, p, hi))
return false;
SASSERT(0 <= lo && lo <= hi);
if (lo + m.two_to_N() < hi)
return false;
ratinoal offset = floor(lo / m.two_to_N()) * m.two_to_N();
lo -= offset;
hi -= offset;
return true;
};
rational lo_p, hi_p, offset_p;
rational lo_q, hi_q, offset_q;
rational lo_r, hi_r, offset_r;
pdd r = q - p;
if (!is_bounded(p, lo_p, hi_p, offset_p))
return false;
if (!is_bounded(q, lo_q, hi_q, offset_q))
return false;
if (!is_bounded(r, lo_r, hi_r, offset_r))
return false;
SASSERT(0 <= lo_r && lo_r <= hi_r);
// for every value of x, p, q are bewteen lo_p, hi_p, lo_q, hi_q
// if a_l_b is non-strict, it is false under all assignments to x, so r > 0
// if a_l_b is strict, we bail also if r = 1
if (lo_r == 0)
return false;
if (a_l_b.is_strict() && lo_r == 1)
return false;
// isolate what is 'y'
// then compute range around y that is admissible based on x_lo, x_hi
#endif
bool saturation::try_add_mul_bound(pvar x, conflict& core, inequality const& a_l_b) {
set_rule("[x] ax + b <= y, ... => a >= u_a");
// try_add_mul_bound2(x, core, a_l_b);
auto& m = s.var2pdd(x);
pdd const X = s.var(x);
pdd a = s.var(x);
@ -1556,6 +1497,126 @@ namespace polysat {
return false;
}
bool saturation::get_bound(pvar x, rational const& bound_x, pdd const& p, rational& bound_p) {
if (p.degree(x) == 0)
return s.try_eval(p, bound_p);
pdd a = p, b = p;
rational a_val, b_val;
p.factor(x, 1, a, b);
if (!get_bound(x, bound_x, a, a_val))
return false;
if (!get_bound(x, bound_x, b, b_val))
return false;
bound_p = bound_x * a_val + b_val;
return true;
}
// wip - outline of what should be a more general approach
bool saturation::try_add_mul_bound2(pvar x, conflict& core, inequality const& a_l_b) {
auto& m = s.var2pdd(x);
pdd p = a_l_b.lhs(), q = a_l_b.rhs();
if (p.degree(x) > 1 || q.degree(x) > 1)
return false;
if (p.degree(x) == 0 && q.degree(x) == 0)
return false;
vector<signed_constraint> bounds;
rational x_min, x_max;
if (!s.m_viable.has_max_forbidden(x, x_min, x_max, bounds))
return false;
VERIFY(x_min != x_max);
// From forbidden interval [x_min, x_max[ compute
// allowed range: [x_max, x_min - 1]
SASSERT(0 <= x_min && x_min <= m.max_value());
SASSERT(0 <= x_max && x_max <= m.max_value());
rational hi = x_min == 0 ? m.max_value() : x_min - 1;
x_min = x_max;
x_max = hi;
SASSERT(x_min != x_max);
if (x_min > x_max)
x_min += m.two_to_N();
SASSERT(x_min <= x_max);
if (x_max == 0) {
SASSERT(x_min == 0);
return false;
}
auto isolate_y = [&](pvar x, pdd const& p, pdd& y, pdd& b) {
if (p.degree(x) != 1)
return false;
p.factor(x, 1, y, b);
return !y.is_val();
};
pdd y = p, b = p;
rational b_val, y_val;
// handle just one-side with x for now
if (p.degree(x) == 1 && q.degree(x) == 1)
return false;
if (!isolate_y(x, p, y, b) && !isolate_y(x, q, y, b))
return false;
if (!s.try_eval(b, b_val))
return false;
if (!s.try_eval(y, y_val))
return false;
// at this point p = x*y + b or q = x*y + b
auto is_bounded = [&](pdd& p, rational& lo, rational& hi) {
verbose_stream() << "is-bounded " << p << "\n";
if (!get_bound(x, x_min, p, lo))
return false;
if (!get_bound(x, x_max, p, hi))
return false;
SASSERT(0 <= lo && lo <= hi);
if (lo + m.two_to_N() < hi)
return false;
rational offset = floor(lo / m.two_to_N()) * m.two_to_N();
lo -= offset;
hi -= offset;
return true;
};
rational lo_p, hi_p;
rational lo_q, hi_q;
rational lo_r, hi_r;
pdd r = q - p;
if (!is_bounded(p, lo_p, hi_p))
return false;
if (!is_bounded(q, lo_q, hi_q))
return false;
if (!is_bounded(r, lo_r, hi_r))
return false;
SASSERT(0 <= lo_r && lo_r <= hi_r);
verbose_stream() << "bounded ranges\n";
verbose_stream() << a_l_b << ": v" << x << " y: " << y << " := " << y_val << "\n";
verbose_stream() << p << " " << lo_p << " " << hi_p << "\n";
verbose_stream() << q << " " << lo_q << " " << hi_q << "\n";
verbose_stream() << r << " " << lo_r << " " << hi_r << "\n";
verbose_stream() << x_min << " " << x_max << "\n";
// for every value of x, p, q are bewteen lo_p, hi_p, lo_q, hi_q
// if a_l_b is non-strict, it is false under all assignments to x, so r > 0
// if a_l_b is strict, we bail also if r = 1
if (lo_r == 0)
return false;
if (a_l_b.is_strict() && lo_r == 1)
return false;
// then compute range around y that is admissible based on x_lo, x_hi
rational max_y, min_y = m.max_value();
if (q.degree(x) == 1) {
// q = x*y + b <= 2^N - 1
// y <= (2^N - 1 - b) / x_max
max_y = floor((m.max_value() - b_val)/x_max);
verbose_stream() << b << " " << b_val << " max-y " << max_y << "\n";
}
return false;
}
/*
* TODO

View file

@ -63,7 +63,10 @@ namespace polysat {
bool try_tangent(pvar v, conflict& core, inequality const& c);
bool try_add_overflow_bound(pvar x, conflict& core, inequality const& axb_l_y);
bool try_add_mul_bound(pvar x, conflict& core, inequality const& axb_l_y);
bool try_add_mul_bound2(pvar x, conflict& core, inequality const& axb_l_y);
bool get_bound(pvar x, rational const& bound_x, pdd const& p, rational& bound_p);
// c := lhs ~ v
// where ~ is < or <=
bool is_l_v(pvar v, inequality const& c);

View file

@ -663,6 +663,8 @@ namespace polysat {
entry const* e = first;
bool found = false;
out_c.reset();
if (!e)
return false;
do {
found = false;
do {
@ -695,6 +697,8 @@ namespace polysat {
entry const* e = first;
bool found = false;
out_c.reset();
if (!e)
return false;
do {
found = false;
do {
@ -727,6 +731,20 @@ namespace polysat {
entry const* first = m_units[v];
entry const* e = first;
bool found = false;
if (!e)
return false;
auto covers_all = [&](rational const& lo1, rational const& hi1, rational const lo2, rational const& hi2) {
SASSERT(lo1 != hi1);
if (lo1 < hi1) {
return lo2 <= hi1 && lo1 <= hi2;
}
else {
// hi1 < lo1
return hi1 <= hi2 && hi2 <= lo2 && lo2 <= lo1;
}
};
do {
found = false;
do {
@ -737,13 +755,16 @@ namespace polysat {
auto const& hi = e->interval.hi();
if (!lo.is_val() || !hi.is_val())
goto next;
if (out_c.contains(e->src))
goto next;
if (out_c.empty()) {
out_c.push_back(e->src);
out_lo = lo.val();
out_hi = hi.val();
found = true;
}
else if (covers_all(out_lo, out_hi, lo.val(), hi.val()))
return false;
// [lo, hi0, hi[
// [lo, hi0, 0, hi[
else if (lo.val() <= out_hi && (out_hi < hi.val() || hi.val() < lo.val())) {