MIP: fix false infeasibility from presolve and domain propagation on wide-range models#2873
MIP: fix false infeasibility from presolve and domain propagation on wide-range models#2873agcmoser wants to merge 1 commit intoERGO-Code:latestfrom
Conversation
Both MIP presolve and domain propagation can accumulate floating-point error on models with wide coefficient ranges and falsely declare kInfeasible on feasible problems. Part 1 (Highs.cpp): After HighsMipSolver::run() returns kInfeasible, solve the LP relaxation of the original model without presolve. If the LP is feasible the MIP result was spurious; log a warning and retry the full MIP solve with presolve disabled and mip_feasibility_tolerance=1e-7. Zero overhead on non-infeasible solves; LP check is fast; no recursion risk (retry runs at HighsMipSolver level, check is at Highs level). Part 2 (HighsDomain.cpp): Add an error-amplification guard to propagateRowUpper/Lower. The existing guard only catches enormous bound values (|boundVal| * kHighsTiny > feastol). A second check now skips propagation when |minresact| * kHighsTiny / |coef| > feastol * 0.01, detecting cases where dividing a large residual activity by a small coefficient amplifies the O(eps*|minresact|) floating-point error beyond 1% of feastol. Fixes ERGO-Code#2872
|
PRs are merged into latest, not master |
|
My two cents:
|
jajhall
left a comment
There was a problem hiding this comment.
Thanks for this. This is also our first experience of an AI-driven PR!
First up, there's no way that we're accepting the proposed change to HiGHS.cpp due to the impact on HiGHS when truly infeasible MIPs are handled correctly. Their relaxation may well be feasible, in which case the MIP would be re-solved from scratch to verify the result.
Although the issue with the wide-range models is best addressed by the modeller scaling the variables and constraints so that the coefficients don't have such wide ranges, I appreciate that this may not be possible, as some models are inherently badly scaled!
The better resolution is for our MIP developers to take the instance that you've shared and the suggested change to HighsDomain.cpp, and identify a fundamental fix that will not impair performance both in general, and in particular for the case where truly infeasible MIPs are handled correctly.
|
Thanks @Opt-Mucca, I realise that I failed to submit the review that I composed earlier! Hence, blind to each other, we basically agree! |
|
Checks involving |
If I understand correctly @agcmoser, the original problematic behaviour has been resolved by improving the model formulation - which is the best approach Hence we mustn't feel obliged to address #2872 |
|
Yes, in my case it seems that changing the model resolved the problem. At least for the given example. We use HiGHS a lot for our MPC applications and a problem with coefficients which are very different in magnitude might occur. However, I, and I guess many other, would highly appreciate if the issue can be investigated further as ill-conditioned problems may always come along the way. For me it was just very surprising at first at even Cbc could solve the problem without problem (and Gurobi as well, but Gurobi also plays in it's on league). And as I stated above: Modifying the solver with AI was just a try to see what happens. If it leads to any improvement I am happy .. and it was clear to me that this is way to complex to just be improved by AI without the steering of an MILP solver developer. I guess with the steering of an experienced solver developer this would be far more effective. |
|
It would be interesting to know whether CBC has the presolve rule that caused the problem - which it may not since it's a less sophisticated solver - and, if it does, how it avoids the error in HiGHS. |
Fixes #2872
Summary
HiGHS falsely reports
kInfeasibleon feasible MIP models when the constraint matrix has a wide coefficient range (≥ 10⁴). Two independent numerical-error paths are responsible — both declare infeasibility before any LP is solved (0 LP iterations, 0–1 nodes). This PR fixes both.A reproduction MPS file (
model_step2.mps.gz, 11498 rows / 9030 cols / 252 binaries, coefficient range [2×10⁻⁴, 1×10⁴]) is attached to issue #2872.Changes
highs/lp_data/Highs.cpp— LP relaxation verification fallbackAfter
HighsMipSolver::run()returnskInfeasible, solve the LP relaxation of the original model (integrality cleared, presolve off). If the LP is feasible or unbounded, the MIP infeasibility was spurious:HighsMipSolverinstance,presolve=off, andmip_feasibility_tolerance=1e-7Properties:
HighsMipSolverlevel; the LP check lives atHighslevelhighs/mip/HighsDomain.cpp— error-amplification guard in domain propagationpropagateRowUpper/propagateRowLowercompute:When
|Rvalue[i]|is small (e.g., 2×10⁻⁴) and|minresact|is large (e.g., 10³), dividing by the small coefficient amplifies the O(ε·|minresact|) ≈ 10⁻¹¹ floating-point error inminresactto ~5×10⁻⁸ — which can exceedfeastol × 0.01. The existing guard (|boundVal| × kHighsTiny > feastol) requires|boundVal| > 10⁸, far too loose for this mode.Added a second filter:
This skips propagation when the amplified error would exceed 1 % of
feastol, preventing unreliable bounds from cascading into conflicting-bound infeasibility.Test results
presolve=on,feastol=1e-6presolve=on,feastol=1e-5presolve=off,feastol=1e-6presolve=off,feastol=1e-5Full HiGHS test suite: 153/153 pass (no regressions).
Root cause (detail)
See issue #2872 for the full analysis. In brief:
Path 1 (presolve):
HighsLinearSumBoundsmaintainsimpliedRowBoundsincrementally across ~10 000 substitution/scaling operations in presolve. On wide-range models this accumulates ~10⁻⁴ of floating-point error — 100× larger than the defaultfeastol = 1e-6— causingcheckRowInfeasibleto wrongly declare infeasibility.Path 2 (domain propagation): Division by a small coefficient in
propagateRowUpper/Loweramplifies activity-sum rounding error into the propagated bound, producing an artificially tight bound that conflicts with the variable's existing domain.🤖 Created by AI agent (pi 0.55.0 · claude-sonnet-4.6)