mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 17:45:32 +00:00
Fixedpoint unit tests and transcribed bv puzzle (#5291)
* simple fixed point arithmetic tests * transcribe puzzle
This commit is contained in:
parent
26893005c7
commit
49e9782238
1 changed files with 217 additions and 3 deletions
|
@ -347,12 +347,14 @@ namespace polysat {
|
|||
|
||||
/**
|
||||
* Monotonicity example from certora
|
||||
*
|
||||
* We do overflow checks by doubling the base bitwidth here.
|
||||
*/
|
||||
static void test_monot() {
|
||||
scoped_solver s(__func__);
|
||||
|
||||
auto baseBw = 5;
|
||||
auto max_int_const = 31; // (2^5 - 1) -- change this if you change baseBw
|
||||
auto max_int_const = 31; // (2^5 - 1) -- change this when you change baseBw
|
||||
|
||||
auto bw = 2 * baseBw;
|
||||
auto max_int = s.var(s.add_var(bw));
|
||||
|
@ -377,8 +379,6 @@ namespace polysat {
|
|||
auto err = s.var(s.add_var(bw));
|
||||
s.add_ule(err, max_int);
|
||||
|
||||
auto zero = a - a;
|
||||
|
||||
auto rem1 = s.var(s.add_var(bw));
|
||||
auto quot2 = s.var(s.add_var(bw));
|
||||
s.add_ule(quot2, max_int);
|
||||
|
@ -429,6 +429,220 @@ namespace polysat {
|
|||
s.pop();
|
||||
}
|
||||
|
||||
/*
|
||||
* Mul-then-div in fixed point arithmetic is (roughly) neutral.
|
||||
*
|
||||
* I.e. we prove "(((a * b) / sf) * sf) / b" to be equal to a, up to some error margin.
|
||||
*
|
||||
* sf is the scaling factor (we could leave this unconstrained, but non-zero, to make the benchmark a bit harder)
|
||||
* em is the error margin
|
||||
*
|
||||
* We do overflow checks by doubling the base bitwidth here.
|
||||
*/
|
||||
static void test_fixed_point_arith_mul_div_inverse() {
|
||||
scoped_solver s(__func__);
|
||||
|
||||
auto baseBw = 5;
|
||||
auto max_int_const = 31; // (2^5 - 1) -- change this when you change baseBw
|
||||
|
||||
auto bw = 2 * baseBw;
|
||||
auto max_int = s.var(s.add_var(bw));
|
||||
s.add_eq(max_int - max_int_const);
|
||||
|
||||
auto zero = max_int - max_int;
|
||||
|
||||
// "input" variables
|
||||
auto a = s.var(s.add_var(bw));
|
||||
s.add_ule(a, max_int);
|
||||
auto b = s.var(s.add_var(bw));
|
||||
s.add_ule(b, max_int);
|
||||
s.add_ult(zero, b); // b > 0
|
||||
|
||||
// scaling factor (setting it, somewhat arbitrarily, to max_int/3)
|
||||
auto sf = s.var(s.add_var(bw));
|
||||
s.add_eq(sf - (max_int_const/3));
|
||||
|
||||
// (a * b) / sf = quot1 <=> quot1 * sf + rem1 - (a * b) = 0
|
||||
auto quot1 = s.var(s.add_var(bw));
|
||||
auto rem1 = s.var(s.add_var(bw));
|
||||
s.add_eq((quot1 * sf) + rem1 - (a * b));
|
||||
s.add_ult(rem1, sf);
|
||||
s.add_ule(quot1 * sf, max_int);
|
||||
|
||||
// (((a * b) / sf) * sf) / b <=> quot2 * b + rem2 - (((a * b) / sf) * sf) = 0
|
||||
auto quot2 = s.var(s.add_var(bw));
|
||||
auto rem2 = s.var(s.add_var(bw));
|
||||
s.add_eq((quot2 * b) + rem2 - (quot1 * sf));
|
||||
s.add_ult(rem2, b);
|
||||
s.add_ule(quot2 * b, max_int);
|
||||
|
||||
// sf / b = quot3 <=> quot3 * b + rem3 = sf
|
||||
auto quot3 = s.var(s.add_var(bw));
|
||||
auto rem3 = s.var(s.add_var(bw));
|
||||
s.add_eq((quot3 * b) + rem3 - sf);
|
||||
s.add_ult(rem3, b);
|
||||
s.add_ule(quot3 * b, max_int);
|
||||
|
||||
// em = sf / b + 1
|
||||
auto em = s.var(s.add_var(bw));
|
||||
s.add_eq(quot3 + 1 - em);
|
||||
|
||||
// we prove quot3 <= a and quot3 + em >= a
|
||||
|
||||
s.push();
|
||||
s.add_ult(a, quot3);
|
||||
s.check_sat();
|
||||
s.expect_unsat();
|
||||
s.pop();
|
||||
|
||||
s.push();
|
||||
s.add_ult(quot3 + em, a);
|
||||
s.check_sat();
|
||||
s.expect_unsat();
|
||||
s.pop();
|
||||
}
|
||||
|
||||
/*
|
||||
* Div-then-mul in fixed point arithmetic is (roughly) neutral.
|
||||
*
|
||||
* I.e. we prove "(b * ((a * sf) / b)) / sf" to be equal to a, up to some error margin.
|
||||
*
|
||||
* sf is the scaling factor (we could leave this unconstrained, but non-zero, to make the benchmark a bit harder)
|
||||
* em is the error margin
|
||||
*
|
||||
* We do overflow checks by doubling the base bitwidth here.
|
||||
*/
|
||||
static void test_fixed_point_arith_div_mul_inverse() {
|
||||
scoped_solver s(__func__);
|
||||
|
||||
auto baseBw = 5;
|
||||
auto max_int_const = 31; // (2^5 - 1) -- change this when you change baseBw
|
||||
|
||||
auto bw = 2 * baseBw;
|
||||
auto max_int = s.var(s.add_var(bw));
|
||||
s.add_eq(max_int - max_int_const);
|
||||
|
||||
auto zero = max_int - max_int;
|
||||
|
||||
// "input" variables
|
||||
auto a = s.var(s.add_var(bw));
|
||||
s.add_ule(a, max_int);
|
||||
auto b = s.var(s.add_var(bw));
|
||||
s.add_ule(b, max_int);
|
||||
s.add_ult(zero, b); // b > 0
|
||||
|
||||
// scaling factor (setting it, somewhat arbitrarily, to max_int/3)
|
||||
auto sf = s.var(s.add_var(bw));
|
||||
s.add_eq(sf - (max_int_const/3));
|
||||
|
||||
// (a * sf) / b = quot1 <=> quot1 * b + rem1 - (a * sf) = 0
|
||||
auto quot1 = s.var(s.add_var(bw));
|
||||
auto rem1 = s.var(s.add_var(bw));
|
||||
s.add_eq((quot1 * b) + rem1 - (a * sf));
|
||||
s.add_ult(rem1, b);
|
||||
s.add_ule(quot1 * b, max_int);
|
||||
|
||||
// (b * ((a * sf) / b)) / sf = quot2 <=> quot2 * sf + rem2 - (b * ((a * sf) / b)) = 0
|
||||
auto quot2 = s.var(s.add_var(bw));
|
||||
auto rem2 = s.var(s.add_var(bw));
|
||||
s.add_eq((quot2 * sf) + rem2 - (b * quot1));
|
||||
s.add_ult(rem2, sf);
|
||||
s.add_ule(quot2 * sf, max_int);
|
||||
|
||||
// b / sf = quot3 <=> quot3 * sf + rem3 - b = 0
|
||||
auto quot3 = s.var(s.add_var(bw));
|
||||
auto rem3 = s.var(s.add_var(bw));
|
||||
s.add_eq((quot3 * sf) + rem3 - b);
|
||||
s.add_ult(rem3, sf);
|
||||
s.add_ule(quot3 * sf, max_int);
|
||||
|
||||
// em = b / sf + 1
|
||||
auto em = s.var(s.add_var(bw));
|
||||
s.add_eq(quot3 + 1 - em);
|
||||
|
||||
// we prove quot3 <= a and quot3 + em >= a
|
||||
|
||||
s.push();
|
||||
s.add_ult(a, quot3);
|
||||
s.check_sat();
|
||||
s.expect_unsat();
|
||||
s.pop();
|
||||
|
||||
s.push();
|
||||
s.add_ult(quot3 + em, a);
|
||||
s.check_sat();
|
||||
s.expect_unsat();
|
||||
s.pop();
|
||||
}
|
||||
|
||||
/*
|
||||
* Transcribed from https://github.com/NikolajBjorner/polysat/blob/main/puzzles/bv.smt2 .
|
||||
|
||||
* We do overflow checks by doubling the base bitwidth here.
|
||||
*/
|
||||
static void test_fixed_point_arith_div_mul_inverse() {
|
||||
scoped_solver s(__func__);
|
||||
|
||||
auto baseBw = 5;
|
||||
auto max_int_const = 31; // (2^5 - 1) -- change this when you change baseBw
|
||||
|
||||
auto bw = 2 * baseBw;
|
||||
auto max_int = s.var(s.add_var(bw));
|
||||
s.add_eq(max_int - max_int_const);
|
||||
|
||||
auto zero = max_int - max_int;
|
||||
|
||||
auto first = s.var(s.add_var(bw));
|
||||
s.add_ule(first, max_int);
|
||||
auto second = s.var(s.add_var(bw));
|
||||
s.add_ule(second, max_int);
|
||||
auto idx = s.var(s.add_var(bw));
|
||||
s.add_ule(idx, max_int);
|
||||
auto r = s.var(s.add_var(bw));
|
||||
s.add_ule(r, max_int);
|
||||
|
||||
// q = max_int / idx <=> q * idx + r - max_int = 0
|
||||
auto q = s.var(s.add_var(bw));
|
||||
auto r = s.var(s.add_var(bw));
|
||||
s.add_eq((q * idx) + r - max_int);
|
||||
s.add_ult(r, idx);
|
||||
s.add_ule(q * idx, max_int);
|
||||
|
||||
/* last assertion:
|
||||
(not
|
||||
(=> (bvugt second first)
|
||||
(=>
|
||||
(=> (not (= idx #x00000000))
|
||||
(bvule (bvsub second first) q))
|
||||
(bvumul_noovfl (bvsub second first) idx))))
|
||||
transforming negated boolean skeleton:
|
||||
(not (=> a (=> (or b c) d))) <=> (and a (not d) (or b c))
|
||||
*/
|
||||
|
||||
// (bvugt second first)
|
||||
s.add_ult(first, second);
|
||||
// (bvumul_noovfl (bvsub second first) idx)
|
||||
s.add_ule((second - first) * idx, max_int);
|
||||
|
||||
// resolving disjunction via push/pop
|
||||
|
||||
// first disjunct: (= idx #x00000000)
|
||||
s.push();
|
||||
s.add_eq(idx);
|
||||
s.check_sat();
|
||||
s.expect_unsat();
|
||||
s.pop();
|
||||
|
||||
// second disjunct: (bvule (bvsub second first) q)
|
||||
s.push();
|
||||
s.add_ule(second - first, q);
|
||||
s.check_sat();
|
||||
s.expect_unsat();
|
||||
s.pop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Goal: we probably mix up polysat variables and PDD variables at several points; try to uncover such cases
|
||||
// NOTE: actually, add_var seems to keep them in sync, so this is not an issue at the moment (but we should still test it later)
|
||||
// static void test_mixed_vars() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue