Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
70b251d
implement racing for strong branching
Kh4ster Feb 9, 2026
6848df1
race on by default
Kh4ster Feb 9, 2026
fc7aa04
tmp
Kh4ster Feb 17, 2026
1614bc1
fix
Kh4ster Feb 17, 2026
4f33531
fix
Kh4ster Feb 18, 2026
e0a530e
workaround for thrust reverse iterator build error
tmckayus Feb 17, 2026
e330718
remove compile file
Kh4ster Feb 18, 2026
dce6d4f
fix
Kh4ster Feb 18, 2026
9c03faf
final cleanup
Kh4ster Feb 18, 2026
6c2fe35
addtional cleanup
Kh4ster Feb 18, 2026
eb60c98
Merge branch 'main' into fix_batch_pdlp
Kh4ster Feb 18, 2026
a43dc0c
address PR comments, add tests, update doc
Kh4ster Feb 19, 2026
c8b8b74
format
Kh4ster Feb 19, 2026
b1be5bb
format
Kh4ster Feb 19, 2026
fe31e7e
Merge branch 'main' into fix_batch_pdlp
Kh4ster Feb 19, 2026
d89af96
style
Kh4ster Feb 19, 2026
c7e3e22
put back changes in restart
Kh4ster Feb 19, 2026
793345c
Merge remote-tracking branch 'upstream/fix_batch_pdlp' into race_stro…
Kh4ster Feb 19, 2026
73a52b1
fix use overall time limit, reduce memory consumtion and add a bigger…
Kh4ster Feb 20, 2026
dbc94fd
switch to double for memory estimator as size_t was hitting overflow …
Kh4ster Feb 24, 2026
8b1ec93
add support for dual simplex warm start
Kh4ster Feb 20, 2026
f97d817
Merge branch 'main' into race_strong_branching
Kh4ster Feb 27, 2026
71e47eb
handle batch pdlp being out of memory not has teramintion error
Kh4ster Mar 5, 2026
d025441
add a basic batch pdlp race strategy in strong branching
Kh4ster Mar 5, 2026
b43d7f3
Merge branch 'main' into race_strong_branching_realibility_branching
Kh4ster Mar 5, 2026
3044887
fix compilation issue
Kh4ster Mar 6, 2026
0108de4
separate the two batch pdlp settings
Kh4ster Mar 9, 2026
721a56a
Fix bug where batch PDLP for strong branching was running on problem …
chris-maes Mar 11, 2026
13ae8b4
Merge remote-tracking branch 'chris/strong_branching_batch_lp_transla…
Kh4ster Mar 11, 2026
ba1e4bd
pass slack and correct problem convertion also in reliabiltiy branch…
Kh4ster Mar 13, 2026
d20efd4
Merge branch 'main' into race_strong_branching_realibility_branching
Kh4ster Mar 13, 2026
d513865
add initial pdlp iteartions to the warm start data and on by default
Kh4ster Mar 18, 2026
a3a458d
put clique table in lp necessary file, add solver setting flag to gen…
Kh4ster Mar 19, 2026
79d05e7
initial version of work stealing
Kh4ster Mar 19, 2026
2c8bbfd
add option to use either dual simplex, bpdlp, or both with work stealing
Kh4ster Mar 19, 2026
0968167
fix: resize the buffers to handle the case where we go to a single co…
Kh4ster Mar 23, 2026
7642ded
general batch pdlp improvements and support work stealing in RB
Kh4ster Mar 23, 2026
84dab81
turn off logs
Kh4ster Mar 24, 2026
004fe3e
Merge branch 'release/26.04' into race_strong_branching_realibility_b…
Kh4ster Mar 24, 2026
a97cca7
few improvements to BPDLP
Kh4ster Mar 24, 2026
6979086
reduce accuracy to 1e-5, no BPDLP if in sub mip, disable BPDLP in RB …
Kh4ster Mar 25, 2026
b0061e4
empty just to run a new benchmark
Kh4ster Mar 25, 2026
f504a75
fix PR review comments
Kh4ster Mar 30, 2026
f456c71
Merge branch 'release/26.04' into race_strong_branching_realibility_b…
Kh4ster Mar 30, 2026
962c2ea
fix: disable batch pdlp if deterministic mode
Kh4ster Mar 30, 2026
496c4fd
fix: add size assertion to shared strong branching context
Kh4ster Mar 30, 2026
9ec3f40
cleanup names
Kh4ster Mar 30, 2026
16e4e5f
multiples fixes: use span only in solver, use tasks to launch bpdlp r…
Kh4ster Mar 30, 2026
843c532
two improvements: mark variables as solved in DS if node became relia…
Kh4ster Mar 30, 2026
a9fd420
fix: avoid early exit if solved at step 0 even when initial pdlp itea…
Kh4ster Mar 30, 2026
edae299
disable both by default
Kh4ster Mar 30, 2026
6e5baa5
styl
Kh4ster Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 58 additions & 57 deletions cpp/include/cuopt/linear_programming/constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,63 +20,64 @@
#define CUOPT_INSTANTIATE_INT64 0

/* @brief LP/MIP parameter string constants */
#define CUOPT_ABSOLUTE_DUAL_TOLERANCE "absolute_dual_tolerance"
#define CUOPT_RELATIVE_DUAL_TOLERANCE "relative_dual_tolerance"
#define CUOPT_ABSOLUTE_PRIMAL_TOLERANCE "absolute_primal_tolerance"
#define CUOPT_RELATIVE_PRIMAL_TOLERANCE "relative_primal_tolerance"
#define CUOPT_ABSOLUTE_GAP_TOLERANCE "absolute_gap_tolerance"
#define CUOPT_RELATIVE_GAP_TOLERANCE "relative_gap_tolerance"
#define CUOPT_INFEASIBILITY_DETECTION "infeasibility_detection"
#define CUOPT_STRICT_INFEASIBILITY "strict_infeasibility"
#define CUOPT_PRIMAL_INFEASIBLE_TOLERANCE "primal_infeasible_tolerance"
#define CUOPT_DUAL_INFEASIBLE_TOLERANCE "dual_infeasible_tolerance"
#define CUOPT_ITERATION_LIMIT "iteration_limit"
#define CUOPT_TIME_LIMIT "time_limit"
#define CUOPT_WORK_LIMIT "work_limit"
#define CUOPT_PDLP_SOLVER_MODE "pdlp_solver_mode"
#define CUOPT_METHOD "method"
#define CUOPT_PER_CONSTRAINT_RESIDUAL "per_constraint_residual"
#define CUOPT_SAVE_BEST_PRIMAL_SO_FAR "save_best_primal_so_far"
#define CUOPT_FIRST_PRIMAL_FEASIBLE "first_primal_feasible"
#define CUOPT_LOG_FILE "log_file"
#define CUOPT_LOG_TO_CONSOLE "log_to_console"
#define CUOPT_CROSSOVER "crossover"
#define CUOPT_FOLDING "folding"
#define CUOPT_AUGMENTED "augmented"
#define CUOPT_DUALIZE "dualize"
#define CUOPT_ORDERING "ordering"
#define CUOPT_BARRIER_DUAL_INITIAL_POINT "barrier_dual_initial_point"
#define CUOPT_ELIMINATE_DENSE_COLUMNS "eliminate_dense_columns"
#define CUOPT_CUDSS_DETERMINISTIC "cudss_deterministic"
#define CUOPT_PRESOLVE "presolve"
#define CUOPT_DUAL_POSTSOLVE "dual_postsolve"
#define CUOPT_MIP_DETERMINISM_MODE "mip_determinism_mode"
#define CUOPT_MIP_ABSOLUTE_TOLERANCE "mip_absolute_tolerance"
#define CUOPT_MIP_RELATIVE_TOLERANCE "mip_relative_tolerance"
#define CUOPT_MIP_INTEGRALITY_TOLERANCE "mip_integrality_tolerance"
#define CUOPT_MIP_ABSOLUTE_GAP "mip_absolute_gap"
#define CUOPT_MIP_RELATIVE_GAP "mip_relative_gap"
#define CUOPT_MIP_HEURISTICS_ONLY "mip_heuristics_only"
#define CUOPT_MIP_SCALING "mip_scaling"
#define CUOPT_MIP_PRESOLVE "mip_presolve"
#define CUOPT_MIP_RELIABILITY_BRANCHING "mip_reliability_branching"
#define CUOPT_MIP_CUT_PASSES "mip_cut_passes"
#define CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS "mip_mixed_integer_rounding_cuts"
#define CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS "mip_mixed_integer_gomory_cuts"
#define CUOPT_MIP_KNAPSACK_CUTS "mip_knapsack_cuts"
#define CUOPT_MIP_CLIQUE_CUTS "mip_clique_cuts"
#define CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS "mip_strong_chvatal_gomory_cuts"
#define CUOPT_MIP_REDUCED_COST_STRENGTHENING "mip_reduced_cost_strengthening"
#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_SOLUTION_FILE "solution_file"
#define CUOPT_NUM_CPU_THREADS "num_cpu_threads"
#define CUOPT_NUM_GPUS "num_gpus"
#define CUOPT_USER_PROBLEM_FILE "user_problem_file"
#define CUOPT_PRESOLVE_FILE "presolve_file"
#define CUOPT_RANDOM_SEED "random_seed"
#define CUOPT_PDLP_PRECISION "pdlp_precision"
#define CUOPT_ABSOLUTE_DUAL_TOLERANCE "absolute_dual_tolerance"
#define CUOPT_RELATIVE_DUAL_TOLERANCE "relative_dual_tolerance"
#define CUOPT_ABSOLUTE_PRIMAL_TOLERANCE "absolute_primal_tolerance"
#define CUOPT_RELATIVE_PRIMAL_TOLERANCE "relative_primal_tolerance"
#define CUOPT_ABSOLUTE_GAP_TOLERANCE "absolute_gap_tolerance"
#define CUOPT_RELATIVE_GAP_TOLERANCE "relative_gap_tolerance"
#define CUOPT_INFEASIBILITY_DETECTION "infeasibility_detection"
#define CUOPT_STRICT_INFEASIBILITY "strict_infeasibility"
#define CUOPT_PRIMAL_INFEASIBLE_TOLERANCE "primal_infeasible_tolerance"
#define CUOPT_DUAL_INFEASIBLE_TOLERANCE "dual_infeasible_tolerance"
#define CUOPT_ITERATION_LIMIT "iteration_limit"
#define CUOPT_TIME_LIMIT "time_limit"
#define CUOPT_WORK_LIMIT "work_limit"
#define CUOPT_PDLP_SOLVER_MODE "pdlp_solver_mode"
#define CUOPT_METHOD "method"
#define CUOPT_PER_CONSTRAINT_RESIDUAL "per_constraint_residual"
#define CUOPT_SAVE_BEST_PRIMAL_SO_FAR "save_best_primal_so_far"
#define CUOPT_FIRST_PRIMAL_FEASIBLE "first_primal_feasible"
#define CUOPT_LOG_FILE "log_file"
#define CUOPT_LOG_TO_CONSOLE "log_to_console"
#define CUOPT_CROSSOVER "crossover"
#define CUOPT_FOLDING "folding"
#define CUOPT_AUGMENTED "augmented"
#define CUOPT_DUALIZE "dualize"
#define CUOPT_ORDERING "ordering"
#define CUOPT_BARRIER_DUAL_INITIAL_POINT "barrier_dual_initial_point"
#define CUOPT_ELIMINATE_DENSE_COLUMNS "eliminate_dense_columns"
#define CUOPT_CUDSS_DETERMINISTIC "cudss_deterministic"
#define CUOPT_PRESOLVE "presolve"
#define CUOPT_DUAL_POSTSOLVE "dual_postsolve"
#define CUOPT_MIP_DETERMINISM_MODE "mip_determinism_mode"
#define CUOPT_MIP_ABSOLUTE_TOLERANCE "mip_absolute_tolerance"
#define CUOPT_MIP_RELATIVE_TOLERANCE "mip_relative_tolerance"
#define CUOPT_MIP_INTEGRALITY_TOLERANCE "mip_integrality_tolerance"
#define CUOPT_MIP_ABSOLUTE_GAP "mip_absolute_gap"
#define CUOPT_MIP_RELATIVE_GAP "mip_relative_gap"
#define CUOPT_MIP_HEURISTICS_ONLY "mip_heuristics_only"
#define CUOPT_MIP_SCALING "mip_scaling"
#define CUOPT_MIP_PRESOLVE "mip_presolve"
#define CUOPT_MIP_RELIABILITY_BRANCHING "mip_reliability_branching"
#define CUOPT_MIP_CUT_PASSES "mip_cut_passes"
#define CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS "mip_mixed_integer_rounding_cuts"
#define CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS "mip_mixed_integer_gomory_cuts"
#define CUOPT_MIP_KNAPSACK_CUTS "mip_knapsack_cuts"
#define CUOPT_MIP_CLIQUE_CUTS "mip_clique_cuts"
#define CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS "mip_strong_chvatal_gomory_cuts"
#define CUOPT_MIP_REDUCED_COST_STRENGTHENING "mip_reduced_cost_strengthening"
#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_BATCH_PDLP_RELIABILITY_BRANCHING "mip_batch_pdlp_reliability_branching"
#define CUOPT_SOLUTION_FILE "solution_file"
#define CUOPT_NUM_CPU_THREADS "num_cpu_threads"
#define CUOPT_NUM_GPUS "num_gpus"
#define CUOPT_USER_PROBLEM_FILE "user_problem_file"
#define CUOPT_PRESOLVE_FILE "presolve_file"
#define CUOPT_RANDOM_SEED "random_seed"
#define CUOPT_PDLP_PRECISION "pdlp_precision"

/* @brief MIP determinism mode constants */
#define CUOPT_MODE_OPPORTUNISTIC 0
Expand Down
17 changes: 10 additions & 7 deletions cpp/include/cuopt/linear_programming/mip/solver_settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,16 @@ class mip_solver_settings_t {
i_t mixed_integer_gomory_cuts = -1;
i_t knapsack_cuts = -1;
i_t clique_cuts = -1;
i_t strong_chvatal_gomory_cuts = -1;
i_t reduced_cost_strengthening = -1;
f_t cut_change_threshold = -1.0;
f_t cut_min_orthogonality = 0.5;
i_t mip_batch_pdlp_strong_branching = 0;
i_t num_gpus = 1;
bool log_to_console = true;
i_t strong_chvatal_gomory_cuts = -1;
i_t reduced_cost_strengthening = -1;
f_t cut_change_threshold = -1.0;
f_t cut_min_orthogonality = 0.5;
i_t mip_batch_pdlp_strong_branching{
0}; // 0 = DS only, 1 = cooperative DS + PDLP, 2 = batch PDLP only
i_t mip_batch_pdlp_reliability_branching{
0}; // 0 = DS only, 1 = cooperative DS + PDLP, 2 = batch PDLP only
i_t num_gpus = 1;
bool log_to_console = true;

std::string log_file;
std::string sol_file;
Expand Down
20 changes: 20 additions & 0 deletions cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <rmm/device_uvector.hpp>

#include <atomic>
#include <span>

namespace cuopt::linear_programming {

Expand Down Expand Up @@ -147,6 +148,12 @@ class pdlp_solver_settings_t {
* @param[in] initial_primal_weight Initial primal weight.
*/
void set_initial_primal_weight(f_t initial_primal_weight);
/**
* @brief Set an initial pdlp iteration.
*
* @param[in] initial_pdlp_iteration Initial pdlp iteration.
*/
void set_initial_pdlp_iteration(i_t initial_pdlp_iteration);

/**
* @brief Set the pdlp warm start data. This allows to restart PDLP with a
Expand Down Expand Up @@ -213,6 +220,8 @@ class pdlp_solver_settings_t {
std::optional<f_t> get_initial_step_size() const;
// TODO batch mode: tmp
std::optional<f_t> get_initial_primal_weight() const;
// TODO batch mode: tmp
std::optional<i_t> get_initial_pdlp_iteration() const;

const rmm::device_uvector<f_t>& get_initial_primal_solution() const;
const rmm::device_uvector<f_t>& get_initial_dual_solution() const;
Expand Down Expand Up @@ -265,6 +274,8 @@ class pdlp_solver_settings_t {
bool inside_mip{false};
// For concurrent termination
std::atomic<int>* concurrent_halt{nullptr};
// Shared strong branching solved flags for cooperative DS + PDLP
std::span<std::atomic<int>> shared_sb_solved;
static constexpr f_t minimal_absolute_tolerance = 1.0e-12;
pdlp_hyper_params::pdlp_hyper_params_t hyper_params;
// Holds the information of new variable lower and upper bounds for each climber in the format:
Expand All @@ -273,6 +284,12 @@ class pdlp_solver_settings_t {
// concurrently i.e. if new_bounds.size() == 2, then 2 versions of the problem with updated bounds
// will be solved concurrently
std::vector<std::tuple<i_t, f_t, f_t>> new_bounds;
// By default to save memory and speed we don't store and copy each climber's primal and dual
// solutions We only retrieve termination statistics and the objective values
bool generate_batch_primal_dual_solution{false};
// Used to force batch PDLP to solve a subbatch of the problems at a time
// The 0 default value will make the solver use its heuristic to determine the subbatch size
i_t sub_batch_size{0};
Comment on lines +287 to +292
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 | 🔴 Critical

Keep batch solution generation opt-out, not opt-in.

With this default at false, generic batch LP solves now return empty primal/dual/reduced-cost buffers unless every caller remembers to opt in. Existing batch tests in cpp/tests/linear_programming/pdlp_test.cu still slice those buffers after solve_lp(..., new_bounds ...), so this silently regresses the non-MIP batch path and can drive helpers like extract_subvector() past a zero-length buffer. The strong/reliability-branching call sites can disable collection explicitly, but the default here needs to preserve the old behavior.

Suggested fix
-  bool generate_batch_primal_dual_solution{false};
+  bool generate_batch_primal_dual_solution{true};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// By default to save memory and speed we don't store and copy each climber's primal and dual
// solutions We only retrieve termination statistics and the objective values
bool generate_batch_primal_dual_solution{false};
// Used to force batch PDLP to solve a subbatch of the problems at a time
// The 0 default value will make the solver use its heuristic to determine the subbatch size
i_t sub_batch_size{0};
// By default to save memory and speed we don't store and copy each climber's primal and dual
// solutions We only retrieve termination statistics and the objective values
bool generate_batch_primal_dual_solution{true};
// Used to force batch PDLP to solve a subbatch of the problems at a time
// The 0 default value will make the solver use its heuristic to determine the subbatch size
i_t sub_batch_size{0};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp` around lines
287 - 292, The default for generate_batch_primal_dual_solution is currently
false which breaks existing batch LP callers that expect
primal/dual/reduced-cost buffers; change the default for the
generate_batch_primal_dual_solution member in solver_settings.hpp back to true
so batch solves continue to populate those buffers by default, and update the
inline comment to reflect that the default preserves previous behavior (callers
that want to opt-out can still set generate_batch_primal_dual_solution=false or
use the existing strong/reliability-branching call sites to disable collection).


private:
/** Initial primal solution */
Expand All @@ -285,6 +302,9 @@ class pdlp_solver_settings_t {
/** Initial primal weight */
// TODO batch mode: tmp
std::optional<f_t> initial_primal_weight_;
/** Initial pdlp iteration */
// TODO batch mode: tmp
std::optional<i_t> initial_pdlp_iteration_;
/** GPU-backed warm start data (device_uvector), used by C++ API and local GPU solves */
pdlp_warm_start_data_t<i_t, f_t> pdlp_warm_start_data_;
/** Warm start data as spans over external memory, used by Cython/Python interface */
Expand Down
10 changes: 7 additions & 3 deletions cpp/src/branch_and_bound/branch_and_bound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -846,7 +846,9 @@ branch_variable_t<i_t> branch_and_bound_t<i_t, f_t>::variable_selection(
exploration_stats_,
upper_bound_,
worker_pool_.num_idle_workers(),
log);
log,
new_slacks_,
original_lp_);
} else {
branch_var = pc_.variable_selection(fractional, solution, log);
}
Expand Down Expand Up @@ -2448,12 +2450,14 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
pc_.resize(original_lp_.num_cols);
{
raft::common::nvtx::range scope_sb("BB::strong_branching");
strong_branching<i_t, f_t>(original_problem_,
original_lp_,
strong_branching<i_t, f_t>(original_lp_,
settings_,
exploration_stats_.start_time,
new_slacks_,
var_types_,
root_relax_soln_.x,
root_relax_soln_.y,
root_relax_soln_.z,
fractional,
root_objective_,
root_vstatus_,
Expand Down
Loading
Loading