From 1258dee51d96b743cf586770e6613685820a4d77 Mon Sep 17 00:00:00 2001 From: Christopher Maes Date: Tue, 17 Feb 2026 13:18:24 -0800 Subject: [PATCH 1/5] Fix issue with incorrect lower bound on air05 --- cpp/src/branch_and_bound/branch_and_bound.cpp | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index b48c8f89eb..90fc55efa8 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -1317,7 +1317,7 @@ dual::status_t branch_and_bound_t::solve_node_lp( simplex_solver_settings_t lp_settings = settings_; lp_settings.set_log(false); if (original_lp_.objective_is_integral) { - lp_settings.cut_off = std::ceil(upper_bound_ - settings_.integer_tol) - 1 + settings_.dual_tol; + lp_settings.cut_off = std::ceil(upper_bound_ - settings_.integer_tol) + settings_.dual_tol; } else { lp_settings.cut_off = upper_bound_ + settings_.dual_tol; } @@ -1426,7 +1426,7 @@ void branch_and_bound_t::plunge_with(branch_and_bound_worker_tlower_bound = lower_bound; - if (lower_bound > upper_bound || rel_gap < settings_.relative_mip_gap_tol) { + if (lower_bound > upper_bound) { search_tree_.graphviz_node(settings_.log, node_ptr, "cutoff", node_ptr->lower_bound); search_tree_.update(node_ptr, node_status_t::FATHOMED); worker->recompute_basis = true; @@ -1536,7 +1536,7 @@ void branch_and_bound_t::dive_with(branch_and_bound_worker_t f_t rel_gap = user_relative_gap(original_lp_, upper_bound, lower_bound); worker->lower_bound = lower_bound; - if (node_ptr->lower_bound > upper_bound || rel_gap < settings_.relative_mip_gap_tol) { + if (node_ptr->lower_bound > upper_bound) { worker->recompute_basis = true; worker->recompute_bounds = true; continue; @@ -2471,8 +2471,25 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut lower_bound = deterministic_compute_lower_bound(); solver_status_ = deterministic_global_termination_status_; } else { - lower_bound = node_queue_.best_first_queue_size() > 0 ? node_queue_.get_lower_bound() - : search_tree_.root.lower_bound; + if (node_queue_.best_first_queue_size() > 0) { + // We need to clear the queue and use the info in the search tree for the lower bound + while (node_queue_.best_first_queue_size() > 0) { + std::optional*> start_node = node_queue_.pop_best_first(); + + if (!start_node.has_value()) { continue; } + if (upper_bound_ < start_node.value()->lower_bound) { + // This node was put on the heap earlier but its lower bound is now greater than the + // current upper bound + search_tree_.graphviz_node( + settings_.log, start_node.value(), "cutoff", start_node.value()->lower_bound); + search_tree_.update(start_node.value(), node_status_t::FATHOMED); + continue; + } + } + lower_bound = search_tree_.root.lower_bound; + } else { + lower_bound = search_tree_.root.lower_bound; + } } set_final_solution(solution, lower_bound); return solver_status_; @@ -2781,7 +2798,7 @@ void branch_and_bound_t::run_deterministic_bfs_loop( f_t upper_bound = worker.local_upper_bound; f_t rel_gap = user_relative_gap(original_lp_, upper_bound, node->lower_bound); - if (node->lower_bound > upper_bound || rel_gap < settings_.relative_mip_gap_tol) { + if (node->lower_bound > upper_bound) { worker.current_node = nullptr; worker.record_fathomed(node, node->lower_bound); search_tree.update(node, node_status_t::FATHOMED); @@ -3570,8 +3587,7 @@ void branch_and_bound_t::deterministic_dive( // Prune check using snapshot upper bound f_t rel_gap = user_relative_gap(original_lp_, worker.local_upper_bound, node_ptr->lower_bound); - if (node_ptr->lower_bound > worker.local_upper_bound || - rel_gap < settings_.relative_mip_gap_tol) { + if (node_ptr->lower_bound > worker.local_upper_bound) { worker.recompute_bounds_and_basis = true; continue; } From b5c6e942b4f56dbe1084a9355c3a19a20f57f7b7 Mon Sep 17 00:00:00 2001 From: Christopher Maes Date: Tue, 17 Feb 2026 13:57:36 -0800 Subject: [PATCH 2/5] Fix compiler error --- cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu b/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu index bc12fb360f..31916e2c32 100644 --- a/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu +++ b/cpp/src/pdlp/restart_strategy/pdlp_restart_strategy.cu @@ -1995,14 +1995,14 @@ void pdlp_restart_strategy_t::solve_bound_constrained_trust_region( f_t* end = threshold_.data() + primal_size_h_ + dual_size_h_; auto highest_negInf_primal = thrust::find(handle_ptr_->get_thrust_policy(), - thrust::make_reverse_iterator(thrust::device_ptr(end)), - thrust::make_reverse_iterator(thrust::device_ptr(start)), + cuda::std::reverse_iterator(thrust::device_ptr(end)), + cuda::std::reverse_iterator(thrust::device_ptr(start)), -std::numeric_limits::infinity()); // Set ranges accordingly i_t index_start_primal = 0; i_t index_end_primal = primal_size_h_ + dual_size_h_; - if (highest_negInf_primal != thrust::make_reverse_iterator(thrust::device_ptr(start))) { + if (highest_negInf_primal != cuda::std::reverse_iterator(thrust::device_ptr(start))) { cuopt_assert(device_to_host_value(thrust::raw_pointer_cast(&*highest_negInf_primal)) == -std::numeric_limits::infinity(), "Incorrect primal reverse iterator"); From c8324c18028ed6617fee7b57f39d8261091b849c Mon Sep 17 00:00:00 2001 From: Christopher Maes Date: Tue, 17 Feb 2026 14:59:39 -0800 Subject: [PATCH 3/5] Handle case where some nodes should still be in the queue --- cpp/src/branch_and_bound/branch_and_bound.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 90fc55efa8..3acd800097 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -2484,9 +2484,11 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut settings_.log, start_node.value(), "cutoff", start_node.value()->lower_bound); search_tree_.update(start_node.value(), node_status_t::FATHOMED); continue; + } else { + break; } } - lower_bound = search_tree_.root.lower_bound; + lower_bound = node_queue_.best_first_queue_size() > 0 ? node_queue_.get_lower_bound() : search_tree_.root.lower_bound; } else { lower_bound = search_tree_.root.lower_bound; } From 99a8bb5675fd17a86423d7911ac35c9d592da5e6 Mon Sep 17 00:00:00 2001 From: Christopher Maes Date: Tue, 17 Feb 2026 15:00:06 -0800 Subject: [PATCH 4/5] Style fixes --- cpp/src/branch_and_bound/branch_and_bound.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index 3acd800097..b94d7e7384 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -2488,7 +2488,8 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut break; } } - lower_bound = node_queue_.best_first_queue_size() > 0 ? node_queue_.get_lower_bound() : search_tree_.root.lower_bound; + lower_bound = node_queue_.best_first_queue_size() > 0 ? node_queue_.get_lower_bound() + : search_tree_.root.lower_bound; } else { lower_bound = search_tree_.root.lower_bound; } From 662efbcd4000c9a8c76e2ba7f18ee7523a2896ba Mon Sep 17 00:00:00 2001 From: Christopher Maes Date: Tue, 17 Feb 2026 15:16:31 -0800 Subject: [PATCH 5/5] Don't loose the last lower bound popped --- cpp/src/branch_and_bound/branch_and_bound.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/src/branch_and_bound/branch_and_bound.cpp b/cpp/src/branch_and_bound/branch_and_bound.cpp index b94d7e7384..436920fd14 100644 --- a/cpp/src/branch_and_bound/branch_and_bound.cpp +++ b/cpp/src/branch_and_bound/branch_and_bound.cpp @@ -2485,6 +2485,8 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut search_tree_.update(start_node.value(), node_status_t::FATHOMED); continue; } else { + node_queue_.push( + start_node.value()); // Needed to ensure we don't lose the correct lower bound break; } }