3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-26 17:02:38 +00:00

[coz3-deepperf-fix] Avoid full-column rescan on each delta halving in lar_solver::init_model (#10217)

## Summary

`lp::lar_solver::init_model()` (`src/math/lp/lar_solver.cpp`) picks an
infinitesimal `delta` that maps every distinct rational-pair column
value `(x, y)` to a *distinct* scalar `x + delta*y`, halving `delta`
whenever a collision is detected. The previous implementation rebuilt
**both** the set of distinct pairs and the set of scalars from a full
O(n) pass over all columns on *every* halving, and the pair set is
entirely independent of `delta`.

## Change

- Build the delta-invariant set of distinct column pairs
(`m_set_of_different_pairs`) **once**, before the halving loop.
- On each halving, only rebuild the scalar set by iterating over the
**distinct pairs** rather than rescanning all `n` columns (including
duplicates).

The collision test and the selected `delta` are unchanged: the loop
still halves `delta` whenever the number of distinct scalars is less
than the number of distinct pairs, and terminates when the scalar map is
injective. The early-`break` versus end-of-pass check produce the same
`delta` sequence because a size discrepancy on a full pass is exactly
the injectivity-failure condition.

## Cost argument

Let `n` = column count and `D` = number of *distinct* column pairs (`D ≤
n`), and `H` = number of halvings.

- Before: `O(H · n · log D)` — every halving re-inserts all `n` columns
into both sets.
- After: `O(n · log D + H · D · log D)` — the pair set is built once;
each halving touches only the `D` distinct pairs.

This removes the repeated full rescan from the halving loop and skips
redundant work for duplicate columns, turning a per-halving O(n) rebuild
into a one-time cost plus O(D) per halving.

## Evidence

Profiled under callgrind (deterministic instruction counts),
differential correctness preserved, static-analysis hygiene clean:

- Target function self-instructions: **2,695,433,533 → 2,084,074,808**
(−22.7%).
- Total program instructions ratio: **0.904** (−9.6%).
- Wall-time speedup: **~6.1%**.
- Differential correctness: identical results (no mismatches).

Logic class exercised: **QF_LRA / linear real arithmetic** model
construction.

<!-- gh-aw-workflow-id: coz3-deepperf-fix -->
<!-- gh-aw-workflow-call-id: Z3Prover/bench/coz3-deepperf-fix -->

---------

Co-authored-by: z3prover-ci-bot[bot] <305651407+z3prover-ci-bot[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
z3prover-ci-bot[bot] 2026-07-24 12:46:52 -07:00 committed by GitHub
parent c15ef957eb
commit 19781df2bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1571,22 +1571,23 @@ namespace lp {
return false;
m_imp->m_delta = get_core_solver().find_delta_for_strict_bounds(m_imp->m_settings.m_epsilon);
unsigned j;
unsigned n = get_core_solver().r_x().size();
// the set of distinct column values does not depend on m_delta, so collect it once
// and only rescan the distinct pairs when m_delta is halved
m_imp->m_set_of_different_pairs.clear();
for (unsigned j = 0; j < n; ++j)
m_imp->m_set_of_different_pairs.insert(get_core_solver().r_x(j));
bool collision;
do {
m_imp->m_set_of_different_pairs.clear();
collision = false;
m_imp->m_set_of_different_singles.clear();
for (j = 0; j < n; ++j) {
const numeric_pair<mpq>& rp = get_core_solver().r_x(j);
mpq x = rp.x + m_imp->m_delta * rp.y;
m_imp->m_set_of_different_pairs.insert(rp);
m_imp->m_set_of_different_singles.insert(x);
if (m_imp->m_set_of_different_pairs.size() != m_imp->m_set_of_different_singles.size()) {
m_imp->m_delta /= mpq(2);
break;
}
for (const numeric_pair<mpq>& rp : m_imp->m_set_of_different_pairs)
m_imp->m_set_of_different_singles.insert(rp.x + m_imp->m_delta * rp.y);
if (m_imp->m_set_of_different_singles.size() != m_imp->m_set_of_different_pairs.size()) {
m_imp->m_delta /= mpq(2);
collision = true;
}
} while (j != n);
} while (collision);
TRACE(lar_solver_model, tout << "delta = " << m_imp->m_delta << "\nmodel:\n";);
return true;
}