Skip to content

MIP: fix false infeasibility from presolve and domain propagation on wide-range models#2873

Draft
agcmoser wants to merge 1 commit intoERGO-Code:latestfrom
agcmoser:fix/mip-false-infeasibility
Draft

MIP: fix false infeasibility from presolve and domain propagation on wide-range models#2873
agcmoser wants to merge 1 commit intoERGO-Code:latestfrom
agcmoser:fix/mip-false-infeasibility

Conversation

@agcmoser
Copy link

Fixes #2872

Summary

HiGHS falsely reports kInfeasible on 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 fallback

After HighsMipSolver::run() returns kInfeasible, solve the LP relaxation of the original model (integrality cleared, presolve off). If the LP is feasible or unbounded, the MIP infeasibility was spurious:

  1. Log a warning: "MIP declared infeasible but LP relaxation is feasible: retrying MIP without presolve"
  2. Retry the full MIP solve with a clean HighsMipSolver instance, presolve=off, and mip_feasibility_tolerance=1e-7

Properties:

  • Zero overhead on non-infeasible solves (check is never reached)
  • LP check is fast — typically < 1 s for models of this size
  • No recursion risk: the retry runs at HighsMipSolver level; the LP check lives at Highs level
  • Catches any false infeasibility source in the MIP solve, not just the two paths identified below

highs/mip/HighsDomain.cpp — error-amplification guard in domain propagation

propagateRowUpper / propagateRowLower compute:

HighsCDouble boundVal = (Rupper - minresact) / Rvalue[i];

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 in minresact to ~5×10⁻⁸ — which can exceed feastol × 0.01. The existing guard (|boundVal| × kHighsTiny > feastol) requires |boundVal| > 10⁸, far too loose for this mode.

Added a second filter:

if (absMinResAct * kHighsTiny / absCoef > mipsolver->mipdata_->feastol * 0.01)
    continue;

This skips propagation when the amplified error would exceed 1 % of feastol, preventing unreliable bounds from cascading into conflicting-bound infeasibility.

Test results

Configuration Before After
presolve=on, feastol=1e-6 ❌ Infeasible ✅ Optimal
presolve=on, feastol=1e-5 ✅ Optimal ✅ Optimal
presolve=off, feastol=1e-6 ✅ Optimal ✅ Optimal
presolve=off, feastol=1e-5 ❌ Infeasible ✅ Optimal

Full HiGHS test suite: 153/153 pass (no regressions).

Root cause (detail)

See issue #2872 for the full analysis. In brief:

Path 1 (presolve): HighsLinearSumBounds maintains impliedRowBounds incrementally across ~10 000 substitution/scaling operations in presolve. On wide-range models this accumulates ~10⁻⁴ of floating-point error — 100× larger than the default feastol = 1e-6 — causing checkRowInfeasible to wrongly declare infeasibility.

Path 2 (domain propagation): Division by a small coefficient in propagateRowUpper/Lower amplifies 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)

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
@agcmoser agcmoser marked this pull request as ready for review February 25, 2026 11:01
@jajhall jajhall changed the base branch from master to latest February 25, 2026 11:40
@jajhall
Copy link
Member

jajhall commented Feb 25, 2026

PRs are merged into latest, not master

@jajhall jajhall self-assigned this Feb 25, 2026
@jajhall jajhall self-requested a review February 25, 2026 11:46
@jajhall jajhall marked this pull request as draft February 25, 2026 11:59
@Opt-Mucca
Copy link
Collaborator

My two cents:

  • The first fix should not be implemented. It will slow down solve times on all trivially infeasible problems that don't stem from LP reasons. It also conflates the issue that an instance can be both "feasible" and "infeasible" (it often depends on where tolerances are used).
  • The second fix looks alright, although I'd need to play around with it. That should provide an additional safeguard like the comments suggest. @fwesselm thoughts?

Copy link
Member

@jajhall jajhall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@jajhall
Copy link
Member

jajhall commented Feb 25, 2026

Thanks @Opt-Mucca, I realise that I failed to submit the review that I composed earlier! Hence, blind to each other, we basically agree!

@fwesselm
Copy link
Collaborator

Checks involving kHighsTiny and feasibility tolerances are performed in different places (e.g., domain propagation and presolve). I do not really like just modifying the domain propagation to fix this issue. I need to experiment with this change.

@jajhall
Copy link
Member

jajhall commented Feb 25, 2026

Checks involving kHighsTiny and feasibility tolerances are performed in different places (e.g., domain propagation and presolve). I do not really like just modifying the domain propagation to fix this issue. I need to experiment with this change.

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

@agcmoser
Copy link
Author

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.

@jajhall
Copy link
Member

jajhall commented Feb 25, 2026

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MIP solver falsely reports kInfeasible on feasible models with wide coefficient ranges

4 participants