Skip to content

fix(presolve): prevent floating-point underflow in dynamism-scaled tolerances - #3203

Open
HuAliAsu wants to merge 2 commits into
ERGO-Code:latestfrom
HuAliAsu:fix/presolve-tolerance-underflow
Open

fix(presolve): prevent floating-point underflow in dynamism-scaled tolerances#3203
HuAliAsu wants to merge 2 commits into
ERGO-Code:latestfrom
HuAliAsu:fix/presolve-tolerance-underflow

Conversation

@HuAliAsu

@HuAliAsu HuAliAsu commented Aug 6, 2026

Copy link
Copy Markdown

Problem

When solving MILP models containing large matrix coefficients (such as micrometer dimensions 10^12 or large Big-M indicators), HiGHS Presolve can incorrectly declare feasible models as Infeasible or fail to reduce valid forcing rows and columns.

Technical Root Cause

In HPresolve.cpp (lines 4300 and 4587), primal_feasibility_tolerance and dual_feasibility_tolerance are scaled by dividing by the matrix dynamism ratio.

When dynamism is high (>= 10^8), tolerance / dynamism underflows below the IEEE 754 double precision machine epsilon (2.22e-16). For example, 1e-7 / 10^12 = 1e-19.

As a result, comparisons like std::abs(bound) <= 1e-19 fail due to standard floating-point noise (~10^-15), causing valid presolve reductions to fail and leading to false Infeasible status returns.

The Fix

Clamp the dynamism-scaled tolerances to a lower bound of 1e-15 (IEEE 754 machine epsilon safeguard):

std::max(double(primal_feastol / dynamism), 1e-15)
std::max(double(options->dual_feasibility_tolerance / dynamism), 1e-15)

Reproducible LP Benchmark

Minimize
  obj: 2976800000000 x1 + 1000 y1
Subject To
  c1: 480000000000 z1 + 600000000000 z2 <= 2976800000000 x1
  c2: pos_x1 + 600000 - pos_x2 - 3000000 delta1 <= 0
Bounds
  0 <= pos_x1 <= 2440000
Binary
  x1 z1 z2 delta1
End

@codecov

codecov Bot commented Aug 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.16%. Comparing base (df4f2cd) to head (f55037b).
⚠️ Report is 415 commits behind head on latest.

Additional details and impacted files
@@           Coverage Diff           @@
##           latest    #3203   +/-   ##
=======================================
  Coverage   73.16%   73.16%           
=======================================
  Files         430      430           
  Lines      103967   103967           
  Branches    16719    16719           
=======================================
+ Hits        76067    76069    +2     
+ Misses      27624    27622    -2     
  Partials      276      276           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Opt-Mucca Opt-Mucca left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't see how this change can hurt. It needs to be formatted correctly though. Nice spot!

@fwesselm

fwesselm commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

I don't really like hard-coding 1e-15, and I would prefer something like std::numeric_limits<double>::epsilon().

@jajhall

jajhall commented Aug 6, 2026

Copy link
Copy Markdown
Member

I don't really like hard-coding 1e-15, and I would prefer something like std::numeric_limits<double>::epsilon().

Agreed

@jajhall
jajhall changed the base branch from master to latest August 6, 2026 13:41
@jajhall jajhall self-assigned this Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants