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

Test forbidden intervals, disequal case

This commit is contained in:
Jakob Rath 2022-01-19 19:06:35 +01:00
parent 175b348948
commit fa75a9109e
6 changed files with 180 additions and 13 deletions

View file

@ -244,6 +244,7 @@ namespace polysat {
fi.coeff = -1;
fi.interval = to_interval(c, false, fi.coeff, lo_val, lo, hi_val, hi);
add_non_unit_side_conds(fi, b1, e1, b2, e2);
SASSERT(!fi.interval.is_currently_empty());
return true;
}
return false;

View file

@ -70,6 +70,8 @@ namespace polysat {
return {interval::full(), rational::zero(), rational::zero()};
}
static eval_interval proper(pdd const &lo, rational const &lo_val, pdd const &hi, rational const &hi_val) {
SASSERT(0 <= lo_val && lo_val <= lo.manager().max_value());
SASSERT(0 <= hi_val && hi_val <= hi.manager().max_value());
return {interval::proper(lo, hi), lo_val, hi_val};
}

View file

@ -77,6 +77,7 @@ namespace polysat {
friend class constraint_manager;
friend class scoped_solverv;
friend class test_polysat;
friend class test_fi;
reslimit& m_lim;
params_ref m_params;

View file

@ -206,6 +206,10 @@ namespace polysat {
return floor((e->interval.hi_val() - coeff_val - 1) / e->coeff);
};
// TODO: looks like there's an infinite loop for:
// [match_linear3] a1 = 3; b1 = 0; e1 = 0
// [match_linear3] a2 = 3; b2 = -2; e2 = -2
// naive widening. TODO: can we accelerate this?
// condition e->interval.hi_val() < coeff_val is
// to ensure that widening is performed on the same interval
@ -255,7 +259,8 @@ namespace polysat {
increase_hi(hi);
}
LOG("forbidden interval " << e->coeff << " * " << e->interval << " [" << lo << ", " << hi << "[");
SASSERT(hi <= max_value);
SASSERT(hi <= mod_value);
if (hi == mod_value) hi = 0;
pdd lop = s.var2pdd(v).mk_val(lo);
pdd hip = s.var2pdd(v).mk_val(hi);
entry* ne = alloc_entry();
@ -291,10 +296,11 @@ namespace polysat {
rational const& b1 = e->interval.lo().val();
rational const& a2 = e->interval.hi_val();
rational const& b2 = e->interval.hi().val();
rational lhs = a1 * val + b1;
rational rhs = a2 * val + b2;
SASSERT(a1 != a2 && a1 != 0 && a2 != 0);
rational lhs = mod(a1 * val + b1, mod_value);
rational rhs = mod(a2 * val + b2, mod_value);
auto delta_l = [&](rational const& val) {
rational m1 = ceil((rhs + 1) / a2);
int corr = e->src.is_negative() ? 1 : 0;
@ -304,7 +310,8 @@ namespace polysat {
else
m3 = ceil(m3);
return std::min(m1, m3) - 1;
// return std::min(m1, m3) - 1;
return std::min(val, std::min(m1, m3) - 1);
};
auto delta_u = [&](rational const& val) {
rational m1 = ceil((mod_value - lhs) / a1);
@ -326,8 +333,10 @@ namespace polysat {
// TODO: increase interval
LOG("refine-disequal-lin: " << " [" << lo << ", " << hi << "[");
SASSERT(0 <= lo);
SASSERT(hi <= max_value);
SASSERT(0 <= lo && lo <= val);
// SASSERT(val <= hi && hi <= max_value);
SASSERT(val <= hi && hi <= mod_value);
if (hi == mod_value) hi = 0;
pdd lop = s.var2pdd(v).mk_val(lo);
pdd hip = s.var2pdd(v).mk_val(hi);
entry* ne = alloc_entry();

View file

@ -29,6 +29,8 @@ namespace polysat {
class solver;
class viable {
friend class test_fi;
solver& s;
struct entry : public dll_base<entry>, public fi_record {

View file

@ -2,6 +2,7 @@
#include "math/polysat/solver.h"
#include "ast/ast.h"
#include "parsers/smt2/smt2parser.h"
#include "util/util.h"
#include <vector>
namespace {
@ -793,21 +794,24 @@ public:
}
// xy < xz and !Omega(x*y) => y < z
static void test_ineq_axiom1(unsigned bw = 32) {
auto const bound = rational::power_of_two(bw/2);
for (unsigned i = 0; i < 6; ++i) {
scoped_solver s(__func__);
static void test_ineq_axiom1(unsigned bw = 32, std::optional<unsigned> perm = std::nullopt) {
if (perm) {
scoped_solver s(std::string(__func__) + " perm=" + std::to_string(*perm));
auto const bound = rational::power_of_two(bw/2);
auto x = s.var(s.add_var(bw));
auto y = s.var(s.add_var(bw));
auto z = s.var(s.add_var(bw));
permute_args(i, x, y, z);
permute_args(*perm, x, y, z);
s.add_ult(x * y, x * z);
s.add_ule(z, y);
s.add_ult(x, bound);
s.add_ult(y, bound);
s.check();
s.expect_unsat();
} else {
for (unsigned i = 0; i < 6; ++i) {
test_ineq_axiom1(bw, i);
}
}
}
@ -1062,8 +1066,41 @@ public:
s.check();
s.expect_unsat();
}
}
static void test_fi_linear4(unsigned bw = 4) {
{
scoped_solver s(__func__);
auto y = s.var(s.add_var(bw));
s.add_ule(3*y + 1, 10*y);
s.check();
s.expect_sat();
}
{
scoped_solver s(__func__);
auto y = s.var(s.add_var(bw));
s.add_ult(3*y + 1, 10*y);
s.check();
s.expect_sat();
}
{
scoped_solver s(__func__);
auto y = s.var(s.add_var(bw));
s.add_ule(y-y+8, y);
s.add_ule(y, y-y+12);
s.add_ult(9*y + 3, 7*y + 1);
s.check();
s.expect_sat();
}
{
scoped_solver s(__func__);
auto y = s.var(s.add_var(bw));
s.add_ule(y-y+8, y);
s.add_ule(y, y-y+12);
s.add_ule(9*y + 3, 7*y + 1);
s.check();
s.expect_sat();
}
}
// Goal: we probably mix up polysat variables and PDD variables at several points; try to uncover such cases
@ -1082,6 +1119,107 @@ public:
}; // class test_polysat
/// Here we deal with linear constraints of the form
///
/// a1*x + b1 <= a2*x + b2 (mod m = 2^bw)
///
/// and their negation.
class test_fi {
static bool is_violated(rational const& a1, rational const& b1, rational const& a2, rational const& b2, rational const& val, bool negated, rational const& m) {
rational const lhs = (a1*val + b1) % m;
rational const rhs = (a2*val + b2) % m;
if (negated)
return lhs <= rhs;
else
return lhs > rhs;
}
// Returns true if the input is valid and the test did useful work
static bool check_one(rational const& a1, rational const& b1, rational const& a2, rational const& b2, rational const& val, bool negated, unsigned bw) {
rational const m = rational::power_of_two(bw);
if (a1.is_zero() && a2.is_zero())
return false;
if (!is_violated(a1, b1, a2, b2, val, negated, m))
return false;
scoped_solver s(__func__);
auto x = s.var(s.add_var(bw));
signed_constraint c = s.ule(a1*x + b1, a2*x + b2);
if (negated)
c.negate();
viable& v = s.m_viable;
v.intersect(x.var(), c);
// Trigger forbidden interval refinement
v.is_viable(x.var(), val);
auto* e = v.m_units[x.var()];
if (!e) {
std::cout << "test_fi: no interval for a1=" << a1 << " b1=" << b1 << " a2=" << a2 << " b2=" << b2 << " val=" << val << " neg=" << negated << std::endl;
// VERIFY(false);
return false;
}
VERIFY(e);
auto* first = e;
SASSERT(e->next() == e); // the result is expected to be a single interval (although for this check it doesn't really matter if there's more...)
do {
rational const& lo = e->interval.lo_val();
rational const& hi = e->interval.hi_val();
for (rational x = lo; x != hi; x = (x + 1) % m) {
// LOG("lo=" << lo << " hi=" << hi << " x=" << x);
if (!is_violated(a1, b1, a2, b2, val, negated, m)) {
std::cout << "test_fi: unsound for a1=" << a1 << " b1=" << b1 << " a2=" << a2 << " b2=" << b2 << " val=" << val << " neg=" << negated << std::endl;
VERIFY(false);
}
}
e = e->next();
}
while (e != first);
return true;
}
public:
static void exhaustive(unsigned bw = 3) {
rational const m = rational::power_of_two(bw);
for (rational a1(1); a1 < m; ++a1) {
for (rational a2(1); a2 < m; ++a2) {
// TODO: remove this to test other cases
if (a1 == a2)
continue;
for (rational b1(0); b1 < m; ++b1)
for (rational b2(0); b2 < m; ++b2)
for (rational val(0); val < m; ++val)
for (bool negated : {true, false})
check_one(a1, b1, a2, b2, val, negated, bw);
}
}
}
static void randomized(unsigned num_rounds = 10'000, unsigned bw = 16) {
rational const m = rational::power_of_two(bw);
VERIFY(bw <= 32 && "random_gen generates 32-bit numbers");
random_gen rng;
unsigned round = num_rounds;
while (round) {
// rational a1 = (rational(rng()) % (m - 1)) + 1;
// rational a2 = (rational(rng()) % (m - 1)) + 1;
rational a1 = rational(rng()) % m;
rational a2 = rational(rng()) % m;
if (a1.is_zero() || a2.is_zero() || a1 == a2)
continue;
rational b1 = rational(rng()) % m;
rational b2 = rational(rng()) % m;
rational val = rational(rng()) % m;
bool useful =
check_one(a1, b1, a2, b2, val, true, bw)
|| check_one(a1, b1, a2, b2, val, false, bw);
if (useful)
round--;
}
}
}; // class test_fi
// convert assertions into internal solver state
// support small grammar of formulas.
pdd to_pdd(ast_manager& m, solver& s, obj_map<expr, pdd*>& expr2pdd, expr* e) {
@ -1203,6 +1341,20 @@ public:
void tst_polysat() {
using namespace polysat;
test_fi::exhaustive();
test_fi::randomized();
return;
test_polysat::test_fi_linear4();
return;
test_polysat::test_ineq_axiom1(32, 2); // crashes
return;
// looks like a fishy conflict lemma?
test_polysat::test_monot_bounds();
return;
test_polysat::test_quot_rem_incomplete();
test_polysat::test_quot_rem_fixed();
return;