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

Revert #10052: eager-commit of infinitesimal LP hint is unsound for shared-symbol objectives (#10057)

## Summary

Reverts #10052, whose eager-commit of infinitesimal LP hints is
**unsound** for shared-symbol objectives.

#10052 added, in `opt_solver::maximize_objective`:

```cpp
if (val.is_finite() && !val.get_infinitesimal().is_zero() && val > m_objective_values[i])
    m_objective_values[i] = val;
```

on the premise that *"a non-zero infinitesimal ⇒ an exact, unattainable
strict optimum, so the LP hint is authoritative."* That holds for a
**pure LP**, but is **false when the objective is a shared symbol** with
another theory (e.g. the auxiliary uninterpreted function used to encode
a `distinct` with > 32 arguments). There the LP relaxation only yields a
*hint* that can be a strict **over-estimate**, and #10052 commits it
without validation — exactly the class of bound that #10028's
`check_bound` exists to reject.

## Counterexample

A 6-mark Golomb ruler over integers `x0..x5`, with the `distinct` padded
to > 32 arguments so `x5` becomes a shared symbol; objective is a real
`obj` with `obj > x5` (full file attached to #5720):

- Ground truth: `minimize x5` (integer) ⇒ **17**; since `obj > x5`, the
true optimum is **`17 + ε`**.
- With #10052, z3 reports **`(obj (+ 5.0 epsilon))`** — wrong and
**infeasible** (`obj < 6` is `unsat`; the returned model itself has `x5
= 35, obj = 49.5`).

| benchmark | with #10052 (master) | after this revert |
| --- | --- | --- |
| Golomb `bug.smt2` (shared symbol) | `(obj (+ 5.0 epsilon))` 
infeasible | `(obj 18)`  consistent |
| #5720 (`max r < 1`) | `1 - epsilon`  | `0`  (regression returns) |

## Tradeoff / follow-up

This revert restores soundness on the shared-symbol case but
**reintroduces the #5720 regression** (`max r<1` ⇒ `0`), which is why
**#5720 has been reopened**. A correct fix should preserve the strict
single-objective supremum **without** trusting an unvalidated
shared-symbol hint — e.g. gate the eager commit on `!has_shared` (pure
LP only), or validate the rational part of the hint while keeping the
infinitesimal. The same blind spot affects the alternative `check_bound`
guard proposed in #10051.

Re: #5720

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Lev Nachmanson 2026-07-06 12:56:13 -07:00 committed by GitHub
parent f37b435923
commit 835679b27d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -342,24 +342,6 @@ namespace opt {
return true;
}
//
// A finite hint with a non-zero infinitesimal is a strict
// supremum/infimum (e.g. maximizing r subject to r < 1 yields
// 1 - epsilon). No concrete model can attain such a value, and
// check_bound() below cannot validate it either: opt_solver::mk_ge
// drops the negative infinitesimal, turning the bound r >= 1 - epsilon
// into the unsatisfiable r >= 1. Commit it eagerly here so that it is
// neither overwritten by the strictly smaller current model value in
// update_objective() nor lost when validation fails and this routine
// returns false (callers such as optsmt::geometric_lex then report
// m_objective_values as the optimum). This restores the pre-#10028
// behavior for strict optima while leaving the plain-rational
// (zero-infinitesimal) over-estimates that #10028 fixed to the
// deferred-commit logic below.
//
if (val.is_finite() && !val.get_infinitesimal().is_zero() && val > m_objective_values[i])
m_objective_values[i] = val;
//
// retrieve value of objective from current model and update
// current optimal.