Skip to content

Improvements to reliability branching#979

Merged
anandhkb merged 27 commits intoNVIDIA:release/26.04from
nguidotti:improve-reliable-branching
Apr 3, 2026
Merged

Improvements to reliability branching#979
anandhkb merged 27 commits intoNVIDIA:release/26.04from
nguidotti:improve-reliable-branching

Conversation

@nguidotti
Copy link
Copy Markdown
Contributor

@nguidotti nguidotti commented Mar 20, 2026

We can use a single dual simplex pivot to estimate the objective change after branch up or down in a variable (#963), which can be later be used for ranking the unreliable variables in reliability branching. To minimize the cost of reliability branching, we only apply trial branching to the 100 best variables. Other variables are not considered. Previously, the 100 unreliable variables were selected at random, which may not lead to best branching decision. In this PR, we also apply a cutoff based on the objective coefficients when no incumbent is available.

MIPLIB Benchmark (10 minute runs) on a GH200 system:

Does not include graphdraw-domain and cbscta

RUN 1

================================================================================
 main f0ba447  (1) vs improve-reliable-branching (2)
================================================================================

------------------------------------------------------------------------------------------------------------------------------
|                                        |       Run 1        |       Run 2        |     Abs. Diff.     |   Rel. Diff. (%)   |
------------------------------------------------------------------------------------------------------------------------------
| Feasible                                                 225                  226                   +1                 --- |
| Optimal                                                   69                   69                   +0                 --- |
| Solutions with <0.1% primal gap                          121                  124                   +3                 --- |
| Nodes explored (mean)                           4301972.7899         4498957.3655         +196984.5756              +4.378 |
| Nodes explored (shifted geomean)                   6414.1273            7705.4824           +1291.3550             +16.759 |
| Relative MIP gap (mean)                               0.3397               0.3403              +0.0006              +0.184 |
| Relative MIP gap (shifted geomean)                    0.1201               0.1216              +0.0016              +1.277 |
| Solve time (mean)                                   452.1236             452.7833              +0.6597              +0.146 |
| Solve time (shifted geomean)                        226.7962             229.4358              +2.6396              +1.150 |
| Primal gap (mean)                                    11.4530              10.9459              -0.5071              -4.428 |
| Primal gap (shifted geomean)                          0.6509               0.6382              -0.0127              -1.953 |
| Primal integral (mean)                               49.5055              47.2675              -2.2380              -4.521 |
| Primal integral (shifted geomean)                    11.4439              10.4722              -0.9717              -8.491 |
------------------------------------------------------------------------------------------------------------------------------

RUN 2

================================================================================
 main f0ba447  (1) vs improve-reliable-branching (2)
================================================================================

------------------------------------------------------------------------------------------------------------------------------
|                                        |       Run 1        |       Run 2        |     Abs. Diff.     |   Rel. Diff. (%)   |
------------------------------------------------------------------------------------------------------------------------------
| Feasible                                                 224                  224                   +0                 --- |
| Optimal                                                   69                   68                   -1                 --- |
| Solutions with <0.1% primal gap                          122                  116                   -6                 --- |
| Nodes explored (mean)                           4490538.7605         4521169.8739          +30631.1134              +0.678 |
| Nodes explored (shifted geomean)                   7308.8805            7525.6319            +216.7514              +2.880 |
| Relative MIP gap (mean)                               0.3269               0.3456              +0.0186              +5.393 |
| Relative MIP gap (shifted geomean)                    0.1192               0.1214              +0.0021              +1.766 |
| Solve time (mean)                                   453.2170             451.1577              -2.0594              -0.454 |
| Solve time (shifted geomean)                        230.0384             227.2056              -2.8328              -1.231 |
| Primal gap (mean)                                    11.6922              11.5728              -0.1194              -1.021 |
| Primal gap (shifted geomean)                          0.6535               0.6617              +0.0082              +1.244 |
| Primal integral (mean)                               50.7645              49.8675              -0.8969              -1.767 |
| Primal integral (shifted geomean)                    12.4180              10.9963              -1.4217             -11.449 |
------------------------------------------------------------------------------------------------------------------------------


Checklist

  • I am familiar with the Contributing Guidelines.
  • Testing
    • New or existing tests cover these changes
    • Added tests
    • Created an issue to follow-up
    • NA
  • Documentation
    • The documentation is up to date with these changes
    • Added new documentation
    • NA

@nguidotti nguidotti added this to the 26.04 milestone Mar 20, 2026
@nguidotti nguidotti self-assigned this Mar 20, 2026
@nguidotti nguidotti requested a review from a team as a code owner March 20, 2026 16:07
@nguidotti nguidotti requested review from kaatish and rg20 March 20, 2026 16:07
@nguidotti nguidotti added non-breaking Introduces a non-breaking change do not merge Do not merge if this flag is set mip labels Mar 20, 2026
@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot bot commented Mar 20, 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.

@nguidotti nguidotti changed the base branch from main to release/26.04 March 20, 2026 16:08
@nguidotti nguidotti changed the title Improve reliable branching Improvements to reliability branching Mar 20, 2026
@nguidotti
Copy link
Copy Markdown
Contributor Author

/ok to test 94c3dc3

@nguidotti nguidotti changed the title Improvements to reliability branching [WIP] Improvements to reliability branching Mar 20, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 20, 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

Added pivot-based single-pivot objective-change estimation and LP transpose storage; integrated these into strong-branching and reliability-branching flows; introduced dual-simplex phase2 helper templates; and widened solver integer parameter ranges for branching modes. (50 words)

Changes

Cohort / File(s) Summary
Branch-and-Bound Core
cpp/src/branch_and_bound/branch_and_bound.cpp
Reordered reliable_variable_selection call arguments; conditional enabling of dual-pivot ranking via settings_.reliability_branching == -2; removed local inf constant; transpose original LP into pc_.AT; pass root_relax_soln_ (full solution), upper_bound_, and basis lists/updates into strong_branching.
Pseudo-costs & Strong-branching
cpp/src/branch_and_bound/pseudo_costs.cpp, cpp/src/branch_and_bound/pseudo_costs.hpp
Added single-pivot objective-change estimation helpers and initialization; changed strong_branching signature to accept const lp_solution_t& root_solution, upper_bound, basic_list, nonbasic_list, and basis_factors; added AT transpose member to pseudo_costs_t; tightened reliable_variable_selection (const node, removed explicit solution, reads worker->leaf_solution.x), uses pivot estimates to rank candidates, and updated dual-solve/cutoff handling.
Dual Simplex Phase2 Helpers
cpp/src/dual_simplex/phase2.cpp, cpp/src/dual_simplex/phase2.hpp
Introduced templated helpers compute_reduced_cost_update and compute_delta_z (file-scope); replaced prior phase2 implementations with calls to these helpers; added explicit template instantiations for int/double.
Solver Settings
cpp/src/math_optimization/solver_settings.cu
Expanded integer parameter ranges: CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING max 1→2; CUOPT_MIP_RELIABILITY_BRANCHING min -1→-2 (defaults unchanged).

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 0.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 'Improvements to reliability branching' is directly related to the main changeset, which focuses on enhancing reliability branching by using dual-simplex pivots for candidate ranking.
Description check ✅ Passed The pull request description clearly relates to the changeset and explains the objectives: improving reliability branching by using a single dual simplex pivot to estimate objective changes for better variable ranking.

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

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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: 4

🤖 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/branch_and_bound/pseudo_costs.cpp`:
- Around line 293-295: dual_phase2() can return dual::status_t::CUTOFF when
child_settings.cut_off is set, but the current helper treats any non-success as
failure and leaves obj as NaN; change the failure handling so that when
dual_phase2 returns CUTOFF you set obj to the applied cutoff
(child_settings.cut_off, i.e., upper_bound + settings.dual_tol) and treat it as
a valid result for seeding pseudo-costs instead of the generic failure path;
apply the same change to the other occurrence handling lines covering the same
logic (the block that currently inspects dual::status_t results and assigns
obj).
- Around line 898-906: Replace the std::cout/std::format usage in the block that
checks unreliabe_list.size() > max_num_candidates with the existing logger: call
log.printf() and pass a formatted message including
strong_branching_lp_iter.load(), branch_and_bound_lp_iters,
unreliable_list.size(), num_tasks, and reliable_threshold; remove the
std::format/include dependency and ensure the message format matches other
log.printf() calls in this file so logging is consistent with the parallel
solver pattern.
- Around line 219-245: The code currently passes basic_map[j] directly into
single_pivot_objective_change_estimate for each j in fractional; add a defensive
check on basic_map[j] before that call (e.g., if basic_map[j] < 0 then skip the
dual-pivot estimate for this j or assert/fail fast), so only valid basic indices
are forwarded; update both occurrences where fractional is iterated (the loop
using basic_map and the second occurrence mentioned) to guard around the call to
single_pivot_objective_change_estimate and avoid pushing an invalid basic index
into structures like e_k.i.

In `@cpp/src/dual_simplex/phase2.cpp`:
- Around line 175-206: The CHECK_CHANGE_IN_REDUCED_COST debug block calls
phase2::compute_reduced_cost_update but references out-of-scope symbols (lp,
basic_list, nonbasic_list); to fix, thread the required context into
compute_delta_z by updating its signature (and all callers) to accept the LP and
basis lists (or pass appropriate local equivalents available in this translation
unit) and then forward those parameters into
phase2::compute_reduced_cost_update, or alternatively construct/obtain the
needed lp, basic_list and nonbasic_list inside compute_delta_z before the debug
call so the debug path compiles; ensure you update function names
compute_delta_z and the call site(s) consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: eb905199-9239-4fa9-a7c5-55a6b8c381b6

📥 Commits

Reviewing files that changed from the base of the PR and between 4d5f5e5 and 94c3dc3.

📒 Files selected for processing (6)
  • cpp/src/branch_and_bound/branch_and_bound.cpp
  • cpp/src/branch_and_bound/pseudo_costs.cpp
  • cpp/src/branch_and_bound/pseudo_costs.hpp
  • cpp/src/dual_simplex/phase2.cpp
  • cpp/src/dual_simplex/phase2.hpp
  • cpp/src/math_optimization/solver_settings.cu

Comment on lines +175 to +206
#ifdef CHECK_CHANGE_IN_REDUCED_COST
const i_t m = A_transpose.n;
const i_t n = A_transpose.m;
std::vector<f_t> delta_y_dense(m);
delta_y.to_dense(delta_y_dense);
std::vector<f_t> delta_z_check(n);
std::vector<i_t> delta_z_mark_check(n, 0);
std::vector<i_t> delta_z_indices_check;
phase2::compute_reduced_cost_update(lp,
basic_list,
nonbasic_list,
delta_y_dense,
leaving_index,
direction,
delta_z_mark_check,
delta_z_indices_check,
delta_z_check,
work_estimate);
f_t error_check = 0.0;
for (i_t k = 0; k < n; ++k) {
const f_t diff = std::abs(delta_z[k] - delta_z_check[k]);
if (diff > 1e-6) {
printf("delta_z error %d transpose %e no transpose %e diff %e\n",
k,
delta_z[k],
delta_z_check[k],
diff);
}
error_check = std::max(error_check, diff);
}
if (error_check > 1e-6) { printf("delta_z error %e\n", error_check); }
#endif
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

CHECK_CHANGE_IN_REDUCED_COST path is currently uncompilable after refactor.

At Line 183, the debug path uses phase2::compute_reduced_cost_update(...) and references lp, basic_list, and nonbasic_list, but those symbols are not in scope in this function. Enabling the macro will fail compilation.

Suggested fix (thread required context into compute_delta_z)
-template <typename i_t, typename f_t>
-void compute_delta_z(const csc_matrix_t<i_t, f_t>& A_transpose,
+template <typename i_t, typename f_t>
+void compute_delta_z(const lp_problem_t<i_t, f_t>& lp,
+                     const std::vector<i_t>& basic_list,
+                     const std::vector<i_t>& nonbasic_list,
+                     const csc_matrix_t<i_t, f_t>& A_transpose,
                      const sparse_vector_t<i_t, f_t>& delta_y,
                      i_t leaving_index,
                      i_t direction,
@@
-  phase2::compute_reduced_cost_update(lp,
-                                      basic_list,
-                                      nonbasic_list,
-                                      delta_y_dense,
-                                      leaving_index,
-                                      direction,
-                                      delta_z_mark_check,
-                                      delta_z_indices_check,
-                                      delta_z_check,
-                                      work_estimate);
+  compute_reduced_cost_update(lp,
+                              basic_list,
+                              nonbasic_list,
+                              delta_y_dense,
+                              leaving_index,
+                              direction,
+                              delta_z_mark_check,
+                              delta_z_indices_check,
+                              delta_z_check,
+                              work_estimate);
-        compute_delta_z(A_transpose,
+        compute_delta_z(lp,
+                        basic_list,
+                        nonbasic_list,
+                        A_transpose,
                         delta_y_sparse,
                         leaving_index,
                         direction,
                         nonbasic_mark,
                         delta_z_mark,
                         delta_z_indices,
                         delta_z,
                         phase2_work_estimate);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/dual_simplex/phase2.cpp` around lines 175 - 206, The
CHECK_CHANGE_IN_REDUCED_COST debug block calls
phase2::compute_reduced_cost_update but references out-of-scope symbols (lp,
basic_list, nonbasic_list); to fix, thread the required context into
compute_delta_z by updating its signature (and all callers) to accept the LP and
basis lists (or pass appropriate local equivalents available in this translation
unit) and then forward those parameters into
phase2::compute_reduced_cost_update, or alternatively construct/obtain the
needed lp, basic_list and nonbasic_list inside compute_delta_z before the debug
call so the debug path compiles; ensure you update function names
compute_delta_z and the call site(s) consistently.

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: 4

🤖 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/branch_and_bound/pseudo_costs.cpp`:
- Around line 420-431: The cutoff sum in the else branch of pseudo_costs.cpp may
multiply objective coefficients by infinite bounds; update the computation in
the block that sets child_settings.cut_off to check
std::isfinite(child_problem.upper[i]) and std::isfinite(child_problem.lower[i])
before doing the multiplication with original_lp.objective[i]; skip terms with
non-finite bounds, and if a skipped term has a non-zero objective coefficient
treat the cutoff as unbounded (set child_settings.cut_off to +/-INFINITY based
on the sign of original_lp.objective[i]) or otherwise keep the finite
accumulated sum, ensuring no multiplication by non-finite values occurs (refer
to child_settings.cut_off, original_lp.objective, and
child_problem.upper/lower).
- Around line 140-161: The debug block wrongly accesses
delta_y.lp_solution.x[k]; delta_y is a sparse_vector_t with arrays .i and .x, so
replace delta_y.lp_solution.x[k] with delta_y.x[k] (keeping the loop over k <
delta_y.i.size()) so the index/value pairs are read correctly; leave the rest of
the CHECK_DELTA_OBJ computation (uses delta_z_indices, vstatus, delta_z,
variable_j, delta_obj_check, step_length) unchanged so the validity check
compiles and compares delta_obj_check to delta_obj as intended.
- Around line 121-131: The debug block references an undefined variable y
causing a compile error; change the code to use the solver's dual vector (e.g.
lp_solution.y) or declare a local alias before use. Specifically, in the
CHECK_DUAL_FEASIBILITY block update the call to matrix_transpose_vector_multiply
to pass lp_solution.y (or create vector<f_t> y = lp_solution.y) instead of the
undefined y so dual_residual, matrix_transpose_vector_multiply, vector_norm_inf
and settings.log.printf work correctly.
- Around line 296-308: The fallback cutoff calculation when upper_bound is not
finite can produce +/-inf by multiplying objective coefficients with infinite
variable bounds; update the block that sets child_settings.cut_off (the else
branch using original_lp.objective, child_problem.upper and child_problem.lower)
to ignore terms where the corresponding child_problem.upper or
child_problem.lower is not finite (or only compute the fallback when all
contributing bounds are finite), summing only finite products and leaving
child_settings.cut_off at a safe default (or signaling no cutoff) if any
required bound is infinite; ensure the logic preserves the existing behavior
when bounds are finite and still adds settings.dual_tol when applicable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7905b856-7959-4fe1-bae4-678afac404e8

📥 Commits

Reviewing files that changed from the base of the PR and between 94c3dc3 and 703f30c.

📒 Files selected for processing (1)
  • cpp/src/branch_and_bound/pseudo_costs.cpp

@nguidotti
Copy link
Copy Markdown
Contributor Author

/ok to test 703f30c

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 (2)
cpp/src/branch_and_bound/branch_and_bound.cpp (2)

1992-1998: Consider documenting the magic value -2 for reliability branching mode.

The value -2 for settings_.reliability_branching enables dual-simplex objective estimation, but this isn't documented in the code. Other code paths check reliability_branching != 0 for general reliability branching (line 858), making the semantics of different values unclear.

Consider adding a comment or using a named constant to clarify the different modes:

  • 0: disabled
  • -2: enabled with dual pivot estimation
  • Other non-zero values: enabled without dual pivot estimation (?)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/branch_and_bound/branch_and_bound.cpp` around lines 1992 - 1998, The
branch sets behavior when settings_.reliability_branching == -2 but that magic
value is undocumented; replace the literal with a named constant or enum (e.g.,
RELIABILITY_BRANCHING_DUAL_PIVOT) and add a short comment describing the modes
(0 = disabled, -2 = dual-pivot objective estimation, other non-zero =
reliability branching without dual pivot). Update the check in this block that
sets pc_.reliability_branching_settings.rank_candidates_with_dual_pivot and any
other checks that compare settings_.reliability_branching (e.g., places using
"reliability_branching != 0") to use the new named constant or enum variants so
the semantics are clear throughout the codebase.

792-810: Debug output should use logger or be removed before merge.

This std::cout with std::format bypasses the existing logging infrastructure (settings_.log) which handles log prefixes, file redirection, and verbosity control. Given the PR is marked "[WIP]", this appears to be temporary diagnostic code.

Consider:

  1. Removing this output before merging, or
  2. Converting to use settings_.log.printf() if the output is intended to be permanent, or
  3. Wrapping in a #ifdef debug macro if needed for development only
♻️ Suggested conversion to logger (if keeping)
-    std::cout << std::format(
-                   "{}: user_obj={:.3g}, solver_obj={:.3g}, user_lower={:.3g}, "
-                   "solver_lower={:.3g}, user_gap={:.3g}, "
-                   "solver_gap={:.3g}, tol={:.3g}",
-                   feasible_solution_symbol(thread_type),
-                   user_upper,
-                   upper,
-                   user_lower,
-                   lower,
-                   std::abs(user_upper - user_lower),
-                   std::abs(upper - lower),
-                   settings_.absolute_mip_gap_tol)
-              << std::endl;
+    settings_.log.debug(
+      "%c: user_obj=%.3g, solver_obj=%.3g, user_lower=%.3g, "
+      "solver_lower=%.3g, user_gap=%.3g, solver_gap=%.3g, tol=%.3g\n",
+      feasible_solution_symbol(thread_type),
+      user_upper,
+      upper,
+      user_lower,
+      lower,
+      std::abs(user_upper - user_lower),
+      std::abs(upper - lower),
+      settings_.absolute_mip_gap_tol);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/src/branch_and_bound/branch_and_bound.cpp` around lines 792 - 810, The
debug std::cout/std::format block should be removed or converted to use the
project's logging API; replace the print in branch_and_bound.cpp (the block that
reads upper_bound_, get_lower_bound(), compute_user_objective(original_lp_,
...), feasible_solution_symbol(...), settings_.absolute_mip_gap_tol) with a call
to settings_.log.printf(...) (or remove entirely) so logs respect prefixes,
redirection and verbosity; if this is for temporary debugging only, wrap it in a
debug macro instead of using std::cout.
🤖 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/branch_and_bound/branch_and_bound.cpp`:
- Around line 1992-1998: The branch sets behavior when
settings_.reliability_branching == -2 but that magic value is undocumented;
replace the literal with a named constant or enum (e.g.,
RELIABILITY_BRANCHING_DUAL_PIVOT) and add a short comment describing the modes
(0 = disabled, -2 = dual-pivot objective estimation, other non-zero =
reliability branching without dual pivot). Update the check in this block that
sets pc_.reliability_branching_settings.rank_candidates_with_dual_pivot and any
other checks that compare settings_.reliability_branching (e.g., places using
"reliability_branching != 0") to use the new named constant or enum variants so
the semantics are clear throughout the codebase.
- Around line 792-810: The debug std::cout/std::format block should be removed
or converted to use the project's logging API; replace the print in
branch_and_bound.cpp (the block that reads upper_bound_, get_lower_bound(),
compute_user_objective(original_lp_, ...), feasible_solution_symbol(...),
settings_.absolute_mip_gap_tol) with a call to settings_.log.printf(...) (or
remove entirely) so logs respect prefixes, redirection and verbosity; if this is
for temporary debugging only, wrap it in a debug macro instead of using
std::cout.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 91d703fa-65d3-4ee5-8b05-99dbef2b49dd

📥 Commits

Reviewing files that changed from the base of the PR and between 703f30c and bb71cde.

📒 Files selected for processing (2)
  • cpp/src/branch_and_bound/branch_and_bound.cpp
  • cpp/src/dual_simplex/phase2.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • cpp/src/dual_simplex/phase2.cpp

Signed-off-by: Nicolas Guidotti <224634272+nguidotti@users.noreply.github.com>
@nguidotti
Copy link
Copy Markdown
Contributor Author

/ok to test 9c9d019

@chris-maes chris-maes added P0 and removed P2 labels Mar 31, 2026
@nguidotti
Copy link
Copy Markdown
Contributor Author

/ok to test 5e16269

Copy link
Copy Markdown
Contributor

@chris-maes chris-maes 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 the PR Nicolas!

Please double check the upper bound on the objective, it looks incorrect.

Also, let's introduce a new setting so we don't conflict with @Kh4ster 's PR.

I think it's also possible to factor out some of the duplicate code here.

…g branching as a setting

Signed-off-by: Nicolas Guidotti <224634272+nguidotti@users.noreply.github.com>
@nguidotti
Copy link
Copy Markdown
Contributor Author

/ok to test c4f246e

…n in strong branching

Signed-off-by: Nicolas Guidotti <224634272+nguidotti@users.noreply.github.com>
Signed-off-by: Nicolas Guidotti <224634272+nguidotti@users.noreply.github.com>
Signed-off-by: Nicolas Guidotti <224634272+nguidotti@users.noreply.github.com>
@nguidotti
Copy link
Copy Markdown
Contributor Author

/ok to test 9ea1248

…istic_test

Signed-off-by: Nicolas Guidotti <224634272+nguidotti@users.noreply.github.com>
@nguidotti
Copy link
Copy Markdown
Contributor Author

/ok to test cd852b5

#define CUOPT_MIP_CUT_CHANGE_THRESHOLD "mip_cut_change_threshold"
#define CUOPT_MIP_CUT_MIN_ORTHOGONALITY "mip_cut_min_orthogonality"
#define CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING "mip_batch_pdlp_strong_branching"
#define CUOPT_MIP_STRONG_BRANCHING_SIMPLEX_ITER_LIMIT "mip_strong_branching_simplex_iter_limit"
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.

This is minor, but it becomes part of the API, so probably worth getting it correct. All other parameters use full word rather than abbreviations.

So we should probably call this mip_strong_branching_simplex_iteration_limit


i_t simplex_iteration_limit = settings.strong_branching_simplex_iteration_limit;

if (simplex_iteration_limit < 1) {
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.

If I understand correctly, you've set the default of strong_branching_simplex_iteration_limit = -1. Does that mean by default we will use initialize_psuedo_costs_with_estimates? Or maybe somewhere else in the code you catch this?

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.

Ah got it. You set it to 200 in solver.cu if it was set to -1.

context.settings.mip_batch_pdlp_strong_branching;

branch_and_bound_settings.strong_branching_simplex_iteration_limit =
context.settings.strong_branching_simplex_iteration_limit < 0
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.

Probably this should be <= 0. Since if the user sets it to zero now, we still need to initialize the pseudocosts for the code to work.

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.

If the user set to 0 here, we use the objective estimate via dual simplex pivot.

Copy link
Copy Markdown
Contributor

@chris-maes chris-maes left a comment

Choose a reason for hiding this comment

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

Thanks so much Nicolas.

I'm removing 'request changes', but please make the following changes before merging

  • CUOPT_MIP_STRONG_BRANCHING_SIMPLEX_ITER_LIMIT -> CUOPT_MIP_STRONG_BRANCHING_SIMPLEX_ITERATION_LIMIT
  • Set iteration limit to 200 if user sets value <= 0. So -1, and 0 both default to 200.

Otherwise LGTM. Excited to see the improvement in reliability branching.

@anandhkb anandhkb merged commit 93f4434 into NVIDIA:release/26.04 Apr 3, 2026
106 checks passed
@nguidotti nguidotti deleted the improve-reliable-branching branch April 3, 2026 08:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Improves an existing functionality mip non-breaking Introduces a non-breaking change P0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants