3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-25 00:12:34 +00:00

Spacer QE: avoid assertion on extended arithmetic model values and add #3845 regression (#10096)

Spacer crashed in quantifier-elimination projection on certain HORN
inputs when model evaluation produced arithmetic expressions that were
not plain numerals. The failure was an assertion in
`spacer_qe_project.cpp` during sign/offset computation for projected
literals.

- **Projection robustness in Spacer arithmetic QE**
- Updated numeral extraction in `src/muz/spacer/spacer_qe_project.cpp`
from `is_numeral` to `is_extended_numeral` at all model-evaluation sites
used by projection.
- This covers evaluated arithmetic forms (e.g., normalized arithmetic
expressions) that are semantically numeric but not syntactic numerals,
preventing assertion failures in disequality/equality handling and bound
selection.

- **Regression coverage for the crashing HORN shape**
- Added a focused regression in `src/test/api_datalog.cpp` that
evaluates the reported Spacer/HORN input pattern through
`Z3_eval_smtlib2_string`.
- The test exercises the exact QE/projection path that previously
triggered the assertion.

```cpp
// Before
VERIFY(a.is_numeral(val, r));

// After
VERIFY(a.is_extended_numeral(val, r));
```

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
Copilot 2026-07-12 18:52:44 -07:00 committed by GitHub
parent d99b6e7b08
commit 4862a10ffd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 144 additions and 5 deletions

View file

@ -367,7 +367,7 @@ class arith_project_util {
cx = mk_mul(c, m_var->x());
cxt = mk_add(cx, t);
val = mdl(cxt);
VERIFY(a.is_numeral(val, r));
VERIFY(a.is_extended_numeral(val, r));
SASSERT(r > rational::zero() || r < rational::zero());
if (r > rational::zero()) {
c = -c;
@ -473,7 +473,7 @@ class arith_project_util {
cx = mk_mul(c, m_var->x());
cxt = mk_add(cx, t);
val = mdl(cxt);
VERIFY(a.is_numeral(val, r));
VERIFY(a.is_extended_numeral(val, r));
if (is_eq) {
TRACE(qe, tout << "equality term\n";);
@ -711,7 +711,7 @@ class arith_project_util {
cx = mk_mul(m_coeffs[max_t], m_var->x());
cxt = mk_add(cx, m_terms.get(max_t));
val = mdl(cxt);
VERIFY(a.is_numeral(val, r));
VERIFY(a.is_extended_numeral(val, r));
// get the offset from the smallest/largest possible value for x
// literal smallest/largest val of x
@ -771,13 +771,13 @@ class arith_project_util {
// evaluate x in mdl
rational r_x;
val = mdl(m_var->x());
VERIFY(a.is_numeral(val, r_x));
VERIFY(a.is_extended_numeral(val, r_x));
for (unsigned i = 0; i < m_terms.size(); ++i) {
rational const &ac = m_coeffs[i];
if (!m_eq[i] && ac.is_pos() == do_pos) {
val = mdl(m_terms.get(i));
VERIFY(a.is_numeral(val, r));
VERIFY(a.is_extended_numeral(val, r));
r /= abs(ac);
// skip the literal if false in the model
if (do_pos) {