3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-06-09 10:30:59 +00:00

Fix broken term_comparer in m_normalized_terms_to_columns lookup

The `m_normalized_terms_to_columns` map in `lar_solver` uses a
`term_comparer` that delegates to `lar_term::operator==`, which
intentionally returns `false` (with comment "take care not to create
identical terms"). This makes `fetch_normalized_term_column` unable to
find any term, rendering the Horner module's `interval_from_term`
bounds-recovery path dead code.

History: `lar_term::operator==` returning `false` has been present since
the original "merge LRA" commit (911b24784, 2018). The
`m_normalized_terms_to_columns` lookup was added later (dfe0e856,
c95f66e0, Aug 2019) as "toward fetching existing terms intervals from
lar_solver". The initial code had `lp_assert(find == end)` on
registration (always true with broken ==) and `lp_assert(find != end)`
on deregister (always false). The very next commit (207c1c50, one day
later) removed both asserts, replacing them with soft checks. The
`term_comparer` struct delegating to `operator==` was introduced during
a later PIMPL refactor (b375faa77).

Fix: Replace the `term_comparer` implementation with a structural
comparison that checks size and then verifies each coefficient-variable
pair via `coeffs().find_core()`. This is localized to the
`m_normalized_terms_to_columns` map and does not change
`lar_term::operator==`, preserving its intentional semantics elsewhere.

Validated: on a QF_UFNIA benchmark, `interval_from_term` lookups go
from 0/573 successful to 34/573 successful. Unit test added for the
`fetch_normalized_term_column` round-trip.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Arie Gurfinkel 2026-04-07 11:54:43 -04:00
parent 58ad1f0918
commit 70e88e56fa
2 changed files with 76 additions and 2 deletions

View file

@ -36,7 +36,12 @@ namespace lp {
struct term_comparer {
bool operator()(const lar_term& a, const lar_term& b) const {
return a == b;
if (a.size() != b.size()) return false;
for (const auto& p : a) {
auto const* e = b.coeffs().find_core(p.j());
if (!e || e->get_data().m_value != p.coeff()) return false;
}
return true;
}
};