From a1eb6b89f7da4511174783ed2cd724ddc9dcad37 Mon Sep 17 00:00:00 2001 From: nicolas Date: Tue, 20 Jan 2026 15:27:19 +0100 Subject: [PATCH 1/9] solved early termination in CMS750_4. fixed hard coded number of thread in cuopt_cli --- cpp/src/mip/diversity/diversity_manager.cu | 14 ++++++-------- cpp/src/mip/presolve/probing_cache.cu | 22 +++++++++++----------- cpp/src/mip/problem/problem.cu | 7 +++++++ cpp/src/mip/problem/problem.cuh | 1 + 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index cfe9876de..f3158316f 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -434,15 +434,13 @@ solution_t diversity_manager_t::run_solver() problem_ptr->handle_ptr->get_stream()); problem_ptr->handle_ptr->sync_stream(); - auto user_obj = problem_ptr->get_user_obj_from_solver_obj(lp_result.get_objective_value()); + // PDLP returns user-space objective (it applies objective_scaling_factor internally) + auto user_obj = lp_result.get_objective_value(); + auto solver_obj = problem_ptr->get_solver_obj_from_user_obj(user_obj); auto iterations = lp_result.get_additional_termination_information().number_of_steps_taken; - // Set for the B&B - problem_ptr->set_root_relaxation_solution_callback(host_primal, - host_dual, - host_reduced_costs, - lp_result.get_objective_value(), - user_obj, - iterations); + // Set for the B&B (param4 expects solver space, param5 expects user space) + problem_ptr->set_root_relaxation_solution_callback( + host_primal, host_dual, host_reduced_costs, solver_obj, user_obj, iterations); } // in case the pdlp returned var boudns that are out of bounds diff --git a/cpp/src/mip/presolve/probing_cache.cu b/cpp/src/mip/presolve/probing_cache.cu index fc2d974e3..0bc2e6733 100644 --- a/cpp/src/mip/presolve/probing_cache.cu +++ b/cpp/src/mip/presolve/probing_cache.cu @@ -856,17 +856,15 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, bound_presolve.settings.iteration_limit = 50; bound_presolve.settings.time_limit = timer.remaining_time(); - // Set the number of threads - const size_t max_threads = 8; - omp_set_num_threads(max_threads); + const int num_threads = 8; // Create a vector of multi_probe_t objects std::vector> multi_probe_presolve_pool; - std::vector>> modification_vector_pool(max_threads); - std::vector>> substitution_vector_pool(max_threads); + std::vector>> modification_vector_pool(num_threads); + std::vector>> substitution_vector_pool(num_threads); // Initialize multi_probe_presolve_pool - for (size_t i = 0; i < max_threads; i++) { + for (size_t i = 0; i < num_threads; i++) { multi_probe_presolve_pool.emplace_back(bound_presolve.context); multi_probe_presolve_pool[i].resize(problem); multi_probe_presolve_pool[i].compute_stats = true; @@ -879,12 +877,14 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, size_t last_it_implied_singletons = 0; bool early_exit = false; const size_t step_size = min((size_t)2048, priority_indices.size()); - for (size_t step_start = 0; step_start < priority_indices.size(); step_start += step_size) { - if (timer.check_time_limit() || early_exit || problem_is_infeasible.load()) { break; } - size_t step_end = std::min(step_start + step_size, priority_indices.size()); + // Main parallel loop -#pragma omp parallel - { +#pragma omp parallel num_threads(num_threads) + { + for (size_t step_start = 0; step_start < priority_indices.size(); step_start += step_size) { + if (timer.check_time_limit() || early_exit || problem_is_infeasible.load()) { break; } + size_t step_end = std::min(step_start + step_size, priority_indices.size()); + #pragma omp for schedule(static, 4) for (size_t i = step_start; i < step_end; ++i) { auto var_idx = priority_indices[i]; diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 9e9b74a2e..8feaee523 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -1988,6 +1988,13 @@ void problem_t::get_host_user_problem( : cuopt::linear_programming::dual_simplex::variable_type_t::INTEGER; } } + +template +f_t problem_t::get_solver_obj_from_user_obj(f_t user_obj) const +{ + return (user_obj / presolve_data.objective_scaling_factor) - presolve_data.objective_offset; +} + template f_t problem_t::get_user_obj_from_solver_obj(f_t solver_obj) const { diff --git a/cpp/src/mip/problem/problem.cuh b/cpp/src/mip/problem/problem.cuh index 9719f0b54..910079916 100644 --- a/cpp/src/mip/problem/problem.cuh +++ b/cpp/src/mip/problem/problem.cuh @@ -91,6 +91,7 @@ class problem_t { void post_process_solution(solution_t& solution); void compute_transpose_of_problem(); f_t get_user_obj_from_solver_obj(f_t solver_obj) const; + f_t get_solver_obj_from_user_obj(f_t user_obj) const; bool is_objective_integral() const { return objective_is_integral; } void compute_integer_fixed_problem(); void fill_integer_fixed_problem(rmm::device_uvector& assignment, From 115a0b17db2988352c9b44a32da705a881130dda Mon Sep 17 00:00:00 2001 From: nicolas Date: Wed, 21 Jan 2026 13:16:52 +0100 Subject: [PATCH 2/9] silenced logs from the concurrent mode when running inside MIP. ignored concurrent solution when the B&B is already running. moved number of threads in the probing cache to the setting struct. --- cpp/src/dual_simplex/branch_and_bound.hpp | 18 +++--- cpp/src/linear_programming/solve.cu | 73 +++++++++++++---------- cpp/src/mip/presolve/bounds_presolve.cuh | 5 +- cpp/src/mip/presolve/probing_cache.cu | 4 +- 4 files changed, 56 insertions(+), 44 deletions(-) diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index dac1ab393..c22d3ef28 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -84,14 +84,16 @@ class branch_and_bound_t { f_t user_objective, i_t iterations) { - root_crossover_soln_.x = primal; - root_crossover_soln_.y = dual; - root_crossover_soln_.z = reduced_costs; - root_objective_ = objective; - root_crossover_soln_.objective = objective; - root_crossover_soln_.user_objective = user_objective; - root_crossover_soln_.iterations = iterations; - root_crossover_solution_set_.store(true, std::memory_order_release); + if (!is_running) { + root_crossover_soln_.x = primal; + root_crossover_soln_.y = dual; + root_crossover_soln_.z = reduced_costs; + root_objective_ = objective; + root_crossover_soln_.objective = objective; + root_crossover_soln_.user_objective = user_objective; + root_crossover_soln_.iterations = iterations; + root_crossover_solution_set_.store(true, std::memory_order_release); + } } // Set a solution based on the user problem during the course of the solve diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index d038ade72..9982924ea 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -42,6 +42,9 @@ #include // For std::thread +#define CUOPT_LP_SOLVER_LOG_INFO(...) \ + if (!settings.inside_mip) { CUOPT_LOG_INFO(__VA_ARGS__); } + namespace cuopt::linear_programming { // This serves as both a warm up but also a mandatory initial call to setup cuSparse and cuBLAS @@ -417,7 +420,7 @@ run_barrier(dual_simplex::user_problem_t& user_problem, auto status = dual_simplex::solve_linear_program_with_barrier( user_problem, barrier_settings, solution); - CUOPT_LOG_INFO("Barrier finished in %.2f seconds", timer.elapsed_time()); + CUOPT_LP_SOLVER_LOG_INFO("Barrier finished in %.2f seconds", timer.elapsed_time()); if (settings.concurrent_halt != nullptr && (status == dual_simplex::lp_status_t::OPTIMAL || status == dual_simplex::lp_status_t::UNBOUNDED || @@ -489,9 +492,9 @@ run_dual_simplex(dual_simplex::user_problem_t& user_problem, auto status = dual_simplex::solve_linear_program(user_problem, dual_simplex_settings, solution); - CUOPT_LOG_INFO("Dual simplex finished in %.2f seconds, total time %.2f", - timer_dual_simplex.elapsed_time(), - timer.elapsed_time()); + CUOPT_LP_SOLVER_LOG_INFO("Dual simplex finished in %.2f seconds, total time %.2f", + timer_dual_simplex.elapsed_time(), + timer.elapsed_time()); if (settings.concurrent_halt != nullptr && (status == dual_simplex::lp_status_t::OPTIMAL || status == dual_simplex::lp_status_t::UNBOUNDED || @@ -530,7 +533,8 @@ static optimization_problem_solution_t run_pdlp_solver( bool is_batch_mode) { if (problem.n_constraints == 0) { - CUOPT_LOG_INFO("No constraints in the problem: PDLP can't be run, use Dual Simplex instead."); + CUOPT_LP_SOLVER_LOG_INFO( + "No constraints in the problem: PDLP can't be run, use Dual Simplex instead."); return optimization_problem_solution_t{pdlp_termination_status_t::NumericalError, problem.handle_ptr->get_stream()}; } @@ -551,14 +555,15 @@ optimization_problem_solution_t run_pdlp(detail::problem_t& auto sol = run_pdlp_solver(problem, settings, timer, is_batch_mode); auto pdlp_solve_time = timer_pdlp.elapsed_time(); sol.set_solve_time(timer.elapsed_time()); - CUOPT_LOG_INFO("PDLP finished"); + CUOPT_LP_SOLVER_LOG_INFO("PDLP finished"); if (sol.get_termination_status() != pdlp_termination_status_t::ConcurrentLimit) { - CUOPT_LOG_INFO("Status: %s Objective: %.8e Iterations: %d Time: %.3fs, Total time %.3fs", - sol.get_termination_status_string().c_str(), - sol.get_objective_value(), - sol.get_additional_termination_information().number_of_steps_taken, - pdlp_solve_time, - sol.get_solve_time()); + CUOPT_LP_SOLVER_LOG_INFO( + "Status: %s Objective: %.8e Iterations: %d Time: %.3fs, Total time %.3fs", + sol.get_termination_status_string().c_str(), + sol.get_objective_value(), + sol.get_additional_termination_information().number_of_steps_taken, + pdlp_solve_time, + sol.get_solve_time()); } const bool do_crossover = settings.crossover; @@ -620,12 +625,12 @@ optimization_problem_solution_t run_pdlp(detail::problem_t& info, termination_status); sol.copy_from(problem.handle_ptr, sol_crossover); - CUOPT_LOG_INFO("Crossover status %s", sol.get_termination_status_string().c_str()); + CUOPT_LP_SOLVER_LOG_INFO("Crossover status %s", sol.get_termination_status_string().c_str()); } if (settings.method == method_t::Concurrent && settings.concurrent_halt != nullptr && crossover_info == 0 && sol.get_termination_status() == pdlp_termination_status_t::Optimal) { // We finished. Tell dual simplex to stop if it is still running. - CUOPT_LOG_INFO("PDLP finished. Telling others to stop"); + CUOPT_LP_SOLVER_LOG_INFO("PDLP finished. Telling others to stop"); *settings.concurrent_halt = 1; } return sol; @@ -653,7 +658,7 @@ optimization_problem_solution_t run_concurrent( const timer_t& timer, bool is_batch_mode) { - CUOPT_LOG_INFO("Running concurrent\n"); + CUOPT_LP_SOLVER_LOG_INFO("Running concurrent\n"); timer_t timer_concurrent(timer.remaining_time()); // Copy the settings so that we can set the concurrent halt pointer @@ -668,7 +673,7 @@ optimization_problem_solution_t run_concurrent( if (settings.num_gpus > 1) { int device_count = raft::device_setter::get_device_count(); - CUOPT_LOG_INFO("Running PDLP and Barrier on %d GPUs", device_count); + CUOPT_LP_SOLVER_LOG_INFO("Running PDLP and Barrier on %d GPUs", device_count); cuopt_expects( device_count > 1, error_type_t::RuntimeError, "Multi-GPU mode requires at least 2 GPUs"); } @@ -752,41 +757,43 @@ optimization_problem_solution_t run_concurrent( 1); f_t end_time = timer.elapsed_time(); - CUOPT_LOG_INFO( + CUOPT_LP_SOLVER_LOG_INFO( "Concurrent time: %.3fs, total time %.3fs", timer_concurrent.elapsed_time(), end_time); // Check status to see if we should return the pdlp solution or the dual simplex solution if (!settings.inside_mip && (sol_dual_simplex.get_termination_status() == pdlp_termination_status_t::Optimal || sol_dual_simplex.get_termination_status() == pdlp_termination_status_t::PrimalInfeasible || sol_dual_simplex.get_termination_status() == pdlp_termination_status_t::DualInfeasible)) { - CUOPT_LOG_INFO("Solved with dual simplex"); + CUOPT_LP_SOLVER_LOG_INFO("Solved with dual simplex"); sol_pdlp.copy_from(problem.handle_ptr, sol_dual_simplex); sol_pdlp.set_solve_time(end_time); - CUOPT_LOG_INFO("Status: %s Objective: %.8e Iterations: %d Time: %.3fs", - sol_pdlp.get_termination_status_string().c_str(), - sol_pdlp.get_objective_value(), - sol_pdlp.get_additional_termination_information().number_of_steps_taken, - end_time); + CUOPT_LP_SOLVER_LOG_INFO( + "Status: %s Objective: %.8e Iterations: %d Time: %.3fs", + sol_pdlp.get_termination_status_string().c_str(), + sol_pdlp.get_objective_value(), + sol_pdlp.get_additional_termination_information().number_of_steps_taken, + end_time); return sol_pdlp; } else if (sol_barrier.get_termination_status() == pdlp_termination_status_t::Optimal) { - CUOPT_LOG_INFO("Solved with barrier"); + CUOPT_LP_SOLVER_LOG_INFO("Solved with barrier"); sol_pdlp.copy_from(problem.handle_ptr, sol_barrier); sol_pdlp.set_solve_time(end_time); - CUOPT_LOG_INFO("Status: %s Objective: %.8e Iterations: %d Time: %.3fs", - sol_pdlp.get_termination_status_string().c_str(), - sol_pdlp.get_objective_value(), - sol_pdlp.get_additional_termination_information().number_of_steps_taken, - end_time); + CUOPT_LP_SOLVER_LOG_INFO( + "Status: %s Objective: %.8e Iterations: %d Time: %.3fs", + sol_pdlp.get_termination_status_string().c_str(), + sol_pdlp.get_objective_value(), + sol_pdlp.get_additional_termination_information().number_of_steps_taken, + end_time); return sol_pdlp; } else if (sol_pdlp.get_termination_status() == pdlp_termination_status_t::Optimal) { - CUOPT_LOG_INFO("Solved with PDLP"); + CUOPT_LP_SOLVER_LOG_INFO("Solved with PDLP"); return sol_pdlp; } else if (!settings.inside_mip && sol_pdlp.get_termination_status() == pdlp_termination_status_t::ConcurrentLimit) { - CUOPT_LOG_INFO("Using dual simplex solve info"); + CUOPT_LP_SOLVER_LOG_INFO("Using dual simplex solve info"); return sol_dual_simplex; } else { - CUOPT_LOG_INFO("Using PDLP solve info"); + CUOPT_LP_SOLVER_LOG_INFO("Using PDLP solve info"); return sol_pdlp; } } diff --git a/cpp/src/mip/presolve/bounds_presolve.cuh b/cpp/src/mip/presolve/bounds_presolve.cuh index 54194b059..b82f442aa 100644 --- a/cpp/src/mip/presolve/bounds_presolve.cuh +++ b/cpp/src/mip/presolve/bounds_presolve.cuh @@ -1,6 +1,6 @@ /* clang-format off */ /* - * SPDX-FileCopyrightText: Copyright (c) 2022-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-FileCopyrightText: Copyright (c) 2022-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 */ /* clang-format on */ @@ -21,6 +21,8 @@ #include "bounds_update_data.cuh" #include "utils.cuh" +#include + namespace cuopt::linear_programming::detail { template @@ -32,6 +34,7 @@ class bound_presolve_t { struct settings_t { f_t time_limit{60.0}; i_t iteration_limit{std::numeric_limits::max()}; + i_t num_threads = std::max(omp_get_max_threads(), 1); bool parallel_bounds_update{true}; }; diff --git a/cpp/src/mip/presolve/probing_cache.cu b/cpp/src/mip/presolve/probing_cache.cu index 0bc2e6733..0c3b34dbb 100644 --- a/cpp/src/mip/presolve/probing_cache.cu +++ b/cpp/src/mip/presolve/probing_cache.cu @@ -856,7 +856,7 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, bound_presolve.settings.iteration_limit = 50; bound_presolve.settings.time_limit = timer.remaining_time(); - const int num_threads = 8; + const size_t num_threads = bound_presolve.settings.num_threads; // Create a vector of multi_probe_t objects std::vector> multi_probe_presolve_pool; @@ -885,7 +885,7 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, if (timer.check_time_limit() || early_exit || problem_is_infeasible.load()) { break; } size_t step_end = std::min(step_start + step_size, priority_indices.size()); -#pragma omp for schedule(static, 4) +#pragma omp for for (size_t i = step_start; i < step_end; ++i) { auto var_idx = priority_indices[i]; if (timer.check_time_limit()) { continue; } From a0a1d93678b2b77d2e88a28c4121d84141fc5b48 Mon Sep 17 00:00:00 2001 From: nicolas Date: Wed, 21 Jan 2026 14:34:02 +0100 Subject: [PATCH 3/9] better num thread initialization --- cpp/src/mip/presolve/bounds_presolve.cuh | 2 +- cpp/src/mip/presolve/probing_cache.cu | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cpp/src/mip/presolve/bounds_presolve.cuh b/cpp/src/mip/presolve/bounds_presolve.cuh index b82f442aa..88a6c740f 100644 --- a/cpp/src/mip/presolve/bounds_presolve.cuh +++ b/cpp/src/mip/presolve/bounds_presolve.cuh @@ -34,7 +34,7 @@ class bound_presolve_t { struct settings_t { f_t time_limit{60.0}; i_t iteration_limit{std::numeric_limits::max()}; - i_t num_threads = std::max(omp_get_max_threads(), 1); + i_t num_threads = -1; bool parallel_bounds_update{true}; }; diff --git a/cpp/src/mip/presolve/probing_cache.cu b/cpp/src/mip/presolve/probing_cache.cu index 0c3b34dbb..a6d41883d 100644 --- a/cpp/src/mip/presolve/probing_cache.cu +++ b/cpp/src/mip/presolve/probing_cache.cu @@ -856,7 +856,10 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, bound_presolve.settings.iteration_limit = 50; bound_presolve.settings.time_limit = timer.remaining_time(); - const size_t num_threads = bound_presolve.settings.num_threads; + size_t num_threads = bound_presolve.settings.num_threads < 0 + ? omp_get_max_threads() + : bound_presolve.settings.num_threads; + num_threads = std::max(num_threads, 1); // Create a vector of multi_probe_t objects std::vector> multi_probe_presolve_pool; From 9f0fe29db07aa5d7797585cbe567d32b466afd90 Mon Sep 17 00:00:00 2001 From: nicolas Date: Wed, 21 Jan 2026 14:38:08 +0100 Subject: [PATCH 4/9] fix compilation --- cpp/src/mip/presolve/probing_cache.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/presolve/probing_cache.cu b/cpp/src/mip/presolve/probing_cache.cu index a6d41883d..03b86940c 100644 --- a/cpp/src/mip/presolve/probing_cache.cu +++ b/cpp/src/mip/presolve/probing_cache.cu @@ -859,7 +859,7 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, size_t num_threads = bound_presolve.settings.num_threads < 0 ? omp_get_max_threads() : bound_presolve.settings.num_threads; - num_threads = std::max(num_threads, 1); + num_threads = std::max(num_threads, 1); // Create a vector of multi_probe_t objects std::vector> multi_probe_presolve_pool; From aa15d8ee956a04bd9a0a268cdf03c3bb8afdc875 Mon Sep 17 00:00:00 2001 From: nicolas Date: Thu, 22 Jan 2026 13:29:07 +0100 Subject: [PATCH 5/9] added additional information in the logs when solving the root relaxation in concurrent mode. --- cpp/src/dual_simplex/branch_and_bound.cpp | 28 ++++++++++++---------- cpp/src/dual_simplex/branch_and_bound.hpp | 14 +++++++++-- cpp/src/mip/diversity/diversity_manager.cu | 5 ++-- cpp/src/mip/problem/problem.cuh | 2 +- cpp/src/mip/solver.cu | 3 ++- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index b2c9f85d2..85c193599 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -1308,6 +1308,8 @@ lp_status_t branch_and_bound_t::solve_root_relaxation( } if (root_crossover_solution_set_.load(std::memory_order_acquire)) { + settings_.log.printf("\nRunning crossover\n\n"); + // Crush the root relaxation solution on converted user problem std::vector crushed_root_x; crush_primal_solution( @@ -1338,12 +1340,11 @@ lp_status_t branch_and_bound_t::solve_root_relaxation( root_crossover_soln_, crossover_vstatus_); - if (crossover_status == crossover_status_t::OPTIMAL) { - settings_.log.printf("Crossover status: %d\n", crossover_status); - } - // Check if crossover was stopped by dual simplex if (crossover_status == crossover_status_t::OPTIMAL) { + settings_.log.printf("\nCrossover found an optimal solution for the root relaxation\n\n"); + root_solver_type_ = root_solver_type_t::CROSSOVER; + set_root_concurrent_halt(1); // Stop dual simplex root_status = root_status_future.get(); // Override the root relaxation solution with the crossover solution @@ -1351,10 +1352,12 @@ lp_status_t branch_and_bound_t::solve_root_relaxation( root_vstatus_ = crossover_vstatus_; root_status = lp_status_t::OPTIMAL; } else { - root_status = root_status_future.get(); + root_status = root_status_future.get(); + root_solver_type_ = root_solver_type_t::DUAL_SIMPLEX; } } else { - root_status = root_status_future.get(); + root_status = root_status_future.get(); + root_solver_type_ = root_solver_type_t::DUAL_SIMPLEX; } return root_status; } @@ -1414,14 +1417,13 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut root_relax_soln_.resize(original_lp_.num_rows, original_lp_.num_cols); - settings_.log.printf("Solving LP root relaxation\n"); - lp_status_t root_status; simplex_solver_settings_t lp_settings = settings_; lp_settings.inside_mip = 1; lp_settings.concurrent_halt = get_root_concurrent_halt(); // RINS/SUBMIP path if (!enable_concurrent_lp_root_solve()) { + settings_.log.printf("\nSolving LP root relaxation with dual simplex\n"); root_status = solve_linear_program_advanced(original_lp_, exploration_stats_.start_time, lp_settings, @@ -1430,6 +1432,7 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut edge_norms_); } else { + settings_.log.printf("\nSolving LP root relaxation in concurrent mode\n"); root_status = solve_root_relaxation(lp_settings); } @@ -1540,10 +1543,11 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut original_lp_, log); - settings_.log.printf("Exploring the B&B tree using %d threads (best-first = %d, diving = %d)\n", - settings_.num_threads, - settings_.num_bfs_workers, - settings_.num_threads - settings_.num_bfs_workers); + settings_.log.printf( + "\nExploring the B&B tree using %d threads (best-first = %d, diving = %d)\n\n", + settings_.num_threads, + settings_.num_bfs_workers, + settings_.num_threads - settings_.num_bfs_workers); exploration_stats_.nodes_explored = 0; exploration_stats_.nodes_unexplored = 2; diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index c22d3ef28..857c95a21 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -70,6 +70,9 @@ struct bnb_stats_t { template class branch_and_bound_t { public: + // Specify which solver was used for solving the root LP relaxation + enum class root_solver_type_t { NONE = 0, CROSSOVER = 1, DUAL_SIMPLEX = 2 }; + branch_and_bound_t(const user_problem_t& user_problem, const simplex_solver_settings_t& solver_settings); @@ -82,9 +85,15 @@ class branch_and_bound_t { const std::vector& reduced_costs, f_t objective, f_t user_objective, - i_t iterations) + i_t iterations, + f_t solve_time) { - if (!is_running) { + if (root_solver_type_ == root_solver_type_t::NONE) { + settings_.log.printf( + "\nRoot relaxation solution found in %d iterations and %.2fs by PDLP/Barrier\n", + iterations, + solve_time); + settings_.log.printf("Root relaxation objective = %+.8e\n", user_objective); root_crossover_soln_.x = primal; root_crossover_soln_.y = dual; root_crossover_soln_.z = reduced_costs; @@ -164,6 +173,7 @@ class branch_and_bound_t { std::atomic root_crossover_solution_set_{false}; bool enable_concurrent_lp_root_solve_{false}; std::atomic root_concurrent_halt_{0}; + std::atomic root_solver_type_{root_solver_type_t::NONE}; // Pseudocosts pseudo_costs_t pc_; diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index f3158316f..c992d7395 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -411,7 +411,7 @@ solution_t diversity_manager_t::run_solver() // to bring variables within the bounds } - // Send PDLP relaxed solution to branch and bound before it solves the root node + // Send PDLP relaxed solution to branch and bound if (problem_ptr->set_root_relaxation_solution_callback != nullptr) { auto& d_primal_solution = lp_result.get_primal_solution(); auto& d_dual_solution = lp_result.get_dual_solution(); @@ -438,9 +438,10 @@ solution_t diversity_manager_t::run_solver() auto user_obj = lp_result.get_objective_value(); auto solver_obj = problem_ptr->get_solver_obj_from_user_obj(user_obj); auto iterations = lp_result.get_additional_termination_information().number_of_steps_taken; + auto solve_time = lp_result.get_additional_termination_information().solve_time; // Set for the B&B (param4 expects solver space, param5 expects user space) problem_ptr->set_root_relaxation_solution_callback( - host_primal, host_dual, host_reduced_costs, solver_obj, user_obj, iterations); + host_primal, host_dual, host_reduced_costs, solver_obj, user_obj, iterations, solve_time); } // in case the pdlp returned var boudns that are out of bounds diff --git a/cpp/src/mip/problem/problem.cuh b/cpp/src/mip/problem/problem.cuh index 910079916..9b552fc69 100644 --- a/cpp/src/mip/problem/problem.cuh +++ b/cpp/src/mip/problem/problem.cuh @@ -216,7 +216,7 @@ class problem_t { std::function&)> branch_and_bound_callback; std::function&, const std::vector&, const std::vector&, f_t, f_t, i_t)> + const std::vector&, const std::vector&, const std::vector&, f_t, f_t, i_t, f_t)> set_root_relaxation_solution_callback; typename mip_solver_settings_t::tolerances_t tolerances{}; diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index 08e1806b9..be84379b9 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -221,7 +221,8 @@ solution_t mip_solver_t::run_solver() std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, - std::placeholders::_6); + std::placeholders::_6, + std::placeholders::_7); // Fork a thread for branch and bound // std::async and std::future allow us to get the return value of bb::solve() From d604cb6b1fa9c124113d5b98cff1f30cc770cc0a Mon Sep 17 00:00:00 2001 From: nicolas Date: Thu, 22 Jan 2026 16:28:24 +0100 Subject: [PATCH 6/9] renamed macro --- cpp/src/linear_programming/solve.cu | 54 +++++++++++++++++------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index 9982924ea..829008651 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -42,8 +42,8 @@ #include // For std::thread -#define CUOPT_LP_SOLVER_LOG_INFO(...) \ - if (!settings.inside_mip) { CUOPT_LOG_INFO(__VA_ARGS__); } +#define CUOPT_LOG_CONDITIONAL_INFO(condition, ...) \ + if ((condition)) { CUOPT_LOG_INFO(__VA_ARGS__); } namespace cuopt::linear_programming { @@ -420,7 +420,8 @@ run_barrier(dual_simplex::user_problem_t& user_problem, auto status = dual_simplex::solve_linear_program_with_barrier( user_problem, barrier_settings, solution); - CUOPT_LP_SOLVER_LOG_INFO("Barrier finished in %.2f seconds", timer.elapsed_time()); + CUOPT_LOG_CONDITIONAL_INFO( + !settings.inside_mip, "Barrier finished in %.2f seconds", timer.elapsed_time()); if (settings.concurrent_halt != nullptr && (status == dual_simplex::lp_status_t::OPTIMAL || status == dual_simplex::lp_status_t::UNBOUNDED || @@ -492,9 +493,10 @@ run_dual_simplex(dual_simplex::user_problem_t& user_problem, auto status = dual_simplex::solve_linear_program(user_problem, dual_simplex_settings, solution); - CUOPT_LP_SOLVER_LOG_INFO("Dual simplex finished in %.2f seconds, total time %.2f", - timer_dual_simplex.elapsed_time(), - timer.elapsed_time()); + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, + "Dual simplex finished in %.2f seconds, total time %.2f", + timer_dual_simplex.elapsed_time(), + timer.elapsed_time()); if (settings.concurrent_halt != nullptr && (status == dual_simplex::lp_status_t::OPTIMAL || status == dual_simplex::lp_status_t::UNBOUNDED || @@ -533,7 +535,8 @@ static optimization_problem_solution_t run_pdlp_solver( bool is_batch_mode) { if (problem.n_constraints == 0) { - CUOPT_LP_SOLVER_LOG_INFO( + CUOPT_LOG_CONDITIONAL_INFO( + !settings.inside_mip, "No constraints in the problem: PDLP can't be run, use Dual Simplex instead."); return optimization_problem_solution_t{pdlp_termination_status_t::NumericalError, problem.handle_ptr->get_stream()}; @@ -555,9 +558,10 @@ optimization_problem_solution_t run_pdlp(detail::problem_t& auto sol = run_pdlp_solver(problem, settings, timer, is_batch_mode); auto pdlp_solve_time = timer_pdlp.elapsed_time(); sol.set_solve_time(timer.elapsed_time()); - CUOPT_LP_SOLVER_LOG_INFO("PDLP finished"); + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "PDLP finished"); if (sol.get_termination_status() != pdlp_termination_status_t::ConcurrentLimit) { - CUOPT_LP_SOLVER_LOG_INFO( + CUOPT_LOG_CONDITIONAL_INFO( + !settings.inside_mip, "Status: %s Objective: %.8e Iterations: %d Time: %.3fs, Total time %.3fs", sol.get_termination_status_string().c_str(), sol.get_objective_value(), @@ -625,12 +629,13 @@ optimization_problem_solution_t run_pdlp(detail::problem_t& info, termination_status); sol.copy_from(problem.handle_ptr, sol_crossover); - CUOPT_LP_SOLVER_LOG_INFO("Crossover status %s", sol.get_termination_status_string().c_str()); + CUOPT_LOG_CONDITIONAL_INFO( + !settings.inside_mip, "Crossover status %s", sol.get_termination_status_string().c_str()); } if (settings.method == method_t::Concurrent && settings.concurrent_halt != nullptr && crossover_info == 0 && sol.get_termination_status() == pdlp_termination_status_t::Optimal) { // We finished. Tell dual simplex to stop if it is still running. - CUOPT_LP_SOLVER_LOG_INFO("PDLP finished. Telling others to stop"); + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "PDLP finished. Telling others to stop"); *settings.concurrent_halt = 1; } return sol; @@ -658,7 +663,7 @@ optimization_problem_solution_t run_concurrent( const timer_t& timer, bool is_batch_mode) { - CUOPT_LP_SOLVER_LOG_INFO("Running concurrent\n"); + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "Running concurrent\n"); timer_t timer_concurrent(timer.remaining_time()); // Copy the settings so that we can set the concurrent halt pointer @@ -673,7 +678,8 @@ optimization_problem_solution_t run_concurrent( if (settings.num_gpus > 1) { int device_count = raft::device_setter::get_device_count(); - CUOPT_LP_SOLVER_LOG_INFO("Running PDLP and Barrier on %d GPUs", device_count); + CUOPT_LOG_CONDITIONAL_INFO( + !settings.inside_mip, "Running PDLP and Barrier on %d GPUs", device_count); cuopt_expects( device_count > 1, error_type_t::RuntimeError, "Multi-GPU mode requires at least 2 GPUs"); } @@ -757,17 +763,20 @@ optimization_problem_solution_t run_concurrent( 1); f_t end_time = timer.elapsed_time(); - CUOPT_LP_SOLVER_LOG_INFO( - "Concurrent time: %.3fs, total time %.3fs", timer_concurrent.elapsed_time(), end_time); + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, + "Concurrent time: %.3fs, total time %.3fs", + timer_concurrent.elapsed_time(), + end_time); // Check status to see if we should return the pdlp solution or the dual simplex solution if (!settings.inside_mip && (sol_dual_simplex.get_termination_status() == pdlp_termination_status_t::Optimal || sol_dual_simplex.get_termination_status() == pdlp_termination_status_t::PrimalInfeasible || sol_dual_simplex.get_termination_status() == pdlp_termination_status_t::DualInfeasible)) { - CUOPT_LP_SOLVER_LOG_INFO("Solved with dual simplex"); + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "Solved with dual simplex"); sol_pdlp.copy_from(problem.handle_ptr, sol_dual_simplex); sol_pdlp.set_solve_time(end_time); - CUOPT_LP_SOLVER_LOG_INFO( + CUOPT_LOG_CONDITIONAL_INFO( + !settings.inside_mip, "Status: %s Objective: %.8e Iterations: %d Time: %.3fs", sol_pdlp.get_termination_status_string().c_str(), sol_pdlp.get_objective_value(), @@ -775,10 +784,11 @@ optimization_problem_solution_t run_concurrent( end_time); return sol_pdlp; } else if (sol_barrier.get_termination_status() == pdlp_termination_status_t::Optimal) { - CUOPT_LP_SOLVER_LOG_INFO("Solved with barrier"); + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "Solved with barrier"); sol_pdlp.copy_from(problem.handle_ptr, sol_barrier); sol_pdlp.set_solve_time(end_time); - CUOPT_LP_SOLVER_LOG_INFO( + CUOPT_LOG_CONDITIONAL_INFO( + !settings.inside_mip, "Status: %s Objective: %.8e Iterations: %d Time: %.3fs", sol_pdlp.get_termination_status_string().c_str(), sol_pdlp.get_objective_value(), @@ -786,14 +796,14 @@ optimization_problem_solution_t run_concurrent( end_time); return sol_pdlp; } else if (sol_pdlp.get_termination_status() == pdlp_termination_status_t::Optimal) { - CUOPT_LP_SOLVER_LOG_INFO("Solved with PDLP"); + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "Solved with PDLP"); return sol_pdlp; } else if (!settings.inside_mip && sol_pdlp.get_termination_status() == pdlp_termination_status_t::ConcurrentLimit) { - CUOPT_LP_SOLVER_LOG_INFO("Using dual simplex solve info"); + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "Using dual simplex solve info"); return sol_dual_simplex; } else { - CUOPT_LP_SOLVER_LOG_INFO("Using PDLP solve info"); + CUOPT_LOG_CONDITIONAL_INFO(!settings.inside_mip, "Using PDLP solve info"); return sol_pdlp; } } From 628c22ba9a3f7d463e069b377a84d01c3380accc Mon Sep 17 00:00:00 2001 From: nicolas Date: Thu, 22 Jan 2026 17:44:09 +0100 Subject: [PATCH 7/9] changed the number of threads in probing cache --- cpp/src/mip/presolve/probing_cache.cu | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/src/mip/presolve/probing_cache.cu b/cpp/src/mip/presolve/probing_cache.cu index 03b86940c..ddc82e84e 100644 --- a/cpp/src/mip/presolve/probing_cache.cu +++ b/cpp/src/mip/presolve/probing_cache.cu @@ -857,9 +857,9 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, bound_presolve.settings.time_limit = timer.remaining_time(); size_t num_threads = bound_presolve.settings.num_threads < 0 - ? omp_get_max_threads() + ? 0.2 * omp_get_max_threads() : bound_presolve.settings.num_threads; - num_threads = std::max(num_threads, 1); + num_threads = std::clamp(num_threads, 1, 8); // Create a vector of multi_probe_t objects std::vector> multi_probe_presolve_pool; From 6956bbc968e44db7b40abe82d92b1158bb75a4aa Mon Sep 17 00:00:00 2001 From: nicolas Date: Thu, 22 Jan 2026 17:57:30 +0100 Subject: [PATCH 8/9] fix type --- cpp/src/mip/presolve/probing_cache.cu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/src/mip/presolve/probing_cache.cu b/cpp/src/mip/presolve/probing_cache.cu index ddc82e84e..c0c9f2f0a 100644 --- a/cpp/src/mip/presolve/probing_cache.cu +++ b/cpp/src/mip/presolve/probing_cache.cu @@ -859,7 +859,7 @@ bool compute_probing_cache(bound_presolve_t& bound_presolve, size_t num_threads = bound_presolve.settings.num_threads < 0 ? 0.2 * omp_get_max_threads() : bound_presolve.settings.num_threads; - num_threads = std::clamp(num_threads, 1, 8); + num_threads = std::clamp(num_threads, 1, 8); // Create a vector of multi_probe_t objects std::vector> multi_probe_presolve_pool; From 6bdd587155232bd058a582ae78b90672fc5a89e9 Mon Sep 17 00:00:00 2001 From: nicolas Date: Fri, 23 Jan 2026 10:39:48 +0100 Subject: [PATCH 9/9] changed the logs for the root relaxation --- cpp/src/dual_simplex/branch_and_bound.cpp | 46 +++++++++++++++------- cpp/src/dual_simplex/branch_and_bound.hpp | 15 ++----- cpp/src/dual_simplex/phase2.cpp | 6 --- cpp/src/mip/diversity/diversity_manager.cu | 3 +- cpp/src/mip/problem/problem.cuh | 2 +- cpp/src/mip/solver.cu | 3 +- 6 files changed, 38 insertions(+), 37 deletions(-) diff --git a/cpp/src/dual_simplex/branch_and_bound.cpp b/cpp/src/dual_simplex/branch_and_bound.cpp index 85c193599..fd6c0644a 100644 --- a/cpp/src/dual_simplex/branch_and_bound.cpp +++ b/cpp/src/dual_simplex/branch_and_bound.cpp @@ -1288,6 +1288,11 @@ template lp_status_t branch_and_bound_t::solve_root_relaxation( simplex_solver_settings_t const& lp_settings) { + f_t start_time = tic(); + f_t user_objective = 0; + i_t iter = 0; + std::string solver_name = ""; + // Root node path lp_status_t root_status; std::future root_status_future; @@ -1308,8 +1313,6 @@ lp_status_t branch_and_bound_t::solve_root_relaxation( } if (root_crossover_solution_set_.load(std::memory_order_acquire)) { - settings_.log.printf("\nRunning crossover\n\n"); - // Crush the root relaxation solution on converted user problem std::vector crushed_root_x; crush_primal_solution( @@ -1342,23 +1345,39 @@ lp_status_t branch_and_bound_t::solve_root_relaxation( // Check if crossover was stopped by dual simplex if (crossover_status == crossover_status_t::OPTIMAL) { - settings_.log.printf("\nCrossover found an optimal solution for the root relaxation\n\n"); - root_solver_type_ = root_solver_type_t::CROSSOVER; - set_root_concurrent_halt(1); // Stop dual simplex root_status = root_status_future.get(); + // Override the root relaxation solution with the crossover solution root_relax_soln_ = root_crossover_soln_; root_vstatus_ = crossover_vstatus_; root_status = lp_status_t::OPTIMAL; + user_objective = root_crossover_soln_.user_objective; + iter = root_crossover_soln_.iterations; + solver_name = "Barrier/PDLP and Crossover"; + } else { - root_status = root_status_future.get(); - root_solver_type_ = root_solver_type_t::DUAL_SIMPLEX; + root_status = root_status_future.get(); + user_objective = root_relax_soln_.user_objective; + iter = root_relax_soln_.iterations; + solver_name = "Dual Simplex"; } } else { - root_status = root_status_future.get(); - root_solver_type_ = root_solver_type_t::DUAL_SIMPLEX; + root_status = root_status_future.get(); + user_objective = root_relax_soln_.user_objective; + iter = root_relax_soln_.iterations; + solver_name = "Dual Simplex"; } + + settings_.log.printf("\n"); + settings_.log.printf("Root relaxation solution found in %d iterations and %.2fs by %s\n", + iter, + toc(start_time), + solver_name.c_str()); + settings_.log.printf("Root relaxation objective %+.8e\n", user_objective); + settings_.log.printf("\n"); + + is_root_solution_set = true; return root_status; } @@ -1543,11 +1562,10 @@ mip_status_t branch_and_bound_t::solve(mip_solution_t& solut original_lp_, log); - settings_.log.printf( - "\nExploring the B&B tree using %d threads (best-first = %d, diving = %d)\n\n", - settings_.num_threads, - settings_.num_bfs_workers, - settings_.num_threads - settings_.num_bfs_workers); + settings_.log.printf("Exploring the B&B tree using %d threads (best-first = %d, diving = %d)\n\n", + settings_.num_threads, + settings_.num_bfs_workers, + settings_.num_threads - settings_.num_bfs_workers); exploration_stats_.nodes_explored = 0; exploration_stats_.nodes_unexplored = 2; diff --git a/cpp/src/dual_simplex/branch_and_bound.hpp b/cpp/src/dual_simplex/branch_and_bound.hpp index 857c95a21..327f99bc4 100644 --- a/cpp/src/dual_simplex/branch_and_bound.hpp +++ b/cpp/src/dual_simplex/branch_and_bound.hpp @@ -70,9 +70,6 @@ struct bnb_stats_t { template class branch_and_bound_t { public: - // Specify which solver was used for solving the root LP relaxation - enum class root_solver_type_t { NONE = 0, CROSSOVER = 1, DUAL_SIMPLEX = 2 }; - branch_and_bound_t(const user_problem_t& user_problem, const simplex_solver_settings_t& solver_settings); @@ -85,15 +82,9 @@ class branch_and_bound_t { const std::vector& reduced_costs, f_t objective, f_t user_objective, - i_t iterations, - f_t solve_time) + i_t iterations) { - if (root_solver_type_ == root_solver_type_t::NONE) { - settings_.log.printf( - "\nRoot relaxation solution found in %d iterations and %.2fs by PDLP/Barrier\n", - iterations, - solve_time); - settings_.log.printf("Root relaxation objective = %+.8e\n", user_objective); + if (!is_root_solution_set) { root_crossover_soln_.x = primal; root_crossover_soln_.y = dual; root_crossover_soln_.z = reduced_costs; @@ -173,7 +164,7 @@ class branch_and_bound_t { std::atomic root_crossover_solution_set_{false}; bool enable_concurrent_lp_root_solve_{false}; std::atomic root_concurrent_halt_{0}; - std::atomic root_solver_type_{root_solver_type_t::NONE}; + bool is_root_solution_set{false}; // Pseudocosts pseudo_costs_t pc_; diff --git a/cpp/src/dual_simplex/phase2.cpp b/cpp/src/dual_simplex/phase2.cpp index 2bc00f636..a54101ec8 100644 --- a/cpp/src/dual_simplex/phase2.cpp +++ b/cpp/src/dual_simplex/phase2.cpp @@ -2077,12 +2077,6 @@ void prepare_optimality(const lp_problem_t& lp, settings.log.printf("Primal infeasibility (abs): %.2e\n", primal_infeas); settings.log.printf("Dual infeasibility (abs): %.2e\n", dual_infeas); settings.log.printf("Perturbation: %.2e\n", perturbation); - } else { - settings.log.printf("\n"); - settings.log.printf( - "Root relaxation solution found in %d iterations and %.2fs\n", iter, toc(start_time)); - settings.log.printf("Root relaxation objective %+.8e\n", sol.user_objective); - settings.log.printf("\n"); } } } diff --git a/cpp/src/mip/diversity/diversity_manager.cu b/cpp/src/mip/diversity/diversity_manager.cu index c992d7395..dcca0b1cc 100644 --- a/cpp/src/mip/diversity/diversity_manager.cu +++ b/cpp/src/mip/diversity/diversity_manager.cu @@ -438,10 +438,9 @@ solution_t diversity_manager_t::run_solver() auto user_obj = lp_result.get_objective_value(); auto solver_obj = problem_ptr->get_solver_obj_from_user_obj(user_obj); auto iterations = lp_result.get_additional_termination_information().number_of_steps_taken; - auto solve_time = lp_result.get_additional_termination_information().solve_time; // Set for the B&B (param4 expects solver space, param5 expects user space) problem_ptr->set_root_relaxation_solution_callback( - host_primal, host_dual, host_reduced_costs, solver_obj, user_obj, iterations, solve_time); + host_primal, host_dual, host_reduced_costs, solver_obj, user_obj, iterations); } // in case the pdlp returned var boudns that are out of bounds diff --git a/cpp/src/mip/problem/problem.cuh b/cpp/src/mip/problem/problem.cuh index 9b552fc69..910079916 100644 --- a/cpp/src/mip/problem/problem.cuh +++ b/cpp/src/mip/problem/problem.cuh @@ -216,7 +216,7 @@ class problem_t { std::function&)> branch_and_bound_callback; std::function&, const std::vector&, const std::vector&, f_t, f_t, i_t, f_t)> + const std::vector&, const std::vector&, const std::vector&, f_t, f_t, i_t)> set_root_relaxation_solution_callback; typename mip_solver_settings_t::tolerances_t tolerances{}; diff --git a/cpp/src/mip/solver.cu b/cpp/src/mip/solver.cu index be84379b9..08e1806b9 100644 --- a/cpp/src/mip/solver.cu +++ b/cpp/src/mip/solver.cu @@ -221,8 +221,7 @@ solution_t mip_solver_t::run_solver() std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, - std::placeholders::_6, - std::placeholders::_7); + std::placeholders::_6); // Fork a thread for branch and bound // std::async and std::future allow us to get the return value of bb::solve()