Skip to content

Fix issue with infinite lower bounds and try to bound free variables in barrier#1001

Merged
anandhkb merged 10 commits intoNVIDIA:release/26.04from
chris-maes:barrier_presolve_fixes
Apr 4, 2026
Merged

Fix issue with infinite lower bounds and try to bound free variables in barrier#1001
anandhkb merged 10 commits intoNVIDIA:release/26.04from
chris-maes:barrier_presolve_fixes

Conversation

@chris-maes
Copy link
Copy Markdown
Contributor

@chris-maes chris-maes commented Mar 30, 2026

  • Fixes a long-standing issue where variables with bounds of the form -inf <= x_j < u_j were not handled correctly by barrier
  • We now try to bound free variables (-inf <= x_j <= inf) in barrier presolve. This is helpful as PSLP can sometimes introduce many free variables.
  • Fixes a bug where we used the complementarity residual at the previous iteration.
  • Initializes the logger so we print out when we start reading an MPS file. Also print out how long we took to read the MPS file
  • Adjust the relative complementarity residual to use take into account either the objective or objective with constant, whichever leads to a larger relative residual. This hopefully, leads to smaller complementarity gaps and thus smaller gaps in the primal and dual objectives.

Results in a 1.07X speed up on GH200. Note that Linf_520c has a big performance variability.

               model               PR         Baseline      Baseline/PR
              16_n14              1.1              0.9              0.8 (1.3)
               a2864              0.8              0.7              0.9 (1.1)
               bdry2              7.0              8.2              1.2 (0.8)
              cont11              0.6              0.9              1.5 (0.7)
               cont1              1.3              4.9              3.9 (0.3)
          datt256_lp              0.7              0.6              0.9 (1.1)
               degme              6.8              6.3              0.9 (1.1)
      L1_sixm1000obs           3600.0           3600.0              1.0 (1.0)
                dlr1            166.0            259.0              1.6 (0.6)
       L1_sixm250obs            484.0            487.0              1.0 (1.0)
               stp3d              3.3              3.1              1.0 (1.0)
             L2CTA3D            116.0            126.0              1.1 (0.9)
           Linf_520c            192.0             25.1              0.1 (7.6)
                lo10             21.4             18.1              0.8 (1.2)
              long15             38.8             36.9              1.0 (1.1)
        neos-3025225              7.7              7.5              1.0 (1.0)
               neos3              0.7              0.7              1.0 (1.0)
 neos-5052403-cygnet              2.5              2.5              1.0 (1.0)
        neos-5251015            418.0            274.0              0.7 (1.5)
                neos            326.0            287.0              0.9 (1.1)
           netlarge1             32.1             31.2              1.0 (1.0)
           netlarge2             52.9             52.5              1.0 (1.0)
           netlarge3             85.5             84.0              1.0 (1.0)
           netlarge6             22.8             23.0              1.0 (1.0)
           ns1687037             31.7             67.4              2.1 (0.5)
       supportcase10              1.2              1.2              0.9 (1.1)
           nug08-3rd              2.2              3.4              1.5 (0.7)
             pds-100              6.5              6.2              1.0 (1.0)
   physiciansched3-3              8.4              8.4              1.0 (1.0)
        Primal2_1000           1690.0           1650.0              1.0 (1.0)
     set-cover-model              9.5              9.0              0.9 (1.1)
             shs1023           3600.0           3600.0              1.0 (1.0)
            square15             28.9             27.5              1.0 (1.1)
            square41              3.5              3.4              1.0 (1.0)
            stat96v2              2.6              1.6              0.6 (1.6)
        stormG2_1000            199.0            194.0              1.0 (1.0)
          Dual2_5000            192.0           3600.0             18.8 (0.1)
       supportcase19            107.0             15.5              0.1 (6.9)
              thk_48            599.0           3600.0              6.0 (0.2)
               qap15              1.4              0.7              0.5 (1.9)
              rail02              7.2              6.7              0.9 (1.1)
            rail4284              3.0              3.1              1.0 (1.0)
             rmine15             22.9             22.4              1.0 (1.0)
                s100              2.6              2.5              1.0 (1.0)
             s250r10              1.9              2.0              1.0 (1.0)
                 s82             28.9             28.9              1.0 (1.0)
           savsched1              2.7              2.6              1.0 (1.0)
               scpm1              1.8              2.0              1.1 (0.9)
                dlr2           3600.0           3600.0              1.0 (1.0)
                ex10              3.4              3.4              1.0 (1.0)
   fhnw-binschedule1            106.0            106.0              1.0 (1.0)
              fome13              6.2              2.0              0.3 (3.1)
          graph40-40              0.6              0.5              0.9 (1.2)
               i_n13              3.8              3.7              1.0 (1.0)
   irish-electricity              1.3              1.0              0.8 (1.3)
              karted              2.8              2.6              0.9 (1.1)
              thk_63            207.0            201.0              1.0 (1.0)
           ns1688926             27.2           3600.0            132.4 (0.0)
      tpl-tub-ws1617              8.2              6.4              0.8 (1.3)
              wide15             38.3             36.2              0.9 (1.1)
         woodlands09              2.5              2.3              0.9 (1.1)
Geomean Baseline / PR: 1.07
Problems  61

@chris-maes chris-maes requested a review from a team as a code owner March 30, 2026 23:55
@chris-maes chris-maes requested review from aliceb-nv and mlubin March 30, 2026 23:55
@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot bot commented Mar 30, 2026

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@chris-maes chris-maes added bug Something isn't working P0 labels Mar 30, 2026
@chris-maes chris-maes added this to the 26.04 milestone Mar 30, 2026
@chris-maes chris-maes changed the base branch from main to release/26.04 March 30, 2026 23:56
@chris-maes chris-maes added the non-breaking Introduces a non-breaking change label Mar 30, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a barrier presolve pass that tightens/removes certain free variables and negates variables with only an upper bound (x' = -x), updates objective/A/Q accordingly, records negated indices and undoes them in solution uncrushing, introduces CLI file-read timing/logging, and adjusts barrier residual/objective logging and normalization.

Changes

Cohort / File(s) Summary
Presolve declarations
cpp/src/dual_simplex/presolve.hpp
Added presolve_info_t::negated_variables to record indices of variables negated during presolve.
Presolve implementation & solution mapping
cpp/src/dual_simplex/presolve.cpp
Added initial free-variable counting and a constraint-based tightening pass that can remove certain free variables; implemented negation substitution for variables with only an upper bound (x' = -x) by flipping problem.lower/upper, negating problem.objective entries, multiplying affected problem.A column coefficients by -1, updating quadratic problem.Q.x terms when exactly one column is negated, removed a duplicate free-variable check, and updated uncrush_solution to undo negations for recorded indices.
CLI timing/logging
cpp/cuopt_cli.cpp
Included utilities/timer.hpp, instantiated a timer in run_single_file, initialized logger earlier, and logged MPS file read time via the configured logger.
Barrier solver diagnostics
cpp/src/barrier/barrier.cu
Switched complementarity residual norm computation to use GPU residual vectors, logged lp.obj_constant, normalized complementarity residuals using compute_user_objective(lp, primal_objective), and updated per-iteration logging to print user objectives and relative residuals while keeping existing convergence checks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly addresses the main changes: fixing infinite lower bounds and bounding free variables in barrier presolve.
Description check ✅ Passed The PR description is directly related to the changeset, detailing specific fixes implemented across multiple files including barrier presolve variable negation, free variable bounding, complementarity residual bug fixes, and logging improvements.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cpp/src/dual_simplex/presolve.cpp`:
- Around line 926-944: Guard the divisions by a_ij in the bounding logic by
skipping or avoiding division when |a_ij| is near zero: introduce a small
tolerance (e.g. tol) and before any 1.0 / a_ij operations check if
std::abs(a_ij) > tol; if not, do not update problem.upper[j] or problem.lower[j]
(and do not set bounded) or handle via a safe fallback; apply this check around
the four locations using the same tol and keep the existing conditions involving
lower_inf_i, upper_inf_i, lower_activity_i, upper_activity_i, rhs and bounded so
only numerically safe divisions occur.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: de8c026d-5d75-460d-a9a8-fb066f9cc6f1

📥 Commits

Reviewing files that changed from the base of the PR and between d03e197 and b86145c.

📒 Files selected for processing (2)
  • cpp/src/dual_simplex/presolve.cpp
  • cpp/src/dual_simplex/presolve.hpp


folding_info_t<i_t, f_t> folding_info;

std::vector<i_t> new_slacks;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Whoops. This line should be removed.

@chris-maes
Copy link
Copy Markdown
Contributor Author

@rg20 to review


// The constraints are in the form:
// sum_j a_j x_j = beta
for (i_t i : constraints_to_check) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we can repurpose the bound strengthening code here.

@rg20
Copy link
Copy Markdown
Contributor

rg20 commented Apr 2, 2026

/ok to test 4432c27

…ig. Use user object for relative complementarity gap. Initialize logger so we get feedback when reading large mps files
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cpp/src/barrier/barrier.cu`:
- Around line 3493-3497: The complementarity normalization uses
compute_user_objective() which includes lp.obj_constant; remove that constant
from the denominator so an arbitrary objective offset cannot shrink the
relative_complementarity_residual. Change the normalization to use the user
objective without lp.obj_constant (either call a new helper like
compute_user_objective_without_constant or subtract lp.obj_constant from
compute_user_objective(lp, primal_objective) before taking std::abs) when
computing relative_complementarity_residual (and the other occurrence noted),
leaving compute_user_objective() behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 44e4dc72-db04-4343-ab6d-560f43805934

📥 Commits

Reviewing files that changed from the base of the PR and between 4432c27 and d8054d1.

📒 Files selected for processing (3)
  • cpp/cuopt_cli.cpp
  • cpp/src/barrier/barrier.cu
  • cpp/src/dual_simplex/presolve.cpp
✅ Files skipped from review due to trivial changes (1)
  • cpp/src/dual_simplex/presolve.cpp

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
cpp/src/dual_simplex/presolve.cpp (1)

861-904: Verify iterative bound propagation handles partial bounds correctly.

The activity computation uses current bounds, which may have been modified by previous constraint iterations. This implements iterative implied bound propagation. However, if a variable receives only one bound (e.g., only upper set at line 931, not lower), it transitions from counting as infinite to contributing finite activity for subsequent constraints.

This is mathematically correct behavior for bound propagation, but consider whether a single-pass approach might miss tightening opportunities compared to iterating until fixpoint.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/dual_simplex/presolve.cpp` around lines 861 - 904, The current
single-pass loop over constraints_to_check (inside the block using Arow,
problem.lower/problem.upper and variables like lower_activity_i,
upper_activity_i, lower_inf_i, upper_inf_i, last_free_i) can miss tightening
opportunities because bounds updated earlier in the pass change contribution
status for later rows; change the algorithm to iterate until a fixpoint: repeat
scanning the relevant constraints (or use a worklist/queue of affected
constraints) and recompute lower_activity_i/upper_activity_i for each row
whenever a variable bound changes, marking any tightened bound and re-enqueueing
its incident constraints, and stop when no bound is tightened in a full pass so
all implied bounds are propagated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@cpp/src/dual_simplex/presolve.cpp`:
- Around line 861-904: The current single-pass loop over constraints_to_check
(inside the block using Arow, problem.lower/problem.upper and variables like
lower_activity_i, upper_activity_i, lower_inf_i, upper_inf_i, last_free_i) can
miss tightening opportunities because bounds updated earlier in the pass change
contribution status for later rows; change the algorithm to iterate until a
fixpoint: repeat scanning the relevant constraints (or use a worklist/queue of
affected constraints) and recompute lower_activity_i/upper_activity_i for each
row whenever a variable bound changes, marking any tightened bound and
re-enqueueing its incident constraints, and stop when no bound is tightened in a
full pass so all implied bounds are propagated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d604ec33-375d-4234-9125-cefe3d8439a7

📥 Commits

Reviewing files that changed from the base of the PR and between d8054d1 and c7816fa.

📒 Files selected for processing (1)
  • cpp/src/dual_simplex/presolve.cpp

@anandhkb
Copy link
Copy Markdown
Contributor

anandhkb commented Apr 3, 2026

/ok to test c7816fa

@mlubin mlubin removed their request for review April 3, 2026 21:38
@anandhkb
Copy link
Copy Markdown
Contributor

anandhkb commented Apr 4, 2026

/ok to test 9133db2

@chris-maes
Copy link
Copy Markdown
Contributor Author

/ok to test 545dfcf

@chris-maes
Copy link
Copy Markdown
Contributor Author

/ok to test 178f934

@anandhkb anandhkb merged commit 54a1822 into NVIDIA:release/26.04 Apr 4, 2026
409 of 415 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working non-breaking Introduces a non-breaking change P0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants