From 18769369742d71c919f330beaf1439ebe176366d Mon Sep 17 00:00:00 2001 From: Christopher Maes Date: Tue, 20 May 2025 15:57:22 -0700 Subject: [PATCH] Fix bug in crossover where numerical was returned instead of time limit Also adds the --relaxation flag to cuopt_cli to solve the LP relaxation of a MIP --- cpp/cuopt_cli.cpp | 11 +++++++++-- cpp/src/dual_simplex/crossover.cpp | 20 ++++++++++++++++++-- cpp/src/linear_programming/solve.cu | 1 + 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/cpp/cuopt_cli.cpp b/cpp/cuopt_cli.cpp index afb59d58b4..a81804a70a 100644 --- a/cpp/cuopt_cli.cpp +++ b/cpp/cuopt_cli.cpp @@ -81,6 +81,7 @@ inline auto make_async() { return std::make_shared& settings_strings) { const raft::handle_t handle_{}; @@ -131,7 +132,7 @@ int run_single_file(const std::string& file_path, initial_solution_file, mps_data_model.get_variable_names()); try { - if (is_mip) { + if (is_mip && !solve_relaxation) { auto& mip_settings = settings.get_mip_settings(); if (initial_solution.size() > 0) { mip_settings.set_initial_solution(initial_solution.data(), initial_solution.size()); @@ -191,6 +192,11 @@ int main(int argc, char* argv[]) .help("path to the initial solution .sol file") .default_value(""); + program.add_argument("--relaxation") + .help("solve the LP relaxation of the MIP") + .default_value(false) + .implicit_value(true); + std::map arg_name_to_param_name; { // Add all solver settings as arguments @@ -257,8 +263,9 @@ int main(int argc, char* argv[]) std::string file_name = program.get("filename"); const auto initial_solution_file = program.get("--initial-solution"); + const auto solve_relaxation = program.get("--relaxation"); auto memory_resource = make_async(); rmm::mr::set_current_device_resource(memory_resource.get()); - return run_single_file(file_name, initial_solution_file, settings_strings); + return run_single_file(file_name, initial_solution_file, solve_relaxation, settings_strings); } diff --git a/cpp/src/dual_simplex/crossover.cpp b/cpp/src/dual_simplex/crossover.cpp index 37a67fc385..957b8d2ca5 100644 --- a/cpp/src/dual_simplex/crossover.cpp +++ b/cpp/src/dual_simplex/crossover.cpp @@ -1217,6 +1217,15 @@ crossover_status_t crossover(const lp_problem_t& lp, std::vector edge_norms; dual::status_t status = dual_phase2(2, 0, start_time, lp, settings, vstatus, solution, dual_iter, edge_norms); + if (toc(start_time) > settings.time_limit) { + settings.log.printf("Time limit exceeded\n"); + return crossover_status_t::TIME_LIMIT; + } + if (settings.concurrent_halt != nullptr && + settings.concurrent_halt->load(std::memory_order_acquire) == 1) { + settings.log.printf("Concurrent halt\n"); + return crossover_status_t::CONCURRENT_LIMIT; + } primal_infeas = primal_infeasibility(lp, settings, vstatus, solution.x); dual_infeas = dual_infeasibility(lp, settings, vstatus, solution.z); primal_res = primal_residual(lp, solution); @@ -1337,6 +1346,15 @@ crossover_status_t crossover(const lp_problem_t& lp, std::vector edge_norms; dual::status_t status = dual_phase2( 2, iter == 0 ? 1 : 0, start_time, lp, settings, vstatus, solution, iter, edge_norms); + if (toc(start_time) > settings.time_limit) { + settings.log.printf("Time limit exceeded\n"); + return crossover_status_t::TIME_LIMIT; + } + if (settings.concurrent_halt != nullptr && + settings.concurrent_halt->load(std::memory_order_acquire) == 1) { + settings.log.printf("Concurrent halt\n"); + return crossover_status_t::CONCURRENT_LIMIT; + } solution.iterations += iter; primal_infeas = primal_infeasibility(lp, settings, vstatus, solution.x); dual_infeas = dual_infeasibility(lp, settings, vstatus, solution.z); @@ -1345,7 +1363,6 @@ crossover_status_t crossover(const lp_problem_t& lp, if (status != dual::status_t::OPTIMAL) { print_crossover_info(lp, settings, vstatus, solution, "Dual phase 2 complete"); } - primal_feasible = primal_infeas <= primal_tol && primal_res <= primal_tol; dual_feasible = dual_infeas <= dual_tol && dual_res <= dual_tol; } else { @@ -1355,7 +1372,6 @@ crossover_status_t crossover(const lp_problem_t& lp, settings.log.printf("Crossover time %.2f seconds\n", toc(crossover_start)); settings.log.printf("Total time %.2f seconds\n", toc(start_time)); - settings.log.printf("\n"); crossover_status_t status = crossover_status_t::NUMERICAL_ISSUES; if (dual_feasible) { status = crossover_status_t::DUAL_FEASIBLE; } diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index 8cca939158..0ab3d848df 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -396,6 +396,7 @@ 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()); } if (crossover_info == 0 && settings.concurrent_halt != nullptr) { // We finished. Tell dual simplex to stop if it is still running.