Skip to content

Commit a290700

Browse files
authored
Race batch PDLP and dual simplex in strong branching / reliability branching (#994)
This PR enables the following regarding batch PDLP: - Enable batch PDLP in reliability branching - Add work stealing so that batch PDLP and Dual Simplex can run concurrently and steal LPs from each other if one solves it first - Use correct problem representation with cuts for batch PDLP - Use a PDLP warm start cache across strong branching at the root and in reliability branching - Increase tolerance on batch PDLP to have higher quality solution - Increase iteration limit to allow instances that needs a high iteration count (with low cost per iteration) to still come through (only while solving the original LP to get warm start data) - Multiple heuristics to not run batch PDLP to not create overheads when Dual Simplex is clearly superior - Don't store and copy primal dual solution unless need it to save on memory - Handle batch PDLP errors better, allowing Dual Simplex to still continue in strong branching even if BPDLP fails - No early exit if the initial warm start PDLP solution is already feasible in BPDLP - Correct objective for BPDLP when there is an offset Currently we still keep BPDLP off by default both at the root and in reliability branching Authors: - Nicolas Blin (https://github.com/Kh4ster) - Trevor McKay (https://github.com/tmckayus) - Chris Maes (https://github.com/chris-maes) Approvers: - Nicolas L. Guidotti (https://github.com/nguidotti) - Trevor McKay (https://github.com/tmckayus) - Chris Maes (https://github.com/chris-maes) URL: #994
1 parent 928a1e1 commit a290700

22 files changed

Lines changed: 1812 additions & 459 deletions

File tree

cpp/include/cuopt/linear_programming/constants.h

Lines changed: 61 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,65 +20,67 @@
2020
#define CUOPT_INSTANTIATE_INT64 0
2121

2222
/* @brief LP/MIP parameter string constants */
23-
#define CUOPT_ABSOLUTE_DUAL_TOLERANCE "absolute_dual_tolerance"
24-
#define CUOPT_RELATIVE_DUAL_TOLERANCE "relative_dual_tolerance"
25-
#define CUOPT_ABSOLUTE_PRIMAL_TOLERANCE "absolute_primal_tolerance"
26-
#define CUOPT_RELATIVE_PRIMAL_TOLERANCE "relative_primal_tolerance"
27-
#define CUOPT_ABSOLUTE_GAP_TOLERANCE "absolute_gap_tolerance"
28-
#define CUOPT_RELATIVE_GAP_TOLERANCE "relative_gap_tolerance"
29-
#define CUOPT_INFEASIBILITY_DETECTION "infeasibility_detection"
30-
#define CUOPT_STRICT_INFEASIBILITY "strict_infeasibility"
31-
#define CUOPT_PRIMAL_INFEASIBLE_TOLERANCE "primal_infeasible_tolerance"
32-
#define CUOPT_DUAL_INFEASIBLE_TOLERANCE "dual_infeasible_tolerance"
33-
#define CUOPT_ITERATION_LIMIT "iteration_limit"
34-
#define CUOPT_TIME_LIMIT "time_limit"
35-
#define CUOPT_WORK_LIMIT "work_limit"
36-
#define CUOPT_PDLP_SOLVER_MODE "pdlp_solver_mode"
37-
#define CUOPT_METHOD "method"
38-
#define CUOPT_PER_CONSTRAINT_RESIDUAL "per_constraint_residual"
39-
#define CUOPT_SAVE_BEST_PRIMAL_SO_FAR "save_best_primal_so_far"
40-
#define CUOPT_FIRST_PRIMAL_FEASIBLE "first_primal_feasible"
41-
#define CUOPT_LOG_FILE "log_file"
42-
#define CUOPT_LOG_TO_CONSOLE "log_to_console"
43-
#define CUOPT_CROSSOVER "crossover"
44-
#define CUOPT_FOLDING "folding"
45-
#define CUOPT_AUGMENTED "augmented"
46-
#define CUOPT_DUALIZE "dualize"
47-
#define CUOPT_ORDERING "ordering"
48-
#define CUOPT_BARRIER_DUAL_INITIAL_POINT "barrier_dual_initial_point"
49-
#define CUOPT_ELIMINATE_DENSE_COLUMNS "eliminate_dense_columns"
50-
#define CUOPT_CUDSS_DETERMINISTIC "cudss_deterministic"
51-
#define CUOPT_PRESOLVE "presolve"
52-
#define CUOPT_DUAL_POSTSOLVE "dual_postsolve"
53-
#define CUOPT_MIP_DETERMINISM_MODE "mip_determinism_mode"
54-
#define CUOPT_MIP_ABSOLUTE_TOLERANCE "mip_absolute_tolerance"
55-
#define CUOPT_MIP_RELATIVE_TOLERANCE "mip_relative_tolerance"
56-
#define CUOPT_MIP_INTEGRALITY_TOLERANCE "mip_integrality_tolerance"
57-
#define CUOPT_MIP_ABSOLUTE_GAP "mip_absolute_gap"
58-
#define CUOPT_MIP_RELATIVE_GAP "mip_relative_gap"
59-
#define CUOPT_MIP_HEURISTICS_ONLY "mip_heuristics_only"
60-
#define CUOPT_MIP_SCALING "mip_scaling"
61-
#define CUOPT_MIP_PRESOLVE "mip_presolve"
62-
#define CUOPT_MIP_RELIABILITY_BRANCHING "mip_reliability_branching"
63-
#define CUOPT_MIP_CUT_PASSES "mip_cut_passes"
64-
#define CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS "mip_mixed_integer_rounding_cuts"
65-
#define CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS "mip_mixed_integer_gomory_cuts"
66-
#define CUOPT_MIP_KNAPSACK_CUTS "mip_knapsack_cuts"
67-
#define CUOPT_MIP_IMPLIED_BOUND_CUTS "mip_implied_bound_cuts"
68-
#define CUOPT_MIP_CLIQUE_CUTS "mip_clique_cuts"
69-
#define CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS "mip_strong_chvatal_gomory_cuts"
70-
#define CUOPT_MIP_REDUCED_COST_STRENGTHENING "mip_reduced_cost_strengthening"
71-
#define CUOPT_MIP_CUT_CHANGE_THRESHOLD "mip_cut_change_threshold"
72-
#define CUOPT_MIP_CUT_MIN_ORTHOGONALITY "mip_cut_min_orthogonality"
73-
#define CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING "mip_batch_pdlp_strong_branching"
74-
#define CUOPT_MIP_STRONG_BRANCHING_SIMPLEX_ITER_LIMIT "mip_strong_branching_simplex_iter_limit"
75-
#define CUOPT_SOLUTION_FILE "solution_file"
76-
#define CUOPT_NUM_CPU_THREADS "num_cpu_threads"
77-
#define CUOPT_NUM_GPUS "num_gpus"
78-
#define CUOPT_USER_PROBLEM_FILE "user_problem_file"
79-
#define CUOPT_PRESOLVE_FILE "presolve_file"
80-
#define CUOPT_RANDOM_SEED "random_seed"
81-
#define CUOPT_PDLP_PRECISION "pdlp_precision"
23+
#define CUOPT_ABSOLUTE_DUAL_TOLERANCE "absolute_dual_tolerance"
24+
#define CUOPT_RELATIVE_DUAL_TOLERANCE "relative_dual_tolerance"
25+
#define CUOPT_ABSOLUTE_PRIMAL_TOLERANCE "absolute_primal_tolerance"
26+
#define CUOPT_RELATIVE_PRIMAL_TOLERANCE "relative_primal_tolerance"
27+
#define CUOPT_ABSOLUTE_GAP_TOLERANCE "absolute_gap_tolerance"
28+
#define CUOPT_RELATIVE_GAP_TOLERANCE "relative_gap_tolerance"
29+
#define CUOPT_INFEASIBILITY_DETECTION "infeasibility_detection"
30+
#define CUOPT_STRICT_INFEASIBILITY "strict_infeasibility"
31+
#define CUOPT_PRIMAL_INFEASIBLE_TOLERANCE "primal_infeasible_tolerance"
32+
#define CUOPT_DUAL_INFEASIBLE_TOLERANCE "dual_infeasible_tolerance"
33+
#define CUOPT_ITERATION_LIMIT "iteration_limit"
34+
#define CUOPT_TIME_LIMIT "time_limit"
35+
#define CUOPT_WORK_LIMIT "work_limit"
36+
#define CUOPT_PDLP_SOLVER_MODE "pdlp_solver_mode"
37+
#define CUOPT_METHOD "method"
38+
#define CUOPT_PER_CONSTRAINT_RESIDUAL "per_constraint_residual"
39+
#define CUOPT_SAVE_BEST_PRIMAL_SO_FAR "save_best_primal_so_far"
40+
#define CUOPT_FIRST_PRIMAL_FEASIBLE "first_primal_feasible"
41+
#define CUOPT_LOG_FILE "log_file"
42+
#define CUOPT_LOG_TO_CONSOLE "log_to_console"
43+
#define CUOPT_CROSSOVER "crossover"
44+
#define CUOPT_FOLDING "folding"
45+
#define CUOPT_AUGMENTED "augmented"
46+
#define CUOPT_DUALIZE "dualize"
47+
#define CUOPT_ORDERING "ordering"
48+
#define CUOPT_BARRIER_DUAL_INITIAL_POINT "barrier_dual_initial_point"
49+
#define CUOPT_ELIMINATE_DENSE_COLUMNS "eliminate_dense_columns"
50+
#define CUOPT_CUDSS_DETERMINISTIC "cudss_deterministic"
51+
#define CUOPT_PRESOLVE "presolve"
52+
#define CUOPT_DUAL_POSTSOLVE "dual_postsolve"
53+
#define CUOPT_MIP_DETERMINISM_MODE "mip_determinism_mode"
54+
#define CUOPT_MIP_ABSOLUTE_TOLERANCE "mip_absolute_tolerance"
55+
#define CUOPT_MIP_RELATIVE_TOLERANCE "mip_relative_tolerance"
56+
#define CUOPT_MIP_INTEGRALITY_TOLERANCE "mip_integrality_tolerance"
57+
#define CUOPT_MIP_ABSOLUTE_GAP "mip_absolute_gap"
58+
#define CUOPT_MIP_RELATIVE_GAP "mip_relative_gap"
59+
#define CUOPT_MIP_HEURISTICS_ONLY "mip_heuristics_only"
60+
#define CUOPT_MIP_SCALING "mip_scaling"
61+
#define CUOPT_MIP_PRESOLVE "mip_presolve"
62+
#define CUOPT_MIP_RELIABILITY_BRANCHING "mip_reliability_branching"
63+
#define CUOPT_MIP_CUT_PASSES "mip_cut_passes"
64+
#define CUOPT_MIP_MIXED_INTEGER_ROUNDING_CUTS "mip_mixed_integer_rounding_cuts"
65+
#define CUOPT_MIP_MIXED_INTEGER_GOMORY_CUTS "mip_mixed_integer_gomory_cuts"
66+
#define CUOPT_MIP_KNAPSACK_CUTS "mip_knapsack_cuts"
67+
#define CUOPT_MIP_IMPLIED_BOUND_CUTS "mip_implied_bound_cuts"
68+
#define CUOPT_MIP_CLIQUE_CUTS "mip_clique_cuts"
69+
#define CUOPT_MIP_STRONG_CHVATAL_GOMORY_CUTS "mip_strong_chvatal_gomory_cuts"
70+
#define CUOPT_MIP_REDUCED_COST_STRENGTHENING "mip_reduced_cost_strengthening"
71+
#define CUOPT_MIP_CUT_CHANGE_THRESHOLD "mip_cut_change_threshold"
72+
#define CUOPT_MIP_CUT_MIN_ORTHOGONALITY "mip_cut_min_orthogonality"
73+
#define CUOPT_MIP_BATCH_PDLP_STRONG_BRANCHING "mip_batch_pdlp_strong_branching"
74+
#define CUOPT_MIP_BATCH_PDLP_RELIABILITY_BRANCHING "mip_batch_pdlp_reliability_branching"
75+
#define CUOPT_MIP_STRONG_BRANCHING_SIMPLEX_ITERATION_LIMIT \
76+
"mip_strong_branching_simplex_iteration_limit"
77+
#define CUOPT_SOLUTION_FILE "solution_file"
78+
#define CUOPT_NUM_CPU_THREADS "num_cpu_threads"
79+
#define CUOPT_NUM_GPUS "num_gpus"
80+
#define CUOPT_USER_PROBLEM_FILE "user_problem_file"
81+
#define CUOPT_PRESOLVE_FILE "presolve_file"
82+
#define CUOPT_RANDOM_SEED "random_seed"
83+
#define CUOPT_PDLP_PRECISION "pdlp_precision"
8284

8385
/* @brief MIP determinism mode constants */
8486
#define CUOPT_MODE_OPPORTUNISTIC 0

cpp/include/cuopt/linear_programming/mip/solver_settings.hpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,14 @@ class mip_solver_settings_t {
9595
i_t knapsack_cuts = -1;
9696
i_t clique_cuts = -1;
9797
i_t implied_bound_cuts = -1;
98-
i_t strong_chvatal_gomory_cuts = -1;
99-
i_t reduced_cost_strengthening = -1;
100-
f_t cut_change_threshold = -1.0;
101-
f_t cut_min_orthogonality = 0.5;
102-
i_t mip_batch_pdlp_strong_branching = 0;
98+
i_t strong_chvatal_gomory_cuts = -1;
99+
i_t reduced_cost_strengthening = -1;
100+
f_t cut_change_threshold = -1.0;
101+
f_t cut_min_orthogonality = 0.5;
102+
i_t mip_batch_pdlp_strong_branching{
103+
0}; // 0 = DS only, 1 = cooperative DS + PDLP, 2 = batch PDLP only
104+
i_t mip_batch_pdlp_reliability_branching{
105+
0}; // 0 = DS only, 1 = cooperative DS + PDLP, 2 = batch PDLP only
103106
i_t strong_branching_simplex_iteration_limit = -1;
104107
i_t num_gpus = 1;
105108
bool log_to_console = true;

cpp/include/cuopt/linear_programming/pdlp/solver_settings.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
#include <atomic>
2020

21+
#include <cuda/std/span>
22+
2123
namespace cuopt::linear_programming {
2224

2325
// Forward declare solver_settings_t for friend class
@@ -162,6 +164,12 @@ class pdlp_solver_settings_t {
162164
* @param[in] initial_primal_weight Initial primal weight.
163165
*/
164166
void set_initial_primal_weight(f_t initial_primal_weight);
167+
/**
168+
* @brief Set an initial pdlp iteration.
169+
*
170+
* @param[in] initial_pdlp_iteration Initial pdlp iteration.
171+
*/
172+
void set_initial_pdlp_iteration(i_t initial_pdlp_iteration);
165173

166174
/**
167175
* @brief Set the pdlp warm start data. This allows to restart PDLP with a
@@ -228,6 +236,8 @@ class pdlp_solver_settings_t {
228236
std::optional<f_t> get_initial_step_size() const;
229237
// TODO batch mode: tmp
230238
std::optional<f_t> get_initial_primal_weight() const;
239+
// TODO batch mode: tmp
240+
std::optional<i_t> get_initial_pdlp_iteration() const;
231241

232242
const rmm::device_uvector<f_t>& get_initial_primal_solution() const;
233243
const rmm::device_uvector<f_t>& get_initial_dual_solution() const;
@@ -280,6 +290,8 @@ class pdlp_solver_settings_t {
280290
bool inside_mip{false};
281291
// For concurrent termination
282292
std::atomic<int>* concurrent_halt{nullptr};
293+
// Shared strong branching solved flags for cooperative DS + PDLP
294+
cuda::std::span<std::atomic<int>> shared_sb_solved;
283295
static constexpr f_t minimal_absolute_tolerance = 1.0e-12;
284296
pdlp_hyper_params::pdlp_hyper_params_t hyper_params;
285297
// Holds the information of new variable lower and upper bounds for each climber in the format:
@@ -288,6 +300,12 @@ class pdlp_solver_settings_t {
288300
// concurrently i.e. if new_bounds.size() == 2, then 2 versions of the problem with updated bounds
289301
// will be solved concurrently
290302
std::vector<std::tuple<i_t, f_t, f_t>> new_bounds;
303+
// By default to save memory and speed we don't store and copy each climber's primal and dual
304+
// solutions We only retrieve termination statistics and the objective values
305+
bool generate_batch_primal_dual_solution{false};
306+
// Used to force batch PDLP to solve a subbatch of the problems at a time
307+
// The 0 default value will make the solver use its heuristic to determine the subbatch size
308+
i_t sub_batch_size{0};
291309

292310
private:
293311
/** Initial primal solution */
@@ -300,6 +318,9 @@ class pdlp_solver_settings_t {
300318
/** Initial primal weight */
301319
// TODO batch mode: tmp
302320
std::optional<f_t> initial_primal_weight_;
321+
/** Initial pdlp iteration */
322+
// TODO batch mode: tmp
323+
std::optional<i_t> initial_pdlp_iteration_;
303324
/** GPU-backed warm start data (device_uvector), used by C++ API and local GPU solves */
304325
pdlp_warm_start_data_t<i_t, f_t> pdlp_warm_start_data_;
305326
/** Warm start data as spans over external memory, used by Cython/Python interface */

cpp/src/branch_and_bound/branch_and_bound.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,9 @@ branch_variable_t<i_t> branch_and_bound_t<i_t, f_t>::variable_selection(
846846
settings_,
847847
upper_bound_,
848848
worker_pool_.num_idle_workers(),
849-
log);
849+
log,
850+
new_slacks_,
851+
original_lp_);
850852
} else {
851853
branch_var = pc_.variable_selection(fractional, solution, log);
852854
}
@@ -2503,10 +2505,10 @@ mip_status_t branch_and_bound_t<i_t, f_t>::solve(mip_solution_t<i_t, f_t>& solut
25032505
original_lp_.A.transpose(pc_.AT);
25042506
{
25052507
raft::common::nvtx::range scope_sb("BB::strong_branching");
2506-
strong_branching<i_t, f_t>(original_problem_,
2507-
original_lp_,
2508+
strong_branching<i_t, f_t>(original_lp_,
25082509
settings_,
25092510
exploration_stats_.start_time,
2511+
new_slacks_,
25102512
var_types_,
25112513
root_relax_soln_,
25122514
fractional,

0 commit comments

Comments
 (0)