mirror of
https://github.com/Z3Prover/z3
synced 2026-07-14 19:15:41 +00:00
Fix non-termination in mod rewriter for symbolic modulus (issue #7403)
Add two rewrite rules in mk_mod_core: 1. mod(a + k*y, y) → mod(a, y): removes summands equal to (or integer multiples of) the symbolic modulus. Valid for all y including y=0 since k*0=0. 2. mod(mod(x, y), y) for symbolic y: rewrites via ite to terminate the E-matching chain created by the quantifier idempotency rule. Also add regression tests in src/test/arith_rewriter.cpp covering both new rules.
This commit is contained in:
parent
6bff306066
commit
b544f7e58c
2 changed files with 69 additions and 3 deletions
|
|
@ -1430,9 +1430,20 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul
|
|||
|
||||
// mod is idempotent on non-zero modulus.
|
||||
expr* t1, *t2;
|
||||
if (m_util.is_mod(arg1, t1, t2) && t2 == arg2 && is_num2 && is_int && !v2.is_zero()) {
|
||||
result = arg1;
|
||||
return BR_DONE;
|
||||
if (m_util.is_mod(arg1, t1, t2) && t2 == arg2) {
|
||||
if (is_num2 && is_int && !v2.is_zero()) {
|
||||
result = arg1;
|
||||
return BR_DONE;
|
||||
}
|
||||
// Symbolic modulus: mod (mod x y) y = ite(y = 0, mod (mod x 0) 0, mod x y).
|
||||
// Valid for all y: for y != 0, idempotency holds; for y = 0, both sides are mod0(mod0(x,0),0).
|
||||
if (!is_num2 && m_util.is_int(arg2)) {
|
||||
expr_ref zero(m_util.mk_int(0), m);
|
||||
result = m.mk_ite(m.mk_eq(arg2, zero),
|
||||
m_util.mk_mod(m_util.mk_mod(t1, zero), zero),
|
||||
arg1);
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
}
|
||||
|
||||
// propagate mod inside only if there is something to reduce.
|
||||
|
|
@ -1465,6 +1476,42 @@ br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & resul
|
|||
}
|
||||
}
|
||||
|
||||
// For symbolic modulus: remove summands that are multiples of the modulus.
|
||||
// mod (a + k*y) y = mod a y (valid for all y, including y=0 since k*0 = 0).
|
||||
if (!is_num2 && m_util.is_int(arg2) && is_add(arg1)) {
|
||||
expr_ref_buffer args(m);
|
||||
bool change = false;
|
||||
for (expr* arg : *to_app(arg1)) {
|
||||
if (arg == arg2) {
|
||||
// summand equals the modulus y: drop it (y ≡ 0 mod y for all y)
|
||||
change = true;
|
||||
}
|
||||
else if (m_util.is_mul(arg, t1, t2)) {
|
||||
rational coeff;
|
||||
if ((m_util.is_numeral(t1, coeff) && t2 == arg2) ||
|
||||
(m_util.is_numeral(t2, coeff) && t1 == arg2)) {
|
||||
// summand is k*y for some numeral k: drop it
|
||||
change = true;
|
||||
}
|
||||
else {
|
||||
args.push_back(arg);
|
||||
}
|
||||
}
|
||||
else {
|
||||
args.push_back(arg);
|
||||
}
|
||||
}
|
||||
if (change) {
|
||||
expr_ref new_arg1(m);
|
||||
if (args.empty())
|
||||
new_arg1 = m_util.mk_int(0);
|
||||
else
|
||||
new_arg1 = m_util.mk_add(args.size(), args.data());
|
||||
result = m_util.mk_mod(new_arg1, arg2);
|
||||
return BR_REWRITE3;
|
||||
}
|
||||
}
|
||||
|
||||
expr* x = nullptr, * y = nullptr, * z = nullptr;
|
||||
if (is_num2 && v2.is_pos() && m_util.is_mul(arg1, x, y) && m_util.is_numeral(x, v1, is_int) && v1 > 0 && divides(v1, v2)) {
|
||||
result = m_util.mk_mul(m_util.mk_int(v1), m_util.mk_mod(y, m_util.mk_int(v2/v1)));
|
||||
|
|
|
|||
|
|
@ -83,4 +83,23 @@ void tst_arith_rewriter() {
|
|||
rw(fml);
|
||||
std::cout << "consecutive product (minus) >= 0: " << mk_pp(fml, m) << "\n";
|
||||
ENSURE(m.is_true(fml));
|
||||
|
||||
// Issue #7403: mod (a + b) b should simplify to mod a b for symbolic b
|
||||
// i.e. (= (mod (+ I S) S) (mod I S)) should reduce to true
|
||||
fml = parse_int_fml(m, "(= (mod (+ I S) S) (mod I S))");
|
||||
rw(fml);
|
||||
std::cout << "mod (a+b) b = mod a b: " << mk_pp(fml, m) << "\n";
|
||||
ENSURE(m.is_true(fml));
|
||||
|
||||
// mod (a + 2*b) b should simplify to mod a b (multiple of modulus dropped)
|
||||
fml = parse_int_fml(m, "(= (mod (+ I (* 2 S)) S) (mod I S))");
|
||||
rw(fml);
|
||||
std::cout << "mod (a+2b) b = mod a b: " << mk_pp(fml, m) << "\n";
|
||||
ENSURE(m.is_true(fml));
|
||||
|
||||
// mod (mod a b) b should simplify for non-zero numeral b
|
||||
fml = parse_int_fml(m, "(= (mod (mod I 3) 3) (mod I 3))");
|
||||
rw(fml);
|
||||
std::cout << "mod (mod a 3) 3 = mod a 3: " << mk_pp(fml, m) << "\n";
|
||||
ENSURE(m.is_true(fml));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue