-
Notifications
You must be signed in to change notification settings - Fork 222
[Draft] Move root relaxation solve call to Branch and Bound #1782
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |
| /* clang-format on */ | ||
|
|
||
| #include <branch_and_bound/branch_and_bound.hpp> | ||
| #include <branch_and_bound/concurrent_root_solver.hpp> | ||
| #include <branch_and_bound/diving_heuristics.hpp> | ||
| #include <branch_and_bound/mip_node.hpp> | ||
| #include <branch_and_bound/pseudo_costs.hpp> | ||
|
|
@@ -2808,9 +2809,53 @@ lp_status_t branch_and_bound_t<i_t, f_t>::solve_root_relaxation( | |
| root_vstatus_, | ||
| edge_norms_, | ||
| nullptr); | ||
| // Dual simplex has finished; stop the GPU competitors if they are still running. | ||
| gpu_root_concurrent_halt_.store(1, std::memory_order_release); | ||
| } | ||
|
|
||
| // Wait for the root relaxation solution to be sent by the diversity manager or dual simplex | ||
| // The diversity manager prepares the GPU problem while dual simplex starts on the CPU. | ||
| // Once the GPU problem is ready, launch PDLP and barrier from here so all root-LP | ||
| // competitors are owned by this function. | ||
| while (!concurrent_root_problem_ready_.load(std::memory_order_acquire) && | ||
| *get_root_concurrent_halt() == 0) { | ||
| std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||
| #pragma omp taskyield | ||
| } | ||
|
|
||
| if (*get_root_concurrent_halt() == 0 && | ||
| concurrent_root_problem_ready_.load(std::memory_order_acquire)) { | ||
| cuopt_assert(concurrent_root_problem_ != nullptr, "Concurrent root problem is not configured"); | ||
| gpu_root_concurrent_halt_.store(0, std::memory_order_release); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win This store erases the cancellation signal from dual simplex. The dual-simplex task sets Do not reset the flag. Instead, check it and skip the GPU solve when it is already set. 🐛 Proposed fix- gpu_root_concurrent_halt_.store(0, std::memory_order_release);
+ if (gpu_root_concurrent_halt_.load(std::memory_order_acquire) != 0) {
+ // Dual simplex already finished; do not start the GPU root solve.
+ return_early_or_skip = true;
+ }Initialize 🤖 Prompt for AI Agents |
||
| try { | ||
| cuopt_assert(concurrent_root_settings_ != nullptr, | ||
| "Concurrent root settings are not configured"); | ||
| const f_t remaining_time = | ||
| std::max<f_t>(settings_.time_limit - toc(exploration_stats_.start_time), 0); | ||
| const f_t root_time_limit = | ||
| std::min(concurrent_root_max_time_, remaining_time * concurrent_root_time_ratio_); | ||
| auto result = solve_concurrent_root_relaxation(concurrent_root_problem_, | ||
| *concurrent_root_settings_, | ||
| root_time_limit, | ||
| &gpu_root_concurrent_halt_); | ||
| if (result.usable) { | ||
| set_root_relaxation_solution(result.primal, | ||
| result.dual, | ||
| result.reduced_cost, | ||
| result.solver_objective, | ||
| result.user_objective, | ||
| result.iterations, | ||
| result.method); | ||
| // Same as the old diversity-manager path: an Optimal GPU root LP is a | ||
| // valid MIP dual bound even if dual simplex / crossover has not finished. | ||
| if (result.optimal) { update_user_bound(result.solver_objective); } | ||
| } | ||
| } catch (const std::exception& e) { | ||
| settings_.log.printf("Concurrent GPU root LP failed: %s\n", e.what()); | ||
| } | ||
| } | ||
|
Comment on lines
+2825
to
+2855
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift The root relaxation can hang when the GPU solve returns no usable result. After this block, control reaches the wait loop at lines 2856-2860. That loop exits only when In the B&B-owned path neither happens when the GPU solve fails:
The B&B thread then spins forever and the diversity manager waits on it. Add an explicit completion signal from the dual-simplex task and make the wait loop observe it. 🐛 Proposed fix sketchAdd a member // Dual simplex has finished; stop the GPU competitors if they are still running.
gpu_root_concurrent_halt_.store(1, std::memory_order_release);
+ dual_simplex_root_done_.store(true, std::memory_order_release);
} while (!root_crossover_solution_set_.load(std::memory_order_acquire) &&
- *get_root_concurrent_halt() == 0) {
+ *get_root_concurrent_halt() == 0 &&
+ !dual_simplex_root_done_.load(std::memory_order_acquire)) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
`#pragma` omp taskyield
}Also apply the same exit condition to the readiness loop at lines 2819-2823, so the GPU solve is skipped once dual simplex already won. As per path instructions: "Verify root-LP coordination for races, deadlocks, cancellation, stale phase state, and correct handling of infeasible/unbounded/degenerate results." 🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| // Wait until either the GPU root solve supplies a crossover point or CPU dual | ||
| // simplex finishes. If dual simplex wins, stop and join the GPU solve. | ||
| while (!root_crossover_solution_set_.load(std::memory_order_acquire) && | ||
| *get_root_concurrent_halt() == 0) { | ||
| std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| /* | ||
| * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #include <branch_and_bound/concurrent_root_solver.hpp> | ||
| #include <utilities/timer.hpp> | ||
|
|
||
| #include <mip_heuristics/problem/problem.cuh> | ||
| #include <pdlp/solve.cuh> | ||
|
|
||
| #include <raft/util/cudart_utils.hpp> | ||
|
|
||
| namespace cuopt::mathematical_optimization::mip { | ||
|
|
||
| template <typename i_t, typename f_t> | ||
| concurrent_root_solution_t<i_t, f_t> solve_concurrent_root_relaxation( | ||
| problem_t<i_t, f_t>* problem, | ||
| const pdlp_solver_settings_t<i_t, f_t>& settings, | ||
| f_t time_limit, | ||
| std::atomic<int>* concurrent_halt) | ||
| { | ||
| concurrent_root_solution_t<i_t, f_t> result; | ||
| auto root_settings = settings; | ||
| root_settings.time_limit = time_limit; | ||
| root_settings.concurrent_halt = concurrent_halt; | ||
|
|
||
| timer_t root_timer(time_limit); | ||
| auto lp_result = solve_lp_with_method<i_t, f_t>(*problem, root_settings, root_timer); | ||
| const auto status = lp_result.get_termination_status(); | ||
| result.usable = | ||
| status != pdlp_termination_status_t::NumericalError && | ||
| status != pdlp_termination_status_t::ConcurrentLimit && | ||
| lp_result.get_primal_solution().size() == static_cast<size_t>(problem->n_variables) && | ||
| lp_result.get_dual_solution().size() == static_cast<size_t>(problem->n_constraints); | ||
| result.optimal = status == pdlp_termination_status_t::Optimal; | ||
| if (!result.usable) { return result; } | ||
|
|
||
| auto& d_primal = lp_result.get_primal_solution(); | ||
| auto& d_dual = lp_result.get_dual_solution(); | ||
| auto& d_reduced_cost = lp_result.get_reduced_cost(); | ||
| result.primal.resize(d_primal.size()); | ||
| result.dual.resize(d_dual.size()); | ||
| result.reduced_cost.resize(d_reduced_cost.size()); | ||
| auto stream = problem->handle_ptr->get_stream(); | ||
| raft::copy(result.primal.data(), d_primal.data(), d_primal.size(), stream); | ||
| raft::copy(result.dual.data(), d_dual.data(), d_dual.size(), stream); | ||
| raft::copy(result.reduced_cost.data(), d_reduced_cost.data(), d_reduced_cost.size(), stream); | ||
| problem->handle_ptr->sync_stream(); | ||
|
|
||
| result.user_objective = lp_result.get_objective_value(); | ||
| result.solver_objective = problem->get_solver_obj_from_user_obj(result.user_objective); | ||
| result.iterations = lp_result.get_additional_termination_information().number_of_steps_taken; | ||
| result.method = lp_result.get_additional_termination_information().solved_by; | ||
| return result; | ||
| } | ||
|
|
||
| template concurrent_root_solution_t<int, double> solve_concurrent_root_relaxation( | ||
| problem_t<int, double>*, const pdlp_solver_settings_t<int, double>&, double, std::atomic<int>*); | ||
|
|
||
| } // namespace cuopt::mathematical_optimization::mip |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| /* | ||
| * SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| #pragma once | ||
|
|
||
| #include <cuopt/mathematical_optimization/pdlp/solver_settings.hpp> | ||
|
|
||
| #include <atomic> | ||
| #include <vector> | ||
|
|
||
| namespace cuopt::mathematical_optimization::mip { | ||
|
|
||
| template <typename i_t, typename f_t> | ||
| class problem_t; | ||
|
|
||
| template <typename i_t, typename f_t> | ||
| struct concurrent_root_solution_t { | ||
| bool usable{false}; | ||
| bool optimal{false}; | ||
| std::vector<f_t> primal; | ||
| std::vector<f_t> dual; | ||
| std::vector<f_t> reduced_cost; | ||
| f_t solver_objective{0}; | ||
| f_t user_objective{0}; | ||
| i_t iterations{0}; | ||
| method_t method{method_t::Unset}; | ||
| }; | ||
|
|
||
| template <typename i_t, typename f_t> | ||
| concurrent_root_solution_t<i_t, f_t> solve_concurrent_root_relaxation( | ||
| problem_t<i_t, f_t>* problem, | ||
| const pdlp_solver_settings_t<i_t, f_t>& settings, | ||
| f_t time_limit, | ||
| std::atomic<int>* concurrent_halt); | ||
|
|
||
| } // namespace cuopt::mathematical_optimization::mip |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: NVIDIA/cuopt
Length of output: 325
🏁 Script executed:
Repository: NVIDIA/cuopt
Length of output: 1355
🏁 Script executed:
Repository: NVIDIA/cuopt
Length of output: 634
Add the missing concurrent root solver files.
cpp/src/branch_and_bound/concurrent_root_solver.cuandcpp/src/branch_and_bound/concurrent_root_solver.hppare absent from bothHEADandorigin/main. The new CMake entry and include will fail unless this PR adds both files.🤖 Prompt for AI Agents
Source: Path instructions