diff --git a/cpp/src/linear_programming/solve.cu b/cpp/src/linear_programming/solve.cu index ed2a30bcf..c3ba4f2c7 100644 --- a/cpp/src/linear_programming/solve.cu +++ b/cpp/src/linear_programming/solve.cu @@ -346,16 +346,30 @@ optimization_problem_solution_t run_dual_simplex( std::get<4>(sol_dual_simplex)); } +template +static optimization_problem_solution_t run_pdlp_solver( + detail::problem_t& problem, + pdlp_solver_settings_t const& settings, + const std::chrono::high_resolution_clock::time_point& start_time) +{ + if (problem.n_constraints == 0) { + CUOPT_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()}; + } + detail::pdlp_solver_t solver(problem, settings); + return solver.run_solver(start_time); +} + template optimization_problem_solution_t run_pdlp(detail::problem_t& problem, pdlp_solver_settings_t const& settings) { auto start_solver = std::chrono::high_resolution_clock::now(); f_t start_time = dual_simplex::tic(); - detail::pdlp_solver_t solver(problem, settings); - auto sol = solver.run_solver(start_solver); // Passing it for time limit - auto end = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast(end - start_solver); + auto sol = run_pdlp_solver(problem, settings, start_solver); + auto end = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(end - start_solver); sol.set_solve_time(duration.count() / 1000.0); CUOPT_LOG_INFO("PDLP finished"); if (sol.get_termination_status() != pdlp_termination_status_t::ConcurrentLimit) { diff --git a/cpp/src/mip/problem/problem.cu b/cpp/src/mip/problem/problem.cu index 5b8511b92..e036696de 100644 --- a/cpp/src/mip/problem/problem.cu +++ b/cpp/src/mip/problem/problem.cu @@ -53,23 +53,7 @@ template void problem_t::op_problem_cstr_body(const optimization_problem_t& problem_) { // Mark the problem as empty if the op_problem has an empty matrix. - if (problem_.get_constraint_matrix_values().is_empty()) { - cuopt_assert(problem_.get_constraint_matrix_indices().is_empty(), - "Problem is empty but constraint matrix indices are not empty."); - cuopt_assert(problem_.get_constraint_matrix_offsets().size() == 1, - "Problem is empty but constraint matrix offsets are not empty."); - cuopt_assert(problem_.get_objective_coefficients().is_empty(), - "Problem is empty but objective coefficients are not empty."); - cuopt_assert(problem_.get_variable_lower_bounds().is_empty(), - "Problem is empty but variable lower bounds are not empty."); - cuopt_assert(problem_.get_variable_upper_bounds().is_empty(), - "Problem is empty but variable upper bounds are not empty."); - cuopt_assert(problem_.get_constraint_lower_bounds().is_empty(), - "Problem is empty but constraint lower bounds are not empty."); - cuopt_assert(problem_.get_constraint_upper_bounds().is_empty(), - "Problem is empty but constraint upper bounds are not empty."); - empty = true; - } + if (problem_.get_constraint_matrix_values().is_empty()) { empty = true; } // Set variables bounds to default if not set and constraints bounds if user has set a row type set_bounds_if_not_set(*this); diff --git a/cpp/tests/linear_programming/pdlp_test.cu b/cpp/tests/linear_programming/pdlp_test.cu index 64908261c..212e4e1d3 100644 --- a/cpp/tests/linear_programming/pdlp_test.cu +++ b/cpp/tests/linear_programming/pdlp_test.cu @@ -871,6 +871,41 @@ TEST(dual_simplex, afiro) afiro_primal_objective, solution.get_additional_termination_information().primal_objective)); } +// Should return a numerical error +TEST(pdlp_class, run_empty_matrix_pdlp) +{ + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/empty_matrix.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path); + + auto solver_settings = pdlp_solver_settings_t{}; + solver_settings.method = cuopt::linear_programming::method_t::PDLP; + + optimization_problem_solution_t solution = + solve_lp(&handle_, op_problem, solver_settings); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_NUMERICAL_ERROR); +} + +// Should run thanks to Dual Simplex +TEST(pdlp_class, run_empty_matrix_dual_simplex) +{ + const raft::handle_t handle_{}; + + auto path = make_path_absolute("linear_programming/empty_matrix.mps"); + cuopt::mps_parser::mps_data_model_t op_problem = + cuopt::mps_parser::parse_mps(path); + + auto solver_settings = pdlp_solver_settings_t{}; + solver_settings.method = cuopt::linear_programming::method_t::Concurrent; + + optimization_problem_solution_t solution = + solve_lp(&handle_, op_problem, solver_settings); + EXPECT_EQ((int)solution.get_termination_status(), CUOPT_TERIMINATION_STATUS_OPTIMAL); + EXPECT_FALSE(solution.get_additional_termination_information().solved_by_pdlp); +} + } // namespace cuopt::linear_programming::test CUOPT_TEST_PROGRAM_MAIN() diff --git a/datasets/linear_programming/empty_matrix.mps b/datasets/linear_programming/empty_matrix.mps new file mode 100644 index 000000000..e387ee780 --- /dev/null +++ b/datasets/linear_programming/empty_matrix.mps @@ -0,0 +1,11 @@ +NAME +ROWS + N OBJ +COLUMNS + x1 OBJ -3 +RHS +RANGES +BOUNDS + MI bounds x1 + UP bounds x1 2 +ENDATA