mirror of
https://github.com/Z3Prover/z3
synced 2025-08-15 15:25:26 +00:00
Test forbidden intervals, disequal case
This commit is contained in:
parent
175b348948
commit
fa75a9109e
6 changed files with 180 additions and 13 deletions
|
@ -244,6 +244,7 @@ namespace polysat {
|
||||||
fi.coeff = -1;
|
fi.coeff = -1;
|
||||||
fi.interval = to_interval(c, false, fi.coeff, lo_val, lo, hi_val, hi);
|
fi.interval = to_interval(c, false, fi.coeff, lo_val, lo, hi_val, hi);
|
||||||
add_non_unit_side_conds(fi, b1, e1, b2, e2);
|
add_non_unit_side_conds(fi, b1, e1, b2, e2);
|
||||||
|
SASSERT(!fi.interval.is_currently_empty());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -70,6 +70,8 @@ namespace polysat {
|
||||||
return {interval::full(), rational::zero(), rational::zero()};
|
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) {
|
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};
|
return {interval::proper(lo, hi), lo_val, hi_val};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,7 @@ namespace polysat {
|
||||||
friend class constraint_manager;
|
friend class constraint_manager;
|
||||||
friend class scoped_solverv;
|
friend class scoped_solverv;
|
||||||
friend class test_polysat;
|
friend class test_polysat;
|
||||||
|
friend class test_fi;
|
||||||
|
|
||||||
reslimit& m_lim;
|
reslimit& m_lim;
|
||||||
params_ref m_params;
|
params_ref m_params;
|
||||||
|
|
|
@ -206,6 +206,10 @@ namespace polysat {
|
||||||
return floor((e->interval.hi_val() - coeff_val - 1) / e->coeff);
|
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?
|
// naive widening. TODO: can we accelerate this?
|
||||||
// condition e->interval.hi_val() < coeff_val is
|
// condition e->interval.hi_val() < coeff_val is
|
||||||
// to ensure that widening is performed on the same interval
|
// to ensure that widening is performed on the same interval
|
||||||
|
@ -255,7 +259,8 @@ namespace polysat {
|
||||||
increase_hi(hi);
|
increase_hi(hi);
|
||||||
}
|
}
|
||||||
LOG("forbidden interval " << e->coeff << " * " << e->interval << " [" << lo << ", " << 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 lop = s.var2pdd(v).mk_val(lo);
|
||||||
pdd hip = s.var2pdd(v).mk_val(hi);
|
pdd hip = s.var2pdd(v).mk_val(hi);
|
||||||
entry* ne = alloc_entry();
|
entry* ne = alloc_entry();
|
||||||
|
@ -291,10 +296,11 @@ namespace polysat {
|
||||||
rational const& b1 = e->interval.lo().val();
|
rational const& b1 = e->interval.lo().val();
|
||||||
rational const& a2 = e->interval.hi_val();
|
rational const& a2 = e->interval.hi_val();
|
||||||
rational const& b2 = 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);
|
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) {
|
auto delta_l = [&](rational const& val) {
|
||||||
rational m1 = ceil((rhs + 1) / a2);
|
rational m1 = ceil((rhs + 1) / a2);
|
||||||
int corr = e->src.is_negative() ? 1 : 0;
|
int corr = e->src.is_negative() ? 1 : 0;
|
||||||
|
@ -304,7 +310,8 @@ namespace polysat {
|
||||||
else
|
else
|
||||||
m3 = ceil(m3);
|
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) {
|
auto delta_u = [&](rational const& val) {
|
||||||
rational m1 = ceil((mod_value - lhs) / a1);
|
rational m1 = ceil((mod_value - lhs) / a1);
|
||||||
|
@ -326,8 +333,10 @@ namespace polysat {
|
||||||
// TODO: increase interval
|
// TODO: increase interval
|
||||||
LOG("refine-disequal-lin: " << " [" << lo << ", " << hi << "[");
|
LOG("refine-disequal-lin: " << " [" << lo << ", " << hi << "[");
|
||||||
|
|
||||||
SASSERT(0 <= lo);
|
SASSERT(0 <= lo && lo <= val);
|
||||||
SASSERT(hi <= max_value);
|
// 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 lop = s.var2pdd(v).mk_val(lo);
|
||||||
pdd hip = s.var2pdd(v).mk_val(hi);
|
pdd hip = s.var2pdd(v).mk_val(hi);
|
||||||
entry* ne = alloc_entry();
|
entry* ne = alloc_entry();
|
||||||
|
|
|
@ -29,6 +29,8 @@ namespace polysat {
|
||||||
class solver;
|
class solver;
|
||||||
|
|
||||||
class viable {
|
class viable {
|
||||||
|
friend class test_fi;
|
||||||
|
|
||||||
solver& s;
|
solver& s;
|
||||||
|
|
||||||
struct entry : public dll_base<entry>, public fi_record {
|
struct entry : public dll_base<entry>, public fi_record {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "math/polysat/solver.h"
|
#include "math/polysat/solver.h"
|
||||||
#include "ast/ast.h"
|
#include "ast/ast.h"
|
||||||
#include "parsers/smt2/smt2parser.h"
|
#include "parsers/smt2/smt2parser.h"
|
||||||
|
#include "util/util.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -793,21 +794,24 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// xy < xz and !Omega(x*y) => y < z
|
// xy < xz and !Omega(x*y) => y < z
|
||||||
static void test_ineq_axiom1(unsigned bw = 32) {
|
static void test_ineq_axiom1(unsigned bw = 32, std::optional<unsigned> perm = std::nullopt) {
|
||||||
auto const bound = rational::power_of_two(bw/2);
|
if (perm) {
|
||||||
|
scoped_solver s(std::string(__func__) + " perm=" + std::to_string(*perm));
|
||||||
for (unsigned i = 0; i < 6; ++i) {
|
auto const bound = rational::power_of_two(bw/2);
|
||||||
scoped_solver s(__func__);
|
|
||||||
auto x = s.var(s.add_var(bw));
|
auto x = s.var(s.add_var(bw));
|
||||||
auto y = s.var(s.add_var(bw));
|
auto y = s.var(s.add_var(bw));
|
||||||
auto z = 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_ult(x * y, x * z);
|
||||||
s.add_ule(z, y);
|
s.add_ule(z, y);
|
||||||
s.add_ult(x, bound);
|
s.add_ult(x, bound);
|
||||||
s.add_ult(y, bound);
|
s.add_ult(y, bound);
|
||||||
s.check();
|
s.check();
|
||||||
s.expect_unsat();
|
s.expect_unsat();
|
||||||
|
} else {
|
||||||
|
for (unsigned i = 0; i < 6; ++i) {
|
||||||
|
test_ineq_axiom1(bw, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1062,8 +1066,41 @@ public:
|
||||||
s.check();
|
s.check();
|
||||||
s.expect_unsat();
|
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
|
// 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
|
}; // 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
|
// convert assertions into internal solver state
|
||||||
// support small grammar of formulas.
|
// support small grammar of formulas.
|
||||||
pdd to_pdd(ast_manager& m, solver& s, obj_map<expr, pdd*>& expr2pdd, expr* e) {
|
pdd to_pdd(ast_manager& m, solver& s, obj_map<expr, pdd*>& expr2pdd, expr* e) {
|
||||||
|
@ -1203,6 +1341,20 @@ public:
|
||||||
void tst_polysat() {
|
void tst_polysat() {
|
||||||
using namespace 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_incomplete();
|
||||||
test_polysat::test_quot_rem_fixed();
|
test_polysat::test_quot_rem_fixed();
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue