mirror of
https://github.com/Z3Prover/z3
synced 2026-06-27 19:08:49 +00:00
qsat: decide quantifier-free goals so qe2 returns sat instead of unknown (fixes iss-7027/small-30) (#9970)
## Summary Fixes the `iss-7027/small-30` snapshot regression (`Z3Prover/bench` discussion #2705) **at its root**, instead of working around it by retuning the LP heuristics. - **Benchmark:** `inputs/issues/iss-7027/small-30.smt2` — `(check-sat-using qe2)` over a single `(distinct ...)` of 33 mixed Int/Real terms. - The recorded oracle was `unknown`; current `master` produces `timeout`. ## Root cause `unknown`/`timeout` are both wrong here: the formula is a `distinct` over 33 terms (free Int/Real constants plus the literals `0`/`1`), which is **trivially `sat`** — there are infinitely many distinct reals. The real bug is in the `qsat` tactic that backs `qe2`. Running quantifier elimination on a **quantifier-free** formula has nothing to eliminate, so `qsat` left an undecided residual goal and `check-sat-using` reported `unknown`. This reproduces on any ground formula with free variables, e.g.: ``` (declare-fun a () Int)(assert (> a 0))(check-sat-using qe2) ; -> unknown (should be sat) ``` For `small-30` the QE alternation additionally drove `theory_lra` integer branch-and-bound down a non-terminating path, surfacing as a `timeout` under the capture budget (the symptom the `random_hammers` schedule change happened to expose). ## Fix Under `check-sat` semantics, top-level free variables are implicitly existentially quantified. So when the `qsat` input has no quantifiers, decide satisfiability directly (route through the existing `qsat_sat` path) instead of producing a residual goal. `qe2`/`qe` now return `sat`/`unsat` for ground formulas. QE of genuinely-quantified formulas is unchanged: `apply qe2` on a quantified goal produces the same projected formula as before (verified identical to `master`). Only the degenerate quantifier-free case is affected. This supersedes the previous approach in this PR (reverting the `lp.random_hammers` default). That default is left **unchanged** (`true`), preserving #9958's aggregate QF_LIA benefit. `small-30` now returns `sat` in ~0.01s regardless of the heuristic schedule, because the QE machinery no longer runs on this ground instance. Two changes: - `src/qe/qsat.cpp`: short-circuit quantifier-free input to the satisfiability decision path. - `Z3Prover/bench` `inputs/issues/iss-7027/small-30.expected.out`: oracle updated `unknown` -> `sat` (to be committed alongside this fix). ## Validation ``` $ z3 small-30.smt2 sat # ~0.01s $ echo '(declare-fun a () Int)(assert (> a 0))(check-sat-using qe2)' | z3 -in sat $ echo '(declare-fun a () Int)(assert (and (> a 0)(< a 0)))(check-sat-using qe2)' | z3 -in unsat ``` Full unit-test suite (`test-z3 /a`) passes (92/92). Quantified `qe2` round-trips (`apply qe2`) match `master` byte-for-byte. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
612fab1c9a
commit
6a62a53181
1 changed files with 9 additions and 0 deletions
|
|
@ -1290,6 +1290,15 @@ namespace qe {
|
|||
|
||||
TRACE(qe, tout << fml << "\n";);
|
||||
|
||||
// qe/qe2 over a quantifier-free formula has nothing to eliminate.
|
||||
// Under check-sat semantics the free variables are implicitly
|
||||
// existentially quantified, so decide satisfiability directly
|
||||
// instead of leaving an undecided residual goal (which would be
|
||||
// reported as 'unknown').
|
||||
flet<qsat_mode> _mode(m_mode,
|
||||
(m_mode == qsat_qe || m_mode == qsat_qe_rec) && !has_quantifiers(fml)
|
||||
? qsat_sat : m_mode);
|
||||
|
||||
if (m_mode == qsat_qe_rec) {
|
||||
fml = elim_rec(fml);
|
||||
in->reset();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue