opt: validate strict optimization optima faithfully with delta-rational bounds#10059
Merged
Conversation
…al bounds Maximizing/minimizing under a strict inequality (e.g. maximize r subject to r < 1) has the delta-rational optimum 1 - epsilon. Reporting it soundly requires check_bound to validate the bound r >= 1 - epsilon, i.e. to assert a LOWER bound whose value carries a NEGATIVE infinitesimal (r, -1). No lconstraint_kind can express that: the kind->epsilon map only produces the matching-sign infinitesimals (GT -> lower (r, +1), LT -> upper (r, -1)) or zero (GE/LE). opt_solver::mk_ge therefore projected the negative infinitesimal away, turning r >= 1 - epsilon into the over-strong, unsatisfiable r >= 1, so validation spuriously failed and the strictly smaller current model value was reported instead (e.g. r = 0). For shared-symbol objectives this also produced values matching neither the model nor the true optimum (issue #10028 follow-up). Fix: carry the infinitesimal faithfully through the bound pipeline. - lp_api::bound gains an eps component so get_value reports the true delta value (no spurious rational fixed-variable equality is propagated to EUF). - lar_base_constraint stores its right-hand side as a delta-rational impq pair; rhs() returns the rational component, bound_eps() the infinitesimal one. - lar_solver bound activation/update threads the whole impq bound, so a lower bound (r, -1) can be asserted; constraint_holds accounts for it using the same strict-bounds delta that flattens the model (computed once per model). - theory_lra::mk_ge builds a fresh predicate for the (r, -1) lower bound to avoid colliding with an already-internalized v >= r literal, and attaches eps = -1; opt_solver::mk_ge passes the unprojected value to theory_lra / theory_mi_arith / theory_inf_arith (whose bounds are inf_rational). Results: maximize r, r<1 -> 1-eps; minimize r, r>1 -> 1+eps; bounded and lex/box strict optima correct; integer optima and the #10028 shared-symbol cases unchanged. Unit tests 92/92; the opt regression corpus (model_validate=true) is byte-identical to baseline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The nonlinear optimization path (max_with_nl) returned the linear-relaxation optimum lp_val including its infinitesimal. That infinitesimal is an artifact of the strict bounds introduced by the linearization, not a genuine strict optimum of the nonlinear problem. Threading it into opt_solver::mk_ge made theory_lra assert a delta-rational lower bound (r, -1) that coincides with the column's existing strict upper bound (r, -1), fixing the objective column at a delta value the LP core never snapped, tripping the non_basic_columns_are_set_correctly assertion in a debug build (unit tests max_reg / max_rev). Project the infinitesimal away in both NLA branches (FC_DONE and FC_CONTINUE); the rational part remains a sound bound for check_bound to validate. The genuinely-strict, NLA-free path (e.g. pure LP suprema and shared-symbol/EUF objectives) is unaffected, so faithful validation of strict optima is preserved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… cache The manual actions/cache step for ~/.opam was causing OpamSystem.Internal_patch_error on macOS when a restore-key hit returned a stale opam repo state incompatible with the current upstream. ocaml/setup-ocaml@v3 has built-in cache management that handles this correctly, so the manual step is removed.
51a71ac to
02a1aec
Compare
Contributor
Author
|
@NikolajBjorner , I think one of the main changes is here: z3/src/math/lp/lar_constraints.h Line 52 in 4065e46 |
| if (eps.is_zero()) | ||
| cT = lp().mk_var_bound(vi, kT, bound); | ||
| else | ||
| cT = lp().mk_var_bound(vi, kT, bound, eps); |
Contributor
There was a problem hiding this comment.
simplify away if-then-else to just else branch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Maximizing/minimizing under a strict inequality has a delta-rational optimum. For
the optimum is the supremum
1 - epsilon, but z3 reportedr = 0.The same defect makes shared-symbol objectives report a value matching neither the model nor the true optimum (issue #10028 follow-up). Minimal reproducer — a 6-mark Golomb ruler (a
>32-argdistinct, so the objective is coupled to EUF) with a strict real objectiveobj > x5, whose true optimum is17 + epsilon:maximize r,r < 10❌1 - epsilon✅minimize r,r > 10❌1 + epsilon✅minimize obj,obj > x535/2/7+eps❌17 + epsilon✅Root cause
check_boundvalidates the LP hint by assertingobjective >= optimum. For a supremum1 - epsilonthis is a lower bound whose value carries a negative infinitesimal(1, -1).No
lconstraint_kindcan express that. The kind->infinitesimal map only yields the matching-sign cases —GT-> lower(r, +1),LT-> upper(r, -1)— or zero (GE/LE). The opposite-sign lower bound(r, -1)(i.e.r >= r0 - delta) is a relaxation that no strict inequality produces.opt_solver::mk_getherefore projected the-epsilonaway, turningr >= 1 - epsiloninto the over-strong, unsatisfiabler >= 1; validation failed and the strictly smaller current model value was reported instead.Fix — carry the infinitesimal faithfully through the bound pipeline
lp_api::boundgains anepscomponent soget_valuereturns the true delta value (no spurious rational fixed-variable equality is propagated to EUF).lar_base_constraintstores its right-hand side as a delta-rationalimpqpair;rhs()returns the rational component,bound_eps()the infinitesimal one.lar_solverbound activation/update threads the wholeimpqbound, so a lower bound(r, -1)can be asserted.constraint_holdsaccounts for it using the same strict-bounds delta that flattens the model, computed once per model.theory_lra::mk_gebuilds a fresh predicate for the(r, -1)lower bound (to avoid colliding with an already-internalizedv >= rliteral) and attacheseps = -1.opt_solver::mk_gepasses the unprojected value totheory_lra/theory_mi_arith/theory_inf_arith(whose bounds are alreadyinf_rational).The pair machinery is what makes the supremum both representable (optimum
1 - epsilon) and validatable; the reported witness model remains the flattened rational (find_delta_for_strict_bounds), consistent with the existing epsilon semantics.Validation
1-eps,1+eps, bounded2<r<5 -> 5-eps, and lex/box variants.model_validate=true) byte-identical to baseline.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com