Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
156 commits
Select commit Hold shift + click to select a range
181aef4
added a dual simplex method for calculating the phase 2 from an exist…
nguidotti Sep 9, 2025
09a5e7e
child nodes now reuse the bounds of the parent. fixed incorrect bound…
nguidotti Oct 6, 2025
0a7a7eb
refactor based on the reuse-parent-branch
nguidotti Oct 9, 2025
7997b12
fixed value of the bounds_change for the child
nguidotti Oct 9, 2025
2688a87
Merge remote-tracking branch 'cuopt/branch-25.10' into dual-simplex-u…
nguidotti Oct 9, 2025
0cb0dcf
changed dual simplex to keep the `basis_update_mpf_t` for future use
nguidotti Oct 9, 2025
21d4bcf
Merge remote-tracking branch 'cuopt/branch-25.10' into bounds-propaga…
nguidotti Oct 9, 2025
316d14f
Merge remote-tracking branch 'cuopt/branch-25.10' into dual-simplex-u…
nguidotti Oct 9, 2025
9368feb
Merge branch 'fix-diving-bounds' into bounds-propagation
nguidotti Oct 10, 2025
5a5090c
Merge remote-tracking branch 'cuopt/branch-25.10' into bounds-propaga…
nguidotti Oct 11, 2025
7cadb8a
Merge branch 'branch-25.10' into dual-simplex-update-basis
nguidotti Oct 11, 2025
80aa125
Merge branch 'branch-25.10' into dual-simplex-update-basis
nguidotti Oct 13, 2025
6ec4806
child nodes can reuse the basis factorization from the parent
nguidotti Oct 13, 2025
861a12e
Merge branch 'branch-25.10' into bounds-propagation
nguidotti Oct 13, 2025
95de283
revert refactor
nguidotti Oct 13, 2025
f44b134
fix incorrect bounds when two nodes shares the same branch variable
nguidotti Oct 13, 2025
1cedfd6
small refactor
nguidotti Oct 13, 2025
1ca6055
fix incorrect thread type in diving
nguidotti Oct 14, 2025
320f162
Merge branch 'branch-25.10' into bounds-propagation
nguidotti Oct 14, 2025
517b54a
Merge branch 'dual-simplex-update-basis' into reuse-basis-factorization
nguidotti Oct 14, 2025
42daa5b
Merge branch 'branch-25.10' into reuse-basis-factorization
nguidotti Oct 14, 2025
bfeb660
constraints_changed is only set for branched variables. refactor boun…
nguidotti Oct 14, 2025
42dee91
Merge branch 'branch-25.10' into bounds-propagation
nguidotti Oct 14, 2025
73b7ad1
Merge branch 'main' into dual-simplex-update-basis
nguidotti Oct 22, 2025
ccb41eb
Merge branch 'main' into reuse-basis-factorization
nguidotti Oct 22, 2025
f954dcf
Merge branch 'branch-25.10' into bounds-propagation
nguidotti Oct 22, 2025
222f95b
Merge branch 'main' into bounds-propagation
nguidotti Oct 22, 2025
b5ed956
small refactor
nguidotti Oct 22, 2025
abeb2cf
fix log spacing
nguidotti Oct 22, 2025
3237913
fix log spacing for repaired solutions
nguidotti Oct 22, 2025
5768f50
fix incorrect matrix dimension in LU after LP presolve
nguidotti Oct 22, 2025
8155322
Merge branch 'dual-simplex-update-basis' into reuse-basis-factorization
nguidotti Oct 22, 2025
f9853b2
initialize the presolver only once per thread
nguidotti Oct 22, 2025
0ed38a9
Refactor, simplify interface, and reduce number of code changes
chris-maes Oct 22, 2025
166c446
Further removal; missing when cherry-picking previous commit
chris-maes Oct 22, 2025
27884e4
Rename functions. Address code-rabbit issues
chris-maes Oct 22, 2025
deffe68
Style fixes
chris-maes Oct 22, 2025
f779cb7
Remove confusing col_permutation_ from middle-product update
chris-maes Oct 23, 2025
ad756d1
Merge branch 'main' into dual-simplex-update-basis
nguidotti Oct 23, 2025
f669f64
removed unused variable
nguidotti Oct 23, 2025
8a06d3e
added missing function declaration
nguidotti Oct 23, 2025
6ccb6d9
fixed race condition
nguidotti Oct 23, 2025
8f55932
small refactor
nguidotti Oct 23, 2025
7f337d4
fixed missing template parameters
nguidotti Oct 23, 2025
06ca86d
fixed crash
nguidotti Oct 23, 2025
b5fc52c
added asserts
nguidotti Oct 23, 2025
d0b5501
Merge branch 'main' into bounds-propagation
nguidotti Oct 23, 2025
f7a38bc
fix compilation error
nguidotti Oct 23, 2025
649c8ca
Update cpp/src/dual_simplex/mip_node.hpp
nguidotti Oct 24, 2025
4afb71d
addresses reviewer's comments
nguidotti Oct 24, 2025
1c92230
fix style
nguidotti Oct 24, 2025
7d3f392
Merge branch 'main' into bounds-propagation
nguidotti Oct 24, 2025
b285251
Merge branch 'dual-simplex-update-basis' into reuse-basis-factorization
nguidotti Oct 24, 2025
a98c792
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Oct 24, 2025
bbd410b
small refactor. added missing recompute flag to diving.
nguidotti Oct 24, 2025
8c420c4
small fixes
nguidotti Oct 24, 2025
5bd70e1
small fix
nguidotti Oct 24, 2025
56747e5
fix logs
nguidotti Oct 24, 2025
c7facc6
fixed log
nguidotti Oct 24, 2025
e26c8d7
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Oct 24, 2025
28f55f0
fixed bugs after merge
nguidotti Oct 24, 2025
eae9ca5
fixed crash due to incorrect nonbasic list
nguidotti Oct 24, 2025
382cbe8
fixed diving queue. added asserts.
nguidotti Oct 24, 2025
8c2e06e
fixed typo
nguidotti Oct 24, 2025
12eb12d
minimize code changes
nguidotti Oct 27, 2025
a1caad8
reserve memory space for nonbasic list
nguidotti Oct 27, 2025
8b294be
fixed starting bounds for diving nodes. replaced rounding direction w…
nguidotti Oct 27, 2025
27f9264
fix missing enum
nguidotti Oct 28, 2025
bf142d5
fixed typo
nguidotti Oct 28, 2025
65981b4
Merge branch 'main' into bounds-propagation
nguidotti Oct 28, 2025
6f77e1d
Merge branch 'main' into bounds-propagation
nguidotti Oct 29, 2025
64d2744
Merge branch 'main' into reuse-basis-factorization
nguidotti Oct 29, 2025
b8e295d
adjusting refactor frequency
nguidotti Oct 29, 2025
8a00e93
removed debug leftover
nguidotti Oct 29, 2025
5b23554
fixed incorrect reporting of an infeasible solution due to timeout
nguidotti Oct 29, 2025
42e583c
Merge branch 'main' into reuse-basis-factorization
nguidotti Oct 29, 2025
971380c
Merge branch 'main' into reuse-basis-factorization
nguidotti Oct 29, 2025
40b2bc9
fix compilation issue for solve_MIP. removed debug leftover
nguidotti Oct 30, 2025
d891e59
Merge branch 'fix-compilation-issue' into reuse-basis-factorization
nguidotti Oct 30, 2025
28ad838
fixed incorrect infeasibility report
nguidotti Oct 30, 2025
6830648
Merge branch 'main' into reuse-basis-factorization
nguidotti Oct 30, 2025
de4e292
Merge branch 'main' into bounds-propagation
nguidotti Oct 31, 2025
cc89861
renamed files and classes
nguidotti Oct 31, 2025
9951513
Merge branch 'main' into reuse-basis-factorization
nguidotti Oct 31, 2025
f152b25
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Oct 31, 2025
874d45f
fix incorrect report of infeasible solution
nguidotti Oct 31, 2025
5bfda56
fixed typo
nguidotti Oct 31, 2025
d71f910
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Oct 31, 2025
04e3063
fixed typo
nguidotti Oct 31, 2025
2d79969
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Oct 31, 2025
1df3b4c
rename variable
nguidotti Nov 3, 2025
2c21c35
Merge branch 'main' into bounds-propagation
nguidotti Nov 3, 2025
093c7a7
Merge branch 'main' into reuse-basis-factorization
nguidotti Nov 3, 2025
d94220b
rename variable
nguidotti Nov 3, 2025
2306ab8
revert refactor frequency change
nguidotti Nov 6, 2025
1b05911
Merge branch 'main' into reuse-basis-factorization
nguidotti Nov 6, 2025
5ec4472
addresses reviewer's comments
nguidotti Nov 10, 2025
015bb00
fixed incorrect stats update
nguidotti Nov 10, 2025
9cfddeb
Merge branch 'main' into bounds-propagation
nguidotti Nov 10, 2025
9d7e486
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Nov 10, 2025
8b762e7
addressing reviewer's comments
nguidotti Nov 10, 2025
4da2c9d
variable renaming
nguidotti Nov 10, 2025
ac199a3
fix typo and styling
nguidotti Nov 10, 2025
59401be
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Nov 10, 2025
770adfb
small refactor
nguidotti Nov 10, 2025
588b6d4
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Nov 10, 2025
3317eaa
fix missing initialization
nguidotti Nov 10, 2025
145f9fc
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Nov 10, 2025
78a4753
Merge branch 'main' into bounds-propagation
nguidotti Nov 11, 2025
8cf8b76
Merge branch 'main' into reuse-basis-factorization
nguidotti Nov 11, 2025
fcfc709
Merge branch 'main' into bounds-propagation
nguidotti Nov 11, 2025
5c2e990
Merge remote-tracking branch 'origin/bounds-propagation' into reuse-b…
nguidotti Nov 11, 2025
a0f8e13
fixed bugs after merge
nguidotti Nov 11, 2025
fb61b44
Merge remote-tracking branch 'origin/bounds-propagation' into reuse-b…
nguidotti Nov 11, 2025
7077dbc
revert parameters to their original values
nguidotti Nov 11, 2025
eb53706
Merge remote-tracking branch 'origin/bounds-propagation' into reuse-b…
nguidotti Nov 11, 2025
d9a45cf
fixing crashes
nguidotti Nov 11, 2025
dbd413d
Merge commit 'd9a45cf1' into bounds-propagation
nguidotti Nov 11, 2025
58c70ac
disable RINS logs
nguidotti Nov 12, 2025
a881b70
tighten tolerance for refactoring
nguidotti Nov 12, 2025
4947fe1
further tighten the tolerance
nguidotti Nov 12, 2025
e763a97
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Nov 13, 2025
0292a13
Merge branch 'main' into reuse-basis-factorization
nguidotti Nov 13, 2025
c36b6d3
Merge branch 'main' into bounds-propagation
nguidotti Nov 13, 2025
5e3cd26
changed refactor tolerances. updated logger to support different modes.
nguidotti Nov 14, 2025
74ef1ba
tighten tolerance for refactoring the basis. disabled RINS logs.
nguidotti Nov 12, 2025
2cadc89
Merge branch 'refactor-tolerance' into bounds-propagation
nguidotti Nov 14, 2025
4ee0250
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Nov 14, 2025
0911341
Merge branch 'main' into refactor-tolerance
nguidotti Nov 17, 2025
64d1b9f
Merge branch 'release/25.12' into refactor-tolerance
nguidotti Nov 18, 2025
669fdbe
add support for building with clang
aliceb-nv Nov 18, 2025
9975fdf
remove debug calls
aliceb-nv Nov 18, 2025
560b402
Merge branch 'release/25.12' into bounds-propagation
nguidotti Nov 18, 2025
f07cc85
Merge branch 'release/25.12' into reuse-basis-factorization
nguidotti Nov 18, 2025
f627a45
replaced mutexes with call to update_tree method
nguidotti Nov 18, 2025
987b75c
Merge branch 'clang-tsan' into bounds-propagation
nguidotti Nov 18, 2025
12cb00e
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Nov 18, 2025
5e1e74d
Merge branch 'release/25.12' into reuse-basis-factorization
nguidotti Nov 19, 2025
1bcc57f
added missing workspace cleaning
nguidotti Nov 19, 2025
7a482fc
Merge branch 'release/25.12' into refactor-tolerance
nguidotti Nov 20, 2025
2cdda9a
Merge branch 'release/25.12' into reuse-basis-factorization
nguidotti Nov 20, 2025
f687ce4
small refactor
nguidotti Nov 20, 2025
d94228e
Merge branch 'release/25.12' into bounds-propagation
nguidotti Nov 20, 2025
11bf8ae
Merge branch 'bounds-propagation' into reuse-basis-factorization
nguidotti Nov 20, 2025
1048327
fixed crash due to selecting the incorrect last column
nguidotti Nov 20, 2025
5daba67
Merge branch 'refactor-tolerance' into reuse-basis-factorization
nguidotti Nov 20, 2025
8b4546e
reverting fix
nguidotti Nov 20, 2025
3240f88
fixed missing vector if the matrix is rank deficient and there is no …
nguidotti Nov 20, 2025
6b6c410
Merge branch 'refactor-tolerance' into reuse-basis-factorization
nguidotti Nov 20, 2025
1f76ad0
Merge branch 'release/25.12' into reuse-basis-factorization
nguidotti Nov 21, 2025
8b879a3
removed debug leftover
nguidotti Nov 21, 2025
e7f6bdb
Merge remote-tracking branch 'cuopt/release/25.12' into reuse-basis-f…
nguidotti Nov 24, 2025
a8772a2
removing unrelated changes
nguidotti Nov 24, 2025
354f4f7
fix compilation issue
nguidotti Nov 24, 2025
e54fc9e
fix missing initialization of the bounds_changed variable
nguidotti Nov 24, 2025
97a14dd
disabled column scaling
nguidotti Nov 24, 2025
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
117 changes: 85 additions & 32 deletions cpp/src/dual_simplex/branch_and_bound.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -579,9 +579,12 @@ node_solve_info_t branch_and_bound_t<i_t, f_t>::solve_node(
mip_node_t<i_t, f_t>* node_ptr,
search_tree_t<i_t, f_t>& search_tree,
lp_problem_t<i_t, f_t>& leaf_problem,
basis_update_mpf_t<i_t, f_t>& basis_factors,
std::vector<i_t>& basic_list,
std::vector<i_t>& nonbasic_list,
bounds_strengthening_t<i_t, f_t>& node_presolver,
thread_type_t thread_type,
bool recompute_bounds,
bool recompute_bounds_and_basis,
const std::vector<f_t>& root_lower,
const std::vector<f_t>& root_upper,
logger_t& log)
Expand All @@ -595,9 +598,10 @@ node_solve_info_t branch_and_bound_t<i_t, f_t>::solve_node(

simplex_solver_settings_t lp_settings = settings_;
lp_settings.set_log(false);
lp_settings.cut_off = upper_bound + settings_.dual_tol;
lp_settings.inside_mip = 2;
lp_settings.time_limit = settings_.time_limit - toc(exploration_stats_.start_time);
lp_settings.cut_off = upper_bound + settings_.dual_tol;
lp_settings.inside_mip = 2;
lp_settings.time_limit = settings_.time_limit - toc(exploration_stats_.start_time);
lp_settings.scale_columns = false;

#ifdef LOG_NODE_SIMPLEX
lp_settings.set_log(true);
Expand All @@ -623,7 +627,7 @@ node_solve_info_t branch_and_bound_t<i_t, f_t>::solve_node(
std::fill(node_presolver.bounds_changed.begin(), node_presolver.bounds_changed.end(), false);

// Set the correct bounds for the leaf problem
if (recompute_bounds) {
if (recompute_bounds_and_basis) {
leaf_problem.lower = root_lower;
leaf_problem.upper = root_upper;
node_ptr->get_variable_bounds(
Expand All @@ -644,20 +648,32 @@ node_solve_info_t branch_and_bound_t<i_t, f_t>::solve_node(
f_t lp_start_time = tic();
std::vector<f_t> leaf_edge_norms = edge_norms_; // = node.steepest_edge_norms;

lp_status = dual_phase2(2,
0,
lp_start_time,
leaf_problem,
lp_settings,
leaf_vstatus,
leaf_solution,
node_iter,
leaf_edge_norms);
lp_status = dual_phase2_with_advanced_basis(2,
0,
recompute_bounds_and_basis,
lp_start_time,
leaf_problem,
lp_settings,
leaf_vstatus,
basis_factors,
basic_list,
nonbasic_list,
leaf_solution,
node_iter,
leaf_edge_norms);

if (lp_status == dual::status_t::NUMERICAL) {
log.printf("Numerical issue node %d. Resolving from scratch.\n", node_ptr->node_id);
lp_status_t second_status = solve_linear_program_advanced(
leaf_problem, lp_start_time, lp_settings, leaf_solution, leaf_vstatus, leaf_edge_norms);
lp_status_t second_status = solve_linear_program_with_advanced_basis(leaf_problem,
lp_start_time,
lp_settings,
leaf_solution,
basis_factors,
basic_list,
nonbasic_list,
leaf_vstatus,
leaf_edge_norms);

lp_status = convert_lp_status_to_dual_status(second_status);
}

Expand Down Expand Up @@ -714,7 +730,7 @@ node_solve_info_t branch_and_bound_t<i_t, f_t>::solve_node(
assert(leaf_vstatus.size() == leaf_problem.num_cols);
search_tree.branch(
node_ptr, branch_var, leaf_solution.x[branch_var], leaf_vstatus, leaf_problem, log);
node_ptr->status = node_status_t::HAS_CHILDREN;
search_tree.update(node_ptr, node_status_t::HAS_CHILDREN);

rounding_direction_t round_dir = child_selection(node_ptr);

Expand All @@ -735,7 +751,7 @@ node_solve_info_t branch_and_bound_t<i_t, f_t>::solve_node(

} else {
if (thread_type == thread_type_t::EXPLORATION) {
lower_bound_ceiling_.fetch_min(node_ptr->lower_bound);
fetch_min(lower_bound_ceiling_, node_ptr->lower_bound);
log.printf(
"LP returned status %d on node %d. This indicates a numerical issue. The best bound is set "
"to "
Expand Down Expand Up @@ -819,9 +835,17 @@ void branch_and_bound_t<i_t, f_t>::exploration_ramp_up(mip_node_t<i_t, f_t>* nod
std::vector<char> row_sense;
bounds_strengthening_t<i_t, f_t> node_presolver(leaf_problem, Arow, row_sense, var_types_);

const i_t m = leaf_problem.num_rows;
basis_update_mpf_t<i_t, f_t> basis_factors(m, settings_.refactor_frequency);
std::vector<i_t> basic_list(m);
std::vector<i_t> nonbasic_list;

node_solve_info_t status = solve_node(node,
*search_tree,
leaf_problem,
basis_factors,
basic_list,
nonbasic_list,
node_presolver,
thread_type_t::EXPLORATION,
true,
Expand Down Expand Up @@ -859,9 +883,12 @@ void branch_and_bound_t<i_t, f_t>::explore_subtree(i_t task_id,
mip_node_t<i_t, f_t>* start_node,
search_tree_t<i_t, f_t>& search_tree,
lp_problem_t<i_t, f_t>& leaf_problem,
bounds_strengthening_t<i_t, f_t>& node_presolver)
bounds_strengthening_t<i_t, f_t>& node_presolver,
basis_update_mpf_t<i_t, f_t>& basis_factors,
std::vector<i_t>& basic_list,
std::vector<i_t>& nonbasic_list)
{
bool recompute_bounds = true;
bool recompute_bounds_and_basis = true;
std::deque<mip_node_t<i_t, f_t>*> stack;
stack.push_front(start_node);

Expand Down Expand Up @@ -891,7 +918,7 @@ void branch_and_bound_t<i_t, f_t>::explore_subtree(i_t task_id,
if (lower_bound > upper_bound || rel_gap < settings_.relative_mip_gap_tol) {
search_tree.graphviz_node(settings_.log, node_ptr, "cutoff", node_ptr->lower_bound);
search_tree.update(node_ptr, node_status_t::FATHOMED);
recompute_bounds = true;
recompute_bounds_and_basis = true;
continue;
}

Expand Down Expand Up @@ -935,14 +962,17 @@ void branch_and_bound_t<i_t, f_t>::explore_subtree(i_t task_id,
node_solve_info_t status = solve_node(node_ptr,
search_tree,
leaf_problem,
basis_factors,
basic_list,
nonbasic_list,
node_presolver,
thread_type_t::EXPLORATION,
recompute_bounds,
recompute_bounds_and_basis,
original_lp_.lower,
original_lp_.upper,
settings_.log);

recompute_bounds = !has_children(status);
recompute_bounds_and_basis = !has_children(status);

if (status == node_solve_info_t::TIME_LIMIT) {
solver_status_ = mip_exploration_status_t::TIME_LIMIT;
Expand All @@ -966,6 +996,8 @@ void branch_and_bound_t<i_t, f_t>::explore_subtree(i_t task_id,
if (get_heap_size() > settings_.num_bfs_threads) {
std::vector<f_t> lower = original_lp_.lower;
std::vector<f_t> upper = original_lp_.upper;
std::fill(
node_presolver.bounds_changed.begin(), node_presolver.bounds_changed.end(), false);
node->get_variable_bounds(lower, upper, node_presolver.bounds_changed);

mutex_dive_queue_.lock();
Expand Down Expand Up @@ -1006,6 +1038,11 @@ void branch_and_bound_t<i_t, f_t>::best_first_thread(i_t task_id,
std::vector<char> row_sense;
bounds_strengthening_t<i_t, f_t> node_presolver(leaf_problem, Arow, row_sense, var_types_);

const i_t m = leaf_problem.num_rows;
basis_update_mpf_t<i_t, f_t> basis_factors(m, settings_.refactor_frequency);
std::vector<i_t> basic_list(m);
std::vector<i_t> nonbasic_list;

while (solver_status_ == mip_exploration_status_t::RUNNING &&
abs_gap > settings_.absolute_mip_gap_tol && rel_gap > settings_.relative_mip_gap_tol &&
(active_subtrees_ > 0 || get_heap_size() > 0)) {
Expand All @@ -1031,7 +1068,15 @@ void branch_and_bound_t<i_t, f_t>::best_first_thread(i_t task_id,
}

// Best-first search with plunging
explore_subtree(task_id, start_node, search_tree, leaf_problem, node_presolver);
explore_subtree(task_id,
start_node,
search_tree,
leaf_problem,
node_presolver,
basis_factors,
basic_list,
nonbasic_list);

active_subtrees_--;
}

Expand All @@ -1057,12 +1102,16 @@ void branch_and_bound_t<i_t, f_t>::diving_thread(const csr_matrix_t<i_t, f_t>& A
{
logger_t log;
log.log = false;

// Make a copy of the original LP. We will modify its bounds at each leaf
lp_problem_t<i_t, f_t> leaf_problem = original_lp_;
std::vector<char> row_sense;
bounds_strengthening_t<i_t, f_t> node_presolver(leaf_problem, Arow, row_sense, var_types_);

const i_t m = leaf_problem.num_rows;
basis_update_mpf_t<i_t, f_t> basis_factors(m, settings_.refactor_frequency);
std::vector<i_t> basic_list(m);
std::vector<i_t> nonbasic_list;

while (solver_status_ == mip_exploration_status_t::RUNNING &&
(active_subtrees_ > 0 || get_heap_size() > 0)) {
std::optional<diving_root_t<i_t, f_t>> start_node;
Expand All @@ -1074,7 +1123,7 @@ void branch_and_bound_t<i_t, f_t>::diving_thread(const csr_matrix_t<i_t, f_t>& A
if (start_node.has_value()) {
if (get_upper_bound() < start_node->node.lower_bound) { continue; }

bool recompute_bounds = true;
bool recompute_bounds_and_basis = true;
search_tree_t<i_t, f_t> subtree(std::move(start_node->node));
std::deque<mip_node_t<i_t, f_t>*> stack;
stack.push_front(&subtree.root);
Expand All @@ -1086,7 +1135,7 @@ void branch_and_bound_t<i_t, f_t>::diving_thread(const csr_matrix_t<i_t, f_t>& A
f_t rel_gap = user_relative_gap(original_lp_, upper_bound, node_ptr->lower_bound);

if (node_ptr->lower_bound > upper_bound || rel_gap < settings_.relative_mip_gap_tol) {
recompute_bounds = true;
recompute_bounds_and_basis = true;
continue;
}

Expand All @@ -1095,14 +1144,17 @@ void branch_and_bound_t<i_t, f_t>::diving_thread(const csr_matrix_t<i_t, f_t>& A
node_solve_info_t status = solve_node(node_ptr,
subtree,
leaf_problem,
basis_factors,
basic_list,
nonbasic_list,
node_presolver,
thread_type_t::DIVING,
recompute_bounds,
recompute_bounds_and_basis,
start_node->lower,
start_node->upper,
log);

recompute_bounds = !has_children(status);
recompute_bounds_and_basis = !has_children(status);

if (status == node_solve_info_t::TIME_LIMIT) {
solver_status_ = mip_exploration_status_t::TIME_LIMIT;
Expand All @@ -1123,17 +1175,18 @@ void branch_and_bound_t<i_t, f_t>::diving_thread(const csr_matrix_t<i_t, f_t>& A
// best first search, then we split the current subtree at the
// lowest possible point and move to the queue, so it can
// be picked by another thread.
if (diving_queue_.size() < min_diving_queue_size_) {
if (std::lock_guard<omp_mutex_t> lock(mutex_dive_queue_);
diving_queue_.size() < min_diving_queue_size_) {
mip_node_t<i_t, f_t>* new_node = stack.back();
stack.pop_back();

std::vector<f_t> lower = start_node->lower;
std::vector<f_t> upper = start_node->upper;
std::fill(
node_presolver.bounds_changed.begin(), node_presolver.bounds_changed.end(), false);
new_node->get_variable_bounds(lower, upper, node_presolver.bounds_changed);

mutex_dive_queue_.lock();
diving_queue_.emplace(new_node->detach_copy(), std::move(lower), std::move(upper));
mutex_dive_queue_.unlock();
}
}
}
Expand Down
12 changes: 9 additions & 3 deletions cpp/src/dual_simplex/branch_and_bound.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ class branch_and_bound_t {
omp_atomic_t<f_t> total_lp_iters = 0;

// This should only be used by the main thread
f_t last_log = 0.0;
omp_atomic_t<f_t> last_log = 0.0;
omp_atomic_t<i_t> nodes_since_last_log = 0;
} exploration_stats_;

Expand Down Expand Up @@ -188,7 +188,10 @@ class branch_and_bound_t {
mip_node_t<i_t, f_t>* start_node,
search_tree_t<i_t, f_t>& search_tree,
lp_problem_t<i_t, f_t>& leaf_problem,
bounds_strengthening_t<i_t, f_t>& node_presolver);
bounds_strengthening_t<i_t, f_t>& node_presolver,
basis_update_mpf_t<i_t, f_t>& basis_update,
std::vector<i_t>& basic_list,
std::vector<i_t>& nonbasic_list);

// Each "main" thread pops a node from the global heap and then performs a plunge
// (i.e., a shallow dive) into the subtree determined by the node.
Expand All @@ -204,9 +207,12 @@ class branch_and_bound_t {
node_solve_info_t solve_node(mip_node_t<i_t, f_t>* node_ptr,
search_tree_t<i_t, f_t>& search_tree,
lp_problem_t<i_t, f_t>& leaf_problem,
basis_update_mpf_t<i_t, f_t>& basis_factors,
std::vector<i_t>& basic_list,
std::vector<i_t>& nonbasic_list,
bounds_strengthening_t<i_t, f_t>& node_presolver,
thread_type_t thread_type,
bool recompute,
bool recompute_basis_and_bounds,
const std::vector<f_t>& root_lower,
const std::vector<f_t>& root_upper,
logger_t& log);
Expand Down
2 changes: 2 additions & 0 deletions cpp/src/dual_simplex/phase2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2235,6 +2235,8 @@ dual::status_t dual_phase2_with_advanced_basis(i_t phase,
phase2::bound_info(lp, settings);
if (initialize_basis) {
std::vector<i_t> superbasic_list;
nonbasic_list.clear();
nonbasic_list.reserve(n - m);
get_basis_from_vstatus(m, vstatus, basic_list, nonbasic_list, superbasic_list);
assert(superbasic_list.size() == 0);
assert(nonbasic_list.size() == n - m);
Expand Down
48 changes: 28 additions & 20 deletions cpp/src/utilities/omp_helpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,38 +91,46 @@ class omp_atomic_t {

T fetch_sub(T inc) { return fetch_add(-inc); }

private:
T val;

#ifndef __NVCC__
friend double fetch_min(omp_atomic_t<double>& atomic_var, double other);
friend double fetch_max(omp_atomic_t<double>& atomic_var, double other);
#endif
};

// Atomic CAS are only supported in OpenMP v5.1
// (gcc 12+ or clang 14+), however, nvcc (or the host compiler) cannot
// parse it correctly yet
#ifndef __NVCC__

T fetch_min(T other)
{
T old;
// Free non-template functions are necessary because of a clang 20 bug
// when omp atomic compare is used within a templated context.
// see https://github.com/llvm/llvm-project/issues/127466
inline double fetch_min(omp_atomic_t<double>& atomic_var, double other)
{
double old;
#pragma omp atomic compare capture
{
old = val;
val = other < val ? other : val;
}
return old;
{
old = atomic_var.val;
if (other < atomic_var.val) { atomic_var.val = other; }
}
return old;
}

T fetch_max(T other)
{
T old;
inline double fetch_max(omp_atomic_t<double>& atomic_var, double other)
{
double old;
#pragma omp atomic compare capture
{
old = val;
val = other > val ? other : val;
}
return old;
{
old = atomic_var.val;
if (other > atomic_var.val) { atomic_var.val = other; }
}
return old;
}
#endif

private:
T val;
};

#endif

} // namespace cuopt